Design language Foundations
Colour
Ink on warm paper carries the text, a copper-green accent marks the verified signal and a muted brick marks what is unverified or flagged. All 11 colour tokens are defined in one file; every colour value and ratio here is read or computed from it, or from a synced copy of the product's tokens.
The palette
#These are the 11 colour values the site defines, read from the token file in the order it declares them. Every other colour on the site is a mix of these tokens. Each chip is painted with the live token; under it is the contrast of that colour against paper and against ink, graded as normal-size text.
- --color-paper#fdfaf6The page, and the main text colour on dark and filled surfaces.
- 16.61:1 ink AAA
- --color-paper-2#f6f3eeThe second surface: footer, alternate sections, figure panels, code blocks.
- 1.06:1 paper Fail
- 15.62:1 ink AAA
- --color-hair#e2dfdb1px dividers between rows and sections. Never text.
- 1.27:1 paper Fail
- 13.02:1 ink AAA
- --color-hair-2#cdcac6The stronger line: button and chip outlines, diagram strokes, the dot grid.
- 1.56:1 paper Fail
- 10.58:1 ink AAA
- --color-ink#0f1c26Primary text, ink panels, the 1px rule that opens a group, the glyph.
- 16.61:1 paper AAA
- --color-ink-muted#5c5753Secondary text: leads, paragraphs, navigation links.
- 6.85:1 paper AA
- 2.42:1 ink Fail
- --color-ink-subtle#736d69Meta text: eyebrows, captions, dates, labels. Also the border of form fields.
- 4.89:1 paper AA
- 3.39:1 ink Fail
- --color-accent#00847eThe verified signal as a line or a dot: underlines, the focus ring, rules. Never text.
- 4.38:1 paper Fail
- 3.78:1 ink Fail
- --color-accent-deep#006560The copper green that carries text: small labels and link hover. Also the fill of the primary button.
- 6.65:1 paper AA
- 2.49:1 ink Fail
- --color-accent-tint#d9f5f3Light surface of the call-to-action band and of tags. Highlighted text on ink.
- 1.10:1 paper Fail
- 15.07:1 ink AAA
- --color-raw#a9432cUnverified, adversarial or flagged: noise, suspicious verdicts, CVEs, critical findings. A muted brick, not alarm red.
- 5.72:1 paper AA
- 2.90:1 ink Fail
Ratios are floored to two decimals. For normal-size text AA means at least 4.5:1 and AAA at least 7:1; Fail means below 4.5:1. A line or a surface is not meant to pass as text: the role under each chip says what the token is for.
What each colour is for.
#Four roles cover the palette: ink, a warm neutral ramp, one accent family and raw. Each role is shown in use, with its tokens and their OKLCH coordinates, computed from the hex values in the token file.
Ink is the backbone.
ink is the primary text colour, the fill of every dark panel, the 1px rule that opens a list or a group and the colour of the glyph. It stands in for black, which a test keeps out of the token file. Its hue, 243, is the only cool hue among the neutrals, so text sits slightly cool on warm paper.
Primary text
Paper text on an ink panel
- ink#0f1c26oklch(0.220 0.027 242.6)
The warm paper ramp
paper is the page and paper-2 the second surface: the footer, alternate sections and figure panels. hair and hair-2 draw the lines; ink-muted and ink-subtle carry secondary and tertiary text. All six sit in one warm band of hue, from 56 to 81. paper stands in for white, which the same test keeps out.
Meta text
Secondary text on paper
OutlineThe second surface
- paper#fdfaf6oklch(0.986 0.006 75.4)
- paper-2#f6f3eeoklch(0.965 0.007 80.7)
- hair#e2dfdboklch(0.905 0.006 75.4)
- hair-2#cdcac6oklch(0.840 0.006 75.4)
- ink-subtle#736d69oklch(0.539 0.010 56.2)
- ink-muted#5c5753oklch(0.460 0.009 61.3)
One accent family.
accent marks the verified signal. It is never a text colour, because on paper it reaches only 4.38:1, so it draws underlines, the 2px focus ring, thin rules, dots and the signal line. accent-deep is the same hue at a lower lightness. It carries small copper-green text and fills the primary button behind paper text, at 6.65:1. accent-tint is the light surface of the call-to-action band and of tags, and the colour of highlighted text on ink. All three sit near hue 189.
- accent-tint#d9f5f3oklch(0.949 0.029 191.4)
- accent#00847eoklch(0.553 0.096 189.2)
- accent-deep#006560oklch(0.457 0.079 188.9)
Raw is brick, not alarm red.
raw marks what is unverified, adversarial or flagged: the falling curve on the home page, a suspicious verdict, a flagged memory region, a finding pin on a timeline, CVE results in fleet search and the critical and high findings in the report figure on the services page. The token file defines it for the evidence scene and says plainly that it is a muted brick tone, not alarm red. Form errors do not use it; they are set in ink with a 3px ink rule. raw text on paper reaches 5.72:1.
- raw#a9432coklch(0.517 0.140 34.2)
Signal and noise
#Two colours carry meaning beyond structure. accent is the one verified signal. In the evidence scenes raw is everything that changed and has not been verified. The token file gives the reason raw exists: with a single colour for meaning, a scene that shows noise and signal reads as a one-colour chart.
rawraw changes: falling curve, round markspackage update, log rotation, temp file, signed binary and config driftaccentsignal: rising curve, square marksverified, correlated and 1 alert
raw and accent tokens.Never colour alone.
accent and raw are close in lightness, OKLCH L 0.553 and 0.517, and the contrast between them is only 1.30:1. What separates them is hue. So the scene also separates them by direction, by the shape of the marks and by a label on every mark, and a reader who cannot tell the two hues apart still reads the scene. On a narrow screen the labels move into the key.
The evidence paints the verdict.
The decision scene on the Zero Door page draws one case and leaves its colour to the evidence. The pill is born as an outline in ink-subtle, with no word in it. Each of the 3 failed checks fills one more equal part of it with raw. Each of the 3 neutral checks, which find nothing either way, leaves a notch in ink-subtle on its top edge and adds no colour. As the last part fills, the outline turns raw, and only then does the word, Suspicious, open in paper. raw is never painted before the evidence that earns it.
Born
0/3 failed · 0/3 neutral
- outline
ink-subtle
- outline
One check left
2/3 failed · 3/3 neutral
- fill
raw - notches
ink-subtle - outline
ink-subtle
- fill
Decided
3/3 failed · 3/3 neutral
- word
paperonraw - outline
raw
- word
Computed contrast
#Every ratio here is computed at build time from the token values with the WCAG 2.x formula and floored to two decimals, so a pair that misses a threshold never displays as passing. The first table lists the main token-on-token text pairs in use, with the file that sets them.
| Text / background | Ratio | Normal text | Where |
|---|---|---|---|
| ink / paper | 16.61:1 | AAA | Body text and headings |
| ink / paper-2 | 15.62:1 | AAA | Footer links |
| ink / accent-tint | 15.07:1 | AAA | Call-to-action band heading |
| ink-muted / paper | 6.85:1 | AA | Leads and page subheads |
| ink-muted / paper-2 | 6.44:1 | AA | Footer brand line |
| ink-muted / accent-tint | 6.21:1 | AA | Call-to-action band text |
| ink-subtle / paper | 4.89:1 | AA | Eyebrows, captions and dates |
| ink-subtle / paper-2 | 4.60:1 | AA | Footer column headings |
| accent-deep / paper | 6.65:1 | AA | Link hover |
| accent-deep / paper-2 | 6.26:1 | AA | Footer link hover |
| accent-deep / accent-tint | 6.04:1 | AA | Note under the call-to-action form |
| paper / accent-deep | 6.65:1 | AA | Primary button |
| paper / ink | 16.61:1 | AAA | Ink button, skip link, ink panels |
| accent-tint / ink | 15.07:1 | AAA | Field names in the security.txt figure |
| accent-tint / accent-deep | 6.04:1 | AA | Caption inside the evidence day pill |
| raw / paper | 5.72:1 | AA | Signature mismatch label |
| raw / paper-2 | 5.38:1 | AA | Kind label on a flagged event |
| paper / raw | 5.72:1 | AA | Suspicious verdict chip |
| paper / raw | 5.72:1 | AA | Verdict word, decision scene |
| ink-subtle / accent-tint | 4.44:1 | Fail | Not used for text |
Under the line
ink-subtle on accent-tint reaches 4.44:1, under 4.5:1, so wherever text sits on accent-tint it is ink, ink-muted or accent-deep. Paper on accent reaches 4.38:1, which is why the primary button is filled with accent-deep. Every pair in use reaches 4.5:1, so each of them also passes AAA for large text.
Text on mixed backgrounds
Some backgrounds are not tokens but a token mixed with paper, written in place with color-mix in OKLab. The ratios below are computed from the same expression the component writes, and a test reads that expression from the file.
| Text / background | Mix | Ratio | Normal text | Where |
|---|---|---|---|---|
| raw / raw + paper | 86 percent paper | 4.67:1 | AA | High finding label, report figure |
| raw / raw + paper | 88 percent paper | 4.81:1 | AA | Fileless event row, hatched |
| ink / raw + paper | 30 percent paper | 5.29:1 | AA | Matched bytes in the hex dump |
| accent-deep / accent-tint + paper | 30 percent paper | 6.22:1 | AA | Draft placeholder in legal texts |
| ink-muted / raw + paper | 92 percent paper | 6.12:1 | AA | Target zone label, attack path |
Lines and the focus ring
Non-text contrast asks for 3:1 where a line or a shape is needed to understand or operate the page. The focus ring is a 2px accent outline, and it clears 3:1 on every surface it can land on, ink included. The fields of the site's forms, in the contact form, the call-to-action band and the glossary filter, have an ink-subtle border, which clears 3:1 against the field's own paper fill and against the paper-2 and accent-tint around it. The hair divider does not reach 3:1, and we do not claim it: it separates content, and the content carries its own meaning in text.
| Colour / surface | Ratio | 3:1 | Role |
|---|---|---|---|
| accent / paper | 4.38:1 | Pass | Focus ring, 2px outline |
| accent / paper-2 | 4.12:1 | Pass | Focus ring, 2px outline |
| accent / accent-tint | 3.97:1 | Pass | Focus ring, 2px outline |
| accent / ink | 3.78:1 | Pass | Focus ring, 2px outline |
| ink-subtle / paper | 4.89:1 | Pass | Field border, on its own paper fill and on the page |
| ink-subtle / paper-2 | 4.60:1 | Pass | Field border, contact form card |
| ink-subtle / accent-tint | 4.44:1 | Pass | Field border, call-to-action band |
| hair / paper | 1.27:1 | Not claimed | Divider between rows and sections |
Dark panels, not a dark theme.
#The site has one theme, and it is light. Ink appears as a local panel: the security.txt file in the Trust centre, the hex dump in research, the proof strip on the Zero Door page, tags and stamps on figures. On ink, text is paper; secondary text is mixed from paper and ink; highlighted text is accent-tint. The hex dump is the exception: it marks matched bytes with a raw mix and draws its scan line in accent.
# Zerone Security vulnerability reporting (RFC 9116)# Please do not report security issues in public channels.Contact: mailto:security@zeronesecurity.comEncryption: https://zeronesecurity.com/.well-known/pgp-key.txtAcknowledgments: https://zeronesecurity.com/trust#acknowledgementsPolicy: https://zeronesecurity.com/trust#disclosureExpires: 2027-09-01T00:00:00.000ZPreferred-Languages: en, trCanonical: https://zeronesecurity.com/.well-known/security.txtsecurity.txt, set the way the Trust centre sets it: paper text, field names in accent-tint, comment lines and the file path mixed from paper and ink.Mixed, not tokenised.
No token exists for text on ink, and no mix scale is defined. Each panel derives its steps inline as color-mix(in oklab, var(--color-paper), var(--color-ink) N%); the steps below are the ones in use, each checked against the file that writes it. Text steps run from N = 25 to N = 45, and the weakest of them still reaches 5.17:1 on ink. Higher steps are dots and divider lines, never text.
- paperText on every panel#fdfaf616.61:1AAA
- N = 25ASCII column of the hex dump#babcbd9.09:1AAA
- N = 28Labels in the proof strip#b2b5b68.39:1AAA
- N = 30File paths, tag captions, the hex dump bar#adb0b27.95:1AAA
- N = 35Comment lines in security.txt#a1a5a76.92:1AA
- N = 45Byte offsets in the hex dump#888d915.17:1AA
- N = 55Window dots, lab session#70777c3.79:1Not text
- N = 70Window dots, hex dump#4d575e2.32:1Not text
- N = 80Caption divider, security.txt#37424b1.68:1Not text
- N = 82Dividers, hex dump#333e471.58:1Not text
- accent-tintHighlights: field names, the proof strip arrow#d9f5f315.07:1AAA
color-mix value on a live ink panel and says where the step is used. Text steps are set as text; dot and line steps are drawn as dots and lines.On ink, the accent is the tint.
On a dark panel the copper green that carries text is accent-tint, at 15.07:1. accent reaches only 3.78:1 on ink and accent-deep 2.49:1, so neither carries text there. accent still draws the focus ring and the hex dump's scan line on ink, where 3:1 is what a line needs.
Do and do not.
#All three pairs follow one rule, and a test holds it: no component sets text in accent. The primary button also states its half of the rule in a comment: accent-deep, not accent, behind paper text. Button text is 14px at weight 600, which WCAG treats as normal-size text, so 4.5:1 applies.
Filled buttons
Small copper-green text
Highlighted text on ink
States
#The shared controls define their hover and focus states once, in the site stylesheet. Each one is shown below in its forced state, painted with the rule the stylesheet writes, with the ratio of its text on its background. Hover never changes a button's text colour, and focus is the same 2px accent ring on every element. The site stylesheet sets it 3px off the element, and some components set their own offset (see Accessibility).
Primary button.btn
- Rest6.65:1
- Hover9.12:1
- Focusring 4.38:1
Ink button.btn-ink
- Rest16.61:1
- Hover13.23:1
- Focusring 4.38:1
Ghost button.btn-ghost
- Rest16.61:1
- Hover15.62:1
- Focusring 4.38:1
Text link.link
- Rest16.61:1
- Hover6.65:1
- Focusring 4.38:1
:hover and :focus-visible rules, so the cell shows what the pointer or the keyboard would.The primary button's hover darkens accent-deep by mixing it with ink. The ink button's hover lightens ink towards paper, the ghost button's hover fills it with paper-2 and a link's hover turns its text accent-deep.
Using the tokens
#The tokens are declared once, in a Tailwind @theme block that first wipes the default palette. Components read them with var(--color-name) in their own scoped styles.
@theme {
--color-*: initial;
--color-transparent: transparent;
--color-current: currentColor;
--color-paper: #fdfaf6;
--color-paper-2: #f6f3ee;
--color-hair: #e2dfdb;
--color-hair-2: #cdcac6;
--color-ink: #0f1c26;
--color-ink-muted: #5c5753;
--color-ink-subtle: #736d69;
--color-accent: #00847e;
--color-accent-deep: #006560;
--color-accent-tint: #d9f5f3;
--color-raw: #a9432c;
}body {
background: var(--color-paper);
color: var(--color-ink);
}
.btn-ink {
background: var(--color-ink);
border-color: var(--color-ink);
color: var(--color-paper);
}
.link:hover {
color: var(--color-accent-deep);
}The @theme block also generates utility classes such as text-ink-muted and bg-paper-2. No component uses them today: every colour in a component is a var(--color-*) reference or a mix of them. Where a component needs a step the palette does not have, it mixes one in place with color-mix in OKLab, as the dark panels do.
Rules and what holds them
#A rule on this page is either checked by a test that runs on every change or labelled as one that review keeps.
The 11 colour values are pinned.
WhyA colour changes only on purpose. The test lists every token with its exact value, so changing the palette means changing the test, in review.
TestEnforced by a test on every change.
No pure black or white in the token file.
Why
inkandpaperstand in for them: the cool ink and the warm paper are part of how the language looks. The test rejects the hex forms, the keywords and thergb()andoklch()forms of both.TestEnforced by a test on every change.
No pure black or white in any shipped stylesheet.
WhyThe gate reads every CSS file in the build output and the inline
<style>blocks of every HTML page, one declaration at a time. It catches the keywordsblackandwhite, hex in three, four, six or eight digits, thergb(),hsl(),hwb(),lab(),lch(),oklab(),oklch()andcolor()forms and the colours inside inline SVG images. A fully transparent colour does not count. One framework default, Tailwind's ring-offset colour, is excluded in the two places it is declared. Inline style attributes are not read: the design pages paint the product's own colours there.TestEnforced by a test on every change.
The default Tailwind palette is reset.
WhyWith the palette wiped, the stock colour utilities, such as
bg-red-500, do not exist. An arbitrary value in a class name would still work; no component writes one.TestEnforced by a test on every change.
Copper-green text uses
accent-deep, neveraccent.Why
accent-deepholds 4.5:1 on paper and onpaper-2;accentdoes not (4.38:1 on paper). The test reads every component and stylesheet and fails if any of them sets text, or SVG text, inaccent. It skips only the components of these design pages, which draw the counter-examples on purpose.TestEnforced by a test on every change.
Each text pair in the contrast tables is checked against the rule that sets it.
WhyThe tables are data, and the test reads the same data. It finds the rule that sets the text colour and the rule that sets the background in the file the table cites, and checks the ratio: pairs in use reach 4.5:1, and the pairs under the line stay under it. A citation that stops being true fails the run.
TestEnforced by a test on every change.
axe finds no contrast violation on any page.
WhyToken ratios are half of the work. axe checks every built page as it renders, where text sits on the surface it really has, and the run must end with zero violations. axe does not see text inside figures marked as images, such as the decision scene and the hex dump; for those, the tables on this page are the check.
TestEnforced by a test on every change.
Components never write a literal colour value; they use
var(--color-*).WhyOne source means a token change reaches every component. Token values are copied only where the token file cannot be read: the browser theme colour, the build-time social card renderer, the logo SVG files and the record list of the separately deployed form service. The resources page lists those files, and a test keeps the list complete.
ReviewKept by review, not by a test.
Colour is never the only signal.
Why
accentandrawdiffer mainly in hue, only 1.30:1 apart in luminance. Scenes separate them by direction, mark shape and a label as well, so the meaning survives without colour.ReviewKept by review, not by a test.
In the Zero Door interface
#The Zero Door interface speaks the calm register of the same language. It has its own tokens, written in OKLCH, with a light and a dark theme, and it opens in the dark theme. Below are 10 of its 15 colour tokens, with the values in its token file, which last changed on 2026-08-23. Contrast is computed here against the three surfaces of each theme.
Light theme
Severity
- Critical
- High
- Medium
- Low
- Info
Dark theme
Severity
- Critical
- High
- Medium
- Low
- Info
The light theme
- --surfaceoklch(0.985 0.006 75)The page surface.
- 15.98:1 fg AAA
- --fgoklch(0.235 0.010 60)Primary text: warm, not the cool site ink.
- 15.98:1 surface AAA
- 15.07:1 subtle AAA
- 16.69:1 raised AAA
- --fg-mutedoklch(0.460 0.010 60)Secondary text.
- 6.83:1 surface AA
- 6.45:1 subtle AA
- 7.14:1 raised AAA
- --fg-subtleoklch(0.540 0.010 60)Tertiary text, and Low and Info severity. The lowest step meaningful text may use.
- 4.85:1 surface AA
- 4.58:1 subtle AA
- 5.07:1 raised AA
- --brandoklch(0.25 0.025 233)Primary actions, the current selection and links.
- 15.25:1 surface AAA
- 14.38:1 subtle AAA
- 15.93:1 raised AAA
- --signal-successoklch(0.53 0.11 150)Success; in a badge, with a check icon and a word.
- 4.80:1 surface AA
- 4.53:1 subtle AA
- 5.01:1 raised AA
- --signal-warningoklch(0.53 0.12 70)Warning, and High severity.
- 5.18:1 surface AA
- 4.89:1 subtle AA
- 5.41:1 raised AA
- --signal-dangeroklch(0.53 0.17 25)Danger, and Critical severity.
- 5.51:1 surface AA
- 5.20:1 subtle AA
- 5.76:1 raised AA
- --signal-infooklch(0.54 0.12 240)Information, and Medium severity.
- 4.77:1 surface AA
- 4.50:1 subtle AA
- 4.98:1 raised AA
- --focus-ringoklch(0.45 0.10 233)Keyboard focus. Needs 3:1 on every surface.
- 6.91:1 surface Non-text AA
- 6.52:1 subtle Non-text AA
- 7.22:1 raised Non-text AA
The dark theme
- --surfaceoklch(0.190 0.008 250)The page surface.
- 15.01:1 fg AAA
- --fgoklch(0.930 0.005 85)Primary text: warm, not the cool site ink.
- 15.01:1 surface AAA
- 13.90:1 subtle AAA
- 12.82:1 raised AAA
- --fg-mutedoklch(0.665 0.008 250)Secondary text.
- 6.05:1 surface AA
- 5.60:1 subtle AA
- 5.16:1 raised AA
- --fg-subtleoklch(0.640 0.010 250)Tertiary text, and Low and Info severity. The lowest step meaningful text may use.
- 5.49:1 surface AA
- 5.08:1 subtle AA
- 4.69:1 raised AA
- --brandoklch(0.62 0.060 245)Primary actions, the current selection and links.
- 5.10:1 surface AA
- 4.73:1 subtle AA
- 4.36:1 raised Fail
- --signal-successoklch(0.66 0.13 150)Success; in a badge, with a check icon and a word.
- 6.26:1 surface AA
- 5.80:1 subtle AA
- 5.35:1 raised AA
- --signal-warningoklch(0.76 0.13 75)Warning, and High severity.
- 8.44:1 surface AAA
- 7.82:1 subtle AAA
- 7.21:1 raised AAA
- --signal-dangeroklch(0.66 0.18 25)Danger, and Critical severity.
- 5.45:1 surface AA
- 5.05:1 subtle AA
- 4.65:1 raised AA
- --signal-infooklch(0.66 0.12 240)Information, and Medium severity.
- 6.02:1 surface AA
- 5.58:1 subtle AA
- 5.14:1 raised AA
- --focus-ringoklch(0.62 0.09 245)Keyboard focus. Needs 3:1 on every surface.
- 5.11:1 surface Non-text AA
- 4.73:1 subtle Non-text AA
- 4.36:1 raised Non-text AA
Brand as link text
brand also colours links in the interface. In the dark theme it reaches 4.36:1 on the raised surface, under 4.5:1. Neither the product's contrast test nor the check here lists brand, so the swatch above shows the miss instead of hiding it.
Where the two meet.
Of the product's light tokens, 6 resolve to exactly the same sRGB value as a site token; the table computes it from both files. Primary text is where they part: the site sets it in ink, the product in a warm near-black, and the product keeps its brand colour, close to the site ink, for actions.
| Site token | Product token | sRGB |
|---|---|---|
| --color-paper | --surface | #fdfaf6 |
| --color-paper-2 | --surface-subtle | #f6f3ee |
| --color-ink-muted | --fg-muted | #5c5753 |
| --color-ink-subtle | --fg-subtle | #736d69 |
| --color-hair | --zd-border | #e2dfdb |
| --color-hair-2 | --zd-border-strong | #cdcac6 |
Download the product token snapshot, product-tokens.json
Borders are hairlines.
The product's border tokens are hairlines, like the site's, and sit well under 3:1 against the surface: 1.27:1 in the light theme and 1.35:1 in the dark one. We do not claim 3:1 for them.
The product's text and signal tokens reach 4.5:1 on all three surfaces, in both themes.
WhyThe values on this page are a synced snapshot of the product token file. The test re-checks the snapshot for
fg,fg-muted,fg-subtleand the four signal colours, so a sync that breaks one of those pairs fails here as well as in the product.brandis not on the list.TestEnforced by a test on every change.
No 3:1 claim for product borders.
WhyThe test pins the border token under 3:1 against the surface, so a page that claimed otherwise would contradict a passing test.
TestEnforced by a test on every change.
Chart series that are not severities never borrow the severity colours.
WhyThe product token file keeps a separate categorical palette for them and says why: two neighbouring charts would otherwise paint a high alert and a modified file the same amber.
ReviewKept by review, not by a test.