# Item

A row with media, a title, a description and actions, for lists of records and settings.

```tsx
import { PlugIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { Item, ItemActions, ItemContent, ItemDescription, ItemMedia, ItemTitle } from "@zenoxai/ui/components/item"

function ItemDemo() {
  return (
    <Item variant="outline" className="max-w-md">
      <ItemMedia variant="icon">
        <PlugIcon />
      </ItemMedia>
      <ItemContent>
        <ItemTitle>Okta connector</ItemTitle>
        <ItemDescription>Last sync 4 minutes ago</ItemDescription>
      </ItemContent>
      <ItemActions>
        <Button variant="outline" size="sm">
          Configure
        </Button>
      </ItemActions>
    </Item>
  )
}
```

## Usage

```tsx
import { PlugIcon } from "lucide-react"

import { Button } from "@zenoxai/ui/components/button"
import {
  Item,
  ItemActions,
  ItemContent,
  ItemDescription,
  ItemMedia,
  ItemTitle,
} from "@zenoxai/ui/components/item"

export function ConnectorRow() {
  return (
    <Item variant="outline">
      <ItemMedia variant="icon">
        <PlugIcon />
      </ItemMedia>
      <ItemContent>
        <ItemTitle>Okta connector</ItemTitle>
        <ItemDescription>Last sync 4 minutes ago</ItemDescription>
      </ItemContent>
      <ItemActions>
        <Button variant="outline" size="sm">
          Configure
        </Button>
      </ItemActions>
    </Item>
  )
}
```

Use an item for a short list of records the user reads one by one: connectors, reports,
on-call analysts, recent activity. `ItemTitle` clamps to one line and `ItemDescription` to two,
so long names never push the actions out of the row.

Use something else when:

- The records have several attributes to compare across rows. Use a [Table](/components/table).
- The content is a whole panel with its own header and footer. Use a [Card](/components/card).

## Variants

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

function ItemVariants() {
  return (
    <div className="grid w-full max-w-md gap-3">
      <Item>
        <ItemContent>
          <ItemTitle>Default</ItemTitle>
          <ItemDescription>No border, for rows inside a card.</ItemDescription>
        </ItemContent>
      </Item>
      <Item variant="outline">
        <ItemContent>
          <ItemTitle>Outline</ItemTitle>
          <ItemDescription>A bordered row on the page surface.</ItemDescription>
        </ItemContent>
      </Item>
      <Item variant="muted">
        <ItemContent>
          <ItemTitle>Muted</ItemTitle>
          <ItemDescription>A tinted row that stands apart.</ItemDescription>
        </ItemContent>
      </Item>
    </div>
  )
}
```

| Variant   | Use for                                                              |
| --------- | -------------------------------------------------------------------- |
| `default` | Rows inside a card or a popover, where the container has the border. |
| `outline` | Rows on the page surface, each with its own border.                  |
| `muted`   | A row that should stand apart, such as a pinned or suggested record. |

## In a list

`ItemGroup` stacks items and renders `role="list"`. Give each `Item` `role="listitem"` so the
list structure reaches assistive technology. `size="sm"` tightens the gap between items and
shrinks image media; `size="xs"` fits menus and popovers.

```tsx
import { DownloadIcon, FileTextIcon } from "lucide-react"
import { Button } from "@zenoxai/ui/components/button"
import { Item, ItemActions, ItemContent, ItemDescription, ItemGroup, ItemMedia, ItemTitle } from "@zenoxai/ui/components/item"

function ItemList() {
  const reports = [
    { name: "Monthly exposure report", meta: "October 2026, PDF, 2.4 MB" },
    { name: "PCI DSS evidence pack", meta: "Q3 2026, ZIP, 18 MB" },
    { name: "Phishing simulation results", meta: "September 2026, CSV, 96 KB" },
  ]

  return (
    <ItemGroup className="max-w-md">
      {reports.map((report) => (
        <Item key={report.name} role="listitem" variant="outline" size="sm">
          <ItemMedia variant="icon">
            <FileTextIcon />
          </ItemMedia>
          <ItemContent>
            <ItemTitle>{report.name}</ItemTitle>
            <ItemDescription>{report.meta}</ItemDescription>
          </ItemContent>
          <ItemActions>
            <Button
              variant="ghost"
              size="icon-sm"
              aria-label={`Download ${report.name}`}
            >
              <DownloadIcon />
            </Button>
          </ItemActions>
        </Item>
      ))}
    </ItemGroup>
  )
}
```

## As a link

An item that opens a record renders an `<a>` through the `render` prop, with the content inside
the link. It gets a hover background and a focus ring.

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

function ItemAsLink() {
  return (
    <Item
      variant="outline"
      className="max-w-md"
      render={
        <a href="#usage">
          <ItemMedia variant="icon">
            <ShieldAlertIcon />
          </ItemMedia>
          <ItemContent>
            <ItemTitle>7 critical alerts need triage</ItemTitle>
            <ItemDescription>Oldest opened 3 hours ago</ItemDescription>
          </ItemContent>
          <ItemActions>
            <ChevronRightIcon className="size-4" />
          </ItemActions>
        </a>
      }
    />
  )
}
```

## Accessibility

- `ItemGroup` has `role="list"`, but `Item` has no role of its own. Pass `role="listitem"` to
  every item in a group, as in the example above; without it, assistive technology finds a list
  with no items.
- An icon-only action needs an `aria-label` that names the record: "Download PCI DSS evidence
  pack", not "Download".
- An item rendered as a link takes its accessible name from all of its text. Keep the title
  first and the description short.

## API reference

`import { ... } from "@zenoxai/ui/components/item"`

### Item

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, {}>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |
| size | `"default" \| "xs" \| "sm" \| null` | `default` | Density. `sm` tightens the vertical padding (8px instead of 10px); `sm` and `xs` shrink an image ItemMedia (40, 32 and 24px) and the gap between Items in an ItemGroup; `xs` also tightens the padding and drops it inside a dropdown menu. |
| variant | `"default" \| "outline" \| "muted" \| null` | `default` | `default` has no border or fill; `outline` adds a border; `muted` a light fill. |

### ItemMedia

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| variant | `"image" \| "default" \| "icon" \| null` | `default` | `default` shows the media as it is; `icon` sizes an icon to 16px; `image` crops an image to a square that follows the Item `size`. |

### ItemContent

No props of its own.

### ItemActions

No props of its own.

### ItemGroup

No props of its own.

### ItemSeparator

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| orientation | `"horizontal" \| "vertical"` | `vertical` | The orientation of the separator. |
| render | `ReactElement<unknown, string \| JSXElementConstructor<any>> \| ComponentRenderFn<HTMLProps, SeparatorState>` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component.  Accepts a `ReactElement` or a function that returns the element to render. |

### ItemTitle

No props of its own.

### ItemDescription

No props of its own.

### ItemHeader

No props of its own.

### ItemFooter

No props of its own.

