Skip to content

ui: brand-grounded terminal style system (truecolor + light/dark + fallback)#367

Merged
LukasWodka merged 1 commit into
developfrom
style/cli-output-system
Jul 21, 2026
Merged

ui: brand-grounded terminal style system (truecolor + light/dark + fallback)#367
LukasWodka merged 1 commit into
developfrom
style/cli-output-system

Conversation

@LukasWodka

@LukasWodka LukasWodka commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Turns the CLI's ad-hoc colour into one brand-grounded style system, built from the design-system tokens and confirmed against tracebloc.io. Organising idea = the homepage gradient: cyan orients (structure), lime moves (action). This is surface 1 of 2 (the bash installer mirrors it next).

Design spec + mock terminals (real colour): the style proposal artifact shared in the thread.

Engine (internal/ui)

  • colorMode {none,16,true} + termBg {dark,light}, detected in New:
    • NO_COLOR / non-TTY / TERM=dumbnone; COLORTERM=truecolor|24bit24-bit; else ANSI-16.
    • COLORFGBG picks the background; unknown → dark (the dev norm).
  • A tone table maps each role to its exact 24-bit hex on dark and light terminals (primary/secondary ramps) + the nearest ANSI-16 fallback. hue() is the single brand-colour chokepoint — bright shades on dark, deep shades (primary.700/secondary.700) on light so it stays legible on white.
  • Colour is never load-bearing: headings/commands also carry Bold and alerts a distinct glyph, so it still reads under NO_COLOR / for colour-blindness.

Roles → the existing Printer methods (decisions A–D)

Role Treatment
Section / Banner / Step cyan #01a5cc (structure)
MenuRow command lime #91e947 bold (A)
MenuRow description soft lime #a7ed6c (B)
✔ / ✓ success brand lime — "green = go", unified with commands (A)
✗ offline · ⚠ warn · ✖ error soft red · amber · bold red
label : value dim neutral
inline run <cmd> (home) command tone

Light-terminal deep-shade swap (C) + truecolor-with-16-fallback (D) are both in the engine.

Test plan

  • brand_tones_test.go pins the exact SGR: #01a5cc/#91e947 on dark, #01637a/#578c2b on light, ANSI-16 (36/32) fallback with no truecolor escape — a token drift fails CI.
  • Existing colour-matrix / plain / NO_COLOR / spinner tests unchanged and green.
  • go build ./..., go vet, gofmt -s, goimports -local, staticcheck (all,-ST1005), deadcode, file-budget all clean.

🤖 Generated with Claude Code


Note

Low Risk
Terminal presentation only with NO_COLOR/TTY/plain fallbacks and CI-pinned tones; no changes to auth, cluster logic, or command behavior beyond how strings are colored.

Overview
Replaces ad-hoc fatih/color usage with a single brand tone pipeline in internal/ui: colorMode (none / 16-color / 24-bit via COLORTERM), termBg from COLORFGBG (deep primary/secondary on light backgrounds), and hue() as the chokepoint for headings, commands, warnings, and labels across existing Printer methods.

Adds Printer.Command for inline actionable text; the home screen’s “run … doctor” tails now pass one styled fragment (p.Command(inv+" "+doctorPath)) instead of separate unstyled pieces.

New brand_tones_test.go locks expected SGR (e.g. #01a5cc / #91e947 on dark, deep shades on light, ANSI 36/32 without 38;2). Spinner/UI tests follow detectMode (replacing autoColor) and honor TERM=dumb as plain output.

Reviewed by Cursor Bugbot for commit 468fcb4. Bugbot is set up for automated code reviews on this repo. Configure here.

…llback)

Turns the CLI's ad-hoc colour into one system, built from the design-system
tokens and the tracebloc.io identity. Organising idea = the homepage gradient:
cyan orients (structure), lime moves (action).

Engine (internal/ui):
- colorMode {none,16,true} + termBg {dark,light}, detected in New: NO_COLOR /
  non-TTY / TERM=dumb → none; COLORTERM=truecolor|24bit → 24-bit; else ANSI-16.
  COLORFGBG picks the background; unknown → dark (the dev norm).
- A tone table maps each semantic role to its exact 24-bit hex on dark AND light
  terminals (primary/secondary ramps) plus the nearest ANSI-16 fallback. hue()
  is the single brand-colour chokepoint; bright shades on dark, deep shades
  (primary.700 / secondary.700) on light so it stays legible on white.
- Meaning never rests on hue alone — headings/commands carry Bold, alerts carry
  a distinct glyph — so output still reads under NO_COLOR / for colour-blindness.

Roles applied to the existing Printer methods (decisions A–D):
- Section / Banner / Step  → cyan  (structure)
- MenuRow command → lime bold; description → soft lime (a7ed6c)  [A,B]
- ✔ / ✓  → brand lime ("green = go", unified with commands)     [A]
- ✗ soft red, ⚠ amber, ✖ bold red, dim neutral labels
- home.go: inline "run <cmd>" now rendered in the command tone.

Truecolor exactness + light/dark shade + 16-fallback pinned by
brand_tones_test.go, so a token drift fails CI. Existing colour-matrix /
plain / NO_COLOR tests unchanged and green.

First of two surfaces; the bash installer mirrors this next.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@LukasWodka LukasWodka self-assigned this Jul 21, 2026
@LukasWodka
LukasWodka requested a review from saadqbal July 21, 2026 08:38
@LukasWodka

Copy link
Copy Markdown
Contributor Author

@BugBot run

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 468fcb4. Configure here.

@aptracebloc aptracebloc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍

@LukasWodka
LukasWodka merged commit 11fe980 into develop Jul 21, 2026
23 checks passed
@LukasWodka
LukasWodka deleted the style/cli-output-system branch July 21, 2026 08:55
LukasWodka added a commit that referenced this pull request Jul 21, 2026
The e2e workflow's PR trigger was label-only (`e2e`), so PRs that break
the suites' assertions merge unexercised: #363/#367/#368 reworded the
`tracebloc delete` teardown copy the offboard e2e asserts on, no PR run
ever executed the suite, and every develop push went red until #378
realigned the assertions post-merge.

Add a ~5s dorny/paths-filter probe (`changes`, PR events only) and gate
both e2e jobs on it alongside the existing label opt-in. PRs touching
the union of the suites' dependency surface — the offboard flow
(delete*.go, nodeboot, api, config), the rendering layer (ui — the #367
lesson), the kind-suite seams (cluster, push), the suites themselves,
or the build inputs (Makefile, go.mod/sum, cmd) — now run the e2e
pre-merge (~2 min, jobs in parallel). Unrelated PRs skip both jobs as
before. Push / nightly / dispatch behavior is unchanged: the probe is
skipped there, hence the !cancelled() guards — the implicit success()
on a skipped need would otherwise skip those runs too.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@LukasWodka

Copy link
Copy Markdown
Contributor Author

/fr-pass — verified live: SGR tones render inside a PTY and NO_COLOR=1 strips every escape sequence to clean text (no raw codes leak).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants