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

Dashboard

Reach for this when several measures share one screen and one narrowing — a snap grid of tiles, each never blank. A dashboard SCREEN is the records floorplan with its dashboard body (RecordsScreen + the dashboard slot); these are the parts it assembles. A tile's tap filters or opens, never both: a filtering tap scopes its value and every tile that reads that dimension reacts, because the bus broadcasts the predicate and excludes each tile from its own dimension, while an OPEN table's row or a map pin opens its record and writes nothing. A tile body insets flow content by 12 and leaves a table, a map or a platter flush — inset: overrides the inference from fillsBody: — and a grouped body is LabeledSection over a category grid, per group.

Spec of record: design-system/components/charts/Dashboard.d.ts

13
Knobs
4
Variants

Variants

Rendered scenarios from playground/scenarios.dart — the same registry the golden baselines gate on, so what you see here is what CI holds the widget to.

The assembled dashboard
records_dashboard
A grouped navigation tile — carded, welded and open
dashboard_grouped_tile
The board row — five boards under the bar, above the chips
dashboard_board_row
The board row, dark
dashboard_board_row_dark

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_dashboard
Knobs
Theme

Author-declared per tile, never a viewer setting. react filters, highlight marks, independent ignores the bus.

Shows when you tap a row in any tile — every tile on this screen FILTERS — and this decides how the other tiles react — press Focus to make the preview interactive.

The fixed-layout posture — no arrange mode, no layout panel and zero resting bar actions, for a board whose composition is the author's decision.

Adds four more named boards, two with long names, which is what makes the board row appear at all — a single board renders no tabs rather than a disabled one. The row is navigation: the first band under the bar, above the chip row, full width, its first tab on the tiles' inline edge. Each board owns its own arrangement; the narrowing is board-independent, a FILTER table's key scopes included, so switching keeps it.

Shows when you narrow the device to a phone — five boards overflow the row, which scrolls inline with an edge fade, and the active board is always scrolled into view — press Focus to make the preview interactive.

carded is framed tiles on the surface with 12px gutters; welded is tiles that abut and share one 1px rule, empty grid space left as surface; open is one paper field of frameless tiles, 24px apart.

viewport — every board's default — solves the row unit so the rows fill the plate exactly, shrinking to the row floor (80, or 70 compact) and scrolling past it; rows is the opt-out, the fixed 94 unit. Any board, locked or arrangeable: arranging freezes the unit for the length of a drag and re-solves on release.

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.

Dart for these values
screen_dashboard()

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.
The dashboard bus
  • **A tile's tap FILTERS or OPENS, never both.** A filtering tap — a bar, a list row, a FILTER table's row — scopes its value onto the bus, and every other tile that reads that dimension reacts; the emitting tile does not filter on its own dimension. An OPEN table's row, a FILTER row's Open action and a map pin open the record and write nothing.
  • **Applied scopes ride the chip row** as ordinary removable chips — one filter system, never a second population beside it.
  • **Clear all** raises an Undo on the modeline, which is the board's only transient channel.
  • **Arrange** — outside locked, the bar's arrange toggle enters and exits the mode. Outside it a tap always means data; inside it none does.
  • **Scroll** — every board fills its plate, and one that cannot, even at the row floor, scrolls itself: drag anywhere, and a drag or fling that reaches a table's end carries on into the board. The thumb shows while it overflows.

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.

DashboardGrid widget

The snap-grid plate. Sizes itself to its content height (host it in a vertical scroll view); in the FOCUS posture it renders the focused tile alone, filling focusHeight.

Full dartdoc

