Design languageZero Door interface

Design language Product

Zero Door interface

Zero Door is designed to be read all day by people who watch a fleet for a living, so the interface speaks in the calm register. Surfaces stay neutral, motion stays short and type stays quiet, so that the one signal that matters is the thing that stands out.

The calm register

#

Zerone speaks one design language in two registers. The website has a case to make, so it is assertive. The interface is an instrument that stays open for a whole shift, so it is calm: what is not information steps back, and colour is kept for status.

Built to be read all day.

The interface is designed to stay open for hours and to be scanned more than read. Surfaces are near-neutral and text uses three inks; the product opens in a dark graphite theme meant for long sessions in a dim room.

Restraint makes room for the signal.

Among the interface colour roles, only the four status signals are saturated. The brand colour is a low-chroma blue that marks actions, links, selection and checked controls. Tooltips and tags carry no status colour, and charts that show something other than severity use their own calibrated ramp, kept measurably clear of every signal.

Frequent actions stay instant.

Keyboard actions taken many times a day, such as opening the command palette, happen in a single frame. Motion is kept for feedback, and the feedback is short: most of it is one 130 millisecond step.

The four public rules, read in the interface
Public ruleIn the interface
Nothing is trusted for having been there yesterday.A rule about what the product trusts, not about how the interface looks, so it has no visual form of its own here.
Evidence checks out without us.Long values are shortened, never lost. A path keeps its file name and a digest keeps both ends; the exact value stays in the title and in text for screen readers.
Change is a question, not an alert.Saturated colour is kept for the status signals, so a coloured word means one thing: look here.
Every detection runs behind your air gap.Both typefaces and the icons ship inside the product, and a CI check fails if shipped code points at an outside host. An air-gapped install looks and reads the same.

The four public rules, and the decisions each one forces, are set out in Principles.

Dark by default, light on request.

#

Both themes declare the same colour roles under the same names, so a component is written once; the 15 interface roles are shown here, and a small chart ramp is left out. Dark is graphite, a near-neutral grey (OKLCH hue 250); light is warm paper (hue 75). The product opens in the dark theme and does not follow the operating system setting; each person can switch from the user menu.

Three surfaces make the depth.

Navigation sits on the subtle surface and content on the base surface; panels, tooltips and the command palette sit on the raised surface. In the dark theme each step up is a little lighter; in the light theme the raised surface is the brightest of the three.

Dark

Light

The same screen in both themes, drawn with the product colour tokens: navigation on the subtle surface, content on the base surface, a detail panel on the raised surface. The selected row takes a faint brand tint. The type is this site’s face, not the product’s.

Graphite, the default

Surfaces

  • --surfaceoklch(0.190 0.008 250)Base surface: the page and the content area.
  • --surface-subtleoklch(0.225 0.008 250)Second layer: navigation, table headers and quiet fills.
  • --surface-raisedoklch(0.255 0.008 250)Raised layer: panels, tooltips and the command palette.

Ink

  • --fgoklch(0.930 0.005 85)Primary text.
    • 15.01:1 surface AAA
    • 13.90:1 subtle AAA
    • 12.82:1 raised AAA
  • --fg-mutedoklch(0.665 0.008 250)Secondary text, labels and table headers.
    • 6.05:1 surface AA
    • 5.60:1 subtle AA
    • 5.16:1 raised AA
  • --fg-subtleoklch(0.640 0.010 250)Tertiary text, the quietest of the three inks.
    • 5.49:1 surface AA
    • 5.08:1 subtle AA
    • 4.69:1 raised AA

Lines

  • --zd-borderoklch(0.300 0.008 250)Hairline between rows and around fields.
  • --zd-border-strongoklch(0.390 0.008 250)Stronger edge: hover, unchecked controls and the keyboard cursor.

Brand and focus

  • --brandoklch(0.62 0.060 245)Primary action, links, selection and checked controls.
  • --brand-mutedoklch(0.45 0.050 245)Primary action on hover.
  • --focus-ringoklch(0.62 0.09 245)Focus ring. Held to 3:1 against each surface.
    • 5.11:1 surface AA
    • 4.73:1 subtle AA
    • 4.36:1 raised AA

