web/lib/opal/src/components/buttons/line-item-button/README.md
Import: import { LineItemButton, type LineItemButtonProps } from "@opal/components";
A composite component that wraps Interactive.Stateful > Interactive.Container > ContentAction into a single API. Use it for selectable list rows such as model pickers, menu items, or any row that acts like a button.
Interactive.Stateful <- selectVariant, state, interaction, onClick, href, ref
└─ Interactive.Container <- width, rounding
└─ ContentAction <- withInteractive, padding
├─ Content <- icon, title, description, sizePreset, variant, ...
└─ rightChildren
The row renders as a focusable <div role="button"> (with Enter/Space activation) rather than a
native <button>, so interactive rightChildren such as action buttons don't produce invalid
button-in-button nesting. With href it renders an anchor instead.
withInteractive is always true and is not exposed. padding is forwarded to the inner
ContentAction, on top of the row's own p-1.5 inset.
It is not an open Spacing: the prop is inherited from ContentActionProps, which narrows it to
0 | 0.5 | 1 | 2 — the four paddings Interactive.Container applies at its size presets, so that a
row's label lines up with an adjacent button. A step outside that set is a type error.
| Prop | Type | Default | Description |
|---|---|---|---|
selectVariant | "select-light" | "select-heavy" | "select-light" | Interactive select variant |
state | InteractiveStatefulState | "empty" | Value state ("empty", "filled", "selected") |
interaction | InteractiveStatefulInteraction | "rest" | JS-controlled interaction state override |
onClick | MouseEventHandler<HTMLElement> | — | Click handler |
href | string | — | Renders an anchor instead of a div |
target | string | — | Anchor target (e.g. "_blank") |
group | string | — | Interactive group key |
ref | React.Ref<HTMLElement> | — | Forwarded ref |
| Prop | Type | Default | Description |
|---|---|---|---|
rounding | InteractiveContainerRoundingVariant | "md" | Corner rounding preset (height is content-driven) |
width | WidthVariant | "full" | Container width |
padding | 0 | 0.5 | 1 | 2 | 0.5 | Padding around the inner ContentAction, as a spacing step (N / 4 rem) |
tooltip | string | — | Tooltip text shown on hover |
tooltipSide | TooltipSide | "top" | Tooltip side |
| Prop | Type | Default | Description |
|---|---|---|---|
title | string | (required) | Row label |
icon | IconFunctionComponent | — | Left icon |
description | string | — | Description below the title |
sizePreset | SizePreset | "headline" | Content size preset |
variant | ContentVariant | "heading" | Content layout variant |
rightChildren | ReactNode | — | Content after the label (e.g. action button) |
All other ContentAction / Content props (editable, onTitleChange, optional, auxIcon, tag, etc.) are also passed through. Note: withInteractive is always true inside LineItemButton and cannot be overridden.
import { LineItemButton } from "@opal/components";
// Simple selectable row
<LineItemButton
selectVariant="select-heavy"
state={isSelected ? "selected" : "empty"}
rounding="sm"
onClick={handleClick}
title="gpt-4o"
sizePreset="main-ui"
variant="section"
/>
// With right-side action
<LineItemButton
selectVariant="select-heavy"
state={isSelected ? "selected" : "empty"}
onClick={handleClick}
title="claude-opus-4"
sizePreset="main-ui"
variant="section"
rightChildren={<Tag title="Default" color="blue" />}
/>