← Back

Patch notes — design system, review, cleanup & search fix (2026-06-12)

Branch: dev (not yet merged to main, so not deployed — main is the prod trigger). This session built one coherent design system across the whole property, reviewed and hardened it, did a safe maintainability pass on the admin server, hardened the smoke harness, and fixed a real search-snippet bug.


1 — Total coherent design system ✅

Problem: the site looked branded on the front page but drifted everywhere else — three styling surfaces (landing inline <style>, editorial.css, the admin ADMIN_CSS) each carried their own variable vocabulary plus a dead warm-cream palette, all only coincidentally resolving to a similar navy. .card meant three different things; the theme toggle key was split (skui-theme vs ethica-theme).

Fix: one token spine + a shared component library, consumed identically by the Hugo pages and the Python admin (both link the same stable, un-fingerprinted static files):

  • static/css/theme.css — single source of truth: brand/logo/font/color/radius/shadow/ layout/space tokens. Dark :root default + :root[data-theme="light"].
  • static/css/components.css — shared primitives on tokens: .btn, .card/.panel (+ --feature/--project), .badge, .chip, .field, .section, .status-*, .reveal, and the site chrome .ev-top/.ev-nav/.ev-foot.
  • static/css/admin.css — admin-only layout/density on the same tokens.
  • assets/css/editorial.css slimmed to layout-only (642 → ~300 lines); every legacy var(--ink/--paper/--green/…) rewritten to canonical --color-*; “Playfair Display” → --font-display (Comfortaa).
  • Theme key unified to ethica-theme everywhere (pre-paint reader + menu writer).
  • Documented in assets/css/DESIGN.md (token catalog, load order, “add a module without drift” guide).

2 — Unified chrome, background, and Products rename ✅

  • Header/footer unified across every page including home (dropped the per-page {{ if not .IsHome }} guards; index.html lost its inline header/footer and now inherits the shared partials via baseof.html) — fixes the “logo not in the same place” / “menu differs per page” reports.
  • Front-page backdrop everywhere: the radial+linear glow moved into a --bg-app token referenced by both editorial.css and admin.css.
  • Blog list/single, products, taxonomy/term redesigned to the glass-navy card language; single posts drop the rail. Dead editorial-card.html removed.
  • Admin “Projects” → “Products” (page H1 + side-menu label); the /admin/projects route and the projects DB table are unchanged.
  • Header alignment: html { scrollbar-gutter: stable; } so the centered header/footer no longer shift sideways between short (no-scrollbar) and tall pages.

3 — Code review of the design system + 12 fixes ✅

Extra-high-effort recall review (review/2026-06-12-design-system.md). No crash-class bugs; 12 findings, all fixed:

  • Regressions: added .badge--maintained (a documented status value had no modifier → silent grey pill); cardpanel on the two add-row admin forms (they hover-lifted); restored .chip .ic accent (deleted in the slim-down, markup still emitted it).
  • Cleanup: removed dead .abar/.apublic/.aright; unified html[data-theme]:root[data-theme="light"]; extracted --bg-app (de-duplicated the gradient); font literal → var(--font-sans); admin icon legend reuses .chip; removed PUBLIC_NAV and the no-op .tt class.

4 — Stale-cache background follow-up documented ✅ (known issue, open)

Returning browsers briefly showed no page background. Not a code defect: the shared CSS is served at stable un-fingerprinted URLs, so a browser with a cached old theme.css (no --bg-app) paired with the fresh fingerprinted editorial.css (which needs it) drops the background. Workaround: hard refresh. Proper fix (open): cache-bust the shared /css/* links in ed-head.html + server.py:chrome_head(). Recorded in the review note.

5 — Safe maintainability pass (admin server) ✅

Behavior-preserving; no routes/APIs/schema/auth/env-var changes (review/2026-06-12- maintainability-pass.md):

  • csrf_input(token) helper replaces 14 copies of the hidden CSRF-field literal.
  • The password-change handler now uses the existing ITERATIONS constant instead of two hardcoded 210000 literals (identical value).
  • Removed vestigial DB_PATH (a data/admin.sqlite path from before the Postgres migration) and its mkdir; startup banner now reads db=postgres.
  • Comments on the non-obvious blocks (auth constants, in-memory login throttle, the LIST_SPECS config-driven-CRUD contract).

6 — Smoke harness hardened ✅

The run-ethica-no smoke asserted admin login → 302 but never rendered an authed page, so a self-recursing form helper that 500’d every admin editor passed an 8-check smoke. Added a 9th check (admin form renders (authed)): log in keeping the session cookie, GET /admin/status, assert a rendered name=csrf value= field. Now 9/9. Documented + a battle-scar gotcha in the skill.

7 — Search snippet corruption fixed ✅

Problem: /api/search snippets leaked literal ,StopSel= and a default </b> around the matched term.

Fix: Postgres ts_headline mis-parses unquoted empty options — StartSel=,StopSel=,… made StartSel the literal ,StopSel= and left StopSel at its </b> default. Quoting the empties (StartSel="",StopSel="") yields the intended marker-free plain-text excerpt. Verified against Postgres directly and on live /api/search.


Files touched

AreaFiles
Tokens / componentsstatic/css/theme.css, static/css/components.css, static/css/admin.css
Page CSS / layoutassets/css/editorial.css, assets/css/DESIGN.md
Templateslayouts/index.html, layouts/_default/{single,list,taxonomy,term}.html, layouts/partials/{ed-masthead,ed-footer,ed-head,ed-row}.html, layouts/products/list.html (deleted editorial-card.html)
Admin serverserver.py, scripts/init_db.py
JSassets/js/sidebar-hydrate.js
Harness.claude/skills/run-ethica-no/{SKILL.md,smoke.sh}
Docs / reviewreview/2026-06-12-design-system.md, review/2026-06-12-maintainability-pass.md

Follow-ups / notes

  • Cache-bust the shared /css/* links (section 4) — the one open item; otherwise returning browsers can need a hard refresh after a token change.
  • UPLOAD_DIR is defined and mkdir’d but never read/written — a stub; wire or drop.
  • server.py is a ~1200-line monolith; a module split (auth/render/admin) is deferred (import-coupling risk; the single-file stdlib server is well-commented).
  • dev is ahead of main and not deployed — merge to main when ready to ship.