Signals

  • --signal-successoklch(0.66 0.13 150)Success: a check that passed.
    • 6.26:1 surface AA
    • 5.80:1 subtle AA
    • 5.35:1 raised AA
  • --signal-warningoklch(0.76 0.13 75)Warning: high severity and drift.
    • 8.44:1 surface AAA
    • 7.82:1 subtle AAA
    • 7.21:1 raised AAA
  • --signal-dangeroklch(0.66 0.18 25)Danger: critical severity and failure.
    • 5.45:1 surface AA
    • 5.05:1 subtle AA
    • 4.65:1 raised AA
  • --signal-infooklch(0.66 0.12 240)Info: medium severity.
    • 6.02:1 surface AA
    • 5.58:1 subtle AA
    • 5.14:1 raised AA

Warm paper

Surfaces

  • --surfaceoklch(0.985 0.006 75)Base surface: the page and the content area.
  • --surface-subtleoklch(0.965 0.007 75)Second layer: navigation, table headers and quiet fills.
  • --surface-raisedoklch(1 0 0)Raised layer: panels, tooltips and the command palette.

Ink

  • --fgoklch(0.235 0.010 60)Primary text.
    • 15.98:1 surface AAA
    • 15.07:1 subtle AAA
    • 16.69:1 raised AAA
  • --fg-mutedoklch(0.460 0.010 60)Secondary text, labels and table headers.
    • 6.83:1 surface AA
    • 6.45:1 subtle AA
    • 7.14:1 raised AAA
  • --fg-subtleoklch(0.540 0.010 60)Tertiary text, the quietest of the three inks.
    • 4.85:1 surface AA
    • 4.58:1 subtle AA
    • 5.07:1 raised AA

Lines

  • --zd-borderoklch(0.905 0.006 75)Hairline between rows and around fields.
  • --zd-border-strongoklch(0.840 0.006 75)Stronger edge: hover, unchecked controls and the keyboard cursor.

Brand and focus

  • --brandoklch(0.25 0.025 233)Primary action, links, selection and checked controls.
  • --brand-mutedoklch(0.40 0.030 233)Primary action on hover.
  • --focus-ringoklch(0.45 0.10 233)Focus ring. Held to 3:1 against each surface.
    • 6.91:1 surface AA
    • 6.52:1 subtle AA
    • 7.22:1 raised AA

Signals

  • --signal-successoklch(0.53 0.11 150)Success: a check that passed.
    • 4.80:1 surface AA
    • 4.53:1 subtle AA
    • 5.01:1 raised AA
  • --signal-warningoklch(0.53 0.12 70)Warning: high severity and drift.
    • 5.18:1 surface AA
    • 4.89:1 subtle AA
    • 5.41:1 raised AA
  • --signal-dangeroklch(0.53 0.17 25)Danger: critical severity and failure.
    • 5.51:1 surface AA
    • 5.20:1 subtle AA
    • 5.76:1 raised AA
  • --signal-infooklch(0.54 0.12 240)Info: medium severity.
    • 4.77:1 surface AA
    • 4.50:1 subtle AA
    • 4.98:1 raised AA

Focus

Keyboard focus is a 2 pixel ring in the focus-ring role, set 2 pixels off the control with a gap in the surface colour, so it reads on any fill. A table row takes the ring inset, inside its own edge. The ring appears only on keyboard focus and is held to 3:1 on every surface.

Dark

Saveweb-01web-01 · sshd_config

Light

Saveweb-01web-01 · sshd_config
A focused button and a focused field in both themes, drawn with the product tokens and the ring geometry from the product’s focus utility. The ring is drawn still here.
  • Every text and signal role reads at 4.5:1 or better on all three surfaces, in both themes.

    WhyStatus is carried by small words. A retuned token that slipped under the line would make a status unreadable, so the values published here are checked in this repository, and the product checks its own in CI.

    TestEnforced by a test on every change.

  • fg-subtle is pinned at the quietest calibration that still reads at 4.5:1 on every surface.

    WhyA later edit cannot pale the tertiary ink without failing the product’s test. The pin covers the token only: in use, some labels are set at reduced opacity, and the test does not check those.

    In the productEnforced by a test in the Zero Door repository.

