paranoid-passwd — Design System & Tokens¶
This document turns brand.md §5 (palette, type scale, iconography,
layout) into concrete, named tokens and component specs that three
surfaces consume so they read as one product:
the ratatui theme module (CLI/TUI —
crates/paranoid-cli),the Slint styles (GUI —
crates/paranoid-gui/ui/paranoid.slint),the Sphinx theme (docs/download site —
docs/_static/custom.css).
It is the single source of truth for values. brand.md owns the
meaning of each value; ia.md owns where each component appears.
Nothing here is re-decided against brand.md — every token traces to brand.md §5,
cited inline. Every token is named by intent, not by appearance (brand.md §5.2;
research lens 0: “semantic color slots are named by function, not appearance”).
Consumption rule. A surface never hard-codes a hex, a size, or a spacing step. It reads the token. When a value must change, it changes here once and all three surfaces move together. The current drift (§7) — where the TUI, Slint, and CSS carry three slightly different palettes — is exactly the failure this rule exists to prevent.
1. Color tokens¶
The canonical values are the ones already correct in docs/_static/custom.css and
the ratatui constants, promoted here as authoritative (brand.md §5.2). Each token
has one meaning and is used only for that meaning. A color never carries meaning
alone — it always doubles a symbol or label so the monochrome layer works (brand.md
§5.1, §5.4).
Token |
Value |
Meaning (brand.md §5.2) — used ONLY for this |
|---|---|---|
|
|
The ground. Near-black, low-glare; the whole surface. |
|
|
A raised panel or focused region. |
|
|
Structure and separation between regions. |
|
|
Primary reading text. |
|
|
Secondary text, labels, footer keys, hints. |
|
|
Verified / safe / passed. Earned only after a real check. |
|
|
The one next thing to do. One per screen, used sparingly. |
|
|
Danger / failure / irreversible. A stop, never decoration. |
|
|
Attention / unverified / in-between — a state to resolve. |
Two hard disciplines from brand.md §5.2, encoded as token-usage rules:
color.status.verifiedis earned. No surface may use it as a default “all fine” wash; it appears only after an actual verification (binary matches release, randomness check passed, vault opened and trusted).color.status.dangeris reserved. Only real danger — verification failed, unlock failed, irreversible destruction. Overuse trains the persona to ignore it.
There is no accent.secondary and no decorative color. The current TUI defines
a PURPLE/#a78bfa constant and the Slint scaffold uses off-palette shades
(#f5d76e, #9fd4c9, #171d26); none of these map to a brand.md meaning and all
are removed in favor of the nine tokens above (see §7).
1.1 Monochrome + 16-ANSI fallbacks (mandatory, brand.md §5.1)¶
Because a locked-down or remote terminal may render no truecolor — and because the real/decoy distinction must never depend on color — every color token declares a 16-ANSI fallback and every meaning declares a monochrome carrier (the symbol/weight that conveys it with zero color). This is the L0 (monochrome) and L1 (16-color) of brand.md’s three-layer discipline.
Token |
16-ANSI fallback |
Monochrome carrier (the thing that still works with no color) |
|---|---|---|
|
default bg / reverse |
panel = bordered box vs. unbordered ground |
|
default fg |
normal weight |
|
bright-black (8) |
dim modifier |
|
green (2) |
|
|
bright-blue (12) |
|
|
red (1) |
|
|
yellow (3) |
|
Test (brand.md §5.1): strip all color — the product must remain fully usable and the real/decoy vaults must remain indistinguishable. If a state reads only by its color, that is a defect: fix the carrier, not the palette.
2. Spacing scale¶
One geometric-ish scale, four steps, so all three surfaces share rhythm. Named by
role. In the TUI, space.* maps to cells (ratatui Margin/Layout
constraints); in the GUI/docs, to pixels/rem (brand.md §5.5 “spatial
consistency”).
Token |
TUI (cells) |
GUI (px) |
Docs (rem) |
Role |
|---|---|---|---|---|
|
1 |
6 |
0.375 |
Between a label and its value; intra-row. |
|
1 |
8 |
0.5 |
Between stacked lines in a group. |
|
2 |
14 |
0.875 |
Between groups; default panel padding. |
|
3 |
22 |
1.375 |
Between major regions; around the primary action. |
Panel padding is space.base; the gap between the two panes of a browse screen is
space.base; the gap around the single accent.action is space.loose so the one
next move has breathing room (ia.md §1, “the next action is singular and visible”).
3. Type scale¶
Four steps, from brand.md §5.3. Hierarchy comes from weight and spacing more than size (an austere instrument does not shout). The TUI expresses steps as bold/normal/dim modifiers; the GUI/docs as sizes. Monospace is mandatory for anything read character-by-character.
Token |
GUI/docs |
TUI modifier |
Role (brand.md §5.3) |
|---|---|---|---|
|
20px / 1.25rem, semibold |
|
Screen title — one per screen, states its one job. |
|
15px / 1rem, regular |
(normal) |
Primary content and reading text. |
|
13px / 0.85rem, medium, |
|
Field labels, footer keys, hints. |
|
15px / 1rem monospace |
(normal, mono cell) |
Secrets, hashes, phrases, generated passwords — anything read or copied character-by-character. |
Font families:
CLI/TUI: monospace everywhere (the terminal’s cell font).
GUI/docs: a neutral sans for prose (
type.title/body/label) paired with a monospace for values (type.mono). The docs Sphinx theme and the Slint GUI must pick the same monospace so a hash reads identically across surfaces.
type.mono is not merely a font choice — it is a safety property. A generated
password or recovery phrase shown in a proportional font can render l/1/I or O/0
ambiguously; type.mono guarantees every character is unambiguous (brand.md §5.3).
4. Component specs¶
Each component is defined once, in intent terms, then mapped to the primitive each surface uses. This is the “note which token maps to which surface primitive” requirement made explicit.
4.1 Panel¶
A raised, bordered region. The fixed layout skeleton (ia.md §1) is built from panels.
Property |
Token |
ratatui primitive |
Slint primitive |
Sphinx primitive |
|---|---|---|---|---|
background |
|
|
|
|
border |
|
|
|
|
radius |
(GUI/docs only) 6px / 0.5rem |
(n/a — cells) |
|
|
padding |
|
|
|
|
The focused panel borders in color.accent.action; unfocused panels border in
color.border. This is how the eye finds the active region without the layout
moving (ia.md §1, rule 4).
4.2 Primary action (the one next move)¶
Exactly one per screen (ia.md §0 rule 5). It is the only element that may use
color.accent.action.
Property |
Token |
ratatui |
Slint |
Sphinx |
|---|---|---|---|---|
marker |
|
prefix |
leading |
|
color |
|
|
button accent color |
|
weight |
bold |
|
|
|
space around |
|
|
|
|
Secondary actions render in type.body / color.text.primary with no accent —
so the single blue target is unmistakable (brand.md §5.2 “if everything is blue,
nothing is”).
4.3 Input / secret field¶
For passphrase entry and item fields.
Property |
Token |
ratatui |
Slint |
Sphinx (docs demos only) |
|---|---|---|---|---|
text |
|
mono line, |
|
|
label |
|
|
|
|
masked state |
|
masked string |
|
native |
Hard gate (brand.md §3 / CLIG, ia.md §3): a secret field’s value is never sourced from a CLI argument — stdin/prompt/file only. This is a code contract the input component enforces, not a styling note.
4.4 Status / danger states¶
Status is a symbol + word + color triple. The symbol and word are the monochrome carriers; color reinforces (brand.md §5.4, §1.1 above).
State |
Symbol |
Token |
Where (ia.md) |
|---|---|---|---|
verified / passed |
|
|
S3, S11 verdicts; title-bar |
failed / danger |
|
|
S3f; unlock failure |
attention / unverified |
|
|
S1 unverified; S5 no-hardware caveat |
locked |
|
|
S14; title-bar |
⊘ (locked) deliberately uses color.text.muted, not danger red: a locked
vault is the safe state, not a failure (brand.md §5.2 “red is a stop, never
decoration”). Coloring lock-state red would be a voice violation.
4.6 Progress affordance (non-blocking ops)¶
Verify (S2), key derivation (S15), evidence-bundle (S19). A determinate or
indeterminate indicator in color.accent.action on color.bg.panel, with ⎋
always live (brand.md §5.5; research lens 0 “never freeze the UI”).
Surface |
Primitive |
|---|---|
ratatui |
|
Slint |
a progress |
Sphinx |
(n/a — docs are static) |
7. Drift to reconcile (current-state audit)¶
The three surfaces have diverged from the canonical palette. This section records the drift so P8 fixes it against the tokens above, not against whichever value each file happens to hold. This is the concrete instance of the consumption rule (§0) being currently violated.
Surface |
Current value |
Canonical token |
Action |
|---|---|---|---|
|
matches brand.md §5.2 exactly |
— |
authoritative, keep; extend with space/type tokens |
ratatui |
match brand.md §5.2 exactly |
— |
keep values; move into |
ratatui |
(no brand.md meaning) |
— |
remove — off-palette, maps to no intent |
Slint |
|
|
retune to |
Slint |
|
|
retune to |
Slint |
|
|
retune |
Slint accent |
verified/caution/panel intents |
map to |
retune to canonical status tokens |
Slint |
|
|
retune to the four-step scale (no 34px) |
The reconciliation is not cosmetic: the drifted Slint palette makes the GUI read as a different product than the TUI and docs, which directly violates brand.md §6 and the PUX.4 mandate that all three “read as one product.” After §6’s token module lands, no surface carries its own literal values and this drift cannot recur.
8. What this binds¶
ratatui theme (
theme.rs) consumes §1–§5 and eliminates the duplicated per-file color constants (§7).Slint styles (
paranoid-tokens.slint) consume §1–§5 and retune off the drifted values (§7) onto the canonical tokens.Sphinx theme (
custom.css) is the already-canonical color source, extended to carry space/type tokens.ia.md components (panels, primary action, footer, status, drill-down
⋯) are rendered from §4’s specs on every surface, so a screen in the TUI, the GUI, and a docs mockup are visibly the same component.P8 build items implement these tokens and specs rather than re-picking values; any P8 color, size, or spacing that contradicts this document is a defect against the system (mirrors brand.md §6).