# Item

A flexible row with media, a title, a description and actions, for settings, lists and cards.

- **Import:** `import { Item, ItemMedia, ItemContent, ItemActions, ItemGroup, ItemSeparator, ItemTitle, ItemDescription, ItemHeader, ItemFooter } from "@booleanpress/ui/item"`
- **Page:** <https://ui.booleanpress.com/components/item> · @booleanpress/ui 0.1.0

## Usage

An item lays out a row: optional media, a content block with a title and a description, and actions. Header and footer parts take a full-width line above and below.

```tsx
import { Item, ItemContent, ItemDescription, ItemTitle } from "@booleanpress/ui/item"

export function Mailer() {
  return (
    <Item variant="outline">
      <ItemContent>
        <ItemTitle>Primary mailer</ItemTitle>
        <ItemDescription>Sends through smtp.example.com on port 587.</ItemDescription>
      </ItemContent>
    </Item>
  )
}
```

`variant` is `default` (no frame), `outline` (a border) or `muted` (a tinted fill); `size` is `default` or `sm`. `ItemMedia` takes `variant="icon"` (a small bordered tile) or `"image"` (a 40 px picture). `ItemGroup` stacks items and `ItemSeparator` draws the line between them.

Pass `asChild` and a link to make the whole item a link: it keeps the link's role, gets a hover fill and the focus ring. Do not put a second link or button inside such an item.

## Examples

### Basic

A title and a description in an outlined item.

```tsx
import { Item, ItemContent, ItemDescription, ItemTitle } from "@booleanpress/ui/item"

export default function ItemBasic() {
  return (
    <Item variant="outline" className="w-full max-w-md">
      <ItemContent>
        <ItemTitle>Primary mailer</ItemTitle>
        <ItemDescription>Sends through smtp.example.com on port 587.</ItemDescription>
      </ItemContent>
    </Item>
  )
}
```

### Variants

`default`, `outline` and `muted`.

```tsx
import { Item, ItemContent, ItemDescription, ItemTitle } from "@booleanpress/ui/item"

export default function ItemVariants() {
  return (
    <div className="flex w-full max-w-md flex-col gap-3">
      {(["default", "outline", "muted"] as const).map((variant) => (
        <Item key={variant} variant={variant}>
          <ItemContent>
            <ItemTitle>The {variant} variant</ItemTitle>
            <ItemDescription>Delivery is checked every five minutes.</ItemDescription>
          </ItemContent>
        </Item>
      ))}
    </div>
  )
}
```

### With media and actions

An icon tile, a title with a badge, and a button.

```tsx
import { MailIcon } from "lucide-react"
import { Badge } from "@booleanpress/ui/badge"
import { Button } from "@booleanpress/ui/button"
import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@booleanpress/ui/item"

export default function ItemWithMediaActions() {
  return (
    <Item variant="outline" className="w-full max-w-md">
      <ItemMedia variant="icon">
        <MailIcon />
      </ItemMedia>
      <ItemContent>
        <ItemTitle>
          Backup mailer <Badge variant="info">Standby</Badge>
        </ItemTitle>
        <ItemDescription>Used when the primary mailer fails.</ItemDescription>
      </ItemContent>
      <ItemActions>
        <Button variant="outline" size="sm">
          Edit
        </Button>
      </ItemActions>
    </Item>
  )
}
```

### Group

Items in a group with separators between them.

```tsx
import { Fragment } from "react"
import { Item, ItemContent, ItemDescription, ItemGroup, ItemSeparator, ItemTitle } from "@booleanpress/ui/item"

const RULES = [
  { name: "Order emails", detail: "Subject contains “Order”" },
  { name: "Support replies", detail: "Sender is support@example.com" },
  { name: "Everything else", detail: "No condition" },
]

export default function ItemGroupExample() {
  return (
    <ItemGroup className="w-full max-w-md rounded-lg border">
      {RULES.map((rule, index) => (
        <Fragment key={rule.name}>
          {index > 0 && <ItemSeparator />}
          <Item>
            <ItemContent>
              <ItemTitle>{rule.name}</ItemTitle>
              <ItemDescription>{rule.detail}</ItemDescription>
            </ItemContent>
          </Item>
        </Fragment>
      ))}
    </ItemGroup>
  )
}
```

