Almanac visual QA — 2026-05
Pass run on 2026-05-12, phareim.no @
8657770(the QA commit itself). Rig:scripts/almanac-qa.mjs. Captures:~/github/phareim.no/.qa/almanac/<page>-<mode>.png+.vars.jsonper cell +status.json+index.json.
Why this exists
Almanac is the newest of four themes in [[phareim.no]] (scandi, hacker, space, almanac), added in commit c70bba1. The older three accumulated per-page CSS overrides one page at a time; Almanac inherits the override mechanism but not the overrides, so each page reveals whether anyone remembered to think about it. This pass is the baseline — next pass diffs against it.
Surfaced from sfl meta WF236ulE (priority HIGH).
Methodology (reproducible)
- Pinned to
phareim.no @ 8657770. Screenshots, vars sidecars, and ledger live in.qa/almanac/at that SHA. - Rig: Playwright headless via
playwright-core, pointingexecutablePathat the host's cached chromium (~/.cache/ms-playwright/chromium-1217/chrome-linux64/chrome) so no browser download. - Server:
npm run devon:3030. - Viewport: 1280 × 900.
- Theme bootstrap: navigate →
localStorage.theme='almanac'→ reload →document.fonts.ready→ 600ms settle. Needed becausecomposables/useTheme.tsrestores theme insideonMounted, so first paint isscandiand the reload is what lands on Almanac. - Routes: 19 (18
pages/files pluserror.vuevia/this-does-not-exist). 19 × {light, dark} = 38 cells.
Known rig bug, not a defect: the overlay-detection selector (.nuxt-error, nuxt-error) does not match Nuxt's actual dev error overlay, which is visible bottom-right of error-light.png. Fix the selector before the next pass.
Out of scope (explicit non-goals)
- First-paint theme flicker / SSR hydration — screenshots are post-hydration only, so any flicker is invisible here. Tracked under sfl meta "Revisit SSR theme persistence". A reader who sees no flicker in this register should not conclude flicker is fixed.
- Interactive state — no hover, no tooltip, no focus rings. The three open
Stats heatmap tooltipmetas are pre-existing and unrelated to Almanac correctness. - Reduced-motion, print, mobile, RTL — separate concerns.
- Pixel-diff baseline — this run IS the baseline. Future passes can diff against
8657770. - Fixes — each cluster below becomes a follow-up
/find-a-fixagainst the metas listed at the bottom.
Almanac correctness checklist (derived from DESIGN.md)
- hairline-not-box — cards/sections use 1px rules (
--almanac-rule-light/--almanac-rule-dark), no shadows, no rounded panels (--theme-card-radius: 0perassets/themes/almanac.css). - accent-only-at-attention —
--almanac-rust(light) /--almanac-amber(dark) appears at most once per primary view, at the moment of attention. - serif-body-always — body type is the Georgia stack (
--font-almanac-body). - stars-dark-only —
AlmanacPaperstars render only in dark, sparse. - no-chrome — no gradients (except midnight bg), no shadows, no synthetic window decoration.
- paper / midnight bg —
--almanac-paper(#f4f0e8) in light, midnight gradient in dark. - CSS vars propagate — canvas/SVG elements read theme via CSS custom properties or inline overrides. Cross-checked via the per-cell
.vars.jsonsidecars. - link discipline — links read as ink, accent only on hover (color rule; hover behavior out of scope).
The .vars.json sidecars confirm the page-level wrapper (.almanac-page) propagates all eight semantic tokens correctly on all 19 routes in both modes — i.e. the theme system itself is sound. Every defect below is a per-page override that ignores those tokens.
Page × mode status table
| Page | Light | Dark | Notes |
|---|---|---|---|
/ (index) |
broken | broken | hardcoded saturated canvas circles, both modes (green/tan light, blue/purple dark) |
/about |
ok | ok | best-in-class Almanac fit |
/activity |
minor | minor | accent overuse (rule 2); dark cards near-invisible |
/clock |
ok | broken | dark: second hand has no visible accent (rust hardcoded, --theme-accent not applied) |
/colophon |
ok | ok | alignment baseline |
/feed |
ok | ok | |
/focus |
minor | broken | dark: 25:00 timer near-illegible, ring nearly invisible |
/gallery |
ok | ok | clean empty state |
/guestbook |
minor | minor | input radius non-zero; dark borders too subtle |
/lab |
broken | broken | green LIVE badges + red-dot emoji-style decoration + rounded cards — off-theme |
/meta |
minor | minor | commit cards rounded; dark borders too subtle |
/morse |
minor | minor | reference letter cards rounded; dark cards near-invisible |
/now |
ok | ok | best-in-class |
/playground |
minor | minor | filled rust/amber button (defensible); textarea + selects + image-frame rounded |
/projects |
minor | minor | cards rounded; dark cards near-invisible; title low contrast in dark |
/stats |
minor | minor | metric cards rounded; dark borders too subtle |
/terminal |
broken | broken | macOS-style window chrome (stoplight buttons + chrome bar + rounded frame) violates no-chrome |
/uses |
ok | ok | |
/this-does-not-exist → error.vue |
ok | ok | the 404 page itself is Almanac-correct; the dev-only error overlay in light is rig artifact, not a defect |
Tally: 9 cells broken, 18 minor, 11 ok, 0 known-gap.
The zero-known-gap count matters: it falsifies a prior assumption. The "Almanac scoped overrides for canvas/SVG pages" meta assumed canvas/SVG would fail to read CSS variables. The .vars.json sidecars and clock-light + morse + terminal text show the opposite — Almanac reaches SVG paint fine via currentColor and direct token reads. The canvas defects that remain (index circles, clock-dark second hand) are page-specific hardcoded colors, not a propagation gap. That meta needs re-scoping or closing — see cross-references.
Defects (ranked by leverage)
1. Landing page canvas decoration ignores Almanac — severity: blocker
File: pages/index.vue (canvas/SVG decoration logic).
Rule violated: 5 (no-chrome) and 7 (CSS vars propagate).
Evidence: .qa/almanac/index-light.png, .qa/almanac/index-dark.png — saturated green/tan (light) and blue/purple (dark) circles dominate the viewport, completely off-theme.
Why blocker: this is the first impression for every visitor on /. Under Almanac the landing page should be paper-with-restraint, not '90s-Geocities-circles.
Proposed fix: make the decoration theme-aware — either swap to an Almanac-specific variant (sparse stars from AlmanacPaper.vue already covers this aesthetic) or hide it entirely when activeTheme === 'almanac', mirroring how <SpaceStarfield v-if="activeTheme === 'space'" /> is conditional in app.vue.
2. Terminal page hardcodes macOS window chrome — severity: blocker
File: pages/terminal.vue (terminal-window decoration).
Rule violated: 5 (no-chrome).
Evidence: terminal-{light,dark}.png — red/yellow/green stoplight buttons, chrome title bar with phareim@phareim.no — bash, rounded window frame. The hacker/space themes are clearly bleeding through.
Proposed fix: when activeTheme === 'almanac', render the terminal flat — drop the stoplights and the chrome bar, render the prompt as plain serif body text on paper / midnight. Could also keep bash-style monospace inside but lose the OS window decoration.
3. pages/clock.vue second hand has no Almanac-dark accent — severity: visible
Rule violated: 2 (accent-only-at-attention).
Evidence: clock-light.png shows rust second hand (correct moment-of-attention). clock-dark.png shows monochrome cream face — the second hand is either hardcoded rust (invisible against the midnight-similar value) or doesn't read --theme-accent (amber #d4a574 in dark).
Proposed fix: change second-hand stroke to var(--theme-accent) (or var(--almanac-amber) when dark mode is the only consumer). getComputedStyle on .almanac-page already returns --theme-accent: #d4a574 in dark — the page just isn't reading it.
4. Dark-mode hairline rule is too subtle — severity: visible (systemic)
File: assets/themes/almanac.css — the token --almanac-rule-dark: rgba(255, 255, 255, 0.10).
Rule violated: 1 (hairline-not-box) — the rule is technically present but visually absent on cards across activity, stats, projects, meta, morse, guestbook, focus.
Evidence: every *-dark.png cell with cards has near-zero card definition; cards float as untethered text.
Proposed fix: raise --almanac-rule-dark opacity to ~`0.18–0.22, OR split into --almanac-rule-dark-card(stronger) and--almanac-rule-dark-separator` (subtle, for short rules between sections). Cross-check that 0.18 doesn't muddy the "hairline" intent — DESIGN.md should be the arbiter.
5. pages/lab.vue has off-theme decoration — severity: visible
Rule violated: 7 (CSS vars propagate) and arguably 2.
Evidence: lab-{light,dark}.png — saturated green LIVE pill badges (color stays the same in both modes), red-dot emoji-style red circle as decoration for the /red-dot/ tool card.
Proposed fix: render LIVE badges as hairline pills with --almanac-ink-mute text (or --theme-accent if "this is live" is the moment-of-attention). The red-dot emoji is illustrative content for the project itself, so keep it but render under [data-theme="almanac"] with currentColor: var(--theme-accent) if it should harmonize.
6. pages/focus.vue dark-mode timer contrast — severity: visible
Rule violated: body text contrast (rule 3 implied via legibility).
Evidence: focus-dark.png — 25:00 numerals rendered in --theme-text-subtle (#5a6a7a) range, near-illegible against #0e1219 midnight. Light mode is fine.
Proposed fix: use var(--theme-text) (#ebe4d4 in dark) for the primary timer numerals, reserve subtle tones for the "ready" / state label.
7. Per-page border-radius overrides ignore --theme-card-radius: 0 — severity: nit (systemic, many sites)
Rule violated: 1 (hairline-not-box — Almanac is flat-rectangular by design).
Evidence: project cards, morse letter chips, playground textarea/selects/image-frame, stats metric cards, meta commit cards, focus keyboard-shortcut chips, guestbook inputs — all visibly rounded (~6–12px). Likely each has a per-page border-radius: <px> instead of reading var(--theme-card-radius, 0).
Proposed fix: grep border-radius: in pages/ + components/, replace literal radius values with var(--theme-card-radius, <fallback>). Set the fallback to whatever the other three themes want (likely 6–8px).
Note: this is a nit only because the visual offense per cell is small. As a pattern across 8+ pages it's the most ubiquitous Almanac violation, so the fix is one PR that touches many files. May be cheaper to address before the per-page bug fixes above.
Cross-references to sibling sfl metas
Almanac scoped overrides for canvas/SVG pages(MEDIUM) — revise framing. The rig disproves the premise: CSS vars propagate to canvas/SVG viacurrentColorand direct token reads on the pages that handle it correctly (clock-light, morse, terminal text). The remaining canvas-related defects are page-specific hardcodes (#1, #3 above), not a propagation gap. Either re-scope this meta to "page-specific canvas hardcodes in index.vue and clock.vue dark" or close and let the per-page metas cover it.Audit accent usage — accent-only-at-attention(LOW) — substantiated. activity-light and colophon both stack 4+ accent moments per view (links + headers + badges + dots). Defects in this register call out the worst offenders but the broader audit remains its own job.Revisit SSR theme persistence (first-paint flicker)— not addressed. Post-hydration screenshots cannot speak to this. Flagged explicitly in non-goals above.Reconcile prefers-color-scheme rule in CLAUDE.md(MEDIUM) — confirmed needs update.~/github/phareim.no/CLAUDE.mdstill describes "three themes" (scandinavian,hacker,space), lists keyboard shortcuts1 / 2 / 3without4, and says "Don't use@media (prefers-color-scheme: dark)— the theme system handles this" — Almanac is the explicit exception. The reconciliation work is genuinely owed.
Follow-up sfl metas filed
After this pass, the following meta items have been added to github.com/phareim/phareim.no (clustered by root cause, not per cell):
m0SDIsE4(priority A, blocker):pages/index.vue— make canvas decoration theme-aware under AlmanacmfkkOfaC(priority A, blocker):pages/terminal.vue— drop macOS window chrome under Almanac-nlSoyPK(priority B, visible):pages/clock.vue— second hand must use--theme-accentin Almanac dark6VPHE3Eb(priority B, visible, systemic): bump--almanac-rule-darkopacity or split card/separator tokens so cards are visible in darkWfI-CU1C(priority B, visible):pages/lab.vue— LIVE badges + decorative icons need theme-aware variants2zB9A8zK(priority B, visible):pages/focus.vue— dark timer contrast (--theme-textnot--theme-text-subtle)sAdRb1rE(priority C, nit, systemic): audit per-pageborder-radiusoverrides; route through--theme-card-radius
Reproducing this pass
cd ~/github/phareim.no
git checkout 8657770 # or HEAD if conventions remain stable
npm install
npm run dev & # binds :3030
npm run qa:almanac # writes to .qa/almanac/
Outputs: 38 PNGs + 38 .vars.json + index.json + status.json in .qa/almanac/. Re-grade by walking status.json against the checklist above.
See also
- sleeper-cognition-stack — phareim.no is one of the producers in the broader Sleeper graph
~/github/almanac-design/DESIGN.md— canonical spec; this checklist is its compression~/github/phareim.no/assets/themes/almanac.css— the implementation; reading it alongside the screenshots is the fastest way to spot which tokens a defective page is ignoring