Specimens
The foundations every other family is built from: colour, type, layout, spacing, radius, elevation, motion, icons and focus. Each card reads its values from the live tokens, so this page cannot drift from the product.
Archivo is set at its normal width throughout. Navy carries the action; nothing else shouts.
Colour roles
One navy for action, white for the page, five status colours that each mean one thing.
Navy does the work. One deep navy marks every action and the sidebar, and white keeps the page quiet so the cloth photos stand out. Status colours are the only other hues, and each means a single thing: green is live, blue is a draft, slate is retired, ochre is a warning, red cannot be undone. Every text pair is measured against its ground and holds 4.5:1 or better.
Surface and text
Quiet by design: a white page, near-black text and one muted grey for everything secondary.
--backgroundPage groundThe ground of every page and card. --card and --popover are the same value.
--
--foregroundBody text and headingsTitles, table text, values.
--
--mutedWells and table headerTable header ground, hover rows, specimen wells.
--
--muted-foregroundQuiet textMeta lines, placeholders, help text, inactive icons.
--
--accentSelectionSelected row, active filter chip, avatar ground.
--
--borderHairlineDividers and card edges. Decorative, so no ratio is asked for.
--
--inputControl edgeBorder of fields, selects, checkboxes and switches. Clears 3:1 on white.
--
Action and focus
Navy marks the one action that matters. Red is only for things that cannot be undone.
--primaryPrimary actionThe single primary button on a screen, and active tabs.
--
--primary-hoverPrimary, hoveredHover and pressed state of the primary button.
--
--secondarySecondary groundOutlined secondary button; its edge is --input.
--
--destructiveDestructiveDelete, retire and the red confirm button.
--
--ringFocusKeyboard focus ring and the focused field edge. The ring sits 2px clear of the control, on the page ground.
--
Status
Five meanings, five colours, each used for one thing. Chips always show the dot and the word, never colour alone.
--successAvailable / successAlias --available. Live products, saved, done.
--
--infoDraft / infoAlias --draft. Work in progress and neutral information.
--
--neutralRetired / lockedAliases --retired and --locked. Archived or not editable.
--
--warningWarning / not wiredAlias --not-wired, and the STAGING badge.
--
Sidebar
The one dark surface. It carries the sheen; nothing else in the product does.
--sidebarSidebar groundRJ midnight navy under the satin sheen.
--
--sidebar-primaryActive rail and icon tintRail on the active item and its icon.
--
--sidebar-accentActive item groundHover and active item background.
--
--sidebar-borderSidebar hairlineDivider above the account block. Decorative.
--
Charts
Five series colours. Each holds 3:1 against white; series are always labelled or carry direct values, never colour alone.
--chart-1Series 1 · blueFirst series, and any single series.
--
--chart-2Series 2 · tealSecond series.
--
--chart-3Series 3 · coralThird series.
--
--chart-4Series 4 · periwinkleFourth series.
--
--chart-5Series 5 · roseFifth series. More than five folds into Other.
--
--chart-mutedComparison · steelPrevious period and reference lines, dotted. Never a category.
--
Control edges. --input is 1.6:1 on white, under the 3:1 that WCAG asks of a control boundary. Fields therefore never rely on the edge alone: each has a visible label, and focus adds the 6:1 ring. Flagged as a contract gap for a decision on a darker edge.
hsl(var(--x))--status-soft-alphaType scale
Archivo for titles and buttons, IBM Plex Sans to read, IBM Plex Mono for codes.
Archivo sets titles, buttons, labels and the menu, always at its normal width and never spaced out. IBM Plex Sans handles everything you read or scan, including tables and prices, with tabular figures so money lines up. Plex Mono is for cloth codes and handles, so HS 81045 can never be misread. Thirteen styles replace the sizes pages set by hand today.
--text-displayKit page title only. Not used in the product.
--text-pageTitle of a page in the app.
--text-sectionSheet, dialog and panel titles.
--text-cardCard titles, panel h3, sidebar items.
--text-labelForm labels.
--text-bodyDefault UI text, table cells.
--text-readHelp drawer and long copy. 62 characters a line at most.
--text-smallMeta lines, table secondary text.
--text-microCounts, chips, captions. The smallest size in the product.
--text-microColumn headings; Archivo 600, never uppercase.
--text-codeCloth codes, handles, versions, ids.
--text-bodyMoney; tabular figures so columns line up.
--text-btnThree sizes; label always says what happens.
Replaces the ad-hoc sizes
Pages today set text at 9, 11 and 15px by hand. Each now has one home on the scale.
- 9pxbecomes
--text-micro11.5px, the floor - 11pxbecomes
--text-small12px for meta, or micro for counts - 15pxbecomes
--text-read15px with a 1.55 line
--font-title--font-ui--font-code--text-*Layout grid
A fixed frame on desktop, a bar and four tabs on the phone.
One frame everywhere: a 232px navy sidebar, a 56px top bar and a page that takes the rest with 24px gutters. The phone is its own layout, not a squeezed copy. It trades the sidebar for a top bar and four tabs, with 16px gutters and taps of at least 44px.
GutterColumn
- Viewport
-design width- Sidebar
-navy, fixed- Top bar
-breadcrumbs left, help and account right- Page gutter
-each side of the page- Content width
-what is left at 1440- Columns
12gap-, each-wide- Table row
-header-
Fixed widths, to scale
--vp-desktop--vp-phone--sidebar-w--topbar-hSpacing
A 4px grid with twelve steps.
Every gap is a multiple of 4. Small steps sit inside controls, 12 to 16 separate related things, 24 to 32 separate groups, and 40 and up separate whole sections. Using the steps and nothing in between is what makes pages feel evenly set.
Spacing scale
Every step is a multiple of 4. Tokens are named by step, not by pixels.
--s-1-Icon to label, tight pairs--s-2-Inline gaps, chip padding--s-3-Control padding, filter gaps--s-4-Card gutter, phone gutter--s-5-Gap between specimen cards--s-6-Page gutter, card padding--s-7-Panel stack gap--s-8-Heading to content--s-10-Between sections--s-12-Large breaks--s-16-Empty-state air--s-18-Page bottomControl and row heights
Heights are tokens too, so a 40px button lines up with a 40px field.
--control-h-Button, input, select--control-h-sm-Small button, row action--control-h-m-Phone tap target--control-h-m-lg-Phone primary button--row-Table row--row-th-Table header--s-1 … --s-18--control-h*--rowRadius
Corners grow with the size of the thing.
Chips are 4px, controls 6px, cards 8px and big frames 15px. Pills are only for switches and avatars. Photos stay at 4px so the edge of a garment or a cloth swatch reads crisp.
--r-chip-Chips--r-control-Buttons, inputs, menu items--r-card-Cards, tables, popovers--r-frame-Panels, app frame, specimen cards--r-media-Photos inside cards--r-pill-Switch, avatar--r-chip--r-control--r-card--r-frameElevation
Flat on the page, lifted only when something floats.
The page is flat and borders do the separating. Shadows appear only on things that float above the page, and the further they sit from it, the larger and softer the shadow. Buttons get a 1px hint at most, and nothing glows.
--e-0Page, tables, inputs--e-1Cards--e-btnPrimary button--e-btn-2Secondary button--e-popDropdown, combobox--e-toastToast--e-sheetSide sheet, help drawer--e-dialogDialog--e-phone-sheetPhone bottom sheet--e-frameApp frame, specimen frame--scrimBehind sheets and dialogs--focus-ringFields on focus--e-*--scrim--focus-ringMotion
Short, small and eased. Nothing bounces.
Hovers take 120ms, menus 160ms, sheets 260ms. Things arrive with a soft landing and leave a little faster than they came, and the biggest move anywhere is a sheet sliding in. With reduced motion switched on, every move becomes instant and every shift is zero.
--motion-hoverHover colour change----motion-pressButton settles on press----motion-focusFocus ring appears----motion-pop-inDropdown opens----motion-pop-outDropdown closes----motion-sheet-inSheet and dialog arrive----motion-sheet-outSheet and dialog leave----motion-toast-inToast rises----motion-toast-outToast leaves----toast-dwell-How long a toast stays--press-shift-Button sinks--pop-shift-Dropdown drops--toast-shift-Toast risesTry it
--motion-*--ease-*--*-shift--toast-dwellIconography
Lucide, 54 icons this admin needs, 24px grid, 1.75 stroke.
One icon set, Lucide, because shadcn ships with it and the build lane already uses lucide-react. Icons sit on a 24px grid with a 1.75 stroke and round ends, and show at 16px (14px inside chips and menus). They take the colour of the text beside them and never appear without a label or a tooltip.
Navigation
Actions
Controls
Status and help
Sizes and stroke
14chips, menus16default20empty states24native grid24px grid, 1.75 stroke, round caps and joins, currentColor. Lucide ships at 2; the kit sets 1.75 so 16px icons sit lighter beside Plex text.
In use
An icon never stands alone: icon-only buttons carry an aria-label and a tooltip, and status icons always sit beside their word.
currentColorstroke-width 1.75Focus ring
Always visible for the keyboard, in one blue.
Keyboard focus is never hidden. Buttons and links get a 2px blue ring held 2px clear of the edge; fields turn their edge blue and add a soft outer ring. The sidebar uses a lighter blue so the ring still holds against navy. Both pass 3:1.
2px ring, 2px clear of the edge.
Same ring on every button.
Edge turns blue, soft outer ring.
Cloth code needs six digits.
Lighter ring, same weight.
--ringRing--
--sidebar-ringSidebar ring--
--ring--sidebar-ring--focus-ring