The brand role also colours links in the interface, but it is not one of the text roles held to 4.5:1: in the dark theme it reaches 4.36:1 on the raised surface. The gap is set out in Colour.

Colour is kept for status.

#

The interface is ink on a neutral ground. Among the 15 interface colour roles shown above, only the four signals are saturated: their OKLCH chroma runs from 0.11 to 0.18, and none of the other roles goes above 0.1. So a coloured word means one thing: look here. There is no row wash and no filled chip; colour stays in a dot, a word or an icon, and it never carries the meaning alone.

Severity uses three of the signals: danger for critical, warning for high and info for medium. Low and info share the quiet ink fg-subtle and are told apart by the word alone. Status badges use all four signals. Charts that show something other than severity use their own calibrated ramp, kept measurably clear of the signals, and the focus ring, a muted blue, appears only on keyboard focus.

The dot and the word

In use, severity in a table or a list is a small dot and a coloured word: each row of the alert, incident and vulnerability tables shows it this way. The word carries the meaning; the dot repeats it in colour.

Dark · Dot and word

12 hosts23 open4 critical

  • Critical
  • High
  • Medium
  • Low
  • Info

Light · Dot and word

12 hosts23 open4 critical

  • Critical
  • High
  • Medium
  • Low
  • Info
Dot and word at their real size: a 6 pixel dot and a 12 pixel word at weight 500, drawn with the product signal tokens on the base surface of each theme. Low and info share one grey; only the count that needs action is coloured.
Severity to colour role
SeverityRoleDarkLight
Criticalsignal-dangeroklch(0.66 0.18 25)oklch(0.53 0.17 25)
Highsignal-warningoklch(0.76 0.13 75)oklch(0.53 0.12 70)
Mediumsignal-infooklch(0.66 0.12 240)oklch(0.54 0.12 240)
Lowfg-subtleoklch(0.640 0.010 250)oklch(0.540 0.010 60)
Infofg-subtleoklch(0.640 0.010 250)oklch(0.540 0.010 60)

The badge

A status that belongs to a check or a state is a badge: an icon and a word on a quiet tint of the signal, with a thin border in the same colour. In use, badges appear in detail panels, settings and some tables.

Dark · Badge

  • Passed
  • Drift
  • Failed
  • Queued
  • v2.1

Light · Badge

  • Passed
  • Drift
  • Failed
  • Queued
  • v2.1
Badges drawn at 2 times their size so the parts can be read. In the product a badge is 24 pixels tall with a 12 pixel word and a 12 pixel icon. The neutral badge has no icon and no status role. The icons are redrawn for this page; the product uses its own icon set.
Badge signals: fill at 0.1, border at 0.3
SignalIconColour role
successCheck in a circlesignal-success
warningTriangle with a marksignal-warning
dangerCross in a circlesignal-danger
infoLetter i in a circlesignal-info
neutralnonefg-muted on surface-subtle, zd-border edge
Badge sizes
Partmd (default)sm
Height24px20px
Word12px · 50011px · 500
Icon12px12px
Icon to word6px4px
Side padding8px6px
Cornerradius-sm · 4pxradius-sm · 4px
web-01sshd_configCritical
DoColour sits in the dot and the word; the row stays neutral.
web-01sshd_configCritical
Do notDo not wash a row or fill a chip with a severity colour.
  • Among the interface colour roles, only the four signals are saturated.

    WhyBrand, focus and every neutral stay low in chroma, so a saturated word reads as status. The chart ramp, left out above, is the one other saturated set, and the product’s test below keeps it clear of the signals. This site pins the published snapshot; a sync that breaks the rule fails the build here.

    TestEnforced by a test on every change.

  • A status badge always carries an icon and a word, never colour alone.

    WhyColour blindness, a greyscale print or glare on a screen all remove the colour. The word and the shape remain.

    In the productEnforced by a test in the Zero Door repository.

  • Chart colours that do not mean severity keep a measured distance from every signal colour.

    WhyA high alert and a modified file must never share a colour on neighbouring panels.

    In the productEnforced by a test in the Zero Door repository.

Two faces, a few sizes, three weights.

#

