Design SpecimenRandom Fact apps
The app guidelines, rendered from the published tokens
Every control, corner, type style and colour below is drawn with the values @randomfact/tokens 0.4.0 ships (palette.json, theme.css, fonts.css) at the sizes DESIGN.md fixes, in the real webfonts. Contrast ratios are computed live from the hexes. Switch Light, Dark or Both above; the page chrome itself uses §6's header values.
Accepted 2026-09-23
§1
Two chromes
Shell (Can, Convo): sidebar + a 56px page-header bar, mobile-first. Workbench (Inv, Taz): a 56px top bar with wordmark, tabs, workspace trigger, icon actions and ⋮. Both ship from @randomfact/ui; nothing else is allowed.
Shell · AppShell
PLAT-58 App design system
Both chromes in @randomfact/ui, one control scale, the extended palette.
PLAT-53 Overview capture on release
Regenerate the screenshots as part of pnpm release.
Page title in the bar is the Section heading style (Exo 600 16px). Sidebar toggle and search are 36-square IconButtons; New ticket is the default Button.
Workbench · WorkbenchShell
September 2026
Scroll inside this frame: the header is sticky, its surface is the page at .84 (light) / .80 (dark) under a 4px blur, with the hairline in --rf-line.
Uncategorised
14 transactions waiting for a category.
Rules
3 rules ran this month.
Exports
Last export 12 Sep 2026.
Team
2 members.
Header row min-h 56, 36px controls, 16px glyphs, 2px progress overlay that never grows the bar. The wordmark is chrome-owned (Exo 700 16.5 + 10px subline), approximated here.
Shell · AppShell
PLAT-58 App design system
Both chromes in @randomfact/ui, one control scale, the extended palette.
PLAT-53 Overview capture on release
Regenerate the screenshots as part of pnpm release.
Page title in the bar is the Section heading style (Exo 600 16px). Sidebar toggle and search are 36-square IconButtons; New ticket is the default Button.
Workbench · WorkbenchShell
September 2026
Scroll inside this frame: the header is sticky, its surface is the page at .84 (light) / .80 (dark) under a 4px blur, with the hairline in --rf-line.
Uncategorised
14 transactions waiting for a category.
Rules
3 rules ran this month.
Exports
Last export 12 Sep 2026.
Team
2 members.
Header row min-h 56, 36px controls, 16px glyphs, 2px progress overlay that never grows the bar. The wordmark is chrome-owned (Exo 700 16.5 + 10px subline), approximated here.
§2
One control height: 36px
Every control that sits in a row is 36px, control text 14px, every glyph 16px. Button has no sm/lg; icon-only is a 36-square IconButton. The one exception is RowAction, 28px, inside a table row or list item only. Chips and badges are one size too: 20px, 12/500, rounded-sm, and never align with a control.
| Payee | Category | Amount | ||
|---|---|---|---|---|
| Northwind Traders | Groceries | −1,204.95 | ||
| Contoso Energy | Utilities | −88.20 | ||
| Salary | Income | 4,250.00 | ||
| Total | 2,956.85 |
Rows: Montserrat 400 14px, numbers tabular-nums right-aligned, total row 600. Chips are the §7 pair at one 20px size, rounded-sm. Input text is 16px below md (iOS zoom), 14px from md; shown at 14 here.
| Payee | Category | Amount | ||
|---|---|---|---|---|
| Northwind Traders | Groceries | −1,204.95 | ||
| Contoso Energy | Utilities | −88.20 | ||
| Salary | Income | 4,250.00 | ||
| Total | 2,956.85 |
Rows: Montserrat 400 14px, numbers tabular-nums right-aligned, total row 600. Chips are the §7 pair at one 20px size, rounded-sm. Input text is 16px below md (iOS zoom), 14px from md; shown at 14 here.
§3
One radius
--radius: 0.625rem (10px) and the shadcn derivation published as aliases. Solid corner is the new value; the dashed corner is what rounded-* meant before (Tailwind's fixed 4/6/8/12), at 3× zoom. Can, Convo and Inv grow 2px at every step; Taz shrinks 2px.
chips, badges, kbd
every control, menus, menu items
popovers, dialogs, sheets
cards and panels
Q3 invoices
12 open, 3 overdue
Write the alias, never bare rounded: bare rounded reads --radius itself (10px), larger than rounded-md.
chips, badges, kbd
every control, menus, menu items
popovers, dialogs, sheets
cards and panels
Q3 invoices
12 open, 3 overdue
Write the alias, never bare rounded: bare rounded reads --radius itself (10px), larger than rounded-md.
§4
App type styles
Exo speaks (titles, figures), Montserrat reads. Eight styles, no fractional sizes, 12px is the floor, no italic. fonts.css loads exactly Exo 600/700 and Montserrat 400/500/600.
--text-2--text-2The weights the apps load
The apps load only these five. A weight they leave out renders as the nearest loaded one: an Exo element left at 400 renders 600, a Montserrat element at 700 renders 600. (This site loads the full variable fonts, so that fallback can't be shown live here.)
Numbers in rows and the wordmark
| Groceries | 1,204.95 |
| Utilities | 88.20 |
| Transport | 312.00 |
| Total | 1,605.15 |
Row numbers are Montserrat tabular-nums at the row's size; Exo numbers are the Figure style only. Wordmark (chrome-owned): InvRandom Fact
--text-2--text-2The weights the apps load
The apps load only these five. A weight they leave out renders as the nearest loaded one: an Exo element left at 400 renders 600, a Montserrat element at 700 renders 600. (This site loads the full variable fonts, so that fallback can't be shown live here.)
Numbers in rows and the wordmark
| Groceries | 1,204.95 |
| Utilities | 88.20 |
| Transport | 312.00 |
| Total | 1,605.15 |
Row numbers are Montserrat tabular-nums at the row's size; Exo numbers are the Figure style only. Wordmark (chrome-owned): InvRandom Fact
§5
Two text tiers
--text-1 and --text-2, no opacity modifiers, no third tier. Taz's --text3 and Inv's --rf-sea-ink-soft both collapse onto --text-2. Ratios are measured against the page and the card.
/70: an opacity modifier is a third tier by another name—What was retired, on its own ground
--text3 on the page#9aa1ac 2.47:1 fails--text-2#6b7280 4.59:1 AA--rf-sea-ink-soft on the page#475569 7.20:1 AA--text-2#6b7280 4.59:1 AA/70: an opacity modifier is a third tier by another name—What was retired, on its own ground
--text3 on the card#5b7185 3.50:1 fails--text-2#94a3b8 6.92:1 AA--rf-sea-ink-soft on the page#94a3b8 8.19:1 AA--text-2 (already the same value)#94a3b8 8.19:1 AA§6
Workbench chrome values
Three tokens, not four: --rf-header-bg, --rf-line, --rf-sand. Tab ink is --text-2 inactive and --text-1 on hover and active; the hover fill is sand at 60%. Frame 1640px with a 16px gutter, shared by the bar and the page.
Palette
Anchors, surfaces and the flipping tokens
The six brand anchors are the same hex in both themes and pair within a layer. The surface system and the data tokens (success, danger, warning) take a value per theme. Brand-blue text (links, marketing headings, the focus ring) is blue in light and sky in dark; blue itself does not flip, and there is no lifted blue (lagoon is retired in tokens 0.4.0). Not restated in DESIGN.md; shown so the app layer can be judged over the ground it sits on.
Anchors, paired by layer
Surfaces and semantic colours
Blue-as-ink, and the data tokens
Anchors, paired by layer
Surfaces and semantic colours
Blue-as-ink, and the data tokens
§7
Data colour
Twelve categorical hues in four families plus a neutral “Other”. A chart hue clears 3:1 on the page; a chip's ink clears 4.5:1 on its ground over the card. The chart order is the one that keeps adjacent series apart under colour-blindness. There is no red or pink hue.
Chart order, hue on the page
Chips, ink on ground over the card, by family
Chart order, hue on the page
Chips, ink on ground over the card, by family
§8 – §9
The token rule, flipped, and one tokens route
No app declares an --rf-* property and no component file carries a raw hex outside an allow-listed chart module. A design value an app needs is a PLAT change to tokens plus the app's adoption; @randomfact/ui/testing enforces it.
Every app's stylesheet
@import "@randomfact/tokens/fonts.css"; @import "tailwindcss"; @import "@randomfact/tokens/theme.css";
Nothing else from the package. palette.css is the file theme.css imports, not an entry.
The gate fails on
- an
--rf-*or--color-rf-*declaration (bar the four--rf-md-*knobs) - an import of
@randomfact/tokens/palette.css - a
components/ui/folder - a raw hex or Tailwind colour-ramp class in a non-test
.ts/.tsx - a
--radius:declaration
Allowed by name
- setting a published knob:
--rf-md-block-gap,--rf-md-heading-leadon a docs selector - a chart module listed in
chartModules - an
allowentry with a non-emptyreason, which fails as stale once it matches nothing