### As a link

`asChild` makes the whole row one link.

```tsx
import { ChevronRightIcon } from "lucide-react"
import { Item, ItemActions, ItemContent, ItemDescription, ItemTitle } from "@booleanpress/ui/item"

export default function ItemAsLink() {
  return (
    <Item asChild variant="outline" className="w-full max-w-md">
      <a href="#email-log">
        <ItemContent>
          <ItemTitle>Email log</ItemTitle>
          <ItemDescription>Every email your site sent this month.</ItemDescription>
        </ItemContent>
        <ItemActions>
          <ChevronRightIcon className="size-4 rtl:rotate-180" aria-hidden="true" />
        </ItemActions>
      </a>
    </Item>
  )
}
```

## Accessibility

**Semantics.** Plain `div`s (and a `p` for the description), with no roles. `ItemGroup` has no `role="list"`: stock adds it, which would require every child to be `role="listitem"`, and `Item` is not one. For a real list, put the items in a `ul` and `li` yourself with `asChild`.

**Labels.** The title is the visible name. A link made with `asChild` is named by its content, so keep title and description short.

**Focus.** An item is not focusable unless it is a link or contains a control. The focus ring shows on keyboard focus.

**Known limits.**

- `ItemDescription` keeps two lines (`line-clamp-2`) and cuts the rest with an ellipsis, so the full text is not available to sighted users: keep descriptions short.
- Without a list role, a screen reader does not announce how many items a group has.

### Keyboard

| Key | Behaviour |
| --- | --- |

## API

### Item

Renders a `div` and passes it every other prop.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` | `false` | Render the child element instead, usually a link, with the item's classes merged onto it. |

### ItemMedia

Renders a `div` and passes it every other prop.

### ItemContent

Renders a `div` and passes it every other prop.

### ItemActions

Renders a `div` and passes it every other prop.

### ItemGroup

Renders a `div` and passes it every other prop.

### ItemSeparator

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `asChild` | `boolean` |  |  |
| `decorative` | `boolean` |  | Whether or not the component is purely decorative. When true, accessibility-related attributes are updated so that that the rendered element is removed from the accessibility tree. |
| `orientation` | `"horizontal" \| "vertical"` | `vertical` | Either `vertical` or `horizontal`. Defaults to `horizontal`. |

### ItemTitle

Renders a `div` and passes it every other prop.

### ItemDescription

Renders a `p` and passes it every other prop.

### ItemHeader

Renders a `div` and passes it every other prop.

### ItemFooter

Renders a `div` and passes it every other prop.

Every part takes `className`, merged with its defaults by `cn()`, and `ref`, which reaches the element it renders.

**Data attributes:** `data-slot="item"` (Item), `data-slot="item-media"` (ItemMedia), `data-slot="item-content"` (ItemContent), `data-slot="item-actions"` (ItemActions), `data-slot="item-group"` (ItemGroup), `data-slot="item-separator"` (ItemSeparator), `data-slot="item-title"` (ItemTitle), `data-slot="item-description"` (ItemDescription), `data-slot="item-header"` (ItemHeader), `data-slot="item-footer"` (ItemFooter), and `data-variant`, `data-size`.

## Theming

`outline` uses `--border`, `muted` a 50 % `--muted` fill, and a link item a 50 % `--accent` fill on hover.

| Token | Used for |
| --- | --- |
| `--accent` | background |
| `--border` | border |
| `--muted` | background |
| `--muted-foreground` | text |
| `--primary` | text |
| `--ring` | border, focus ring |