Interface text is set in Inter, from a single variable font file that ships inside the product, so an air-gapped install needs no font server. Hierarchy comes from size and weight: four fixed roles for the dense parts of the screen, a 24 pixel page title and an uppercase table header, in the weights 400, 500 and 600. Nothing heavier is used.

Product type roles

  • Page title24 / 30 · 600Alerts
  • Body14 / 20 · set per useModified on web-01 by a package update
  • Compact13 / 18 · set per useAlerts · Fleet · Compliance
  • Meta12 / 16 · set per useSeen 5m ago · agent 4.2
  • Overline11 / 16 · 500 · 0.08emprod · 12 hosts · last scan 1m
  • Table header12 / 16 · 500 · 0.05emServer · Severity · Age
The type roles at their real sizes, line heights, tracking and weights, on the light base surface, drawn in this site’s face. The product sets them in Inter from InterVariable.woff2, so the letter shapes and widths here are not the product’s.
Type roles
RoleSize / line (px)TrackingWeightIn use
Page title24 / 300600The title in a page header
Body14 / 200set per useTable cells, panels and forms
Compact13 / 180set per useNavigation rows, small buttons and fields
Meta12 / 160set per useSecondary lines in panels and lists
Overline11 / 160.08em500Uppercase context lines above a table
Table header12 / 160.05em500Table header cells, in uppercase

Numbers and machine text

Tables set every figure in tabular numerals, so columns of counts and times line up. Machine values that must align character by character, such as a digest before and after a change, are set in Geist Mono, the face this site uses for machine text, from a second variable font file that also ships inside the product.

  • Three dense roles are pinned to exact sizes and line heights, and the overline role to its tracking and weight.

    WhyA dense screen stays legible only if every table, list and panel draws from the same few sizes.

    In the productEnforced by a test in the Zero Door repository.

  • Hierarchy uses the weights 400, 500 and 600 only.

    WhyA heavier weight or a display face would make the interface louder than its data.

    ReviewKept by review, not by a test.

The website’s typefaces and type scale are set out in Typography.

One space scale, two densities.

#

The token file defines a scale of 12 steps, from 2 to 64 pixels. In use, components space with the matching Tailwind steps, plus 10 and 14 pixel steps that the scale does not define. A page gutter of 24 pixels lines up tables, page headers and footers.

  1. --space-12px
  2. --space-24px
  3. --space-36pxCompact row padding
  4. --space-48px
  5. --space-512pxComfortable row padding
  6. --space-616px
  7. --space-720px
  8. --space-824pxPage gutter
  9. --space-932px
  10. --space-1040px
  11. --space-1148px
  12. --space-1264px
The scale from the product token file, each bar drawn at its actual length, with the uses this page can name.

Density

There are two density modes: comfortable by default, compact on request, switched from the user menu. Compact changes one thing: the vertical padding of table rows drops from 12 to 6 pixels. The type size does not change, so a compact table is denser without being smaller.

Comfortable · 12 px

Compact · 6 px

The same table in both densities, dark theme. Row padding is 12 and 6 pixels; the type is the same size in both.
  • Density changes row padding, never type size.

    WhyShrinking the text to fit more rows would trade legibility for count. The product keeps the type and tightens the rows.

    ReviewKept by review, not by a test.

The website’s container, rhythm and radius are set out in Layout and shape.

Corners and depth

#

Corners come in 3 sizes and stop at 12 pixels. Depth comes mostly from the surface steps and the hairlines; the token file defines two shadows, a flat one that can stand in for a border and a raised one for things that float.

  • --radius-sm4pxFields, badges, tags, tooltips and small buttons.
  • --radius-md8pxButtons, error states and bordered empty states.
  • --radius-lg12pxDialogs, sheets, the command palette and floating action bars.
The radii from the product token file, drawn at actual size.

Two shadows, set per theme.

The flat shadow lifts a row off a quiet layer, as the active row in the sidebar does. The raised shadow is for side panels, tooltips and the command palette. Each theme sets its own values, because a shadow that reads on paper disappears on graphite.

Dark

Flat
0 1px 2px rgb(0 0 0 / 0.20)
Raised
0 8px 24px rgb(0 0 0 / 0.40)

Light

