Skip to content

docs(website): a user-facing Appearance page, and PADDOCK_BRAND_ACCENT is now hue-only - #814

Open
edspencer wants to merge 6 commits into
mainfrom
docs/appearance-page
Open

docs(website): a user-facing Appearance page, and PADDOCK_BRAND_ACCENT is now hue-only#814
edspencer wants to merge 6 commits into
mainfrom
docs/appearance-page

Conversation

@edspencer

@edspencer edspencer commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Why

v0.67 shipped four runtime themes, a ten-name accent picker and a ground tint, all
switchable from the Config screen. No page on the docs site mentioned any of it:
"Appearance" matched zero pages under website/src/content/docs/. This is the
largest documentation gap in the v0.66.2 → v0.69.0 range.

Every claim is ground-truthed against source in this branch, not against the
changelog. Cites below are by file + symbol per CLAUDE.md, not by line number.

What's in it

  • New configuration/appearance.md — where the section lives and why it is on
    /config rather than in a dialog; the four themes with their registry blurbs and
    that Foundation is the default; the accent strip and the ten named colours;
    what the solver actually promises (below); the ground tint; the per-device /
    localStorage
    scope, stated plainly because it is the thing a reader will get
    wrong; light/dark as a separate toggle; how PADDOCK_BRAND_ACCENT composes; and
    a contributor pointer to docs/DESIGN.md.

    It records as a :::note that the instance-default half of the two-scope model
    (theme: in the config file plus a PADDOCK_THEME) is a deliberate stub
    rather than a defect — PADDOCK_THEME appears nowhere in packages/ except the
    source comment saying it is not built yet.

  • Sidebar entry in website/astro.config.mjs, first in the Configuration group.
    Starlight does not auto-discover; without this the page is unreachable.

  • configuration/environment.mdPADDOCK_BRAND_ACCENT no longer sets a
    colour. Only the hue of that hex survives; the theme supplies the saturation
    and solves the lightness against its own surfaces (appearance.ts,
    instanceDefaultHue, which discards L and C). The server still injects its
    :root{--accent…} block (server/src/brand.ts, accentRootStyle), but the
    [data-theme] blocks and the solved inline values outrank it.

  • architecture/overview.md — the same correction in the env-surface table.

  • contributing.md — the first inbound link anywhere to docs/DESIGN.md.
    It is 573 lines, current and contributor-facing, and grep -rn 'DESIGN.md' website/ README.md CONTRIBUTING.md returned nothing before this PR.
    docs/DESIGN.md itself is untouched.

How the page scopes the AA claim

This was the hardest paragraph in the PR. Its final shape is three-part: the
mechanism is real, the residual case is silent, the precision is unverified.

Real, and documented as such:

  • AA_TEXT = 4.5 / AA_MARK = 3 (accent.ts) are correctly applied per channel.
    Text channels solve against AA_TEXT + MARGIN; --accent uses AA_MARK + MARGIN
    — the correct WCAG floor for a non-text mark, not an under-application of the
    text floor.
  • solve() takes floor = target, and solveChannels passes a lower floor
    explicitly — so the floor genuinely is the guarantee and matching the theme
    genuinely is a preference. The page says exactly that.
  • repairFill (defined in applyAccent) re-reads what the theme derived and
    writes a repaired value; it is called for --accent-solid and
    --accent-solid-hover, with a third repair pass for --accent-text. That is what
    makes the floor hold for a theme nobody has written yet.

Silent, and linked:

  • #813 — an accent that
    cannot physically reach the floor is applied with no warning: solve()'s hit is
    discarded by every caller, and report.ok / report.checks are never rendered
    (AppearancePanel.tsx reads only .hue and .mode).
  • #816 — the tint's
    ground check is measured by tintGround and folded into report.ok by
    applyAppearance, but never repaired; the pushed check hardcodes
    repaired: false. The page names that asymmetry against the accent explicitly,
    because "folds it into the same readability check" otherwise implies a repair that
    does not happen.

Unverified, and scoped precisely:

  • No numeric contrast figure is published as if it were measured. An earlier draft
    quoted a solved 13.6:1; accent.ts's MARGIN comment records that rounding
    moved three of 110 combinations from a solved 4.50 to a rendered 4.48, so a
    solved ratio is not a painted one. The page now names WCAG's own thresholds as
    the thing being aimed at and quotes no achieved figure.
  • The page distinguishes what is tested from what is not: the four themes' own
    palettes are covered by a real suite (styles/themes.test.ts, tokens.test.ts,
    theme-{parchment,terminal,scifi}.test.ts), but those checks parse the static
    stylesheets. There is no accent.test.ts or appearance.test.ts, so the runtime
    solve is never exercised — the source makes the same point in repairFill's
    comment: "a SOLVED hue is a value no static guard ever sees."

No code is softened to match the docs.