The snap-grid plate. Sizes itself to its content height (host it in a vertical scroll view); in the FOCUS posture it renders the focused tile alone, filling focusHeight. Content height is `rows × the row unit + gaps`. A FIXED unit lands no integer row count on an arbitrary plate, so under DashboardFit.viewport — the default, on every board (Dashboard.d.ts EVERY BOARD FILLS ONE VIEW, 2026-10-01, #968) — the unit is SOLVED against fitHeight, the host surface's measured height (dashboardFitUnit): `rows × unit + gaps` is exactly the plate, stretching with no ceiling and shrinking every tile in proportion until the first one reaches its KIND's minimum (minHeightOf); only then does the board hold that unit and scroll. Below 600 a row is never less than an eighth of the plate. A host that cannot measure degrades to the fixed unit. DashboardFit.rows opts out. ARRANGE UNDER FILL: the unit is FROZEN from a pointer layout gesture's start to its end, so the resize corner stays under the finger and the snap pitch never moves mid-drag; on release the board re-solves and every tile glides to its new rect at the move band. A keyboard step re-solves at once — there is no gesture to hold a pitch for — and the snap-grid overlay always draws the resolved pitch. Arrange mode: a TOUCH or PEN layout gesture begins with a HOLD (2026-08-24); a MOUSE press arms on its drag instead — past the pan slop it moves or resizes at once, with no hold (ARM BEFORE MOVE split by pointer class, #934), because a mouse press never scrolls the board and the hold guarded nothing there. A hold anywhere on the tile — the injected drag grip included, since it carries no recognizer of its own — lifts it for reorder, splicing the order LIVE by hit-testing the tile under the pointer; a hold on the bottom-end resize corner — the RESOLVED `--touch` square (DesignTokens.touch: 44 on touch, 32 at a fine pointer, #696) — (2px accent border on the end + bottom edges) commits whole grid steps through onResize (the bus clamps 2…12 × 1…8). The corner absorbs its own pointer, so the two can never contend for one press. The hold is not a flourish. A pan-started drag loses the gesture arena to any enclosing scrollable on every DOWNWARD gesture — a vertical drag recognizer claims the pointer at `kTouchSlop` while a pan waits for `kPanSlop`, twice as far — so the corner could be dragged sideways and never down, and the grip could not move a tile toward the bottom of the plate. A delayed recognizer accepts on a timer, before any movement, which is the only arming that makes the vertical axis reachable at all. A quick flick still falls through to the plate's scroll. Arrange motion (the Dashboard.d.ts 2026-08-23 amendment): placement changes GLIDE — every non-dragged tile and its resize corner tween position and size at the move band, so the live splice, each resize step, and surviving ids across a preset/visibility change read as tiles making room. The dragged tile leaves grid flow: it lifts (kDashboardLiftScale, quick band), paints above every sibling, and tracks the pointer by its grab offset while its destination cell renders the applied-grammar placeholder; release settles it in at the move band. Selection-tick haptics per committed splice/resize step. Reduced motion zeroes durations via AppMotion; the lift, the placeholder, and the haptics remain — state indication, not decoration. A RE-SOLVE IS NOT A PLACEMENT (Dashboard.d.ts THE CHROME ABOVE THE PLATE; THE DESK POSTURE, #735). The same glide carries two causes: an ARRANGE move — the placements changed (a splice, a resize step, a board switch, a visibility toggle) or a layout gesture was released — and a plate RE-SOLVE, where the placements did not change and the plate's measure did (the chip row, the arrange banner or the board tab row above it; a width). On touch both glide. At the desk a re-solve CUTS to its new layout — every tile gliding under the pointer when a search keystroke raises the chip row reads as the board moving on its own — while an arrange move keeps the move band, because there the user IS placing tiles. Arrange completion (the Dashboard.d.ts 2026-08-24 amendment): a LAYOUT GESTURE's pointer inside kDashboardAutoScrollBand of the hosting scroll viewport's top/bottom edge auto-scrolls the board (linear ramp to kDashboardAutoScrollMaxVelocity; stays on under reduced motion — function, not decoration; a grid hosted in no scrollable simply doesn't). BOTH gestures ride it (2026-09-03): a lift re-tests its splice as content moves, and a resize spends what the board scrolled as drag travel, so a corner dragged past the viewport edge keeps sizing instead of being lost until the pointer lifts. A drag over unoccupied plate space splices to the END of the order. The grip and resize corner are real focusable controls — arrows move one order step / resize one grid step (each press one gesture through the same boundaries), Escape fires onExitArrange. Arrange draws the snap grid as hairline gutter rules (every seam; never in the settled read tree). A held tile eases to kDashboardPreLiftScale over the hold delay (dropped under reduced motion). seam sets the gutter, the board's inset and who draws the rules (THE THREE SEAMS, DashboardSeam). The grid owns the welded rules and the welded and open seams' one card; tileBuilder passes the same seam to `DashboardTile.seam` so each tile shapes its own card and head. TWO WIDTHS: the LAYOUT width is the plate the grid is given, and the tracks divide it. The BAND width (bandWidth) decides the column count and every collapsed span — the plate's own width, unless the host holds it: a record pane that PUSHES the board holds it at the plate's width before the pane docked (Dashboard.d.ts THE RECORD PANE HAS THREE POSTURES AT >=960), so the board narrows without re-flowing.

DashboardGriddefault constructor

Knob Type Default What it does
arrange bool false Arrange mode — resize corners + grip-driven reorder.
bandWidth double? null The BAND width: the width that decides the column count (dashboardColumnsFor) and every collapsed span (dashboardCollapsedSpan), apart from the LAYOUT width the tracks divide. Null — the default — is the plate's own width, so a grid that narrows re-flows to its new band. A host HOLDS it to narrow…
compactDensity bool false FORCES the compact row unit, 94 → 82. Unforced, the unit follows the ambient token density (DesignTokens.density), so it goes compact with every control beside it while a fine pointer is attached — the web kit's `(pointer: fine)` block in `Dashboard.css`.
fit DashboardFit
rows · viewport
DashboardFit.viewport How the board spends the plate's vertical space. DashboardFit.viewport (the default) solves the row unit against fitHeight — down to the unit that keeps every tile at its kind's minimum (minHeightOf), with no ceiling; DashboardFit.rows is the opt-out, the density's fixed unit. Allowed with arrange:…
fitHeight double? null The plate height a DashboardFit.viewport board solves its row unit against — the host SURFACE's own measured height, the same thing focusHeight carries and measured the same way (a `LayoutBuilder` on the surface, never the screen). Null, non-finite, or DashboardFit.rows leaves the density's fixed…
focus String? null The focused tile id — renders that tile alone, full plate.
focusHeight double? null The focus posture's height; null falls back to six rows.
minHeightOf double Function(String id)? null Each tile's MINIMUM HEIGHT in logical pixels, its KIND's (dashboardTileMinHeight; Dashboard.d.ts EVERY BOARD FILLS ONE VIEW, THE KIND MINIMUMS, 2026-10-01, #968): under DashboardFit.viewport the unit never solves below the one that keeps every placed tile at its minimum, so the board scrolls only…
onExitArrange VoidCallback? null Escape pressed on a focused arrange handle — the keyboard mirror of the arrange bar toggle; the host exits arrange (Dashboard.d.ts arrange-completion clause). Never fired outside arrange.
onGestureEnd ValueChanged<DashboardArrangeGesture>? null Reports the gesture's end (release or cancel). An arrange exit mid-drag may drop the pair's end — the bus discards its open snapshot on arrange exit, so an unmatched start is harmless.
onGestureStart ValueChanged<DashboardArrangeGesture>? null Reports an arrange gesture's start — once per reorder drag (at lift) and once per resize drag (at grab). The bus's one-deep layout undo snapshots the pre-gesture arrangement on it (Dashboard.d.ts 2026-08-23); the live onReorder/onResize splices keep firing between the pair.
onReorder ValueChanged<List<String>>? null Reports the spliced id order live during a grip drag.
onResize void Function(String id, int w, int h)? null Reports a whole-step resize (the bus clamps).
resizeReadout String? null The live resize readout ("6 × 3", "12 × 3 · at limit"), rendered ON the tile being resized (Dashboard.d.ts 2026-08-24 — it used to ride the modeline, stating the span on a strip at the far edge of the screen while the thing being sized was under the finger). The host owns the STRING because the host…
rowFloorOf int Function(String id)? null Each tile's least row span (`Dashboard.d.ts` A HEADED TILE IS AT LEAST TWO ROWS): the layout renders a shorter placement at it, and the resize corner's felt step stops there. The host knows each tile's face (`dashboardTileRowFloor`); null floors every tile at 1.
seam DashboardSeam
carded · welded · open
DashboardSeam.carded The plate seam — carded (the default), welded or open.
tileBuilder * DashboardTileBuilder — Builds each tile (null skips an unknown id).
tiles * List<DashboardTilePlacement> — The placements, in flow order. Pass a FRESH list instance whenever the set changes — the grid's built-tile cache keys on this list's identity (alongside width, arrange, seam, density, tokens and the DashboardBoardScope's answer), so a memoized list mutated in place would serve stale tile subtrees.…

DashboardTile widget

The dashboard tile shell — THE HOUSE CARD with its card head (or the readout face) over the body slot, or the never-blank empty face when emptyReason is set. THE BOARD IS CARDS IN THE WORKSPACE (`Dashboard.d.ts`, Contained, 2026-09-29, #895).

Full dartdoc

The dashboard tile shell — THE HOUSE CARD with its card head (or the readout face) over the body slot, or the never-blank empty face when emptyReason is set. THE BOARD IS CARDS IN THE WORKSPACE (`Dashboard.d.ts`, Contained, 2026-09-29, #895). In seam: * `carded` — the tile IS a card: DesignTokens.paper, the DesignTokens.cardEdge hairline, DesignTokens.rM corners its content is clipped to, DesignTokens.sh1. The edge takes layout space, as the web kit's CSS border does, so the content sits one hairline in. * `welded` — the BOARD is the card: the tile paints the card's paper and no frame of its own, and the grid draws each tile's rule into the 1px gutter around it, so neighbors share one. * `open` — the board is one card, a paper field: the tile draws no frame, separated from its neighbors by space alone. When the tile SOURCES an applied filter (isFilterSource) a 2px accent edge runs its inline-start side, INSET INSIDE the card and clipped by its corners, in every seam, head included. It takes layout space too: a table row's own 2px selection edge sits beside it, never beneath it — a FILTER table tile's normal posture is a source whose rows are selected. THE CARD HEAD (face DashboardTileFace.card): DesignTokens.cardHeadH (48) tall, no fill of its own, padded 16 at the inline start and 12 at the end, members 10 apart, over a DesignTokens.hair rule in EVERY seam — a hairline inside the card. In order: drag grip (arrange only, injected by the grid via dragGrip) · index (the data role at 11, ink40, hidden below 3 columns) · title (THE TITLE ROLE, ink, sentence case, VERBATIM — the kit renders the string it is given and never transforms it) · the drill breadcrumb (an applied AppChip whose ✕ clears the drill) · the slack · legend (a chart content's, on ONE line — `Charts.d.ts` THE LEGEND RIDES THE CARD HEAD) · meta (the label role, ink40, hidden below 4 columns) · Add · the focus toggle, the head's ONE QUIET ICON ACTION (kDashboardHeadActionDesk at the desk, the `--touch` 44 on touch). THE READOUT FACE (face DashboardTileFace.readout): no head band and no rule. The tile pads the KPI (THE FRAME PADS, kpiPadding) and draws its LABEL LINE — the grip in arrange, title in the KPI label style, the focus toggle at its end — then the KPI, in the posture rowSpan gives it through KitKpiPostureScope: stacked at two rows or more, inline at one. The index, the meta, the breadcrumb and Add do not render. The tile is its body's frame in every seam: a table in the body draws no box of its own (Dashboard.d.ts A TABLE IN A TILE BODY DRAWS NO FRAME OF ITS OWN), which the body declares to it through a kit-internal scope. THE TRAILING CONTROL IS SINGLE AND CONSTANT (the 2026-08-21 owner amendment): the focus toggle alone at every span, PINNED to the trailing corner — the slack sits between the title and the meta cluster, never after the toggle. Retirement applies to index and meta only. Under pressure the title and the meta cluster split the remaining room EVENLY, each ellipsizing within its half — strict title-first yield is not expressible in one flex pass without either un-pinning the corner control or letting the cluster overflow it, and a loose Flexible's cap is its allocation, so biasing the weights would clip the meta at rest instead. While arrange is on the BODY ignores pointers — outside arrange mode a tap always means data; inside it, none does. THE TILE BODY IS THE SCROLL BOX (Dashboard.d.ts, 2026-09-27). The body's behavior is DECLARED by its content through fillsBody, never detected from its constraints: * `false` (the default) — the body scrolls child vertically at its own height, never less than the body's. The scroller takes the ambient physics, so its edge overscroll CHAINS to the board exactly as a tile scroller's does (THE BOARD SCROLLS ITSELF): nothing here contains it. * `true` — the body hands child its exact box and clips, and the child scrolls itself: a table, a map, a chart platter, or any host content that is its own scroller. The empty face ignores fillsBody: it fills the body and scrolls within it when a short tile cannot hold it, in both postures. A TILE BODY INSETS FLOW CONTENT (Dashboard.d.ts, 2026-09-29). inset DECLARES the body's padding, and a null one is inferred from fillsBody (resolvedInset): content that fills the body's exact box owns its edge and stays flush, while content the body scrolls is inset by kDashboardTileContentInset. In the scrolling body the inset is the scroll view's own padding, so the content scrolls under it and its last line rests one inset above the frame. The empty face and a readout ignore it. A stat card in the body draws no frame of its own (Card.d.ts THE TILE IS A STAT CARD'S FRAME), through the same kit-internal scope a table reads. A TILE BODY MAY LEAD WITH A BAND (Dashboard.d.ts, 2026-10-01, #882). band is a row of members (stats, say) laid inside the body above its content: one row of EQUAL tracks kDashboardTileContentInset apart, each member stretched to the tallest, padded by the content inset on its top and both inline sides and set the same inset above the content, whatever the body's own inset. The band holds still; the content takes the rest (a fillsBody table stays flush beneath it). It stays over the empty face, a readout ignores it, and under arrange it takes no tap.

DashboardTiledefault constructor

Knob Type Default What it does
addDisabled bool false Whether the Add is shown DISABLED (`Dashboard.d.ts` A MAP TILE MAY ADD, #961; and #883): a map tile with nowhere to drop a point, or a required seed missing. Disabled, never hidden — the Add says it exists and why it cannot act yet.
addDisabledReason String? null Why the Add is disabled — its tooltip while addDisabled. Null keeps the "Add addNoun" title.
addNoun String? null The Add's noun: its accessible name and tooltip read "Add addNoun". The visible label is "Add" whatever this says — the title beside it already names the records. Null reads "Add".
arrange bool false Arrange mode — shows dragGrip and suppresses body pointers.
band List<Widget>? null A TILE BODY MAY LEAD WITH A BAND (`Dashboard.d.ts`, #882): members laid as one row of equal tracks above the body's content, at the content inset whatever inset resolves to; the content takes the rest of the body. A readout ignores it. Null or empty (the default): no band, no room reserved.
breadcrumb String? null The drill breadcrumb label ("North"); rendered as an applied chip whose ✕ fires onClearDrill.
child Widget? null The tile body.
clearFiltersLabel String 'Clear filters' The empty face's action label.
columnSpan int 12 The tile's RENDERED column span after band collapse — drives the head's progressive retirement, and a readout's narrow numeral rung (two columns or fewer).
dragGrip Widget? null The grid-injected drag grip (a pan target); rendered first in the head (or the readout's label line) while arrange is on.
emptyClearable bool true Whether the empty face offers onClearFilters (`Dashboard.d.ts`, 2026-09-27). Default true; false withdraws Clear for a tile a board-wide narrowing cannot explain — hydrating, scope-blocked or genuinely empty — because clearing cannot help it. ONE renderer serves every reason; only the title, the…
emptyReason String? null When set, the body renders the empty face (this reason + one Clear) instead of child — a filtered-empty tile never goes blank.
emptyTitle String? null The empty face's headline (`Dashboard.d.ts` AN EMPTY TILE STATES WHY, 2026-09-27). Null reads "No records match" — the NARROWED reason. A content that is empty for another reason states it: the hydrating face's "Records on the way", or a host's scope-blocked / genuinely empty title.
face DashboardTileFace
card · readout
DashboardTileFace.card THE CARD HEAD or THE READOUT FACE (see the class doc). Default DashboardTileFace.card.
fillsBody bool false THE TILE BODY IS THE SCROLL BOX. False (the default): the body is the scroll box — child keeps its own height and the body scrolls it, chaining its overscroll to the board. True: child is handed the body's exact box and scrolls itself; the body clips.
focused bool false Whether this tile is the focused (full-plate) one — flips the toggle glyph to collapse.
index String? null The tile's ordinal on the plate ("01"). Hidden below 3 columns, and never on a readout.
inset DashboardTileInset?
flush · content
null A TILE BODY INSETS FLOW CONTENT. DashboardTileInset.content pads the body by kDashboardTileContentInset; DashboardTileInset.flush does not. Null (the default) is inferred from fillsBody — see resolvedInset.
isFilterSource bool false Whether this tile currently SOURCES an applied filter — the 2px accent start edge (the selection grammar spent on the bus).
legend Widget? null THE LEGEND RIDES THE CARD HEAD (`Charts.d.ts` / `Dashboard.d.ts`, #921): a legend — normally a `ChartLegend` — drawn in the head after the slack, capped at ONE line (the "+N" more chip past it), 14 before the meta. The tile mounts ONE highlight channel round its head AND its body, so a chip here and…
localFilterCount int 0 How many of the tile's own facets narrow it — the glyph's badge, and its applied treatment while above zero.
meta String? null Head meta, prose in the label role ("18 of 68", "2 of 4 selected"). Hidden below 4 columns, and never on a readout.
onAdd VoidCallback? null The head's Add (`Dashboard.d.ts` A TABLE TILE MAY ADD, 2026-09-29), its seed already bound — `DashboardTableTile.onAdd` through `DashboardTileContent.onAdd`. Null renders no Add. It sits between the meta cluster and the pinned focus toggle at EVERY span (it never retires), renders over the empty…
onClearDrill VoidCallback? null Clears the drill (the breadcrumb's ✕).
onClearFilters VoidCallback? null The empty face's Clear action.
onLocalFilter VoidCallback? null The head's TILE FILTER (`Dashboard.d.ts` A RECORDS TILE MAY FILTER ITSELF, 2026-09-30, #936): an action immediately inboard of the focus toggle that opens the tile's scoped filter panel. Null renders none. Hidden under arrange; never retired.
onToggleFocus VoidCallback? null The focus toggle; null renders no toggle.
rowSpan int 2 The tile's RENDERED row span — a readout's KPI posture: stacked at two rows or more, inline at one (THE READOUT FACE).
seam DashboardSeam
carded · welded · open
DashboardSeam.carded The board seam this tile sits in — the grid's (THE THREE SEAMS): the carded tile that is its own card, the welded tile inside the board's card whose rule the grid draws, or the open tile with no frame. The filter-source edge renders in every seam.
status String? null THE READOUT CARRIES THE KPI'S STATUS (`Dashboard.d.ts`, #948): drawn on a readout's title line — the KPI's label line — as an AppStatusMark in statusTone, 8 after the title, the title ellipsizing first — only down to 64, or its own width when shorter — and then the status's word, its marker or tag…
statusTone AppStatusMarkTone?
pending · active · done · blocked
null The readout status's StatusMark tone — AppStatusMarkTone.blocked is the needs-action tag. Null draws status as the plain word.
title * String — The head's title — the title role, verbatim (sentence case is the host's copy; the kit never transforms it). Under [DashboardTileFace .readout] it is the KPI's label line.

DashboardFreshnessLine widget

The single screen-level freshness line (Δ12): "as of {last sync}" plus a `caution` "Syncing…" chip **while a cycle is in flight** — once per screen, never per unit. This chip (not the pull-to-refresh arc, Δ15) carries the ongoing in-flight state; it resolves independently when the sync lands or…

Full dartdoc

The single screen-level freshness line (Δ12): "as of {last sync}" plus a `caution` "Syncing…" chip **while a cycle is in flight** — once per screen, never per unit. This chip (not the pull-to-refresh arc, Δ15) carries the ongoing in-flight state; it resolves independently when the sync lands or times out. FRESHNESS IS A LIVE REGION (`Charts.d.ts` `FreshnessLineProps`, #1428): the line is one `Semantics(liveRegion: true)` node — polite.

DashboardFreshnessLinedefault constructor

Knob Type Default What it does
asOfLabel * String — The caller-formatted last-sync time ("2:14 PM").
syncing bool false True while a background sync cycle is running.

RecordsDashboard widget

The dashboard view pattern. Hosts DashboardGrid + DashboardTile inside RecordsChrome (one narrowing, one chip row — the bus's range and viewport ride as synthetic chips) with the TRANSIENT modeline carrying Clear-all's "filters cleared" + Undo for its moment (transient-only, Dashboard.d.ts…

Full dartdoc

The dashboard view pattern. Hosts DashboardGrid + DashboardTile inside RecordsChrome (one narrowing, one chip row — the bus's range and viewport ride as synthetic chips) with the TRANSIENT modeline carrying Clear-all's "filters cleared" + Undo for its moment (transient-only, Dashboard.d.ts 2026-08-24 — the four standing segments and the posture word are deleted), and the pop-out detail pane in its DECK posture below the threshold (the record stays beside the plate, never over it). THE RECORD PANE HAS THREE POSTURES AT >=960 (`Dashboard.d.ts`, 2026-09-25) — detailPosture: PUSH docks the pane in flow and holds the board's columns and collapsed spans at the BAND width, the plate's width before the pane docked, so only the tracks shrink; OVERLAY floats the same pane over the board's inline-end edge and leaves the layout alone; AUTO, the default, pushes while a pushed track stays at or above kDashboardReadableTrack and overlays below it. The pane is kAppDetailPaneWidth (`--dv-pane-w`) wide, measured against this surface, never the viewport. A WIDE MAP TILE OPENS ITS PIN IN ITS OWN PANE instead, and ONE RECORD IS OPEN AT A TIME across the two: the pattern holds the record a tile's own pane shows (DashboardTilePaneScope), with Escape and predictive back. ONE TAP MEANING PER TILE, ONE SELECTION MECHANISM (`Dashboard.d.ts`, 2026-09-25): a tile that shows a table or a map builds its content with `DashboardTableTile` or `DashboardMapTile`, so its primary tap has ONE declared meaning — FILTER or OPEN — and a row filter is a key scope on the bus, never a second selection state. A key dimension no facet column carries gets its chip here, labeled by its def's DashboardTileDef.emitLabel. The one-deep arrange-gesture undo rides the BAR (relocated with the band's standing removal): a transient labeled Undo affordance inline-start of the arrange toggle, present only while an undo is armed — not a resting action, so the charter's two-action ceiling stands. THE BOARD SCROLLS ITSELF (`Dashboard.d.ts` 2026-09-24): the plate is the board's OWN vertical scroller, under the chip row, the board tab row and the arrange banner, which stay put — and beside a docked pane, which never scrolls with it. Fill (fit) removes most overflow; for the rest: a tile scroller's vertical overscroll at its edge CHAINS into the board (a fling hands over its remaining velocity), tile bodies run under clamping physics so a bounce cannot absorb that edge, and with no overscroll indicator so a drag that chains does not stretch the tile too, the board carries an always-visible, draggable thumb exactly while it overflows (past kDashboardOverflowTolerance), and it tells its tiles so through DashboardBoardScope — the map tile's cue to cooperate. No scroll mode, and no entering arrange to scroll. T is the FACET vocabulary's row type — the canonical record set the chip row and filter panel read. Tiles hold their own row types inside their defs' closures.

RecordsDashboarddefault constructor

Knob Type Default What it does
band FloorplanBand? null The HOST's transient band — the floorplan's `band`. A non-blank message wins the modeline over messages and over the pattern's own, carrying its own ONE action; a blank one is not a message (FloorplanBand.resolvedMessage). Null changes nothing.
banner Widget? null A STANDING operational notice under the chip row — the floorplan's `banner`, which survives every face.
boardLabels Map<String, String> const {} The board tab row's labels, keyed by board id. A board with no entry is labeled by its id — every board before this knob, and the React twin's `boardLabels`.
bodyFace Widget? null Rendered IN PLACE OF THE PLATE, under the chrome — the floorplan's error and loading faces, which outrank a dashboard body exactly as they outrank every other body. The chrome stays, so a failed load never drops the search. Null renders the plate.
bus * DashboardBus — The filter bus — every tile is a pure function of it.
compactDensity bool false Compact density (pointer-gated by the host).
detailBuilder Widget Function(BuildContext context, String id)? null Builds the docked record's detail body.
detailId String? null The docked record's id (controlled, host-owned) — non-null docks the detail pane (side ≥960, DECK below).
detailPosture DashboardDetailPosture
auto · push · overlay
DashboardDetailPosture.auto How the docked record's pane sits at ≥960 (`Dashboard.d.ts` THE RECORD PANE HAS THREE POSTURES AT >=960): DashboardDetailPosture.push docks it in flow and holds the board's columns, DashboardDetailPosture.overlay floats it over the board's inline-end edge, and DashboardDetailPosture.auto — the…
detailTitle String Function(String id)? null The docked pane's ACCESSIBLE NAME for the open record (its id) — the region's semantics label, so a screen-reader user hears which record is docked rather than one name for every record. It names the REGION; the pane's content carries the record's own title in its own header. Null names nothing,…
facetColumns * List<RecordsColumn<T>> — The facet vocabulary (the filter panel's specs + the search columns).
facetRows * List<T> — The canonical FULL record set — facet distinct values read it, before narrowing.
fit DashboardFit
rows · viewport
DashboardFit.viewport How the board spends the plate's vertical height (`Dashboard.d.ts` EVERY BOARD FILLS ONE VIEW, 2026-10-01, #968): DashboardFit.viewport — the default, on every board, locked or arrangeable — solves the row unit so the rows fill the plate exactly, shrinking until the first tile reaches its KIND's…
hydration SyncHydration? null THE HYDRATING FACT the records floorplan hands its dashboard body (`RecordsScreen.d.ts` THE HYDRATING FACE: "the floorplan hands the same `hydration` to its tiles"). An EMPTY tile states it as the FIRST of its empty reasons (`Dashboard.d.ts` AN EMPTY TILE STATES WHY): "Records on the way" with the…
locked bool false A fixed-layout dashboard: no Board or Arrange bar action, no layout panel, and onPersistPrefs is never written — the WHOLE prefs record, not merely the layout and visibility a locked dashboard cannot change anyway. DashboardPrefs also carries `activeBoard`, so boards switch in session (a board is a…
messages TransientModelineMessage? null The host-owned transient message clock, rendered into the modeline's message slot. Null renders no transient channel.
onDetailDismiss VoidCallback? null The pane's dismissal report.
onDetailOpenFull void Function(String id)? null Escalates the docked record (its id) to the host's full-screen route — the expand affordance in the pane's chrome row. Null omits it.
onPersistPrefs ValueChanged<DashboardPrefs>? null Persists the bus's DashboardPrefs whenever the view state (active board, per-board arrangement + hidden) changes.
paneAlwaysModal bool false Whether the view-options pane is the MODAL BOTTOM sheet at every width — the records floorplan's EMBEDDED posture, where the inline-end edge belongs to the host screen (RecordsChrome.paneAlwaysModal).
panels List<RecordsPanel> const [] Host panels, rendered as extra tabs of the view-options pane before the board configuration — the records floorplan's body switch rides here when the screen offers more than one body. Empty (the default) adds none.
retapCloses bool true Whether a table tile's OPEN gesture on the row the board's open record was opened from CLOSES that record (`Dashboard.d.ts` A RE-TAP CLOSES THE OPEN RECORD, 2026-10-01, #970). True (the default): an OPEN tile's row tap or ↵, or a FILTER tile's Open action, on its ACTIVE row — the one its active edge…
seam DashboardSeam
carded · welded · open
DashboardSeam.carded The plate seam (`Dashboard.d.ts` THE THREE SEAMS): carded (the default), welded or open. The grid and every tile read the same one.
searchable bool true Whether the chrome offers search — the records floorplan's `searchable`, reaching its dashboard body. Default true.
selectionChips DashboardSelectionChips
plate · filters · reserved
DashboardSelectionChips.plate Where a row-tap selection's key chip renders (`Dashboard.d.ts` A ROW TAP NEED NOT MOVE THE BOARD): DashboardSelectionChips.plate (the default) in the chip row above the plate, DashboardSelectionChips.filters at the head of the Filter tab — and so never moving the plate. Without a facet column there…
tileIndex DashboardTileIndex
ordinal · none
DashboardTileIndex.none Whether the tile heads carry an index (`Dashboard.d.ts` THE INDEX IS A BOARD KNOB): DashboardTileIndex.none (the default since #895) omits it — slot and width — and DashboardTileIndex.ordinal fills each with its plate ordinal.
tiles * List<DashboardTileDef> — The tile declarations, in index order ("01"…).

DashboardBoardScope widget

What the dashboard BOARD tells the tiles on it about its own scroll (`Dashboard.d.ts` THE BOARD SCROLLS ITSELF, 2026-09-24): whether it overflows its plate, so the board's own scroller has somewhere to go, and the VISIBLE plate's plateHeight. The map tile is the reader it exists for.

Full dartdoc

What the dashboard BOARD tells the tiles on it about its own scroll (`Dashboard.d.ts` THE BOARD SCROLLS ITSELF, 2026-09-24): whether it overflows its plate, so the board's own scroller has somewhere to go, and the VISIBLE plate's plateHeight. The map tile is the reader it exists for. A map takes every one-finger drag and every wheel turn, so on a board that scrolls it must leave the board a way to scroll (`Dashboard.d.ts` MAPS NEVER TRAP THE BOARD): at the desk it COOPERATES (`AppMapView.gestureHandling`, `MapView.d.ts` COOPERATIVE GESTURES), and on touch it stays direct and never renders taller than plateHeight less the scroll band (A MAP ON A BOARD TAKES ONE FINGER, `DashboardMapTile.gestureHandling`). Read it with the context a tile is built in, and a tile def's own builder context is fine: DashboardGrid builds every tile in its own layout context, which sits below the scope, and it keys its built-tile cache on overflows, so a tile it has cached rebuilds the moment the board starts or stops overflowing. plateHeight is not in that key — it moves with every resize and every frame a deck rises — so read it from a widget's OWN build, whose dependency rebuilds it without the grid: ```dart builder: (context, bus, mode) => DashboardTileContent( child: AppMapView<T>( mode: geo, pins: pins, gestureHandling: DashboardBoardScope.overflowsOf(context) ? MapGestureHandling.cooperative : MapGestureHandling.direct, ), ), ``` `RecordsDashboard` publishes it from its board scroller's own metrics, the one place that KNOWS whether the board scrolls, and decides it past kDashboardOverflowTolerance. The grid cannot: under the deck hold it lays out against the plate it held, not the shorter one the deck left, and a board that scrolls above a deck is exactly where a map must cooperate. A host composing DashboardGrid in a scroller of its own publishes it the same way; with no scope at all the answer is false, the direct default.

DashboardBoardScopedefault constructor

Knob Type Default What it does
child * dynamic —
overflows * bool — Whether the board is taller than its plate — its scroller can scroll.
plateHeight double? null The height of the plate the board is SEEN through — its scroller's viewport, which a deck below it shortens — or null when the host does not know it. What a direct map tile caps itself against so the board always keeps a band to scroll by (`Dashboard.d.ts` A MAP ON A BOARD TAKES ONE FINGER, THE…

LabeledSection widget

One labeled section: an optional label (the label role in DesignTokens.ink60), an optional note rendered as a kit AppBanner, and the section's child, each labelGap apart. Stack several in a LabeledSectionStack, which spaces them by sectionGap.

Full dartdoc

One labeled section: an optional label (the label role in DesignTokens.ink60), an optional note rendered as a kit AppBanner, and the section's child, each labelGap apart. Stack several in a LabeledSectionStack, which spaces them by sectionGap. Inside a dashboard tile the section takes the tile's content inset like any flow content (`Dashboard.d.ts` A TILE BODY INSETS FLOW CONTENT), so it pads nothing of its own. ```dart LabeledSectionStack( children: [ LabeledSection(label: 'Dashboards', child: AppCardGrid.category(...)), LabeledSection(label: 'Records', child: AppCardGrid.category(...)), ], ) ```

LabeledSectiondefault constructor

Knob Type Default What it does
child * Widget — The section's body — a kit collection.
label String? null The heading, in the label role — sentence case, rendered as written. Null or empty renders no heading.
note String? null One note that applies to every row of the section, rendered once between the heading and the body. Null or empty renders none.

LabeledSectionStack widget

Stacks LabeledSections on the section rhythm: the first sits where the stack's container puts it, and each later one LabeledSection.sectionGap below the one before. It sizes to its content, so a scrolling host (a dashboard tile body, a settings surface) scrolls it.

LabeledSectionStackdefault constructor

Knob Type Default What it does
children * List<Widget> — The sections, conventionally LabeledSections.

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.

DashboardBus support

The dashboard filter bus (`design-system/components/charts/Dashboard.d.ts`) — ONE controller the whole plate is a pure function of. The bus COMPOSES a RecordsNarrowing (owner decision O2, spec 2026-08-19): categorical dimensions ARE narrowing facets, so the chip row, search, and the view-options…

Field Type Default What it does
board String? null
boards * Map<String, List<DashboardTilePlacement>> —
defaultLinkModes Map<String, DashboardLinkMode> const {}
hierarchy DashboardHierarchy const DashboardHierarchy.empty() THE DECLARED DIMENSION HIERARCHY (`Dashboard.d.ts` A DIMENSION MAY HAVE A PARENT, 2026-09-30, #956): `dim → parentDim` plus the edges from the rows the tiles hold. A write to a dimension prunes its descendants' orphaned values (setValues), and a dimension with no values of its own is scoped to an…
narrowing * RecordsNarrowing — The composed narrowing controller — facets, search, chip row.
requiredTiles Set<String> const {}
tileIds List<String> const []

DashboardPrefs support

Device-local, per-dashboard persisted view state — the active board plus one DashboardBoardPrefs per touched board, so every board keeps its own arrangement and hidden set across a switch (Dashboard.d.ts, 2026-08-24 boards amendment — MAJOR). The `TableViewPrefs` shape: tolerant decode, additive…

Field Type Default What it does
activeBoard * String — The active board id.
arrangeHintDismissed bool false Whether the user has dismissed the arrange-mode coaching banner with "Got it" (`Dashboard.d.ts` ARRANGE COACHING). Persisted so it never returns; exiting arrange mode without dismissing records nothing.
boards Map<String, DashboardBoardPrefs> const {} Per-board layout + visibility, keyed by board id. Unknown board ids are harmless and never pruned (the stale-hidden-id rule, one level up).

DashboardTileDef support

One tile's declaration — identity, linkage, and the content builder. Geometry lives in the bus's BOARDS (DashboardTilePlacement lists), not here: a def says what a tile IS, a board says where it sits.

Field Type Default What it does
builder * DashboardTileContent Function(BuildContext context, DashboardBus bus, DashboardLinkMode mode) — Builds the tile's content for the current bus state. The builder applies `bus.predicateFor` itself (with its own typed columns and accessors) — predicate broadcast, never a materialized hand-off.
defaultLinkMode DashboardLinkMode DashboardLinkMode.react The tile's declared link mode — AUTHOR config, the only channel (Dashboard.d.ts, 2026-08-19 amendment). Collect the declarations with dashboardDefaultLinkModes for `DashboardBus(defaultLinkModes:)`.
emitDim String? null The dimension this tile EMITS (a facet key, a KEY dimension — the word a FILTER table tile writes its row keys on — or the reserved kDashboardRangeDim / kDashboardViewportDim). The bus self-excludes it from this tile's predicate, and an applied scope on it paints the tile's source edge. Null = the…
emitDimLabel String? null emitDim's DISPLAY NAME — the NAME half of its chip (the chip's key, "PO" before the value "PO-10023"; `Dashboard.d.ts` THE CHIP, 2026-09-29, #839; drawn as `AppChip.name` since #893). Null humanizes the dimension (dashboardDimLabel: `target-due-date` → "Target due date"), so a key never prints where…
emitLabel String Function(String value)? null Maps a value applied on emitDim to the label its chip shows — a KEY dimension's row key to that record's label (`Dashboard.d.ts` ONE SELECTION MECHANISM, THE CHIP), so the chip never shows a raw id. A key dimension no facet column carries gets its chip from the pattern: its name (emitDimLabel), then…
id * String — Stable tile id — the board/prefs key.
required bool false Whether the user may never hide this tile — CODE's half of the visibility split (Dashboard.d.ts, 2026-08-21). Its checklist row renders checked and disabled. Collect the declarations with dashboardRequiredTiles for `DashboardBus(requiredTiles:)`.
title * String — The head's title — the title role, SENTENCE CASE, rendered verbatim (the kit never transforms it); a readout's label line.

DashboardTilePlacement support

One tile's grid placement — integer column/row spans on the snap grid. A board's list is SIZE PLUS ORDER, flowed dense; an EMPTY list is the count-based default (dashboardDefaultLayout).

Field Type Default What it does
h * int — Row span (clamped by the grid to 1…8).
id * String — The tile id.
w * int — Column span (clamped by the grid to 2…12).

FloorplanBand support

A floorplan's ONE TRANSIENT BAND — the contract's `ScaffoldHeaderBandProps` on both floorplans that declare one, stated once here rather than twice. `DetailScreen.d.ts` and `RecordsScreen.d.ts` each type their `band` member as that shared props type and forward it VERBATIM, so a Dart type per…

Field Type Default What it does
actionLabel String? null Label for the ONE action beside message (the Undo pattern).
message String? null The transient message. Null draws nothing.
onAction VoidCallback? null Callback for that single action.

RecordsColumn support

A filterable column of a records surface — the display column (AppColumn, the sole carrier of cell grammar) plus the paint-free filter metadata the records filter pane needs. Kept map-free: filterValue (when set) and column.value are typed accessors over T, never a string-keyed lookup.

Field Type Default What it does
column * AppColumn<T> — The display column — carries the label, cell type, and value accessor.
compactVisible bool? null Whether this column belongs to the REDUCED set a records table renders below the medium window class (the spec's adaptation contract: the host passes fewer columns below 600 — id + primary + status; horizontal scroll is the fallback, never the primary posture). Null = no declaration. When no column…
filterValue Object? Function(T row)? null Typed raw value for filtering, when column.value returns a display-formatted string (e.g. a formatted date). Falls back to column.value when null.
filterable bool true Whether this column is offered as a facet in the filter pane. Free-text columns typically set this false and rely on the search field. Read by the PATTERNS (`RecordsTableScaffold`, `RecordsMapView`, `RecordsChrome`).

RecordsPanel support

One host-contributed view-control panel of a records surface — the Group and Columns panels a table adds beside the built-in Filter. The chrome owns the posture (in-flow pane ≥960, modal sheet below), the control's state grammar, and the one-open-at-a-time rule; the host owns only the panel's body…

Field Type Default What it does
applied bool false Whether this panel currently narrows/alters the view — the control's applied grammar (paper + accent border + accent mark).
badge int? null Data-font count on the control while applied (hidden-column count).
body * Widget Function(BuildContext context, VoidCallback? dismiss) — Builds the panel content. `dismiss` is non-null ONLY in the modal posture, where it pops the hosting route — a body with a terminal action (the table's Reset column widths) uses it to close the sheet, while the in-flow pane stays open because live-apply has nothing to commit.
glyph * KitIconRef — The control's mark.
id * String — Stable identity — the open-panel key, and the control's widget key (`records-<id>-button`). Never rendered.
title * String — The panel heading, and the control's accessible title ("Group").

Where it lives

Class File
DashboardGrid packages/app_widgets/lib/src/kit/charts/dashboard_grid.dart
DashboardTile packages/app_widgets/lib/src/kit/charts/dashboard_tile.dart
DashboardFreshnessLine packages/app_widgets/lib/src/kit/charts/dashboard_states.dart
RecordsDashboard packages/app_widgets/lib/src/patterns/records_dashboard.dart
DashboardBoardScope packages/app_widgets/lib/src/kit/charts/dashboard_grid.dart
LabeledSection packages/app_widgets/lib/src/patterns/labeled_section.dart
LabeledSectionStack packages/app_widgets/lib/src/patterns/labeled_section.dart