Flat
0 1px 2px rgb(0 0 0 / 0.04)
Raised
0 8px 24px rgb(0 0 0 / 0.08)
Flat and raised shadows in both themes, from the product token file.
  • The radius scale stops at 12 pixels.

    WhySmall corners keep dense layouts tight and aligned; the largest radius marks surfaces that float. This site pins the published snapshot; a sync that breaks the rule fails the build here.

    TestEnforced by a test on every change.

  • The token file defines two shadows: flat and raised.

    WhyDepth is carried by surface steps and hairlines first, so a shadow still means that something floats. In use, a small default Tailwind shadow also appears twice: on the switch thumb and on one panel.

    TestEnforced by a test on every change.

Short, functional motion.

#

Motion in the interface confirms an action or keeps a change in view; it does not decorate. The duration tokens run from 130 to 400 milliseconds, and in use nearly all motion is the 130 millisecond feedback step on the ease-out curve. Three things run outside the tokens: a row touched by a live update glows for 2200 milliseconds, a skeleton pulses slowly while it waits and a busy button spins.

Durations

  1. --duration-feedback130 msPress, colour and row feedback across the interface. Almost all motion in use.
  2. --duration-fast160 msA few hover borders and moves in the story graph.
  3. --duration-default200 msDefined. No component uses it yet.
  4. --duration-slow300 msThe arc of the posture score.
  5. --duration-drawer400 msDefined for drawers. No component uses it yet.
The durations from the product token file, as bars to scale, with where each one is used.

Curves

The token file defines 3 curves. Pick a duration and play them to compare how each one arrives.

  • --ease-outcubic-bezier(0.23, 1, 0.32, 1)Press and colour feedback, tooltips and row states. Almost all motion in use.
  • --ease-in-outcubic-bezier(0.77, 0, 0.175, 1)Defined for movement across the screen. No component uses it yet.
  • --ease-drawercubic-bezier(0.32, 0.72, 0, 1)Defined for drawers. No component uses it yet.
The curves plotted from the product token file. Each press of Play runs the dots from the start; they travel the same distance with each curve at the chosen duration. With reduced motion they move at once.

Press feedback

Buttons press to 0.97 of their size over 130 milliseconds on the ease-out curve and spring back the same way. A disabled button does not move. Large cards and panels use a gentler press, 0.99.

Light

Press and hold: each button scales to 0.97 in 130 milliseconds. The disabled one stays still. The buttons do nothing else.

What does not move.

Keyboard actions taken many times a day do not animate: the command palette opens and closes in a single frame. Charts draw without animation. When the reader asks for reduced motion, smooth scrolling stops and every animation and transition is cut to a hundredth of a millisecond and played once.

Motion in use
ElementMotionDurationCurve
Button pressScales to 0.97130 msease-out
TooltipGrows from 0.96 at its trigger, after a 400 ms wait130 msease-out
Row hover and selectionColour change130 msease-out
Command paletteNone: opens in one frame0 msnone
ChartsNone0 msnone
  • Shared parts transition named properties only: never all, never layout geometry.

    WhyTransforms and colours are cheap and predictable. Animating width, height or margins makes a dense screen reflow and jitter.

    In the productEnforced by a build check in the Zero Door repository.

  • Reduced motion turns smooth scrolling off.

    WhyA reader who asked for less motion should not have the page glide under them.

    In the productEnforced by a test in the Zero Door repository.

  • Keyboard actions taken many times a day do not animate.

    WhyAn animation repeated many times a day is a delay every time.

    ReviewKept by review, not by a test.

  • The duration tokens run from 130 to 400 milliseconds.

    WhyShort enough to confirm without making anyone wait, long enough to be seen. This site pins the published snapshot, not the product’s components; a sync that breaks the range fails the build here.

    TestEnforced by a test on every change.

The website’s motion, which explains rather than confirms, is set out in Motion.

A small vocabulary of parts.

#

The interface is built from a few dozen shared parts, named here by role. Buttons show rest, hover, focus, pressed, disabled and loading; fields show focus, invalid and disabled; data views load with skeletons.

Actions

Buttons come in four kinds (primary, secondary, ghost and danger) and three heights: 32, 36 and 44 pixels. Icon-only buttons grow to a 44 pixel target on touch screens. A loading button keeps its label and shows a small spinner beside it; a disabled button is drawn at half strength and does not press.