Checks

  • cd website && npm install && npm run build → exit 0, 53 pages.
  • Orphan/dangling check (runbook §7): ORPHANS [] / DANGLING [].
  • /configuration/environment/#branding-per-instance resolves in the built output.
  • Leak check: 127.0.0.1 count across website/src/content/docs/** + README.md
    is 46, unchanged — this diff adds none. No hostnames, LAN IPs or box-specific
    values in the diff.
  • Docs-only: no changeset, no version bump.

Not in scope

  • Screenshots. Every image on the site predates the overhaul, so the theme
    swatches and the accent strip would be the site's only post-overhaul images —
    an argument for shooting them with the rest of the re-shoot, not here. Another
    agent owns media; this page ships prose-only and reads fine without a picture.
  • The configuration/instance-settings.md Appearance row and the Config screen: one measure, a surface, and rows (structural) #768
    section-rail/filter subsection. The Appearance page covers the filter/rail
    behaviour in its "Where it is" section; the table row is a one-line follow-up.

HomeLab Agent added 3 commits August 10, 2026 13:52
…BRAND_ACCENT

The v0.67 visual overhaul shipped four runtime themes, a ten-name accent
picker, a ground tint and an AA-contrast-floor solver, and no page on the
site mentioned any of it -- "Appearance" matched zero pages.

- New configuration/appearance.md, ground-truthed against
  packages/web/src/lib/{appearance,accent,theme}.ts and AppearancePanel.tsx.
- Sidebar entry (Starlight does not auto-discover).
- PADDOCK_BRAND_ACCENT in configuration/environment.md and the env-surface
  table in architecture/overview.md: only the hue of that hex is used now.
- contributing.md: the first inbound link anywhere to docs/DESIGN.md.
…e does

The brief said the picker has an AA-contrast-floor solver. A second source
trace disproves it, and I re-verified all four points in this tree:

- solve()'s `hit` flag has no consumer anywhere (accent.ts:262,266,284 are
  the only occurrences -- the return sites).
- applyAccent computes report.ok / report.checks and no UI renders them;
  AppearancePanel holds the AccentReport but reads only .hue and .mode.
- There are no solver tests: 24 *.test.ts under packages/web/src/lib/ and
  neither accent.test.ts nor appearance.test.ts is among them. The shipped
  contrast guards are static-CSS-only and never see a picked accent.
- --accent's floor is AA_MARK + MARGIN = 3.12, not 4.5. Only a600/a700 get
  4.62 and --accent-solid's repair pass 4.5.

So the picker ATTEMPTS a floor, applies its best effort silently when it
cannot reach one, and surfaces nothing either way. The page now says it aims
at AA rather than guaranteeing it, and tells a reader what to do if a colour
looks wrong. Same treatment for the tint (measured, not enforced) and for
PADDOCK_BRAND_ACCENT in environment.md.

The code side is a separate finding and is being filed as an issue; nothing
in the code is softened here.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 10, 2026

Copy link
Copy Markdown

Deploying paddock with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4995d11
Status: ✅  Deploy successful!
Preview URL: https://894b9055.paddock-7u2.pages.dev
Branch Preview URL: https://docs-appearance-page.paddock-7u2.pages.dev

View logs

HomeLab Agent added 3 commits August 10, 2026 14:02
…se honest

An earlier commit over-corrected: told the solver did not enforce a floor, I
stripped out the floor and the repair pass entirely and under-claimed a real
feature. Re-read from source and restored, with the parts that ARE unenforced
scoped precisely rather than generalised.

What source shows (accent.ts):
- AA_TEXT = 4.5 / AA_MARK = 3 are real WCAG AA floors, correctly applied per
  channel (:63-65). The earlier '--accent targets 3.12 not 4.5' framing was
  misleading: 3.12 is AA_MARK + MARGIN, the correct floor for a non-text mark.
  Text channels get AA_TEXT + MARGIN = 4.62.
- solve()'s `floor` genuinely is the guarantee and `target` genuinely is a
  preference -- `floor = target` by default and callers pass a lower floor
  explicitly (:255-262, :505).
- repairFill re-reads what the theme DERIVED and writes a repaired value
  (:634), which is what makes the floor hold for themes not yet written.

What is genuinely unenforced, now stated as exactly that and no wider:
- #813 -- an accent that cannot physically reach the floor is applied
  silently; solve()'s `hit` is discarded and report.ok is never rendered.
- #816 -- the tint's ground check is measured and folded into report.ok
  (appearance.ts:216-226) but never repaired; the check hardcodes
  `repaired: false`. The page now names that asymmetry against the accent
  rather than implying a repair that does not happen.

Both issues are linked from the page, which is house style here.
Two precision fixes on the accent section. Neither walks back the floor or
the repair pass -- both are real and stay.

- Dropped the '13.6:1' figure for terminal. accent.ts:73 records that
  rounding moved three of 110 measured combinations from a solved 4.50 to a
  RENDERED 4.48, so a solved ratio is not the painted one and must not be
  published as if it were -- same class as the blend-mode finding from the
  theme work. The point survives without the number ('clears the floor
  several times over') and cannot rot.
- Kept 4.5 and 3, now attributed to WCAG as the standard's own thresholds
  rather than asserted as what the rendered pixel achieves.
- The note is now the honest three-part shape: the mechanism is real, the
  residual case is silent (#813), and the precision is unverified -- the
  solve is arithmetic on colour values, MARGIN exists to absorb the 8-bit
  rounding, and nothing samples the rendered result. Confirmed no test file
  under packages/web/src imports lib/accent.
…e solve

'Untested' was too blunt and unfair to the theme work. What exists:
styles/{themes,tokens,theme-parchment,theme-terminal,theme-scifi}.test.ts --
a real contrast suite that walks each theme's palette pair by pair. What
does not exist is any test of the SOLVER: there is no accent.test.ts or
appearance.test.ts, and the guards that do exist parse the static
stylesheets, so a runtime-solved accent is never in their sample. The
source makes the same point at accent.ts, in the repairFill comment: 'a
SOLVED hue is a value no static guard ever sees.'

The page now draws that line explicitly rather than implying the theming
is unverified. It is the more interesting fact and the fairer one.

Also correcting my own earlier commit message: lib/accent IS imported by
non-test source (AppearancePanel.tsx and lib/appearance.ts). The narrow
claim -- no TEST file imports it -- is the accurate one and is what the
page rests on.
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