# Item

- Page: https://jiguang132.com/resources/astryx/docs/item
- Resource: Astryx (https://jiguang132.com/resources/astryx.md)
- All guides: https://jiguang132.com/resources/astryx/docs.md

A single, flexible item primitive that unifies the "start content + label + description + end content" pattern across Astryx. Use it wherever you need a structured row: dropdown menus, selectors, contact lists, notifications, file browsers, and activity feeds.

## Installation

```bash
npm install @astryxdesign/core@0.7.1 @astryxdesign/theme-neutral@0.7.1 @stylexjs/stylex@0.19.0
```

Requires React 19+, base CSS and a Theme provider. See [Setup](/resources/astryx/docs/getting-started).

## Item

A single, flexible item primitive that unifies the "start content + label + description + end content" pattern across Astryx. Use it wherever you need a structured row: dropdown menus, selectors, contact lists, notifications, file browsers, and activity feeds.

### Usage guidance

- Use named slots (startContent, label, description, endContent) for the common layout. These cover the 80% case.
- Use density="compact" for menus and dense lists, "balanced" for standard rows, and "spacious" for roomier layouts.
- Set labelLines and descriptionLines to control truncation when content length varies.
- Use align="start" when start or end content is taller than a single line of text.
- Give a swipe action a second home in the row (a menu or a button in endContent): the swipe is a touch accelerator, and a verb reachable only by swipe is unreachable by keyboard and by mouse.
- Avoid: Don't nest interactive elements (buttons, links) inside an interactive Item; it creates confusing focus and click targets.
- Avoid: Don't use Item for navigation between views; use proper navigation components instead.
- Avoid: Don't add read/unread or inbox-specific behavior directly; compose a thin wrapper like PreviewItem instead.

### Theming API

```json
{
  "targets": [
    {
      "className": "astryx-item",
      "visualProps": [
        "density",
        "align"
      ]
    },
    {
      "className": "astryx-item-swipe-panel",
      "visualProps": [
        "side"
      ]
    },
    {
      "className": "astryx-item-swipe-action",
      "visualProps": [
        "variant"
      ]
    }
  ],
  "vars": [
    {
      "name": "--_item-label-color",
      "description": "Color of the label line. Unset by default (the label uses the primary text token); a parent sets it to recolor the label it renders, as the destructive dropdown/context menu item does.",
      "default": "var(--color-text-primary)",
      "private": true
    },
    {
      "name": "--_item-description-color",
      "description": "Companion to --_item-label-color for the secondary description line.",
      "default": "var(--color-text-secondary)",
      "private": true
    },
    {
      "name": "--_item-inset-inline",
      "description": "Inline inset of the row. Item derives its paddingInline from this variable, and List reads it to cancel the inset when edgeCompensation=\"inline\". Set paddingInline on `item` in a theme and both stay in sync.",
      "default": "var(--spacing-2) (var(--spacing-3) for density=\"spacious\")",
      "private": true
    }
  ],
  "derived": [
    {
      "property": "paddingInline",
      "vars": [
        "--_item-inset-inline"
      ]
    }
  ]
}
```

## Item

A universal item primitive that unifies the "start content + label + description + end content" layout pattern. Use as a building block for list items, menu items, contact rows, notifications, and more.

| Prop | Type | Default | Description |
|---|---|---|---|
| label * | ReactNode |  | Primary text identifying this item. Accepts string (auto-truncated) or ReactNode (for rich content). |
| marker | ReactNode |  | Marker rendered before startContent as a direct flex child. Use for list bullets/counters that need custom baseline alignment. |
| startContent | ReactNode |  | Content rendered before the label/description area, such as an icon, avatar, or checkbox. |
| description | ReactNode |  | Secondary text: subtitle, description, or supporting info. |
| endContent | ReactNode |  | Content rendered after the label/description area, such as badges, metadata, timestamps, or action buttons. |
| as | 'div' \| 'li' \| 'span' \| ElementType | 'div' | What the root renders as: an HTML element, or a component for a caller that needs the root to be something else. A menu row that navigates passes the application's link component here, so the row's root IS the anchor. Give a component only when the row carries a role, and only when no interactive node sits in startContent or endContent. |
| align | 'center' \| 'start' | 'center' | Vertical alignment of start/end content slots. |
| density | 'compact' \| 'balanced' \| 'spacious' | 'balanced' | Spacing density. "compact" uses 4px block padding, "balanced" uses 8px, and "spacious" uses 12px block and inline padding. |
| labelLines | number |  | Max lines before label truncates with ellipsis. |
| descriptionLines | number |  | Max lines before description truncates with ellipsis. |
| layout | 'stacked' \| 'inline' | 'stacked' | How the label and description sit together. stacked puts the description on its own line below the label; inline keeps both on one line, description ellipsizing first, so the row fits a fixed-height host. |
| onClick | (event: MouseEvent) => void |  | Click handler. Makes the item clickable with button semantics. |
| interactiveRef | RefObject<HTMLElement \| null> |  | Ref to a nested control (e.g. a checkbox in startContent) that owns the item's keyboard access and action. The row becomes an enlarged click/tap target that delegates surface clicks to it (useClickableContainer) and renders no invisible button/anchor, so the row adds no second tab stop (WCAG 4.1.2). Mutually exclusive with onClick/href; those are ignored when set. |
| href | string |  | Link URL. Makes the item a link via an invisible anchor element. A row whose root is already a link component (see `as`) carries the address on that root instead, and no invisible anchor is rendered. |
| target | '_blank' \| '_self' |  | Link target. Only used with href. target="_blank" automatically adds noopener noreferrer. |
| rel | string |  | Link relationship tokens. noopener noreferrer are merged automatically for target="_blank". |
| isHighlighted | boolean | false | Highlighted state (hover/keyboard focus appearance). |
| isSelected | boolean | false | Selected state. |
| isDisabled | boolean | false | Disabled state. |
| swipeActions | {leading?: ItemSwipeAction[]; trailing?: ItemSwipeAction[]} |  | Swipe actions for touch: the verbs a sideways drag uncovers on each side, outermost last, each `{id?, label, icon?, onActivate, isDisabled?, variant?: 'neutral' \| 'accent' \| 'destructive', hasRemoval?}`. `leading` is uncovered by a drag toward the inline end, `trailing` toward the inline start. A mouse never starts the drag and a mostly vertical drag stays the scroller's. The gesture is a touch accelerator and never a row's only path to a verb: a verb reachable only by swipe is unreachable by keyboard and by mouse, so every entry must also be reachable through the row's own content. Served on a row whose role permits interactive descendants (a listitem, a row with no role) and not on an option, a menu item, or a row that is the enlarged target of a native radio. The element containing the rows clips in the inline axis with overflow-inline: clip — List does; any other host does it once. `hasRemoval` only says the row holds out after the action fires instead of springing back. The gesture and its panels are loaded on demand the first time a row has swipe actions, so a row without them ships none of it. |
| swipeBehavior | 'reveal' \| 'commit' | 'reveal' | What a swipe does. `reveal` rests the row open with every entry a real button and fires the outermost on a long drag or a fling; `commit` slides the row out and fires the outermost on release, with nothing resting and a presentational panel. `commit` fits one entry per side. |
| ref | React.Ref<HTMLDivElement> |  | Ref forwarded to the root element. |
| xstyle | StyleXStyles |  | StyleX styles for layout customization. Must be a stylex.create() value. |
| data-testid | string |  | Test selector for automated testing frameworks. |

### Anatomy

- **Marker**: Optional list bullet/counter rendered before start content.
- **Start content**: Leading visual: avatar, icon, image, or checkbox.
- **Label**: Primary text identifying the item.
- **Description**: Secondary supporting text below the label.
- **End content**: End-aligned content: badges, timestamps, or action buttons.


## Examples

- Item: 
- Item — Basic: A basic item with a label, supporting description, and end-aligned timestamp. Use this for simple rows that need consistent text alignment and spacing.
- Item — Document Tabs: Single-row, horizontally scrollable document switcher with compact selected rows, leading file icons, instant reveal-on-hover close actions with reserved geometry, stable separators, and an add control outside the scroll area. Use for editor tabs, workspace tabs, open documents, files, canvases, or panels.
- Item — Start Content: Items with leading avatars and icons in the startContent slot. Keep start content small so the row stays compact and easy to scan.
- Item — Metadata: Items with end-aligned metadata and badges. Use the endContent slot for counts, status, timestamps, and other secondary row information.

## Complete example dependencies

Some examples import optional icons. The per-example install command in the examples list identifies exactly what that example needs. For all examples on this page:

```bash
npm install @heroicons/react@2.2.0
```

## Source StyleX compilation

ItemDocumentTabs contain stylex.create and require a StyleX source build pipeline. Precompiled Core CSS alone does not compile these examples. See [Styling](/resources/astryx/docs/styling) before copying them.

---

Index of all JGuang resources: https://jiguang132.com/llms.txt