Light

Kindsm · 32 pxmd · 36 pxlg · 44 px
PrimarySaveSaveSave
SecondaryCancelCancelCancel
GhostDetailsDetailsDetails
DangerDeleteDeleteDelete
LoadingSaving
Disabled
The four kinds at the three heights, then a loading and a disabled button, drawn with the light theme tokens. The spinner is drawn still here; in the product it turns while the work runs.

A two-step confirmation

Light · Live

312 alerts selected

Light · Armed, after the first press

Live: the first press arms the button and states the scope; the second press acts. Escape or 5 seconds without a second press disarms it. The still beside it shows the armed state.
  • The confirm button arms on the first press and shows the question and its consequence.

    WhyClosing a thousand alerts should take as much thought as closing one takes effort.

    In the productEnforced by a test in the Zero Door repository.

  • Terminal or wide actions use the two-step confirmation.

    WhyThe product’s test holds how the button behaves, not where it is used; which actions take it is decided in review.

    ReviewKept by review, not by a test.

Inputs

Text fields match the button heights, so a field and its button sit on one line. An invalid field gets a danger border and is marked invalid for assistive technology. The search field waits 300 milliseconds after the last key before it searches. Selection controls (the checkbox and the switch) use the brand colour when on, never a status colour.

Light

Field and button
web-01Add server
Invalid
80800
A field and its button at the same 36 pixel height, the default size of both, and an invalid field with its danger border, drawn with the light theme tokens.

Status

The dot and word and the badge, described above. A tag is a quiet neutral chip for facets and filters and never carries a status colour. Tooltips stay neutral too, even when they explain a critical count.

Icons

Icons are sized on a ladder of 12, 16, 20, 24 and 32 pixels, and the stroke thickens as an icon gets smaller so that a small icon does not fade into the surface. An icon takes the colour of the text beside it and is hidden from screen readers unless it is the only carrier of meaning. The 12 pixel step is kept for icons inside a labelled badge.

Light

  1. xs · 12 pxstroke 2.5 · 1.25 px on screen
  2. sm · 16 pxstroke 2.25 · 1.5 px on screen
  3. md · 20 pxstroke 2 · 1.67 px on screen
  4. lg · 24 pxstroke 1.75 · 1.75 px on screen
  5. xl · 32 pxstroke 1.5 · 2 px on screen
One icon at each step of the ladder, with the stroke the product sets for it and the line width that results on screen. The glyph is redrawn for this page.
  • Icons are decorative unless they are the only carrier of meaning.

    WhyAn icon beside a word would make a screen reader say the same thing twice.

    In the productEnforced by a test in the Zero Door repository.

  • Every icon size on the ladder has its own stroke width.

    WhyA single stroke drawn at every size turns into a hairline at the small steps and fades into the surface.

    In the productEnforced by a test in the Zero Door repository.

Empty and loading states

A skeleton is a quiet block in the shape of what is coming, hidden from screen readers. An empty state teaches: it says what will appear and how to make it appear, with at most one quiet action. An error state says what failed, offers a retry and keeps the technical detail one click away.

Light

Skeleton

Empty state

No servers enrolled yet

Servers appear here once the agent is installed on them.

Add a server

Error state

The fleet could not be loaded

The request timed out.

RetryShow details
A skeleton, an empty state and an error state, drawn with the light theme tokens. The skeleton is drawn still here; in the product it pulses slowly while it waits.
  • A skeleton is hidden from assistive technology.

    WhyA shape that means loading has nothing to say to a screen reader; the region announces its content when it arrives.

    In the productEnforced by a test in the Zero Door repository.

  • An empty block inside a section is a status line, not a new heading.

    WhyA heading inside a dense panel would break the outline that screen reader users move through.

    In the productEnforced by a test in the Zero Door repository.

  • Data views load with skeletons, not a spinner in the middle of the screen.

    WhyA skeleton keeps the layout still and shows what is coming; a spinner hides both.

    ReviewKept by review, not by a test.

  • An empty state teaches what will appear and how to make it appear.

    WhyAn empty screen is often a first visit, and a bare “nothing here” leaves the reader with nowhere to go.

    ReviewKept by review, not by a test.

Navigation

