Skip to content
Glint UI

Item

The generic dense row — a media gutter, a content column, and the actions against the trailing edge.

Usage

import { Item, ItemMedia, ItemMediaVariant, ItemContent, ItemTitle, ItemDescription, ItemActions } from "@glint/components/item.slint";
import { Button, ButtonVariant, ButtonSize } from "@glint/components/button.slint";
import { Tokens } from "@glint/theme/tokens.slint";
import { IconSet } from "@lucide";
export component AppWindow inherits Window {
width: 520px;
height: 160px;
background: Tokens.color-background;
VerticalLayout {
padding: 24px;
alignment: start;
Item {
ItemMedia {
variant: ItemMediaVariant.icon;
icon: IconSet.FileText;
}
ItemContent {
ItemTitle { text: "Q3 report.pdf"; }
ItemDescription { text: "1.4 MB · updated yesterday"; }
}
ItemActions {
Button {
variant: ButtonVariant.ghost;
size: ButtonSize.sm;
text: "Download";
}
}
}
}
}

The row’s one slot is its band: media, content, actions, laid out left to right. A component gets exactly one @children, written once and never inside an if (ADR-0027), and the band is what Item spends it on — so a plain row costs no wrapper at all.

That is also why header and footer are strings the row draws above and below the band rather than slots of their own. ItemHeader and ItemFooter stay exported as the type styles those lines use, for a stack built by hand.

PartWhat it is
ItemMediaThe leading square: a slot, a lucide icon on a tile, or an image
ItemContentThe middle cell. It takes the slack, so a long description never pushes the actions off the row
ItemTitle / ItemDescriptionThe row’s name and the line under it. The title elides rather than widening its container
ItemActionsThe trailing cluster, centred against the band
ItemGroupThe stack a set of rows sits in, and the list assistive technology walks

Item is Card one density down, and it is named to read that way: ItemTitle and ItemDescription stand to ItemContent exactly as CardTitle and CardDescription stand to CardHeader. What it adds is the row’s own axis and the opt-in whole-row trigger a list needs.

Examples

Variants

variant is the row’s surface treatment. ItemVariant.default draws nothing at all — a row is a band of a list, and a list of bordered rows is a list of cards. outline gives it a hairline edge, muted a fill.

Density

size packs the row: the padding, the gap between the band’s cells and the media square all follow it. The square’s side is given rather than read — a slotted component cannot see its host — so a row that changed its density hands the number on with size: row.media-size. Sidebar’s regions are told about their collapse the same way.

The media gutter

ItemMedia.variant is what the square holds. default is the slot: an Avatar goes straight in, since Glint already has one and a media variant for it would be a second spelling of the same thing. icon draws a lucide glyph on a backing tile, and image draws a picture from source, clipped to the square’s radius.

A row that is a trigger

A row is a surface, not an affordance, until it is asked to be one — Card’s rule. interactive is the ask: it gives the row a pointer cursor, a press dip, a tab stop and a clicked callback. disabled is the refusal — the row dims, keeps its place and swallows every activation path; selected is the current row of a list, which wears the highlight the pointer raises.

The controls in ItemActions sit above the row’s own trigger, so they keep their presses and their tab stops. A keyboard user reaches the row first, then its actions.

An interactive row is named at the call site. Slint gives a host no way to read the text it was slotted, so the row cannot borrow the ItemTitle inside it: label and description are what assistive technology announces, and the slotted title goes on speaking for itself.

A list of rows

ItemGroup is the list a set of rows sits in. It counts what it holds from item-count rather than from its own children — Slint lets a host neither address nor count what was slotted into it, which is the same reason each row is told its own index.

A row with an index is a list item carrying that position and its selected state; an interactive row without one, standing alone, is announced as a button instead. Membership of a list is not an interaction, so a plain row inside a group still has a position — and a row that is neither interactive nor indexed has no node of its own at all.

separator-before opens a row with the group hairline. It belongs to the row below it rather than being an entry of its own, so every child of the group stays a real, indexable row (ADR-0012).

A line above and below the band

header and footer are the row’s first line and its last — a group name over the band, a timestamp under it. Empty draws nothing and costs nothing.

A stack built by hand

The four type styles and the hairline are exported so a column that never becomes an ItemGroup can still be built out of the family’s own parts. ItemSeparator is Separator under the item family’s name, so it takes the same orientation — a vertical rule between two rows standing side by side — and the same optional caption.

API Reference

Properties

