implement
Item

Item

A row with media, a title, and controls.

NotificationsNew
Get told when a deployment finishes or a build breaks.
Billing
import { BellIcon, ChevronRightIcon } from "@implementjs/lucide";
import { Badge } from "@/lib/components/ui/badge";
import { Button } from "@/lib/components/ui/button";
import {
	Item,
	ItemActions,
	ItemContent,
	ItemDescription,
	ItemGroup,
	ItemMedia,
	ItemSeparator,
	ItemTitle,
} from "@/lib/components/ui/item";
import { Switch } from "@/lib/components/ui/switch";

export default function ItemDemo() {
	return ItemGroup(
		{ class: "w-full max-w-md rounded-lg border" },
		Item(
			ItemMedia({ variant: "icon" }, BellIcon({ "aria-hidden": true })),
			ItemContent(
				ItemTitle("Notifications", Badge({ variant: "secondary" }, "New")),
				ItemDescription("Get told when a deployment finishes or a build breaks."),
			),
			ItemActions(Switch({ checked: true, "aria-label": "Notifications" })),
		),
		ItemSeparator(),
		Item(
			{ size: "sm" },
			ItemContent(ItemTitle("Billing")),
			ItemActions(
				Button(
					{ variant: "ghost", size: "icon-sm", "aria-label": "Open billing" },
					ChevronRightIcon(),
				),
			),
		),
	);
}

Installation

npx jsrepo add @implementjs/ui/item

It installs tailwind-variants at the same time.

Usage

A settings row, a search result, a file listing, a member of a team — they turn out to be the same shape. Item is that shape.

import {
	Item,
	ItemActions,
	ItemContent,
	ItemDescription,
	ItemGroup,
	ItemMedia,
	ItemTitle,
} from "@/lib/components/ui/item";

ItemGroup(
	Item(
		ItemMedia({ variant: "icon" }, BellIcon({ "aria-hidden": true })),
		ItemContent(ItemTitle("Notifications"), ItemDescription("Get told when a build breaks.")),
		ItemActions(Switch({ "aria-label": "Notifications" })),
	),
);

Media alignment

ItemMedia centers itself on a single-line row and jumps to the top when the row has a description — group-has-[[data-slot=item-description]]/item:self-start. Nothing to set: adding a description moves the icon.

Variants

outline gives the row a border, muted a fill; size: "sm" tightens the padding for a dense list. A row that navigates can be an A with the same classes, and the hover state ([a&]:hover:bg-accent/50) switches on only then.

Headers and footers

ItemHeader and ItemFooter are full-width rows above and below the main line — the root wraps, and both are basis-full, so they break onto their own line without any extra layout.

API Reference

ItemGroup

Sets role="list". A list of items. Renders a Div; extra props are forwarded onto it.

ItemSeparator

Sets role="separator". A line between items. Renders a Div; extra props are forwarded onto it.

Item

One row: something on the left, a title and description in the middle, controls on the right — the shape a settings row, a search result, and a file listing all turn out to share. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
variant"default" | "outline" | "muted""default"Transparent, bordered, or on a muted fill. Also set as data-variant.
size"default" | "sm""default"Padding and gap. Also set as data-size.

ItemMedia

The leading block. It self-aligns to the top only when the row has a description, so single-line rows stay centered. Renders a Div; extra props are forwarded onto it.

PropTypeDefaultDescription
variant"default" | "icon" | "image""default"icon gives a bordered tile; image clips a picture to a rounded square.

ItemContent

Title and description, taking the remaining width. Renders a Div; extra props are forwarded onto it.

ItemTitle

The row's name. Renders a Div; extra props are forwarded onto it.

ItemDescription

A line or two under the title, clamped at two. Renders a Div; extra props are forwarded onto it.

ItemActions

The trailing block: buttons, a menu, a switch. Renders a Div; extra props are forwarded onto it.

ItemHeader

A full-width row above the item's content. Renders a Div; extra props are forwarded onto it.

ItemFooter

A full-width row below it. Renders a Div; extra props are forwarded onto it.