A sidebar of 240 pixels collapses to a 56 pixel rail. The top bar is 56 pixels tall and centres on the command palette trigger, which shows the Command key and K on Apple devices and Ctrl K elsewhere. Paged tables share one footer: previous appears only after the first page and next only before the last; a single page shows no controls at all.

Light

Sidebar · 240 px
Zero Door
AlertsFleetCompliance
Rail · 56 px
AlertsFleetCompliance
The sidebar at 240 pixels and the rail at 56 pixels, drawn at actual size with the light theme tokens. The icons are redrawn for this page.

Panels

Detail opens in a side panel on the end edge, 480 pixels wide by default, so the list stays in view. It sits on the raised surface with the raised shadow, and when it closes, focus returns to whatever opened it.

Tables

Header labels are small uppercase words on a header row that stays in place while the rows scroll, and a sortable header tells assistive technology which way it sorts. A row can be selected, open and under the keyboard cursor at once. Selection is a faint brand tint and a checked box, the open row takes the subtle surface and the cursor is a thin inset ring; on a row that is both selected and open, the selection tint shows. Above 100 rows the table renders only what is on screen.

Light

ServerState
web-01Selected
db-02Open
build-07Keyboard cursor
api-03All three
The row states in the light theme: selected, open and under the keyboard cursor, then all three on one row.
  • Sortable and plain table headers share one casing.

    WhyOne header row in two casings reads as two kinds of column.

    In the productEnforced by a test in the Zero Door repository.

Exact values within reach.

A narrow column shortens a path at the directory and keeps the file name; it shortens a digest in the middle and keeps both ends, and it writes a large count in compact form. Point at a value to see it whole; screen readers read the whole value.

Light

FileDigestChanges
/usr/lib/x86_64-linux-gnu/libssl.so.39f2c4b7a1e8d3f6c5b0a9e2d7c4f1b8a6e3d0c9b2a5f8e1d4c7b0a3f6e9d2c5b12.4k
File/usr/lib/x86_64-linux-gnu/libssl.so.3
Digest9f2c4b7a1e8d3f6c5b0a9e2d7c4f1b8a6e3d0c9b2a5f8e1d4c7b0a3f6e9d2c5b
Changes12.4k
Shortened in a narrow column, exact on hover and for screen readers. The count 12.4k carries 12,431 in its title. On a narrow screen the same values stand in rows.

Shared with the website, and different.

#

The two registers sit on one language. They share the glyph, the neutral paper ramp, the ease-out curve, the 8 pixel radius and the press, along with two rules: colour never works alone and reduced motion is honoured. They differ on purpose where the job differs.

6 neutrals, one value
Website tokenWebsite valueInterface role (light)Interface valueRenders as
--color-paper#fdfaf6--surfaceoklch(0.985 0.006 75)#fdfaf6
--color-paper-2#f6f3ee--surface-subtleoklch(0.965 0.007 75)#f6f3ee
--color-hair#e2dfdb--zd-borderoklch(0.905 0.006 75)#e2dfdb
--color-hair-2#cdcac6--zd-border-strongoklch(0.840 0.006 75)#cdcac6
--color-ink-muted#5c5753--fg-mutedoklch(0.460 0.010 60)#5c5753
--color-ink-subtle#736d69--fg-subtleoklch(0.540 0.010 60)#736d69
  • --color-paper--surface
  • --color-paper-2--surface-subtle
  • --color-hair--zd-border
  • --color-hair-2--zd-border-strong
  • --color-ink-muted--fg-muted
  • --color-ink-subtle--fg-subtle