PropertyTypeDefaultDescription
variantin ItemVariantItemVariant.defaultSurface treatment — none, a hairline edge, or a muted fill.
sizein ItemSizeItemSize.defaultDensity: the media square, the padding and the band's gap together.
headerin stringno defaultA line above the band; empty draws nothing.
footerin stringno defaultA line below it; empty draws nothing.
interactivein boolfalseMakes the whole row activatable. Off by default: a surface is not an affordance until it is asked to be one.
disabledin boolfalseA row that refuses: it dims, swallows every activation, keeps its place and reports itself unavailable (CONTEXT.md "Refusal"). Spelled the way every other Glint control that is not a text entry spells it.
selectedin boolfalseThe current row of a list — it wears the same highlight the pointer raises, and says so when it is announced.
separator-beforein boolfalseOpens the row with the group hairline. It belongs to the row below it rather than being an entry of its own, so every child of a group stays a real, indexable row (ADR-0012).
indexin int-1Where the row stands in its list. -1 — the default — is a row standing alone, which is announced as a button; anything else is a list item carrying this index. A slotted child cannot read its host, so the position is given rather than counted.
labelin stringno defaultWhat assistive technology announces the row as, and the line under it. An interactive row needs the name: the title it was slotted is out of reach.
descriptionin stringno default
media-sizeout lengthno defaultThe media square's side at this density, published because the call site is what hands it to the ItemMedia it slotted.
surface-colorout colorno defaultThe surface behind the row, and its edge — published so a slotted action can match the row it sits on.
edge-colorout colorno default

Callbacks

CallbackDescription
clicked()Fired when a live, interactive row is activated by pointer, by Enter or Space, or through the accessible default action.

Enums

EnumValues
ItemVariantdefault, outline, muted
ItemSizedefault, sm, xs

ItemGroup

The list a set of rows sits in.

Properties

PropertyTypeDefaultDescription
labelin stringno defaultWhat the list is called. A page with two lists needs them told apart.
item-countin intno defaultHow many rows it holds, for assistive technology's "3 of 12".
spacingin length0pxGap between two rows. Rows touch by default, which is what makes separator-before read as one hairline between them.

ItemMedia

The leading square.

Properties

PropertyTypeDefaultDescription
variantin ItemMediaVariantItemMediaVariant.defaultWhat the gutter holds.
iconin LucideIconno defaultThe lucide icon drawn by the icon variant.
sourcein imageno defaultThe picture drawn by the image variant.
sizein length40pxThe square's side. Item.media-size is the number that follows the row's density.

Enums

EnumValues
ItemMediaVariantdefault, icon, image

ItemContent

The band’s middle cell: the title, the description, and anything else the row stacks under them.

ItemContent publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint VerticalLayout it inherits.

ItemTitle

The row’s name. It elides rather than growing its container, and takes a zero minimum width so a narrow list does not overflow.

ItemTitle publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint Text it inherits.

ItemDescription

The line under the title, at the muted tone and wrapping by word.

ItemDescription publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint Text it inherits.

ItemActions

The trailing cluster. Centred against the band rather than stretched down it, so a two-line row does not stand its buttons at the top.

ItemActions publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint VerticalLayout it inherits.

ItemHeader

The row’s first line, as a type style — for a stack built by hand.

ItemHeader publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint Text it inherits.

ItemFooter

The row’s last line, as a type style.

ItemFooter publishes no properties, callbacks or functions of its own. What a call site can set on it is the Slint Text it inherits.

ItemSeparator

The hairline between two rows, under the item family’s own name. It is Separator, so it carries that component’s orientation and caption.

Properties

PropertyTypeDefaultDescription
orientationin SeparatorOrientationSeparatorOrientation.horizontalhorizontal (default) or vertical. Ignored while there is a caption, which is horizontal by construction.
textin stringno defaultCaption centred in the rule; empty (the default) is the bare hairline.

Enums

EnumValues
SeparatorOrientationhorizontal, vertical

Accessibility

  • A plain row has no node. An Item that is neither interactive nor indexed declares no role: what a screen reader reads is what you put inside it. That is Card’s rule at one lower density.
  • Two nodes, one of which exists. accessible-role must be a constant in Slint, so a row standing alone (index: -1, the default) is announced as a button and a row inside a list as a list-item carrying its index and its selected state. Only the button half is gated on interactive — membership of a list is not an interaction.
  • The name is the call site’s. A host cannot read the text it was slotted, so label and description are what the row announces. The slotted ItemTitle keeps speaking for itself; there is nothing here that could silence it.
  • One activation path. The pointer, Enter and Space, and the accessible default action all land in one place, and that is where disabled refuses. The refusal is never the TouchArea’s enabled — a disabled TouchArea is transparent to the pointer, and the press would land on whatever sits under the row.
  • A row nobody can activate takes no tab stop. An affordance that does nothing is not one. An interactive row takes the stop before the controls it holds, so a keyboard user reaches the row and then its actions.
  • ItemGroup is the list. It carries accessible-role: list with its label and item-count, which is what gives assistive technology its “3 of 12”. The count is a property because a host can neither address nor count what was slotted into it.
  • The media gutter is decoration. An ItemMedia holding an icon adds no node of its own; a slotted Avatar announces itself through its own alt.