Theme
Customize the design system

Every control starts at the preset. Change one and every preview on the site follows, on this page and the next, until you reset it.

Contrast

Text against what it sits on. Below 4.5:1 fails WCAG AA for body text — the check a reviewer runs on any surface that takes a tone.

Waiting for the first preview to report its palette.

Corners

Sets all three radii at once. The individual values are under Geometry.

Brand 5

The accent seam an app actually brands itself through. Change these and you have made the decision a real `DesignTokens.graphite(...)` call makes.

Geometry 15

Square or rounded, and how heavy every line is. The radius row above the controls sets all three corners at once, because "what does square feel like" is one decision rather than three numbers.

Surfaces 17

Everything the app is painted ON — the ground behind the content (the frame paints it too), the cards over it, the well inset in a card, and the washes that show state.

Bands 10

The rail's head and the view bar beside it (one band across the top at 960 and wider), a pushed screen's bar, the compact bar and the compact nav bar — the band's fill, its rule, its hover and active faces, the resting field box on it, and the ink and accent control that sit on it. The rail's tree and its footer stay on the ground.

Text and controls 11

The ink ramp, the control outlines and the field's focus halo. Every pair the panel can break is watched by a contrast badge above.

Status 7

The three voices that mean something — danger, caution and ok — each as a foreground and a surface wash, danger with a border as well.

Data 5

The default pin head, its glyph and its halo, and the group bands. A pin's category color comes from the palette, named in the theme seed.

Map 11

The basemap's roles — land, water, parks, roads and their casings, buildings, labels — and the pending pin's gray. A raster basemap keeps its own imagery; these paint the map's plate and any kit-drawn map.

Scrollbars 6

Every scroller's bar: the thumb at rest and while hovered or dragged, the strip it rides, and the desk bar's thickness, side gap and corners. The desk half shows only while a fine pointer is attached; at touch the platform's own overlay keeps its size.

Advanced 6

Real controls that are not decisions — motion durations, the focus ring offset, the touch floor. Opens collapsed on purpose.

Take it with you

The Dart that reproduces what you are looking at. It is a real constructor call, because the configurator edits a real preset — what you copy is what an app can paste.

Screens

Records cards

Reach for this to browse records as cards — five postures from dense scan rows to media browse tiles, with onOpen: as the card's single primary action. cardFor binds each row to posture-free slots and the grid picks the posture itself, so the record-to-media swap is the grid's decision rather than the screen's. A verb on this screen has three homes and none of them is the bar: the primary one is the Fab (fab), view verbs like Export are the view-options surface's header affordance, and a record's own actions belong to the pushed record surface — see Detail's hosting knob.

Spec of record: design-system/components/data/Card.d.ts

12
Knobs

Live

Real widgets, built from the real kit. Turn a knob and it rebuilds; the theme bar above applies the design system's own tokens — including anything you changed under Customize — and the device selector uses the same sizes the golden baselines gate on, so what you see here is what CI holds the widget to.

Press Focus on any preview to take it to the full window — at a desktop width that renders most devices at 1:1, which is when the widget's own controls become clickable. The bar always states the device and the scale, so a shrunk preview says it is shrunk. Esc leaves.

screen_records_cards
Knobs
Theme

Set to 0 for the empty state.

Shows when you tap a card — selection paints on the ones you pick, and starts empty — press Focus to make the preview interactive.

Assembly

Which nav the compact posture mounts — the drawer behind the hamburger, or a bottom bar.

The primary tier, and on a records destination the ONLY loud action the bar's neighborhood has. The scaffold renders it in the thumb zone at every window class; a screen never places it itself.

Actions in the drawer's own footer. The drawer carries them at EVERY window class — at 960 and wider it exists only because they do, since the expanded shell otherwise mounts a standing inline rail instead.

Shows when you open the drawer — the hamburger below 960, an edge swipe at 960 and wider, where the rail head's menu button toggles the rail instead. The actions sit in the drawer's own footer; the resting frame is identical either way. — press Focus to make the preview interactive.

The app's identity mark, and nothing else — it never opens a picker. At 960 and wider it sits beside the menu button in the rail's head at the same place in both postures, overhanging the collapsed rail so the bar beside it steps in. Below 960 the drawer's header band states it.

The scope — one value of the app's own kind (here an Area) — as the FIRST item of the bar's subtitle, ahead of the meta. With a picker it is a quiet text button with a chevron; on a phone the whole title block opens the picker as a bottom sheet. A pushed record's bar shows it read-only.

Global commands with no page ("Send feedback", "Help") pinned in the rail's footer under the footer destinations — the same row as a destination, but never lit and never counted in the destination index.

An organization's or partner's logo at the very bottom of the rail and the drawer — identity, never a control. Collapsed it shows its compact square, or nothing when the host gives none.

View

The Group and Columns panels beside the built-in Filter. `RecordsChrome.panels` is host-contributed and view-agnostic — the same cluster a table gets. OFF leaves Filter standing alone, which is a real posture: the tab row is absent at a single available control, because one control needs no tabs.

Shows when you open the view-options pane (tap Filter's control) — panels are TABS of that one pane, so the resting bar looks identical either way and only the pane's content differs — press Focus to make the preview interactive.

Dart for these values
screen_records_cards()

What you can do in it

Capability the preview already carries that no knob can express — you reach it by using the widget. Worth knowing before concluding something is missing: most of this is only clickable once the preview is at Focus size.

The records chrome
  • **Search** — type in the field; the table narrows live. Collapsing search never clears the query, which survives as a removable search:… chip.
  • **Filter** — the funnel opens the view-options pane. Facets are type-appropriate: enums and text offer distinct values, numbers a min/max, dates a From/To.
  • **Chips** — every applied filter renders as a removable chip, and Clear all removes the lot.
  • **Narrowed to zero** states the narrowing and offers Clear, rather than the author-a-record empty state.

What it is made of

The widgets this screen assembles, with their full API below. They used to live on a second entry of their own; one thing you choose gets one page.

AppCard widget

The design-system Card — one outlined-card archetype, five postures (AppCardPosture). The whole card is ONE button (onOpen, min DesignTokens.touch target); secondary actions render as the always-visible corner `⋯` menu and footerActions as the media footer's ghost row — both outside the tap target,…

Full dartdoc

The design-system Card — one outlined-card archetype, five postures (AppCardPosture). The whole card is ONE button (onOpen, min DesignTokens.touch target); secondary actions render as the always-visible corner `⋯` menu and footerActions as the media footer's ghost row — both outside the tap target, never an accent slab in a grid. State contract: enabled · hover (DesignTokens.rowHover wash — pointer enhancement only, suppressed while selected) · focus-visible (1px accent, inset) · pressed (1px translateY on the content) · selected (DesignTokens.rowSel wash + border swap to DesignTokens.accent — deliberately NO start-edge bar at card scale) · blocked (DesignTokens.dangerEdge border + danger-weight status word; selected wins) · disabled (DesignTokens.ink40 text, DesignTokens.hair border, no affordance) · loading (structural skeleton mirroring the posture, no shimmer). Empty slots collapse: a dense(AppCard.dense) card with only a title and meta renders them on one row (the ladder's "minimal" rung). ```dart AppCard.record( id: 'SR-1042', title: 'Recoat east span guardrail', supporting: 'Pump house access road', meta: 'upd 09:41', status: 'Blocked', danger: true, onOpen: () => openRecord('SR-1042'), actions: [ AppCardAction(label: 'Edit', glyph: KitGlyph.edit, onSelected: edit), ], ) AppCard.stat( title: 'Open work', value: '6', delta: const ChartDelta( direction: DeltaDirection.up, magnitudeText: '2', sentiment: DeltaSentiment.worse, ), deltaCaption: 'vs prior 7 days', ) ```

AppCard.category