Each neutral painted twice: the website token on the left half, the interface role on the right. Where the two halves meet, no edge shows.
Shared
AspectWebsiteInterface
GlyphThe Zerone glyph in the current text colourThe same glyph path, in the current text colour
Main curve--ease-out-quart · cubic-bezier(0.23, 1, 0.32, 1)--ease-out · cubic-bezier(0.23, 1, 0.32, 1)
Medium radius--radius-md · 8px--radius-md · 8px
PressButtons press to 0.97Buttons press to 0.97
Colour aloneNever the only carrier of meaningNever the only carrier of meaning
Reduced motionHonoured: scenes show their final stateHonoured: transitions are cut to an instant
FontsSelf-hosted; nothing loads from a font serviceShipped inside the product; nothing loads from a font service
Different on purpose
AspectWebsiteInterface
RegisterAssertive: it makes a caseCalm: it is designed to be read all day
ThemeLight only, on warm paperDark graphite by default; warm paper on request
Text inkThe Zerone ink, #0f1c26A warm near-black, #221d19 in the light theme; the brand role (#15242c) is tuned to the Zerone ink and marks actions, links and selection
AccentOne accent, #00847e, that means verified signalNo accent: brand marks actions, links and selection, and only the status signals are saturated
TypefaceNew Black for language, Geist Mono for machine textInter for interface text and Geist Mono for machine text, each from one variable font file
Radius scale6px · 8px4px · 8px · 12px
ShadowOne ink-tinted floating shadowTwo neutral shadows, flat and raised, set per theme
MotionMotion that explains: scroll scenes and reveals, with a pause control beside anything that loopsShort feedback; frequent keyboard actions do not animate
Pure black and whiteNever; a build gate fails on themPage surfaces stay off pure black and white; the raised layer in the light theme is pure white
  • The ease-out curve is the same value on both surfaces.

    WhyThe website’s press and the interface’s feedback should feel like one hand.

    TestEnforced by a test on every change.

  • The glyph is the same path on both surfaces.

    WhyOne mark should read as one company. The snapshot keeps a digest of the product’s glyph path and its viewBox, and a test in this repository compares both with this site’s glyph.

    TestEnforced by a test on every change.

  • In use, the website’s paper, hairlines and quiet inks render to the same colours as the interface’s light theme.

    WhyNo document defines this match; it is observed. A test in this repository flags any drift on either side.

    TestEnforced by a test on every change.

The website’s palette, including these neutrals, is set out in Colour.

Where the values come from.

#

Every token value on this page (colour, space, radius, shadow, curve and duration) is read at build time from a snapshot of the product token file, and every ratio is computed from it. The same sync script reads further values from the product source code: the two typefaces, the default theme, the reduced-motion settings, the press scale, the badge, button, field and icon sizes, the four dense type roles, the shell widths and a digest of the glyph path. It stops with an error when a pattern is missing or an assumption no longer holds, and its check mode reports any drift from the product. A test in this repository runs that check wherever the product repository is present, which leaves out CI. Some mappings and measurements are still copied by hand into one file, each with its source path, among them the severity dot, the tooltip, the focus ring, the spinner, the page title and table header roles and a few timings. No test compares those with the product.

Product snapshot
FieldValue
Branchmain
Token file last changed2026-08-23
Component files last changed2026-10-09

The snapshot is published as product-tokens.json, and the website’s own token file as tokens.css.

product-tokens.jsonExcerpt, rendered from the snapshot
{
  "source": {
    "ref": "main",
    "date": "2026-08-23"
  },
  "radius": {
    "radius-sm": "4px",
    "radius-md": "8px",
    "radius-lg": "12px"
  },
  "ease": {
    "ease-out": "cubic-bezier(0.23, 1, 0.32, 1)",
    "ease-in-out": "cubic-bezier(0.77, 0, 0.175, 1)",
    "ease-drawer": "cubic-bezier(0.32, 0.72, 0, 1)"
  },
  "duration": {
    "duration-feedback": "130ms",
    "duration-fast": "160ms",
    "duration-default": "200ms",
    "duration-slow": "300ms",
    "duration-drawer": "400ms"
  }
}

What holds the rules.

Rule cards on this page say what holds each rule: a test in this website's repository, or a test or a build check in the Zero Door product repository, which runs in the product's own CI.

Checks behind this page
CheckKind
Text and signal contrast, both themesUnit test
Dense type rolesUnit test
Named transitions onlyCI check
No outside hosts in shipped codeCI check
Badge carries an icon and a wordUnit test
Icon ladder sizes and strokesUnit test
Chart colours clear of signalsUnit test
Published snapshot reads at 4.5:1Unit test, this site
Snapshot shape, sync errors, the glyph digest and drift from the productUnit test, this site
Shared curve, neutrals, radius, durations, shadows, signal chroma and the product facts this page states in wordsUnit test, this site