Align dashboard palette with canonical tokens, add theme selector - #13
Merged
Conversation
The dashboard maintained its own invented palette: a warm cream base with five accent hues, three of which were declared and never used. Meanwhile prax/packages/tokens already ships an MIT set that this file's own header said to swap to, and prax-site/brand/brand.css names Proof UI in its scope and assigns Proof a teal. The product and the page selling it did not look like the same tool. Adopt the canonical tokens name-for-name so the block can later be replaced by an @praxity/tokens import. Delete the dead accent-3, warn and space tokens. Reduce five hues to three with one meaning each: teal reads finished, yellow reads look here, neutral reads still going. The offset shadow was 1.10:1 against the page and read as misregistration rather than a device; it is now the canonical card shadow. Bar tracks were 1.31:1 and effectively invisible; the accent fill now clears 3:1 against them. Add a Theme control (Auto/Light/Dark). Tokens are light-dark() pairs, so the selector only flips color-scheme on the root and native controls follow. Served as /theme.js rather than inline because the CSP is default-src 'self' with no unsafe-inline; blocking in <head> so there is no flash. Public pages carry the preference but have no control of their own. Unify the type scale. Four sizes sat within 1.6px of each other, and the hero stat outranked the page title by 10%, close enough to read as a mistake. Now 0.8/1/1.25/1.5/1.75/2rem and three weights. Fix two real defects found while testing dark mode: - input/select/button set a background but no color, so controls kept UA-default black ink on the dark canvas. WebKit ignores author color on a native menulist, so inputs and selects are left to color-scheme; buttons do need explicit colors because a UA button is silver and .prax-danger does not clear 4.5:1 against it. - background was set on body only, leaving the root transparent, which made contrast tooling read the page as white. Give null table cells a visually-hidden "None": a bare em dash can leave a cell sounding empty depending on punctuation level. Verified: every rendered text node measured in both themes, zero contrast failures; Praxity Check over 7 pages x 2 states, 1267 evaluations, zero findings and zero needsReview, axe:color-contrast passed 14/14. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The dark capture was going to gitignored test-results/, so the only screenshot anyone saw was light. Write it to docs/assets/ and show both. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proof's dashboard maintained its own invented palette rather than using the design system that already exists for it.
prax/packages/tokensships an MIT set thatstyles.ts's own header said to swap to, andprax-site/brand/brand.css:3names "Proof UI" in its scope while assigning Proof a teal. The result was that the product and the marketing page selling it did not look like the same tool.--praxity-*tokens sit outside the MIT boundary (prax/docs/open-source-boundary.md:45), so this adopts the canonical MIT set rather than copying brand.css. Relicensing the Proof brand subset remains open and this does not foreclose it.Palette
@praxity/tokensimport.accent-3(#FF48B0, the loudest value in the file, referenced nowhere),warn, andspace.h1::afterrule: a 4px bar in a colour used nowhere else, at 1.99:1, encoding nothing.Theme selector
Auto / Light / Dark in the header. Tokens are
light-dark()pairs, so the selector only flipscolor-schemeon the root and native controls and scrollbars follow without extra rules. Auto honoursprefers-color-schemewith no JS.Served as
/theme.jsrather than an inline script because the CSP isdefault-src 'self'with nounsafe-inline— the header is unchanged. Blocking in<head>, so no flash. Public pages carry a pinned preference but have no control of their own. The control is hidden without script, so it is never a dead widget.Type scale
Four sizes sat within 1.6px of each other (
0.78/0.8/0.85/0.9rem), and the hero stat outranked the page title by 10% — close enough to read as a mistake rather than a hierarchy. Now0.8 / 1 / 1.25 / 1.5 / 1.75 / 2remand three weights instead of five. Help text moved up to body size since it is guidance people have to read.Bugs fixed
Both found by the new dark-mode test, both would have affected real users:
input, select, buttonset a background but nocolor, so controls kept UA-default black ink on the dark canvas. WebKit ignores authorcoloron a native menulist, so inputs and selects are now left tocolor-scheme; buttons do need explicit colours, because a UA button is silver#c0c0c0and.prax-dangeronly reaches 3.61:1 against it.bodyonly, leaving the root transparent, which made contrast tooling read the page as white.Accessibility
Null table cells now carry a visually-hidden "None". A bare em dash can leave a cell sounding empty depending on punctuation level, and it was doing four different jobs (no score, no data, not applicable, no drop).
The drop-off row is a highlighter stroke on the words rather than a filled row — a full-width band was too loud on a dark page and put the teal bar directly on saturated yellow.
Verification
axe:color-contrastpassed 14/14. The only 2 reported items arelocal-resource-missingartefacts of the static capture folder.pnpm verifygreen: typecheck, 197 unit tests, 52 a11y tests across desktop/mobile x Chromium/WebKit — including a new test that pins a theme, follows it across navigation to a page with no selector, runs axe in dark, and confirms Auto resets and survives reload.Known limits
axe:image-altandsvg-img-altwere inapplicable everywhere, so the bar chart and funnel were not tested as graphics. That is a coverage gap, not a pass.🤖 Generated with Claude Code