Skip to content

Add a terminal skin, and make the board's branding settings real - #5

Merged
ralyodio merged 1 commit into
mainfrom
terminal-skin-and-branding
Sep 3, 2026
Merged

Add a terminal skin, and make the board's branding settings real#5
ralyodio merged 1 commit into
mainfrom
terminal-skin-and-branding

Conversation

@ralyodio

@ralyodio ralyodio commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

board.accent, board.logoUrl, board.faviconUrl and board.theme have been in DEFAULT_SETTINGS since the beginning and were read by nothing — a board could set an accent and watch the page not change. All four work now, and there is a third skin to use them with.

The terminal skin

A layer over the modern sheet, exactly as classic is, so a component's structure stays defined in one place:

  • neutral surfaces, so the accent is the only colour on the page
  • hairline rules instead of shadows — depth the way a tiling terminal separates panes
  • monospace for chrome (nav, meta, counts, buttons), proportional for prose, because reading a paragraph in a monospace face is the part of the aesthetic that actually hurts
  • the three dots of a title bar on section headers, drawn as one element's box-shadow so no markup changes and nothing reaches the accessibility tree

Nothing in it hard-codes a hue. It is a green board, an amber one or a blue one depending on board.accent.

The accent is derived, not stored twice

A colour legible on a dark board is usually illegible on a light one — #5fff87 reads beautifully on near-black and vanishes on white. So the chosen hue is emitted darkened for the light theme and brightened for the dark one, and every token that follows the accent (--ring, --primary-hot, --gradient-brand, --glow-primary, --page-wash, --row-hover) moves with it. One setting, readable in both themes.

It is baked into the stylesheet rather than written as an inline <style>: style-src carries no 'unsafe-inline', and widening the policy for a handful of custom properties is a poor trade. Because the sheet is served under a content hash, changing the accent in the admin panel changes the URL, so the new colour reaches a returning reader immediately instead of waiting out a year-long max-age.

board.accent defaulted to #4f46e5, an indigo nothing read. Wiring that up as-is would have repainted every existing board on upgrade, so the default is now empty — meaning "the built-in palette".

Two bugs found on the way

  • The footer's GitHub link was broken by the board's own CSP. It carried style="display:inline-flex;…", and style-src governs style attributes, not only <style> blocks — so the icon sat above the word instead of beside it. It is a class now. 30 more inline style attributes elsewhere in the app are refused the same way; that is a separate change, not this one.
  • /prefs/theme deleted the cookie for "system" instead of storing it. Identical behaviour on a board that defaults to system, and wrong on any other: a dark-default board would read the absence of a cookie straight back as dark, so the three-state toggle cycled dark → dark. All three choices are stored now.

Checks

pnpm typecheck clean, pnpm test 156/156 (10 new: the colour maths, the skin layering, the accent in the served sheet, logo/favicon in the header, and board.theme never overriding a reader's cookie).

Not run: pnpm format. It reformats ~70 files at HEAD and breaks two tests, so this branch is hand-formatted to match the surrounding code.

Rendered at 1280px, dark and light, with the HQTUI wordmark and #5fff87.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JQRBzkLAusnFQHWMjgBLDZ

`board.accent`, `board.logoUrl`, `board.faviconUrl` and `board.theme` have
been in DEFAULT_SETTINGS since the beginning and were read by nothing. A board
could set an accent and watch the page not change. All four now work, and
there is a third skin to use them with.

**terminal** is a layer over the modern sheet, exactly as classic is: neutral
surfaces so the accent is the only colour on the page, hairline rules instead
of shadows, monospace for chrome and proportional for prose, and the three
dots of a title bar on section headers. Nothing in it hard-codes a hue, so it
is a green board, an amber one or a blue one depending on the accent.

**The accent is derived, not stored twice.** A colour legible on a dark board
is usually illegible on a light one, so the chosen hue is emitted darkened for
the light theme and brightened for the dark one. One setting, readable in
both. It is baked into the stylesheet rather than written as an inline <style>,
because style-src carries no 'unsafe-inline' — and since the sheet is served
under a content hash, changing the accent changes the URL and reaches a
returning reader immediately instead of waiting out a year-long max-age.

`board.accent` defaulted to '#4f46e5', an indigo nothing read. Wiring that up
as-is would have repainted every existing board on upgrade, so the default is
now empty, meaning "the built-in palette".

Two fixes found on the way:

- The footer's GitHub link carried `style="display:inline-flex..."`, which the
  board's own CSP refuses — style-src governs style ATTRIBUTES too, not just
  <style> blocks — so the icon sat above the word instead of beside it. It is a
  class now. 30 more inline style attributes elsewhere are still refused the
  same way; they are a separate change.
- `/prefs/theme` deleted the cookie for "system" instead of storing it. On a
  board whose own default is dark that reads straight back as dark, so the
  three-state toggle would have cycled dark -> dark. All three choices are
  stored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQRBzkLAusnFQHWMjgBLDZ
@ralyodio
ralyodio merged commit ddbb579 into main Sep 3, 2026
3 checks passed
@ralyodio
ralyodio deleted the terminal-skin-and-branding branch September 3, 2026 10:40
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.

1 participant