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.

Components

Status and feedback

What a surface says when it is not simply showing data: banners, status pills and status marks, the waiting states, the two empty states that are not the same state, and the bulk bar.

Spec of record: design-system/components/feedback/Banner.d.ts

1
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.

family_status
Knobs
Theme

Every specimen at once, in one state. Per-specimen controls would rebuild the wall a board exists to remove.

Dart for these values
family_status()

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.

AppBanner widget

The design-system Banner — a persistent, inline notice about the surface it stands with (offline, sync failed, stale data, a blocked record). **Not a floating toast.** Toasts do not exist in this system; a transient confirmation (e.g. "SR-1042 deleted") belongs in the Modeline, never here.

Full dartdoc

The design-system Banner — a persistent, inline notice about the surface it stands with (offline, sync failed, stale data, a blocked record). **Not a floating toast.** Toasts do not exist in this system; a transient confirmation (e.g. "SR-1042 deleted") belongs in the Modeline, never here. AppBanner sits inline, above scroll, describing a *persistent* condition. WHERE it stands is its host's clause (`RecordsScreen.d.ts` THE BANNER'S RANK AGAINST THE FACES, `DetailView.d.ts` THE SURFACE NOTICE IS THIS COMPONENT'S SLOT); a host that used to bleed it across a surface insets it to that surface's padding. THE CONTAINED BANNER (`Banner.d.ts`, 2026-09-29, #896) is a TINTED PLATE: the tone's fill at DesignTokens.rS corners, padded 10 × 12 and sized by its content, with no border, hairline or shadow (the tint alone separates it from the card or ground beneath). A leading 16 glyph — KitGlyph.warning by default for AppBannerTone.danger, none by default for AppBannerTone.info — stands 10 before the message on its FIRST line. The message wraps and is never truncated (12.5 at the body line height, weight 400); an optional lead opens it at the em weight ("**Blocked since Sep 22.** Check valve 2 failed its test."). The meta, the one ghost action and the dismiss "✕" trail at the plate's inline end, aligned to its top. The 2px start edge, the strip's bottom hairline and the 40 minimum height are DELETED. Both tones are live regions (`Semantics(liveRegion: true)`) so assistive tech announces the notice as it appears. ```dart AppBanner( message: 'Working offline. Changes sync on reconnect.', glyph: KitGlyph.cloudOff, ) AppBanner( tone: AppBannerTone.danger, lead: 'Blocked since Sep 22.', message: 'Check valve 2 failed its test.', actionLabel: 'Retry', onAction: retry, ) ```

AppBannerdefault constructor

Knob Type Default What it does
actionLabel String? null Label for the single trailing action, rendered as a ghost AppButton. Renders only when onAction is also non-null.
dismissLabel String 'Dismiss' Tooltip and accessible name for the dismiss "✕". Ignored when onDismiss is null.
glyph KitIconRef? null The leading 16 glyph. Null takes the tone's default: KitGlyph.warning for AppBannerTone.danger, none for AppBannerTone.info — an operational notice names its condition's glyph (KitGlyph.cloudOff offline, KitGlyph.sync a sync fault, KitGlyph.clock stale data), or starts on its text.
lead String? null The lead run that opens the message at the em weight ("Blocked.", "Blocked since Sep 22.") — the intra-sentence run of the Anatomy rules. Null or blank renders no lead.
message * String — The condition, stated factually — cause + consequence, no exclamation marks (e.g. "Working offline. Changes sync on reconnect.").
meta String? null Quiet data-font count/timestamp beside the message (`Banner.d.ts` `meta`): "3 pending", "synced 09:41". Machine text — renders in DesignTokens.dataS in the plate's ink, never restated in the message prose.
onAction VoidCallback? null Callback for the single trailing action. Renders only when actionLabel is also non-null.
onDismiss VoidCallback? null Fires when the dismiss "✕" is tapped. Null omits the dismiss control entirely — a live failure a caller wants to keep visible simply omits this, rather than rendering a disabled "✕".
tone AppBannerTone
info · danger
AppBannerTone.info Closed tone set — AppBannerTone.info (operational notice) or AppBannerTone.danger (failed state demanding action). Governs the plate's fill and ink, and the default glyph.

AppStatusPill widget

The design-system StatusPill — a tinted plate carrying a short word or figure (+ optional leading 12px glyph) in the tone's ink. For LIVE, transient, or derived state only.

Full dartdoc

The design-system StatusPill — a tinted plate carrying a short word or figure (+ optional leading 12px glyph) in the tone's ink. For LIVE, transient, or derived state only. A stored status stays a word in its cell; this widget never re-encodes a value. ```dart AppStatusPill(tone: StatusTone.caution, label: 'Syncing…') AppStatusPill( tone: StatusTone.positive, glyph: KitGlyph.trendUp, data: true, label: '+12%', ) ```

AppStatusPilldefault constructor

Knob Type Default What it does
data bool false Render label in the small data role (counts, deltas — machine text).
glyph KitIconRef? null Leading design-system glyph, 12px, in the tone's ink. Wins over icon.
icon IconData? null A Material glyph for a consumer not yet on glyph.
label * String — Short word or figure.
tone StatusTone
positive · info · caution · critical · neutral
StatusTone.neutral The annotation tone. Defaults to StatusTone.neutral.

AppStatusMark widget

The design-system StatusMark — a stored status as its WORD beside an 8px square marker (DesignTokens.rMark), or, for AppStatusMarkTone.blocked, the filled danger tag. The word renders in DesignTokens.ink60, the UI font at the HOST's size: style is the host's text style, and the word takes its size…

Full dartdoc

The design-system StatusMark — a stored status as its WORD beside an 8px square marker (DesignTokens.rMark), or, for AppStatusMarkTone.blocked, the filled danger tag. The word renders in DesignTokens.ink60, the UI font at the HOST's size: style is the host's text style, and the word takes its size (and line height) — a table cell passes nothing and the word reads at DesignTokens.body, the cell's own size; a larger host passes its value style. Color and family are the mark's, never the host's. The blocked tag is always the label role (12/500), whatever the host. Semantics: ONE node reading the word alone. The marker is excluded — it is the word's glyph, and a reader hearing "square" before every status would be hearing decoration. ```dart AppStatusMark(label: 'In progress', tone: AppStatusMarkTone.active) AppStatusMark(label: 'Blocked', tone: AppStatusMarkTone.blocked) ```

AppStatusMarkdefault constructor

Knob Type Default What it does
label * String — The stored value's human word — always rendered; it carries the meaning.
style TextStyle? null The host's text style, whose SIZE the word takes (`StatusMark.d.ts`: "the UI font at the host's size"). Null reads DesignTokens.body. Ignored by AppStatusMarkTone.blocked, whose tag is the label role.
tone * AppStatusMarkTone
pending · active · done · blocked
— Which of the four tones the host mapped the value onto.

AppProgress widget

The design-system Progress — the live-operation loader. An optional accent micro-label (the state word — "generating", "queued") over a 2px indeterminate track: a 28%-wide DesignTokens.accent bar sweeping a DesignTokens.accentTint rail, one sweep per DesignTokens.tMove × 5.75 (1.15s in graphite),…

Full dartdoc

The design-system Progress — the live-operation loader. An optional accent micro-label (the state word — "generating", "queued") over a 2px indeterminate track: a 28%-wide DesignTokens.accent bar sweeping a DesignTokens.accentTint rail, one sweep per DesignTokens.tMove × 5.75 (1.15s in graphite), easing with DesignTokens.easeIn. A record mid-operation server-side renders this in its row's status slot AND its detail's build state — the same treatment both places, so "building" reads identically everywhere. Division of labor (the spec states it once): content whose SHAPE is known but not yet fetched is `AppSkeleton`; a live operation the user is watching is AppProgress; never a spinner in either seat. Indeterminate only — a determinate percentage stays host anatomy (a table's progress cell, a detail fact's inline bar), not a knob here. Under reduced motion (reducedMotionOf — the framework flag OR the platform's own Reduce Motion) the bar rests static at the inline start and no AnimationController is ever created; under RTL the sweep mirrors (start → end is a reading direction). The sweep is a LOOP period, so it keeps the stock DesignTokens.tMove multiple at the desk. The track is decorative to assistive tech; the widget announces label through its own `Semantics` node when one is given. ```dart const AppProgress(label: 'generating') const AppProgress(label: 'queued') const AppProgress() // track alone — the word is carried elsewhere ```

AppProgressdefault constructor

Knob Type Default What it does
label String? null The state word, rendered as the accent micro-label above the track. Null (or empty) renders the track alone — the posture for a host that already states the word beside it.

SyncStatusButton widget

App-bar sync control. A split pill: the main zone fires a one-tap manual sync (and is the retry affordance when a cycle failed); the trailing caret zone opens the detail panel.

Full dartdoc

App-bar sync control. A split pill: the main zone fires a one-tap manual sync (and is the retry affordance when a cycle failed); the trailing caret zone opens the detail panel. The main zone is enabled in EVERY phase, `syncing` included: a tap during an in-flight cycle is the user's request that the engine promotes to a visible, spinning cycle, so onSyncNow must tolerate being called while a cycle runs (the sync worker's `runOnceFromUser` does — it promotes and queues, never double-runs). **Icon-only at every window class** (`design-system/components/navigation/ AppBar.prompt.md`, pin `839c92d`), and it **rests on the sync glyph** — the idle-with-nothing-queued state is not a ✓. Both were labeled/checked until 2026-08-12: a verb beside the glyph competed with the bar's one labeled control (the slab primary) and made the pill's width jump between window classes, and a checkmark reads as a status readout, which is not something a field user taps — so the force-a-sync affordance vanished exactly when the queue was empty. Up-to-date is stated by the tooltip and the absent badge. `offline` and `failed` still swap the glyph: those are conditions, not a completed job. Drops into a scaffold's header-bar action slot. Consumes plain streams plus an onSyncNow callback, so it carries no dependency on the sync engine. Optionally takes onCancelOne / onDiscardAll / onEvacuateOne callbacks for the detail panel's row-resolution affordances; each is independently null-able, and an unwired one renders no control at all rather than a disabled one.

SyncStatusButtondefault constructor

Knob Type Default What it does
offline SyncOfflineLine? null THE OFFLINE LINE (`SyncStatus.d.ts`, steady-core #1920): the app on screen and its offline readiness, which the detail panel states under its status sentence, and keeps live while it is open. The host passes it only inside an app it keeps ready offline; null draws no line.
onCancelOne Future<void> Function(String intentId)? null Optional: cancel a single queued change by intent id. When non-null, the detail panel renders a Cancel-X on each row.
onDiscardAll Future<void> Function()? null Optional: cancel every queued change. When non-null AND there are pending changes, the detail panel footer shows a destructive "Discard all" button.
onEvacuateOne Future<void> Function(String intentId, {String? note})? null Optional: send a REJECTED or STUCK change to the team and give up on it locally. When non-null, the detail panel renders the labeled send-to-support action on problem rows only (failed or stuck — never plain queued). Wire it to `PendingChangesController.evacuateOne`, which ships the intent before it…
onSyncNow * VoidCallback — Invoked when the user taps the main zone to sync now.
pendingChanges * Stream<List<PendingChange>> — Stream of the queued local changes awaiting sync. Listened again on every mount, like syncState.
syncState * Stream<SyncState> — Stream of the sync engine's condition. Both streams are LISTENED AGAIN on every mount, and this control is remounted without its host rebuilding: `AppScaffold` re-parents its bar when the window crosses the expanded class. So each must take a second listen — `SyncWorker.syncState`, a drift…

AppEmptyState widget

The design-system EmptyState — a centered "nothing here" surface stating what the view would show plus exactly one way out. Anatomy (per `EmptyState.d.ts` / `.prompt.md`): a title (a flat fact, e.g.

Full dartdoc

The design-system EmptyState — a centered "nothing here" surface stating what the view would show plus exactly one way out. Anatomy (per `EmptyState.d.ts` / `.prompt.md`): a title (a flat fact, e.g. "No records match." — never an apology), an optional one-line message (cause or consequence, e.g. "4 filters applied." — never decoration), and an optional single actionLabel + onAction pair rendered as one ghost/outline AppButton (the primary action of the empty surface; the accent slab belongs to the app bar, never here). **No illustration, no decorative icon** — the constitution bans both for this surface, and the spec's Flutter mapping calls this out explicitly: "no `Illustration` slot exists on purpose." There is no icon slot on this widget at all. Centers itself in whatever space its parent gives it (typically the body of a list/table/detail view once loading resolves to zero rows). Adaptation strategy: **scale** (density only — the same anatomy at every M3 window class). ```dart AppEmptyState( title: 'No records match.', message: '4 filters applied.', actionLabel: 'Clear filters', onAction: clearFilters, ) const AppEmptyState(title: 'No photos on this record.') ```

AppEmptyStatedefault constructor

Knob Type Default What it does
action Widget? null The ONE action as a slot (`EmptyState.d.ts` `action` — "ONE action (ghost/outline Button)"), for a caller whose action is not the default ghost: a related detail section's accent outline "+ Add" (`DetailView.d.ts`, THE EMPTY FACE CARRIES ITS ONE ACTION). Renders in the action position; null (the…
actionLabel String? null Label for the single action button. Renders only when both actionLabel and onAction are non-null.
actionNavigates bool false THE ACTION THAT ONLY OPENS A PAGE (`ActionStrip.d.ts`, #1744): the actionLabel / onAction button pushes another screen and writes nothing, so it reports a link to assistive technology. Declared, never inferred from onAction. A caller passing action sets `navigates` on its own button instead.
message String? null One factual line of cause or consequence — e.g. "4 filters applied." Null renders no second line. Rendered as DesignTokens.body in DesignTokens.ink60 (the web `.fb-empty__detail` role).
onAction VoidCallback? null Callback for the single action button. Renders only when both actionLabel and onAction are non-null.
title * String — What this view shows, stated flat — e.g. "No records match." Rendered as DesignTokens.bodyEm (title text emphasized, per the web `.fb-empty__title` role: body size, weight 600). Never phrased as an apology.

AppRouteFace widget

The kit's face for a router with no screen to show: the cause stated flat and one way out, to the start location. A whole route surface: it brings its own Scaffold, because a router's error page is pushed with no chrome above it.

Full dartdoc

The kit's face for a router with no screen to show: the cause stated flat and one way out, to the start location. A whole route surface: it brings its own Scaffold, because a router's error page is pushed with no chrome above it. ```dart GoRouter( routes: ..., errorBuilder: (context, state) => AppRouteFace( reason: AppRouteFaceReason.notFound, location: state.uri.path, onHome: () => context.go('/'), ), ); ```

AppRouteFacedefault constructor

Knob Type Default What it does
location String? null The path that matched nothing, stated under the not-found title so the reader sees what they asked for. Pass the PATH only, never the query: a query can carry a token. Ignored when the navigation failed, because the path is not the cause there.
onHome * VoidCallback — Navigates to the start location. The face's ONE action.
reason * AppRouteFaceReason
notFound · failed
— Which of the two faces to show.

AppSkeleton widget

The design-system Skeleton — a structural loading placeholder. Renders a flat block filled with DesignTokens.ink4, radius DesignTokens.rS (a true circle for AppSkeletonVariant.circle), in the shape of the content that will load. **Never a spinner** — the constitution's "loading = structural…

Full dartdoc

The design-system Skeleton — a structural loading placeholder. Renders a flat block filled with DesignTokens.ink4, radius DesignTokens.rS (a true circle for AppSkeletonVariant.circle), in the shape of the content that will load. **Never a spinner** — the constitution's "loading = structural placeholders" rule: compose several AppSkeletons (or AppSkeletonLine for a text-line stack) in the real layout's geometry so the loading state previews the coming content. Carries a quiet opacity pulse (1.0 → 0.45, looping, easing via DesignTokens.easeIn / DesignTokens.easeOut) — no shimmer sweep, no stagger. The pulse is **disabled under reduced motion** (reducedMotionOf — the framework flag OR the platform's own Reduce Motion); the block renders static (opacity 1.0) instead. The pulse is a LOOP period, so it keeps the stock DesignTokens.tMove multiple at the desk rather than riding the desk's tighter band. The widget is `ExcludeSemantics`d — it is decorative chrome, not content; wrap the loading region itself in a live-region `Semantics` at the call site if an announcement is wanted. ```dart // A detail-view hero placeholder. const AppSkeleton(variant: AppSkeletonVariant.block, width: 320, height: 160) // An avatar placeholder. const AppSkeleton(variant: AppSkeletonVariant.circle, width: 40, height: 40) // A text-line stack (see AppSkeletonLine for the convenience wrapper). const AppSkeleton(width: 120) ```

AppSkeletondefault constructor

Knob Type Default What it does
height double? null Placeholder height. Defaults to kSkeletonLineHeight for AppSkeletonVariant.line and AppSkeletonVariant.circle; required in practice for AppSkeletonVariant.block since it has no default.
variant AppSkeletonVariant
line · block · circle
AppSkeletonVariant.line Shape variant — governs geometry defaults. Defaults to AppSkeletonVariant.line.
width double? null Placeholder width. Null fills the parent's available width (the common case for a text-line placeholder in a `Column`); required in practice for AppSkeletonVariant.block since it has no default.

AppSkeletonLine widget

Convenience wrapper: a stack of AppSkeletonLine.lines text-line placeholders, gapped by kSkeletonLineGap, the last line rendered at 60% width (mirrors the web Skeleton's `lines` prop — a paragraph tapering to its final line).

Full dartdoc

Convenience wrapper: a stack of AppSkeletonLine.lines text-line placeholders, gapped by kSkeletonLineGap, the last line rendered at 60% width (mirrors the web Skeleton's `lines` prop — a paragraph tapering to its final line). ```dart const AppSkeletonLine(lines: 3) ```

AppSkeletonLinedefault constructor

Knob Type Default What it does
lines int 1 Number of stacked lines. The final line renders at 60% width regardless of width so the stack reads as a tapering paragraph. Must be >= 1.
width double? null Width applied to every line. Null fills the parent's available width.

AppBulkBar widget

The design-system BulkBar — a contextual bar that appears while rows are selected, replacing (or docking above) the surface it describes. Styled AS a selection object, per the constitution's selection grammar: a 2px DesignTokens.edge-wide accent start-edge spanning the bar's full height, over a…

Full dartdoc

The design-system BulkBar — a contextual bar that appears while rows are selected, replacing (or docking above) the surface it describes. Styled AS a selection object, per the constitution's selection grammar: a 2px DesignTokens.edge-wide accent start-edge spanning the bar's full height, over a DesignTokens.rowSel tint wash — the same encoding a selected table row carries, so the bar reads as "this IS the selection," not a floating toolbar. Anatomy, leading to trailing: a leading ✕ (AppIconButton) that clears the selection (onExit), the selection count in the data font at em weight (DesignTokens.dataEm) plus a noun label, a flexible spacer, and the caller-supplied actions — **outline (or ghost) `AppButton`s only**. There is never an accent slab here: the bar already carries the one accent statement (the start edge), and bulk operations are peers, not a single primary action. A destructive bulk action (e.g. Delete) is a danger-tone outline button; the caller confirms or offers undo, this widget never gates the tap itself. AppBulkBar renders unconditionally for whatever count it's given — the "hide at zero" rule from the spec is the **caller's** concern (a host simply stops building this widget when its selection empties), matching the Widget-slot composition policy: no `showX` boolean paired with a value prop. Adaptation strategy: **scale** (density only — the same anatomy at every M3 window class; on compact widths the actions cluster wraps rather than truncating, same as `AppButton` rows elsewhere in the kit). The bar is a live region (`Semantics(liveRegion: true)`) so assistive tech announces the selection count as it changes. ```dart AppBulkBar( count: 3, onExit: () => setState(() => selection.clear()), actions: [ AppButton(variant: AppButtonVariant.outline, label: 'Assign', onPressed: assign), AppButton(variant: AppButtonVariant.outline, label: 'Export', onPressed: export), AppButton( variant: AppButtonVariant.outline, tone: AppButtonTone.danger, label: 'Delete', onPressed: delete, ), ], ) ```

AppBulkBardefault constructor

Knob Type Default What it does
actions List<Widget> const [] The bulk-operation actions, supplied by the caller as already-built widgets — an `AppButton` per action, **outline or ghost only**. Never a `slab`: the bar already carries the surface's one accent statement (the selection start edge), and bulk operations are peers. Empty renders no trailing cluster.
count * int — Selection size, rendered in the data font at em weight (DesignTokens.dataEm/DesignTokens.ink).
exitLabel String 'Clear selection' Tooltip and accessible name for the leading ✕.
noun String 'selected' The noun following count (e.g. `'selected'`, `'records selected'`), rendered in DesignTokens.body/DesignTokens.ink60.
onExit * VoidCallback — Callback for the leading ✕ that clears the selection. The bar itself holds no selection state — this is the only way selection mode exits from within the bar.

AppModeline widget

The design-system Modeline — the estate's TRANSIENT message band (`Modeline.d.ts`, transient-only since 2026-08-24 Phase D). A thin hairline strip locked at the `--mode-h` 28px component height.

Full dartdoc

The design-system Modeline — the estate's TRANSIENT message band (`Modeline.d.ts`, transient-only since 2026-08-24 Phase D). A thin hairline strip locked at the `--mode-h` 28px component height. It DRAWS only while it has something to carry — a message with its one actionLabel (the Undo pattern: a reversible destructive action offers undo here instead of a confirm dialog), or, in the footer's ONE sanctioned case, live work-state segments (the form's save-bar line). The standing records segments, the posture word, and the band's scope segment are DELETED — the counts restated what the surface shows, and the scope leads the bar's subtitle (`AppScaffold.scope`). **THE SILENT REGION** (`Modeline.d.ts` SILENT REGION, THE COLLAPSE IS KEYED PER KIT; #483). Silent, the strip draws nothing and reserves no height, but it is NOT removed: the same element — and the same `liveRegion` semantics node — stays mounted, so a message arriving is a text change inside a region assistive tech already knows, never a region mounting WITH its first message (announced unreliably). A zero-size box will not do on this kit: a semantics node whose rect is empty is dropped from the tree. So the strip keeps its full 28px LAYOUT and is laid OVER the surface's edge — above a footer's bottom edge, below a band's top edge — unclipped, painting nothing and taking no hits, while its reserved height is zero. A host therefore keeps an AppModeline mounted for as long as its surface can message; mounting it on demand is the same defect by another route. The strip ENTERS AND LEAVES on the sheet band (AppMotion.move; a jump-cut under reduced motion): its reserved height grows or shrinks while it fades, and the leaving strip keeps the message it was carrying until it has gone. No clip is ever involved — a clip would empty the region's rect for the flight and drop the node this widget exists to keep. AppModeline owns no `Timer` and no auto-dismiss clock. The host decides when a message appears and clears it (e.g. a delayed callback that sets message back to null after ~6 seconds). The live region is the MESSAGE ALONE — never the action, because an interactive element inside a live region is re-announced by everything around it — and never the ambient segments. **An overflowing message ellipsizes; the action keeps its room** (`Modeline.d.ts`, #760): the message is the one shrinking item, the action never yields, and in the segmented footer the message yields before the segments. **A message may be dismissed** (`Modeline.d.ts`, 2026-10-11, #1342): with onMessageDismiss wired and a message carried, the strip's LAST item is a quiet "Dismiss" ✕ after the action, and Escape with focus inside the strip calls it too. **Two postures** (AppModelinePosture): the footer at the surface's bottom edge, or the band the shell composes under the app bar. One channel per view, never both. ```dart AppModeline( message: '3 records deleted', actionLabel: 'Undo', onAction: undoDelete, onMessageDismiss: clearMessage, ) AppModeline(segments: ['2 required', '1 invalid']) // form save bar ```

AppModelinedefault constructor

Knob Type Default What it does
actionLabel String? null Label for the single trailing action, beside message (the Undo pattern). Renders only when onAction and message are also non-null — the message-less standalone action (the retired posture-action pattern, Modeline.d.ts 2026-08-24) does not render: a transient band has nowhere to persist a control.
message String? null The transient message, e.g. `'3 records deleted'`. Non-null renders it at the strip's trailing edge in DesignTokens.dataS/DesignTokens.ink. The host owns timing: set this when the transient event occurs, clear it after its own duration.
onAction VoidCallback? null Callback for the single trailing action. Renders only when actionLabel is also non-null.
onMessageDismiss VoidCallback? null A MESSAGE MAY BE DISMISSED (`Modeline.d.ts`, 2026-10-11, #1342). Wired while a message is carried, the strip's LAST item is a quiet dismiss control after the action — KitGlyph.x at 12px in DesignTokens.ink60, named and tooltipped "Dismiss", with the quiet hover (an DesignTokens.ink4 wash, the glyph…
posture AppModelinePosture
footer · band
AppModelinePosture.footer Where the strip sits — AppModelinePosture.footer (default) or AppModelinePosture.band.
segments List<String> const [] Live work-state items — the FOOTER's one sanctioned segments consumer (the form's save-bar line, e.g. `['2 required', '1 invalid']`). Rendered in DesignTokens.dataS/DesignTokens.ink60, separated by a `·` divider in DesignTokens.ink40, hugging the strip's leading edge. A records surface passes none…

AppModelineAction widget

The strip's single trailing action — a compact data-font accent affordance (`.fo-mode__act`), NOT an `AppButton`: a 44px control would inflate the geometry-locked 28px strip. PUBLIC since 2026-08-24: this grammar is also the app bar's TRANSIENT affordance (the dashboard's relocated arrange-gesture…

Full dartdoc

The strip's single trailing action — a compact data-font accent affordance (`.fo-mode__act`), NOT an `AppButton`: a 44px control would inflate the geometry-locked 28px strip. PUBLIC since 2026-08-24: this grammar is also the app bar's TRANSIENT affordance (the dashboard's relocated arrange-gesture Undo — `Dashboard.d.ts` "a transient data-font accent 'Undo' affordance"), so the records chrome renders the same atom rather than a second implementation or a full `AppButton` in the wrong type role. A bar host passes minHeight (`DesignTokens.ctl`) so the hit region meets the touch floor the 28px strip cannot host.

AppModelineActiondefault constructor

Knob Type Default What it does
label * String — The action's text ("Undo") — data-font accent at emphasis weight.
minHeight double? null Minimum hit-region height. Null keeps the strip's own 28px geometry; a bar host passes `DesignTokens.ctl` to meet the touch floor.
onAction * VoidCallback — Invoked on tap.

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.

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 —

SyncOfflineLine support

THE OFFLINE LINE's value (`SyncStatus.d.ts`, steady-core #1920): the app on screen and its readiness, which the sync panel states under its status sentence. The host builds it for an app it keeps ready offline, named by its display name, and passes none anywhere else.

Field Type Default What it does
readiness * OfflineReadiness — How ready the app is.
subject * String — The app's display name, verbatim. A blank one draws no line.

Where it lives

Class File
AppBanner packages/app_widgets/lib/src/kit/feedback/banner.dart
AppStatusPill packages/app_widgets/lib/src/kit/feedback/status_pill.dart
AppStatusMark packages/app_widgets/lib/src/kit/feedback/status_mark.dart
AppProgress packages/app_widgets/lib/src/kit/feedback/progress.dart
SyncStatusButton packages/app_widgets/lib/src/sync_status/sync_status_button.dart
AppEmptyState packages/app_widgets/lib/src/kit/feedback/empty_state.dart
AppRouteFace packages/app_widgets/lib/src/floorplans/route_face.dart
AppSkeleton packages/app_widgets/lib/src/kit/feedback/skeleton.dart
AppSkeletonLine packages/app_widgets/lib/src/kit/feedback/skeleton.dart
AppBulkBar packages/app_widgets/lib/src/kit/data/bulk_bar.dart
AppModeline packages/app_widgets/lib/src/kit/data/modeline.dart
AppModelineAction packages/app_widgets/lib/src/kit/data/modeline.dart