Themes and contrast¶
/theme switches palette live as you scroll, and reverts if you press Esc. Whatever you
pick is remembered in ~/.packetsmith/config.json.
Contrast is a test, not a promise¶
Every colour has a role, and each role declares the contrast ratio it must clear
against all three surfaces (bg, panel, sunken). bun test audits all 13 themes and
fails the build if one falls short.
| Role | What it draws | Minimum |
|---|---|---|
fg |
primary text | 10:1 |
dim |
secondary: device model, interface names, section titles, the ⏱ line |
5:1 |
faint |
tertiary: tree guides, shortcuts, hints | 3:1 |
wire |
links on the canvas plan — these are data | 3:1 |
alert warn live brand |
state and identity | 4.5:1 |
line shadow |
chrome only: borders, rules, the empty half of a gauge | 1.5:1, capped at 4.5 |
The cap on line is not a typo. A chrome colour that clears a text ratio stops reading
as chrome and starts competing with the content.
Why this exists¶
The original palette was not short of themes — it had one colour doing two jobs. rule
sat at 1.38:1 against the background, which is fine for drawing a hairline and
illegible for text. It was used as text in 25 places: the device model, the interface
names, the ⏱ line — the whole point of which is explaining why a turn is slow — the
FABRIC/DEVICES titles, and every instruction on the first-run screen, which is the first
thing anyone reads.
Any new theme would have inherited the defect, so the work started by splitting the role, not by adding palettes.
Measured before the fix, against #0A0A0A:
| hex | ratio | ||
|---|---|---|---|
fg |
#EAEAEA |
16.46:1 | fine |
alert |
#E61919 |
4.26:1 | below AA — and it is the error colour |
dim |
#6B6B6B |
3.72:1 | borderline |
wire |
#4E4E4E |
2.38:1 | low — and the plan says links are data |
rule |
#2A2A2A |
1.38:1 | illegible |
The palettes¶
Own family: telemetry (the house one), amber, phosphor, ice, contrast, paper
(light).
Adapted: catppuccin, gruvbox, nord, tokyo-night, dracula, rose-pine,
solarized.
Adapted, not copied. Hue and saturation are kept and lightness is pushed until the role's minimum is met. Where that would have wrecked the colour, the background was darkened instead; where even that fell short, the theme's own comment says so. A theme that does not pass the contrast test does not ship.
Writing your own¶
Themes are data in src/tui/themes.ts. Add one, run bun test, and the audit tells you
which role on which surface falls short and by how much — not "it looks fine".
Check it renders with bun run preview, which prints the interface in fixed states in
about a second, with no Packet Tracer and no tokens.
Effects¶
/effects turns on CRT scanlines, vignette and a rolling bar. Off by default: it is
character, not legibility.