Knob Type Default What it does
enabled bool true False renders the disabled state: DesignTokens.ink40 text, DesignTokens.hair border, no tap/hover/press, `⋯` hidden.
glyph KitIconRef? null The category plate's design-system glyph. Wins over icon.
glyphColor String? null THE PLATE TAKES A PALETTE COLOR (`Card.d.ts`, steady-core #1928): a palette NAME (`tokens/palette/Palette.d.ts`) the category plate paints in place of the accent — its tint under the glyph in its ink. Null, or a name DesignTokens.palette does not hold, is the accent-tint plate. An image plate…
icon IconData? null The category plate's Material glyph, for consumers not yet on glyph.
image String? null Image src for the media block / record thumb / category plate, resolved through AppImage's ladder. Null renders the posture's fallback mark — the Photo glyph on the DesignTokens.ink4 void (media/record), or the category plate's own glyph/icon on the accent tint.
imageSemanticLabel String? null Content description for image (media identity, never decorative).
loading bool false Renders the structural loading skeleton for this posture — gray DesignTokens.ink4 blocks mirroring the anatomy, excluded from semantics, no shimmer.
meta String? null Quiet data-font identity text — a timestamp, a count. Never a sentence.
navigates bool false THE ACTION THAT ONLY OPENS A PAGE (`Card.d.ts` `navigates`, `ActionStrip.d.ts`; #1734): opening the card pushes another screen and writes nothing, so the card's one button reports a link to assistive technology (`Semantics(link: true)`). A navigation-hub card (AppCard.category) is the usual one.…
onOpen VoidCallback? null The card's ONE primary action — the whole card is this button. Null renders a non-interactive card (no hover/focus/press paint).
selected bool? null Card-scale selection: DesignTokens.rowSel wash + border swap to DesignTokens.accent, surfaced as `Semantics.selected` — deliberately NO start-edge bar. Null = not a selection target (no semantics flag).
state AppCardState? null THE CATEGORY STATE (`Card.d.ts`, steady-core #1920): one live state about the place the card opens, drawn as an AppStatusPill under the supporting line; null draws nothing and pays no height. The card's name stays its title; the pill's word is read after it. Category posture only.
supporting String? null One supporting body line, DesignTokens.ink60, ellipsized. The media posture may raise supportingMaxLines for the ladder's rich rung; the category posture wraps it to two lines (THE CATEGORY LINE WRAPS TO TWO, #1891).
title * String — The record's primary label — DesignTokens.title, 2-line clamp (one line in the category/dense postures). In the stat posture: the KPI's label line above the value.

AppCard.dense

Knob Type Default What it does
danger bool false Blocked/error grammar: DesignTokens.dangerEdge border + danger-weight status word (media / record / dense). Never set by the stat posture (#895: a KPI's only semantic color is its delta). Loses the border to selected.
enabled bool true False renders the disabled state: DesignTokens.ink40 text, DesignTokens.hair border, no tap/hover/press, `⋯` hidden.
loading bool false Renders the structural loading skeleton for this posture — gray DesignTokens.ink4 blocks mirroring the anatomy, excluded from semantics, no shimmer.
meta String? null Quiet data-font identity text — a timestamp, a count. Never a sentence.
metaFormat FormatRule<Object?>? null The resolved rule the meta line wears, every paint, the data font kept — the record posture's `id · meta` row, the media footer's meta, the dense meta line. Null: the plain line.
navigates bool false THE ACTION THAT ONLY OPENS A PAGE (`Card.d.ts` `navigates`, `ActionStrip.d.ts`; #1734): opening the card pushes another screen and writes nothing, so the card's one button reports a link to assistive technology (`Semantics(link: true)`). A navigation-hub card (AppCard.category) is the usual one.…
onOpen VoidCallback? null The card's ONE primary action — the whole card is this button. Null renders a non-interactive card (no hover/focus/press paint).
selected bool? null Card-scale selection: DesignTokens.rowSel wash + border swap to DesignTokens.accent, surfaced as `Semantics.selected` — deliberately NO start-edge bar. Null = not a selection target (no semantics flag).
title * String — The record's primary label — DesignTokens.title, 2-line clamp (one line in the category/dense postures). In the stat posture: the KPI's label line above the value.
titleFormat FormatRule<Object?>? null A CARD'S LINES WEAR THEIR FORMAT RULE (`Card.d.ts`, #1006): the RESOLVED rule (the resolver's output, resolveFormat) the title line wears, exactly as a list row's title does (`List.d.ts` FORMAT RULES ON A ROW'S LINES): the mark before the first word, 'tag' read as 'ink'. The media, record and dense…

AppCard.media

Knob Type Default What it does
actions List<AppCardAction> const [] Secondary actions — the always-visible corner `⋯` menu, OUTSIDE the tap target. Never hover-revealed. The category posture renders none.
danger bool false Blocked/error grammar: DesignTokens.dangerEdge border + danger-weight status word (media / record / dense). Never set by the stat posture (#895: a KPI's only semantic color is its delta). Loses the border to selected.
enabled bool true False renders the disabled state: DesignTokens.ink40 text, DesignTokens.hair border, no tap/hover/press, `⋯` hidden.
footerActions List<AppCardAction> const [] Media-posture footer ghost buttons (label-only), trailing meta on the footer's end. Outside the tap target, behind a top hairline.
id String? null Record id — the media posture renders it as the IdChip-grammar corner chip; the record posture leads the data-font meta row with it.
image String? null Image src for the media block / record thumb / category plate, resolved through AppImage's ladder. Null renders the posture's fallback mark — the Photo glyph on the DesignTokens.ink4 void (media/record), or the category plate's own glyph/icon on the accent tint.
imageSemanticLabel String? null Content description for image (media identity, never decorative).
loading bool false Renders the structural loading skeleton for this posture — gray DesignTokens.ink4 blocks mirroring the anatomy, excluded from semantics, no shimmer.
mediaAspect AppCardMediaAspect
wide · square · portrait
AppCardMediaAspect.wide The media block's shape (`Card.d.ts` THE MEDIA BLOCK TAKES A SHAPE AND A FIT, #1378): AppCardMediaAspect.wide 16:9 by default. Media posture only — always AppCardMediaAspect.wide on the others.
mediaFit AppCardMediaFit
cover · contain
AppCardMediaFit.cover How image inscribes the media block or the record thumb: AppCardMediaFit.cover by default, AppCardMediaFit.contain showing the whole image on the ink4 void. Always cover on the category, stat and dense postures, which draw no such frame.
meta String? null Quiet data-font identity text — a timestamp, a count. Never a sentence.
metaFormat FormatRule<Object?>? null The resolved rule the meta line wears, every paint, the data font kept — the record posture's `id · meta` row, the media footer's meta, the dense meta line. Null: the plain line.
navigates bool false THE ACTION THAT ONLY OPENS A PAGE (`Card.d.ts` `navigates`, `ActionStrip.d.ts`; #1734): opening the card pushes another screen and writes nothing, so the card's one button reports a link to assistive technology (`Semantics(link: true)`). A navigation-hub card (AppCard.category) is the usual one.…
onOpen VoidCallback? null The card's ONE primary action — the whole card is this button. Null renders a non-interactive card (no hover/focus/press paint).
selected bool? null Card-scale selection: DesignTokens.rowSel wash + border swap to DesignTokens.accent, surfaced as `Semantics.selected` — deliberately NO start-edge bar. Null = not a selection target (no semantics flag).
status String? null The status WORD — DesignTokens.ink60, no pill, no dot ("encode once"); danger adds the one color-coded treatment. On the stat posture it is THE STATUS, drawn on the label line as an AppStatusMark in statusTone.
supporting String? null One supporting body line, DesignTokens.ink60, ellipsized. The media posture may raise supportingMaxLines for the ladder's rich rung; the category posture wraps it to two lines (THE CATEGORY LINE WRAPS TO TWO, #1891).
supportingFormat FormatRule<Object?>? null The resolved rule the supporting line wears, every paint. Media and record only. Null: the plain line.
supportingMaxLines int 1 Line cap for supporting in the media posture (default 1; the rich ladder rung uses 2). Other postures always clamp to one line.
title * String — The record's primary label — DesignTokens.title, 2-line clamp (one line in the category/dense postures). In the stat posture: the KPI's label line above the value.
titleFormat FormatRule<Object?>? null A CARD'S LINES WEAR THEIR FORMAT RULE (`Card.d.ts`, #1006): the RESOLVED rule (the resolver's output, resolveFormat) the title line wears, exactly as a list row's title does (`List.d.ts` FORMAT RULES ON A ROW'S LINES): the mark before the first word, 'tag' read as 'ink'. The media, record and dense…
titleMaxLines int 2 Line cap for title in the media posture (default 2 — the spec's clamp). AppCardGrid.rail pins 1 so the band's height is deterministic. Other postures keep their own fixed clamps.

AppCard.record

Knob Type Default What it does
actionBar AppCardActionBar? null Record posture only: the ACTION BAR below the body, outside the tap target (`Card.d.ts` A RECORD CARD MAY CARRY AN ACTION BAR, #451). Set, the thumb draws only for a real image and the title and supporting lines wrap unclamped. Null renders today's record card.
actions List<AppCardAction> const [] Secondary actions — the always-visible corner `⋯` menu, OUTSIDE the tap target. Never hover-revealed. The category posture renders none.
danger bool false Blocked/error grammar: DesignTokens.dangerEdge border + danger-weight status word (media / record / dense). Never set by the stat posture (#895: a KPI's only semantic color is its delta). Loses the border to selected.
enabled bool true False renders the disabled state: DesignTokens.ink40 text, DesignTokens.hair border, no tap/hover/press, `⋯` hidden.
id String? null Record id — the media posture renders it as the IdChip-grammar corner chip; the record posture leads the data-font meta row with it.
image String? null Image src for the media block / record thumb / category plate, resolved through AppImage's ladder. Null renders the posture's fallback mark — the Photo glyph on the DesignTokens.ink4 void (media/record), or the category plate's own glyph/icon on the accent tint.
imageSemanticLabel String? null Content description for image (media identity, never decorative).
loading bool false Renders the structural loading skeleton for this posture — gray DesignTokens.ink4 blocks mirroring the anatomy, excluded from semantics, no shimmer.
mediaFit AppCardMediaFit
cover · contain
AppCardMediaFit.cover How image inscribes the media block or the record thumb: AppCardMediaFit.cover by default, AppCardMediaFit.contain showing the whole image on the ink4 void. Always cover on the category, stat and dense postures, which draw no such frame.
meta String? null Quiet data-font identity text — a timestamp, a count. Never a sentence.
metaFormat FormatRule<Object?>? null The resolved rule the meta line wears, every paint, the data font kept — the record posture's `id · meta` row, the media footer's meta, the dense meta line. Null: the plain line.
navigates bool false THE ACTION THAT ONLY OPENS A PAGE (`Card.d.ts` `navigates`, `ActionStrip.d.ts`; #1734): opening the card pushes another screen and writes nothing, so the card's one button reports a link to assistive technology (`Semantics(link: true)`). A navigation-hub card (AppCard.category) is the usual one.…
onOpen VoidCallback? null The card's ONE primary action — the whole card is this button. Null renders a non-interactive card (no hover/focus/press paint).
selected bool? null Card-scale selection: DesignTokens.rowSel wash + border swap to DesignTokens.accent, surfaced as `Semantics.selected` — deliberately NO start-edge bar. Null = not a selection target (no semantics flag).
status String? null The status WORD — DesignTokens.ink60, no pill, no dot ("encode once"); danger adds the one color-coded treatment. On the stat posture it is THE STATUS, drawn on the label line as an AppStatusMark in statusTone.
supporting String? null One supporting body line, DesignTokens.ink60, ellipsized. The media posture may raise supportingMaxLines for the ladder's rich rung; the category posture wraps it to two lines (THE CATEGORY LINE WRAPS TO TWO, #1891).
supportingFormat FormatRule<Object?>? null The resolved rule the supporting line wears, every paint. Media and record only. Null: the plain line.
title * String — The record's primary label — DesignTokens.title, 2-line clamp (one line in the category/dense postures). In the stat posture: the KPI's label line above the value.
titleFormat FormatRule<Object?>? null A CARD'S LINES WEAR THEIR FORMAT RULE (`Card.d.ts`, #1006): the RESOLVED rule (the resolver's output, resolveFormat) the title line wears, exactly as a list row's title does (`List.d.ts` FORMAT RULES ON A ROW'S LINES): the mark before the first word, 'tag' read as 'ink'. The media, record and dense…

AppCard.stat

Knob Type Default What it does
compact bool false Renders the stat value at the kpi-c rung (DesignTokens.kpiC).
delta ChartDelta? null The stat posture's delta (THE DELTA IS A FIGURE).
deltaCaption String? null The stat posture's delta caption ("vs prior 30 days").
loading bool false Renders the structural loading skeleton for this posture — gray DesignTokens.ink4 blocks mirroring the anatomy, excluded from semantics, no shimmer.
status String? null The status WORD — DesignTokens.ink60, no pill, no dot ("encode once"); danger adds the one color-coded treatment. On the stat posture it is THE STATUS, drawn on the label line as an AppStatusMark in statusTone.
statusTone AppStatusMarkTone?
pending · active · done · blocked
null The stat posture's status tone (`Card.d.ts` `statusTone`, #948): AppStatusMarkTone.blocked is the KPI's needs-action tag; null draws status as the plain word. Always null on the other postures, whose status stays a word.
title * String — The record's primary label — DesignTokens.title, 2-line clamp (one line in the category/dense postures). In the stat posture: the KPI's label line above the value.
trend List<(DateTime, double)>? null The stat posture's trend — the window's points, the lead-series sparkline (THE KPI).
value * String? — The stat posture's scalar, rendered in the KPI role.

AppCardGrid widget

The design-system CardGrid — the record-browse collection over AppCard. Adaptation: **swap**, resolved from the grid's OWN measured width (so a pane-hosted grid classifies by its pane, not the window): single-column record posture `< 600` (media collapses to the 56px thumb, 8px gaps) · 2-up media…

Full dartdoc

The design-system CardGrid — the record-browse collection over AppCard. Adaptation: **swap**, resolved from the grid's OWN measured width (so a pane-hosted grid classifies by its pane, not the window): single-column record posture `< 600` (media collapses to the 56px thumb, 8px gaps) · 2-up media posture at medium · 3-up at expanded · **4-up at large (≥1200)**, 16px gutters throughout. Rows equalize card heights so a grid row reads as one band. Selection (the card-scale border swap + wash) stays live across the swap — same record, same selected read, both renders. Stat and dense cards do not swap — the host lays those out itself (a dashboard row). Category cards have their own layout, AppCardGrid.category. ```dart AppCardGrid<WorkOrder>( items: workOrders, keyFor: (wo) => wo.id, selectedKeys: {openId}, onOpen: (wo) => openRecord(wo.id), cardFor: (wo) => AppCardGridItem( id: wo.id, title: wo.title, supporting: wo.site, meta: wo.updatedLabel, status: wo.status, danger: wo.status == 'Blocked', image: wo.coverPhoto, ), ) ```

AppCardGriddefault constructor

Knob Type Default What it does
cardExtent double? null The card extent, per layout. Rail: each card's EXACT width, default 300. Category: the TRACK FLOOR, default 220 — as many columns as fit at that width or wider.
cardFor * AppCardGridItem Function(T item) — Binds one row to its posture-free card slots. Pure `T -> slots`.
emptyActionLabel String? null Empty-state single action label (renders with onEmptyAction).
emptyActionNavigates bool false THE ACTION THAT ONLY OPENS A PAGE (`ActionStrip.d.ts`, #1749): the empty face's action pushes another screen and writes nothing, so it reports a link to assistive technology. Declared, never inferred from onEmptyAction. Default false.
emptyLabel String 'No records.' Empty-state title — what the grid would show.
items * List<T> — The row data, in display order.
keyFor Object Function(T item)? null Extracts a row's stable identity for selectedKeys matching. Null uses the item itself (value equality) — the old deck's `Set<T>` idiom.
onEmptyAction VoidCallback? null Empty-state single action callback (renders with emptyActionLabel).
onOpen ValueChanged<T>? null Row tap — the card's primary action, unless the item's own AppCardGridItem.onOpen overrides it.
selectedKeys Set<Object> const {} Keys rendered selected (the card-scale border swap + wash) — typically the one open record. Selection survives the posture swap by identity.
windowClassOverride AppWindowClass?
compact · medium · expanded · large
null Pins the window class instead of measuring the container — for specimens and goldens that show both postures side by side.

AppCardGrid.category

Knob Type Default What it does
cardExtent double? 220 The card extent, per layout. Rail: each card's EXACT width, default 300. Category: the TRACK FLOOR, default 220 — as many columns as fit at that width or wider.
cardFor * AppCardGridItem Function(T item) — Binds one row to its posture-free card slots. Pure `T -> slots`.
emptyActionLabel String? null Empty-state single action label (renders with onEmptyAction).
emptyActionNavigates bool false THE ACTION THAT ONLY OPENS A PAGE (`ActionStrip.d.ts`, #1749): the empty face's action pushes another screen and writes nothing, so it reports a link to assistive technology. Declared, never inferred from onEmptyAction. Default false.
emptyLabel String 'No records.' Empty-state title — what the grid would show.
items * List<T> — The row data, in display order.
keyFor Object Function(T item)? null Extracts a row's stable identity for selectedKeys matching. Null uses the item itself (value equality) — the old deck's `Set<T>` idiom.
onEmptyAction VoidCallback? null Empty-state single action callback (renders with emptyActionLabel).
onOpen ValueChanged<T>? null Row tap — the card's primary action, unless the item's own AppCardGridItem.onOpen overrides it.
selectedKeys Set<Object> const {} Keys rendered selected (the card-scale border swap + wash) — typically the one open record. Selection survives the posture swap by identity.

AppCardGrid.rail

Knob Type Default What it does
cardExtent double? 300 The card extent, per layout. Rail: each card's EXACT width, default 300. Category: the TRACK FLOOR, default 220 — as many columns as fit at that width or wider.
cardFor * AppCardGridItem Function(T item) — Binds one row to its posture-free card slots. Pure `T -> slots`.
emptyActionLabel String? null Empty-state single action label (renders with onEmptyAction).
emptyActionNavigates bool false THE ACTION THAT ONLY OPENS A PAGE (`ActionStrip.d.ts`, #1749): the empty face's action pushes another screen and writes nothing, so it reports a link to assistive technology. Declared, never inferred from onEmptyAction. Default false.
emptyLabel String 'No records.' Empty-state title — what the grid would show.
items * List<T> — The row data, in display order.
keyFor Object Function(T item)? null Extracts a row's stable identity for selectedKeys matching. Null uses the item itself (value equality) — the old deck's `Set<T>` idiom.
onEmptyAction VoidCallback? null Empty-state single action callback (renders with emptyActionLabel).
onOpen ValueChanged<T>? null Row tap — the card's primary action, unless the item's own AppCardGridItem.onOpen overrides it.
selectedKeys Set<Object> const {} Keys rendered selected (the card-scale border swap + wash) — typically the one open record. Selection survives the posture swap by identity.

Types its knobs take

These are not things you choose — they are the shapes the knobs above expect. They live under this entry rather than in the navigation for exactly that reason.

AppCardAction support

One secondary action on an AppCard — a row in the card's always-visible trailing `⋯` menu, or (via AppCard.footerActions) a ghost button in the media footer. Secondary actions live OUTSIDE the card's tap target.

Field Type Default What it does
danger bool false Paints the action in DesignTokens.danger (menu-row form) — ink only, never a red slab.
enabled bool true Disables the action (DesignTokens.ink40, not tappable) when false.
glyph KitIconRef? null The design-system glyph for the menu-row mark. Wins over icon.
icon IconData? null A Material glyph, for a consumer that has not moved to glyph yet.
label * String — The action's accessible name / menu-row label / footer-button label.
onSelected * VoidCallback? — Fires when the action is chosen (menu-row select, footer-button press).

AppCardActionBar support

A record card's ACTION BAR (`Card.d.ts` A RECORD CARD MAY CARRY AN ACTION BAR, #451): kit buttons INSIDE the card, below the body and OUTSIDE its tap target — for a card that is answered or acted on in place (a checklist item's answer pair, a task's verbs) rather than opened. actions is the LEADING…

Field Type Default What it does
actions * List<AppCardBarAction> — The leading set — an answer set, or the card's verbs.
label String? null The group's accessible name (typically the item the bar answers).
trailing List<AppCardBarAction> const [] The trailing set at the inline end.

AppCardGridItem support

One record's posture-free slot bindings for AppCardGrid — the grid chooses the posture per window class and builds the AppCard itself.

Field Type Default What it does
actions List<AppCardAction> const [] See AppCard.actions.
danger bool false See AppCard.danger.
enabled bool true See AppCard.enabled. False renders the card disabled: no tap, the quiet ink and border.
footerActions List<AppCardAction> const [] See AppCard.footerActions — rendered in the media posture only.
glyph KitIconRef? null The category plate's glyph (AppCard.glyph) — required by AppCardGrid.category, whose plate falls back to it when image is absent or fails. The browse and rail postures do not render it.
glyphColor String? null The category plate's palette color (AppCard.glyphColor): its tint under the glyph in its ink. Null, or a name the palette does not hold, is the accent plate. The browse and rail postures do not render it.
id String? null See AppCard.id.
image String? null See AppCard.image. Collapses to the 56px thumb in the compact swap.
imageSemanticLabel String? null See AppCard.imageSemanticLabel.
mediaAspect AppCardMediaAspect AppCardMediaAspect.wide See AppCard.mediaAspect — the browse grid's media posture reads it; the rail pins AppCardMediaAspect.wide, and the record and category postures have no media block to shape.
mediaFit AppCardMediaFit AppCardMediaFit.cover See AppCard.mediaFit — the media block and the record thumb, at both sides of the swap and on the rail; the category plate ignores it.
meta String? null See AppCard.meta.
metaFormat FormatRule<Object?>? null See AppCard.metaFormat. The browse and rail postures read it; the category posture ignores it.
navigates bool? null See AppCard.navigates. Null leaves the card undeclared, which a host that states a default (`RecordsCardsSlot.navigates`) may fill; a card that states its own, true or false, stands.
onOpen VoidCallback? null Per-item open handler. Wins over AppCardGrid.onOpen when both are set.
state AppCardState? null THE CATEGORY STATE (AppCard.state, `Card.d.ts`, #1920): one live state about the place the card opens, under its content. Read by AppCardGrid.category only.
status String? null See AppCard.status.
supporting String? null See AppCard.supporting.
supportingFormat FormatRule<Object?>? null See AppCard.supportingFormat. The browse posture reads it; the rail draws no supporting line and the category posture ignores it.
title * String — See AppCard.title.
titleFormat FormatRule<Object?>? null See AppCard.titleFormat (`Card.d.ts` A CARD'S LINES WEAR THEIR FORMAT RULE, #1006). The browse and rail postures read it; the category posture ignores it.

AppCardState support

THE CATEGORY STATE's value (`Card.d.ts`, steady-core #1920): one LIVE state about the place a category card opens, drawn as an AppStatusPill under its supporting line — a short word, its tone and an optional 12px glyph. Derived state only, never a stored value (the encode-once rule and the…

Field Type Default What it does
glyph KitIconRef? null The leading glyph, or null for none.
label * String — The short word or phrase — never a sentence.
tone StatusTone StatusTone.neutral The pill's tone.

ChartDelta support

The resolved delta (`Charts.d.ts` `ChartDelta`): direction, display magnitude and the app's sentiment — given, or derived from goodDirection. Rendering is `ChartDeltaFigure`'s (THE DELTA IS A FIGURE); the semantics live here so the visual figure and the Δ9 spoken trend word read the identical…

Field Type Default What it does
direction * DeltaDirection — Direction, read off the display-rounded delta — never the raw float.
goodDirection ChartGoodDirection? null THE GOOD DIRECTION (`Charts.d.ts`, #1425): which way is good for this measure. With no sentiment given, a direction matching it derives DeltaSentiment.better and the opposite DeltaSentiment.worse; flat and noBaseline derive none.
magnitudeText String? null The rounded magnitude at the metric's DecimalDigits, T16-grouped ("3", "12%"; a pre-signed "+12%" or "−2" is kept as given); `null` in the no-baseline state (no figure).
sentiment DeltaSentiment? null

FormatRule support

One FORMAT RULE (`FormatRules.d.ts` THE FORMAT RULE): a match and its effects. A rule list is ORDERED and the FIRST holding rule formats the value; a rule with no match always holds, so written last it is the column's default.

Field Type Default What it does
color KitColor? null A palette name or a color parsed from data — on a map pin, its FILL.
em bool false The word at the em weight.
glyph KitIconRef? null A glyph — on a map pin, the head's glyph.
italic bool false The word italic.
label String? null What the rule means, in the host's words — read by a legend, a chip and the accessible name.
match FormatMatch<T>? null The predicate, or null for "always holds" (the default rule).
muted bool false The word at ink40 (ignored under FormatPaint.ink and FormatPaint.tag).
paint FormatPaint FormatPaint.mark How color lands on a value (FormatPaint.mark by default).
strike bool false The word struck through, in its own ink.

KitIconRef support

A glyph, by NAME — what every glyph-bearing kit API takes (a pin, a legend key, a nav item, an action, an empty face, a category card). Two shapes implement it: - a KitGlyph — a glyph in the kit's own name table, checked by the compiler (`KitGlyph.syringe`); - KitIconRef.named — any name as a…

Field Type Default What it does
name * String —

Where it lives

Class File
AppCard packages/app_widgets/lib/src/kit/data/card.dart
AppCardGrid packages/app_widgets/lib/src/kit/data/card.dart