{"name":"attachment","title":"Attachment","description":"Displays a file or image attachment with media, metadata, upload state, and actions.","installCommand":"bunx marko-ui add attachment -y","usageTags":"<Attachment>, <AttachmentMedia>, <AttachmentContent>, <AttachmentTitle>, <AttachmentDescription>, <AttachmentActions>, <AttachmentAction>, <AttachmentTrigger>, <AttachmentGroup>","importSnippet":"import Attachment from \"@/components/ui/attachment/attachment.marko\";\nimport AttachmentMedia from \"@/components/ui/attachment/media.marko\";\nimport AttachmentContent from \"@/components/ui/attachment/content.marko\";\nimport AttachmentTitle from \"@/components/ui/attachment/title.marko\";\nimport AttachmentDescription from \"@/components/ui/attachment/description.marko\";\nimport AttachmentActions from \"@/components/ui/attachment/actions.marko\";\nimport AttachmentAction from \"@/components/ui/attachment/action.marko\";","usageSnippet":"<Attachment>\n  <AttachmentMedia>\n    <Icon name=\"FileTextIcon\"/>\n  </AttachmentMedia>\n  <AttachmentContent>\n    <AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>\n    <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>\n  </AttachmentContent>\n  <AttachmentActions>\n    <AttachmentAction aria-label=\"Remove sales-dashboard.pdf\">\n      <Icon name=\"X\"/>\n    </AttachmentAction>\n  </AttachmentActions>\n</Attachment>","tags":["Attachment","AttachmentAction","AttachmentActions","AttachmentContent","AttachmentDescription","AttachmentGroup","AttachmentMedia","AttachmentTitle","AttachmentTrigger"],"parts":[],"props":[{"name":"orientation","type":"'horizontal' | 'vertical' | null | undefined","required":false},{"name":"size","type":"'default' | 'sm' | 'xs' | null | undefined","required":false},{"name":"state","type":"'idle' | 'uploading' | 'processing' | 'error' | 'done'","required":false}],"events":[],"keyboard":[],"accessibilityNotes":["`AttachmentAction` is usually icon-only, so give each one an `aria-label` describing the action and its target, e.g. `<AttachmentAction aria-label=\"Remove sales-dashboard.pdf\">`.","`AttachmentTrigger` covers the card with no text of its own, so give it an `aria-label` describing what activating it does. It renders a real `<button>` (`packages/shadcn/ui/attachment/trigger.marko`) positioned `absolute inset-0` behind `AttachmentActions` in the stacking order, so the trigger and any action stay separately focusable and clickable — neither traps the other.","`AttachmentTrigger` accepts an `asChild` prop for API parity with upstream, but this port has no dynamic-tag/props-merging primitive to back it — `asChild` is a no-op here and the trigger always renders its own `<button>`, unlike upstream's `render`-prop version which can become an `<a>` or open a dialog trigger.","An `AttachmentGroup` scrolls horizontally. When its attachments are interactive (a trigger or actions), keyboard users reach off-screen items by tabbing to them. For a row of purely presentational attachments, make the group itself focusable and scrollable with `tabIndex={0}`, `role=\"group\"`, and an `aria-label`.","The `error` state applies a destructive color treatment. Keep the failure reason in `AttachmentDescription` so the state isn't conveyed by color alone."],"examples":[{"id":"attachment-demo","title":"Basic","source":"import Attachment from \"@marko-ui/shadcn/ui/attachment/attachment.marko\";\nimport AttachmentGroup from \"@marko-ui/shadcn/ui/attachment/group.marko\";\nimport AttachmentMedia from \"@marko-ui/shadcn/ui/attachment/media.marko\";\nimport AttachmentContent from \"@marko-ui/shadcn/ui/attachment/content.marko\";\nimport AttachmentTitle from \"@marko-ui/shadcn/ui/attachment/title.marko\";\nimport AttachmentDescription from \"@marko-ui/shadcn/ui/attachment/description.marko\";\nimport AttachmentActions from \"@marko-ui/shadcn/ui/attachment/actions.marko\";\nimport AttachmentAction from \"@marko-ui/shadcn/ui/attachment/action.marko\";\nimport Spinner from \"@marko-ui/shadcn/ui/spinner/spinner.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<const/images=[\n  {\n    name: \"workspace.png\",\n    meta: \"PNG · 820 KB\",\n    src: \"https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80\",\n    alt: \"Workspace\",\n  },\n  {\n    name: \"desk-reference.jpg\",\n    meta: \"JPG · 1.1 MB\",\n    src: \"https://images.unsplash.com/photo-1497215728101-856f4ea42174?w=900&auto=format&fit=crop&q=80\",\n    alt: \"Desk\",\n  },\n  {\n    name: \"office-reference.jpg\",\n    meta: \"JPG · 940 KB\",\n    src: \"https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=900&auto=format&fit=crop&q=80\",\n    alt: \"Office\",\n  },\n]/>\n\n<div class=\"mx-auto flex w-full max-w-sm flex-col gap-3 py-12\">\n  <AttachmentGroup>\n    <for|image| of=images by=(image) => image.name>\n      <Attachment orientation=\"vertical\">\n        <AttachmentMedia variant=\"image\">\n          <img src=image.src alt=image.alt/>\n        </AttachmentMedia>\n        <AttachmentContent>\n          <AttachmentTitle>${image.name}</AttachmentTitle>\n          <AttachmentDescription>${image.meta}</AttachmentDescription>\n        </AttachmentContent>\n      </Attachment>\n    </for>\n  </AttachmentGroup>\n  <Attachment state=\"uploading\" class=\"w-full\">\n    <AttachmentMedia>\n      <Spinner/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>sales-dashboard.pdf</AttachmentTitle>\n      <AttachmentDescription>Uploading · 64%</AttachmentDescription>\n    </AttachmentContent>\n    <AttachmentActions>\n      <AttachmentAction aria-label=\"Cancel upload\">\n        <Icon name=\"X\"/>\n      </AttachmentAction>\n    </AttachmentActions>\n  </Attachment>\n  <Attachment class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"FileCodeIcon\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>message-renderer.tsx</AttachmentTitle>\n      <AttachmentDescription>TypeScript · 12 KB</AttachmentDescription>\n    </AttachmentContent>\n    <AttachmentActions>\n      <AttachmentAction aria-label=\"Remove message-renderer.tsx\">\n        <Icon name=\"X\"/>\n      </AttachmentAction>\n    </AttachmentActions>\n  </Attachment>\n</div>","description":"An `AttachmentGroup` of images above a card mid-upload and a finished file attachment."},{"id":"attachment-image","title":"Image","source":"import Attachment from \"@marko-ui/shadcn/ui/attachment/attachment.marko\";\nimport AttachmentGroup from \"@marko-ui/shadcn/ui/attachment/group.marko\";\nimport AttachmentMedia from \"@marko-ui/shadcn/ui/attachment/media.marko\";\nimport AttachmentContent from \"@marko-ui/shadcn/ui/attachment/content.marko\";\nimport AttachmentTitle from \"@marko-ui/shadcn/ui/attachment/title.marko\";\nimport AttachmentDescription from \"@marko-ui/shadcn/ui/attachment/description.marko\";\nimport AttachmentActions from \"@marko-ui/shadcn/ui/attachment/actions.marko\";\nimport AttachmentAction from \"@marko-ui/shadcn/ui/attachment/action.marko\";\nimport AttachmentTrigger from \"@marko-ui/shadcn/ui/attachment/trigger.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<const/images=[\n  {\n    name: \"workspace.png\",\n    meta: \"PNG · 820 KB\",\n    src: \"https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80\",\n    alt: \"Workspace\",\n  },\n  {\n    name: \"desk-reference.jpg\",\n    meta: \"JPG · 1.1 MB\",\n    src: \"https://images.unsplash.com/photo-1497215728101-856f4ea42174?w=900&auto=format&fit=crop&q=80\",\n    alt: \"Desk\",\n  },\n  {\n    name: \"office-reference.jpg\",\n    meta: \"JPG · 940 KB\",\n    src: \"https://images.unsplash.com/photo-1497366811353-6870744d04b2?w=900&auto=format&fit=crop&q=80\",\n    alt: \"Office\",\n  },\n]/>\n\n<div class=\"mx-auto w-full max-w-sm py-12\">\n  <AttachmentGroup class=\"w-full\">\n    <for|image| of=images by=(image) => image.name>\n      <Attachment orientation=\"vertical\">\n        <AttachmentMedia variant=\"image\">\n          <img src=image.src alt=image.alt/>\n        </AttachmentMedia>\n        <AttachmentContent>\n          <AttachmentTitle>${image.name}</AttachmentTitle>\n          <AttachmentDescription>${image.meta}</AttachmentDescription>\n        </AttachmentContent>\n        <AttachmentActions>\n          <AttachmentAction aria-label=`Remove ${image.name}`>\n            <Icon name=\"X\"/>\n          </AttachmentAction>\n        </AttachmentActions>\n\n        <a\n          data-slot=\"attachment-trigger\"\n          href=image.src\n          target=\"_blank\"\n          rel=\"noreferrer\"\n          aria-label=`Open ${image.name}`\n          class=\"mu-attachment-trigger absolute inset-0 z-10 outline-none\"\n        />\n      </Attachment>\n    </for>\n  </AttachmentGroup>\n</div>","description":"Set `variant=\"image\"` on `AttachmentMedia` and render an `<img>` inside it, with `orientation=\"vertical\"` stacking the media above the content."},{"id":"attachment-states","title":"States","source":"import Attachment from \"@marko-ui/shadcn/ui/attachment/attachment.marko\";\nimport AttachmentMedia from \"@marko-ui/shadcn/ui/attachment/media.marko\";\nimport AttachmentContent from \"@marko-ui/shadcn/ui/attachment/content.marko\";\nimport AttachmentTitle from \"@marko-ui/shadcn/ui/attachment/title.marko\";\nimport AttachmentDescription from \"@marko-ui/shadcn/ui/attachment/description.marko\";\nimport AttachmentActions from \"@marko-ui/shadcn/ui/attachment/actions.marko\";\nimport AttachmentAction from \"@marko-ui/shadcn/ui/attachment/action.marko\";\nimport Spinner from \"@marko-ui/shadcn/ui/spinner/spinner.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<div class=\"mx-auto flex w-full max-w-sm flex-col gap-2 py-12\">\n  <Attachment state=\"idle\" class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"ClockIcon\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>selected-file.pdf</AttachmentTitle>\n      <AttachmentDescription>Ready to upload</AttachmentDescription>\n    </AttachmentContent>\n    <AttachmentActions>\n      <AttachmentAction aria-label=\"Remove selected-file.pdf\">\n        <Icon name=\"X\"/>\n      </AttachmentAction>\n    </AttachmentActions>\n  </Attachment>\n  <Attachment state=\"uploading\" class=\"w-full\">\n    <AttachmentMedia>\n      <Spinner/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>design-system.zip</AttachmentTitle>\n      <AttachmentDescription>Uploading · 64%</AttachmentDescription>\n    </AttachmentContent>\n    <AttachmentActions>\n      <AttachmentAction aria-label=\"Cancel upload\">\n        <Icon name=\"X\"/>\n      </AttachmentAction>\n    </AttachmentActions>\n  </Attachment>\n  <Attachment state=\"processing\" class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"FileTextIcon\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>market-research.pdf</AttachmentTitle>\n      <AttachmentDescription>Processing document</AttachmentDescription>\n    </AttachmentContent>\n    <AttachmentActions>\n      <AttachmentAction aria-label=\"Remove market-research.pdf\">\n        <Icon name=\"X\"/>\n      </AttachmentAction>\n    </AttachmentActions>\n  </Attachment>\n  <Attachment state=\"error\" class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"FileWarningIcon\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>financial-model.xlsx</AttachmentTitle>\n      <AttachmentDescription>Upload failed. Try again.</AttachmentDescription>\n    </AttachmentContent>\n    <AttachmentActions>\n      <AttachmentAction aria-label=\"Retry upload\">\n        <Icon name=\"RefreshCwIcon\"/>\n      </AttachmentAction>\n      <AttachmentAction aria-label=\"Remove financial-model.xlsx\">\n        <Icon name=\"X\"/>\n      </AttachmentAction>\n    </AttachmentActions>\n  </Attachment>\n  <Attachment state=\"done\" class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"Check\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>uploaded-report.pdf</AttachmentTitle>\n      <AttachmentDescription>Uploaded · 1.8 MB</AttachmentDescription>\n    </AttachmentContent>\n    <AttachmentActions>\n      <AttachmentAction aria-label=\"Remove uploaded-report.pdf\">\n        <Icon name=\"X\"/>\n      </AttachmentAction>\n    </AttachmentActions>\n  </Attachment>\n</div>","description":"Set `state` to reflect the upload lifecycle — `uploading` and `processing` shimmer the title, and `error` switches to a destructive treatment."},{"id":"attachment-sizes","title":"Sizes","source":"import Attachment from \"@marko-ui/shadcn/ui/attachment/attachment.marko\";\nimport AttachmentMedia from \"@marko-ui/shadcn/ui/attachment/media.marko\";\nimport AttachmentContent from \"@marko-ui/shadcn/ui/attachment/content.marko\";\nimport AttachmentTitle from \"@marko-ui/shadcn/ui/attachment/title.marko\";\nimport AttachmentDescription from \"@marko-ui/shadcn/ui/attachment/description.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<div class=\"mx-auto flex w-full max-w-sm flex-col gap-3 py-12\">\n  <Attachment size=\"default\" class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"FileTextIcon\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>Default attachment</AttachmentTitle>\n      <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>\n    </AttachmentContent>\n  </Attachment>\n  <Attachment size=\"sm\" class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"FileTextIcon\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>Small attachment</AttachmentTitle>\n      <AttachmentDescription>PDF · 2.4 MB</AttachmentDescription>\n    </AttachmentContent>\n  </Attachment>\n  <Attachment size=\"xs\" class=\"w-full\">\n    <AttachmentMedia>\n      <Icon name=\"FileTextIcon\"/>\n    </AttachmentMedia>\n    <AttachmentContent>\n      <AttachmentTitle>Extra small attachment</AttachmentTitle>\n    </AttachmentContent>\n  </Attachment>\n</div>","description":"Use `size` to switch between `default`, `sm`, and `xs`."},{"id":"attachment-group","title":"Group","source":"import Attachment from \"@marko-ui/shadcn/ui/attachment/attachment.marko\";\nimport AttachmentGroup from \"@marko-ui/shadcn/ui/attachment/group.marko\";\nimport AttachmentMedia from \"@marko-ui/shadcn/ui/attachment/media.marko\";\nimport AttachmentContent from \"@marko-ui/shadcn/ui/attachment/content.marko\";\nimport AttachmentTitle from \"@marko-ui/shadcn/ui/attachment/title.marko\";\nimport AttachmentDescription from \"@marko-ui/shadcn/ui/attachment/description.marko\";\nimport AttachmentActions from \"@marko-ui/shadcn/ui/attachment/actions.marko\";\nimport AttachmentAction from \"@marko-ui/shadcn/ui/attachment/action.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<const/items=[\n  { name: \"briefing-notes.pdf\", meta: \"PDF · 1.4 MB\", icon: \"FileTextIcon\" },\n  {\n    name: \"workspace.png\",\n    meta: \"PNG · 820 KB\",\n    src: \"https://images.unsplash.com/photo-1497366754035-f200968a6e72?w=900&auto=format&fit=crop&q=80\",\n  },\n  { name: \"customers.csv\", meta: \"CSV · 18 KB\", icon: \"TableIcon\" },\n  { name: \"renderer.tsx\", meta: \"TSX · 12 KB\", icon: \"FileCodeIcon\" },\n]/>\n\n<div class=\"mx-auto w-full max-w-sm py-12\">\n  <AttachmentGroup class=\"w-full\">\n    <for|item| of=items by=(item) => item.name>\n      <Attachment class=\"w-64\">\n        <if=item.src>\n          <AttachmentMedia variant=\"image\">\n            <img src=item.src alt=item.name/>\n          </AttachmentMedia>\n        </if>\n        <else if=item.icon>\n          <AttachmentMedia>\n            <Icon name=item.icon/>\n          </AttachmentMedia>\n        </else>\n        <AttachmentContent>\n          <AttachmentTitle>${item.name}</AttachmentTitle>\n          <AttachmentDescription>${item.meta}</AttachmentDescription>\n        </AttachmentContent>\n        <AttachmentActions>\n          <AttachmentAction aria-label=`Remove ${item.name}`>\n            <Icon name=\"X\"/>\n          </AttachmentAction>\n        </AttachmentActions>\n      </Attachment>\n    </for>\n  </AttachmentGroup>\n</div>","description":"Wrap attachments in `AttachmentGroup` to lay them out in a horizontally scrollable, snapping row with an edge fade."},{"id":"attachment-trigger","title":"Trigger","source":"import Attachment from \"@marko-ui/shadcn/ui/attachment/attachment.marko\";\nimport AttachmentMedia from \"@marko-ui/shadcn/ui/attachment/media.marko\";\nimport AttachmentContent from \"@marko-ui/shadcn/ui/attachment/content.marko\";\nimport AttachmentTitle from \"@marko-ui/shadcn/ui/attachment/title.marko\";\nimport AttachmentDescription from \"@marko-ui/shadcn/ui/attachment/description.marko\";\nimport AttachmentActions from \"@marko-ui/shadcn/ui/attachment/actions.marko\";\nimport AttachmentAction from \"@marko-ui/shadcn/ui/attachment/action.marko\";\nimport AttachmentTrigger from \"@marko-ui/shadcn/ui/attachment/trigger.marko\";\nimport Dialog from \"@marko-ui/shadcn/ui/dialog/dialog.marko\";\nimport Icon from \"@marko-ui/shadcn/ui/icon/icon.marko\";\n\n<Dialog>\n  <@trigger|props|>\n    <Attachment class=\"w-full\">\n      <AttachmentMedia>\n        <Icon name=\"FileSearchIcon\"/>\n      </AttachmentMedia>\n      <AttachmentContent>\n        <AttachmentTitle>research-summary.pdf</AttachmentTitle>\n        <AttachmentDescription>Open preview dialog</AttachmentDescription>\n      </AttachmentContent>\n      <AttachmentActions>\n        <AttachmentAction aria-label=\"Copy link\">\n          <Icon name=\"Copy\"/>\n        </AttachmentAction>\n        <AttachmentAction aria-label=\"Remove research-summary.pdf\">\n          <Icon name=\"X\"/>\n        </AttachmentAction>\n      </AttachmentActions>\n      <AttachmentTrigger ...props aria-label=\"Preview research-summary.pdf\"/>\n    </Attachment>\n  </@trigger>\n  <@title>research-summary.pdf</@title>\n  <@description>\n    The attachment trigger fills the card and opens the dialog, while the\n    actions stay independently clickable above it.\n  </@description>\n</Dialog>","description":"Add an `AttachmentTrigger` to make the whole card open a link or dialog — it fills the card behind the actions, so the actions stay clickable."}],"importInstallCommand":"bun add @marko-ui/shadcn marko-zag","requires":["button","icon"],"nativeAttributes":"div","subcomponents":[{"name":"action","props":[],"nativeAttributes":"button"},{"name":"actions","props":[],"nativeAttributes":"div"},{"name":"content","props":[],"nativeAttributes":"div"},{"name":"description","props":[],"nativeAttributes":"span"},{"name":"group","props":[],"nativeAttributes":"div"},{"name":"media","props":[{"name":"variant","type":"'image' | 'icon' | null | undefined","required":false,"default":"icon"}],"nativeAttributes":"div"},{"name":"title","props":[],"nativeAttributes":"span"},{"name":"trigger","props":[{"name":"asChild","type":"boolean","required":false}],"nativeAttributes":"button"}]}
