# Three sizes carrying three levels of an answer

> Three sizes carrying three levels of an answer

A shadcn/ui block from the [@reui](https://reui.io) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/reui/c-attachment-4).

- Page, with a live preview: https://shadcn.exchange/r/reui/c-attachment-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/reui/c-attachment-4-light-b5e7d1e2e18b0b12.png
- Registry item: https://reui.io/r/new-york-v4/c-attachment-4.json
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://reui.io/r/new-york-v4/c-attachment-4.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- Registry items: `attachment`

## Files

### `c-attachment-4.tsx`

```tsx
import {
  Attachment,
  AttachmentContent,
  AttachmentDescription,
  AttachmentGroup,
  AttachmentMedia,
  AttachmentTitle,
} from "@/components/ui/attachment"
import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"

const ICON_REPORT = (
  <IconPlaceholder
    lucide="FileTextIcon"
    tabler="IconFileText"
    hugeicons="File02Icon"
    phosphor="FileTextIcon"
    remixicon="RiFileTextLine"
    aria-hidden="true"
  />
)

const ICON_SHEET = (
  <IconPlaceholder
    lucide="FileSpreadsheetIcon"
    tabler="IconFileSpreadsheet"
    hugeicons="GoogleSheetIcon"
    phosphor="FileTextIcon"
    remixicon="RiFileTextLine"
    aria-hidden="true"
  />
)

const ICON_JSON = (
  <IconPlaceholder
    lucide="FileJsonIcon"
    tabler="IconFileCode"
    hugeicons="FileBracesCornerIcon"
    phosphor="FileCodeIcon"
    remixicon="RiFileCodeLine"
    aria-hidden="true"
  />
)

const ICON_CODE = (
  <IconPlaceholder
    lucide="CodeIcon"
    tabler="IconCode"
    hugeicons="SourceCodeIcon"
    phosphor="CodeIcon"
    remixicon="RiCodeSSlashLine"
    aria-hidden="true"
  />
)

export function Pattern() {
  // Size is hierarchy here, not decoration: default is the one thing the
  // reader is meant to open, sm is the working set behind it, and xs is the
  // evidence the answer cites. Three rungs, three jobs, no controls.
  return (
    <div className="mx-auto flex w-full max-w-sm flex-col gap-4">
      <div className="flex flex-col gap-2">
        {/*
         * Each band still says in text what its size is already saying. Size
         * alone is a weak signal on screen and no signal at all in a reader.
         */}
        <p className="text-muted-foreground text-xs font-medium">Deliverable</p>
        <Attachment className="w-full">
          <AttachmentMedia>{ICON_REPORT}</AttachmentMedia>
          <AttachmentContent>
            <AttachmentTitle>billing-incident-report.pdf</AttachmentTitle>
            <AttachmentDescription>
              PDF
              <span
                aria-hidden="true"
                className="bg-muted-foreground/40 mx-1.5 inline-block size-1 rounded-full align-middle"
              />
              2.3 MB
            </AttachmentDescription>
          </AttachmentContent>
        </Attachment>
      </div>

      <div className="flex flex-col gap-2">
        <p className="text-muted-foreground text-xs font-medium">
          Working files
        </p>
        {/*
         * The first two bands stretch. The xs chips below keep their natural
         * width instead, so the group can scroll them rather than squeeze them.
         */}
        <Attachment size="sm" className="w-full">
          <AttachmentMedia>{ICON_SHEET}</AttachmentMedia>
          <AttachmentContent>
            <AttachmentTitle>refund-queue.csv</AttachmentTitle>
            <AttachmentDescription>
              CSV
              <span
                aria-hidden="true"
                className="bg-muted-foreground/40 mx-1.5 inline-block size-1 rounded-full align-middle"
              />
              96 KB
            </AttachmentDescription>
          </AttachmentContent>
        </Attachment>
        <Attachment size="sm" className="w-full">
          <AttachmentMedia>{ICON_JSON}</AttachmentMedia>
          <AttachmentContent>
            <AttachmentTitle>webhook-replays.json</AttachmentTitle>
            <AttachmentDescription>
              JSON
              <span
                aria-hidden="true"
                className="bg-muted-foreground/40 mx-1.5 inline-block size-1 rounded-full align-middle"
              />
              41 KB
            </AttachmentDescription>
          </AttachmentContent>
        </Attachment>
      </div>

      <div className="flex flex-col gap-2">
        <p className="text-muted-foreground text-xs font-medium">
          Cited snippets
        </p>
        {/*
         * Only this band takes tabIndex: its chips carry no buttons, so the
         * ones scrolled off the end would be unreachable by keyboard without
         * a tab stop on the group itself.
         */}
        <AttachmentGroup tabIndex={0} role="group" aria-label="Cited snippets">
          {/*
           * Dropping AttachmentDescription is also what tightens the padding
           * here: the per-style rule keys off whether a content slot exists.
           */}
          <Attachment size="xs">
            <AttachmentMedia>{ICON_CODE}</AttachmentMedia>
            <AttachmentContent>
              <AttachmentTitle>dunning.ts</AttachmentTitle>
            </AttachmentContent>
          </Attachment>
          <Attachment size="xs">
            <AttachmentMedia>{ICON_CODE}</AttachmentMedia>
            <AttachmentContent>
              <AttachmentTitle>webhook-router.ts</AttachmentTitle>
            </AttachmentContent>
          </Attachment>
          <Attachment size="xs">
            <AttachmentMedia>{ICON_REPORT}</AttachmentMedia>
            <AttachmentContent>
              <AttachmentTitle>retry-policy.md</AttachmentTitle>
            </AttachmentContent>
          </Attachment>
        </AttachmentGroup>
      </div>
    </div>
  )
}
```
