[Mappe·Shared] Token foundation close-out (radius/shadow/avatar-palette tokens, tag-sand naming, theme key) #854

Closed
opened 2026-06-16 10:53:18 +02:00 by marcel · 0 comments
Owner

Shared component · Story 1 close-out. Part of #853.

Context

frontend/src/routes/layout.css already defines ~95% of DESIGN_RULES §1. This issue closes the remaining gaps the audit found so the rest of the work has a complete token substrate.

Scope (frontend/src/routes/layout.css, src/app.html)

  • Add --radius-sm:2px; --radius-md:4px; --radius-full:9999px as first-class :root tokens (today only in Tailwind utilities / the handoff colors_and_type.css). Radius is theme-invariant — no dark override needed.
  • Add --shadow-sm and --shadow-md as first-class :root tokens with explicit dark-mode values in the @media (prefers-color-scheme: dark) / .dark block. A navy-on-sand shadow is near-invisible on dark surfaces; dark cards need their own shadow value so the 3px-mint-top-border card signature retains its lift.
  • Add the §5 10-color person/avatar palette as a single exported TS constant in $lib/shared/ (e.g. $lib/shared/avatarPalette.ts). This is the single source of truth — both <Avatar> (person domain) and the tag-dot colors must import from here. Do not create a parallel CSS token set for the same 10 values; a TS constant avoids drift and is boundary-safe for both $lib/person/ and $lib/tag/ consumers per eslint.config.js.
  • Tag-sand naming — decided: keep --c-tag-sand (the name already shipped in layout.css; value stays #9a8040). Correct the handoff doc (colors_and_type.css) to use --c-tag-sand — do not rename the live token.
  • Theme storage key — decided: keep theme (current localStorage key used by the boot script in src/app.html and ThemeToggle). Do not migrate to fa-theme; update the handoff docs to reflect theme. This avoids a client-state migration across the rolling deploy and keeps the existing t === 'dark' || t === 'light' allow-list guard intact (see Security note below).

Out of Scope

  • Do NOT migrate personFormat.ts's existing 5-color hashing to the new 10-color palette here. Rewiring existing avatar consumers belongs to the Avatar issue that this unblocks.
  • No backend changes, no Flyway migration, no new env var, no CI workflow change.
  • No ADR — these are additive tokens and issue-body resolutions, not irreversible architectural decisions.

Security note (implementer)

The boot script in src/app.html runs before Svelte hydration and writes directly to document.documentElement. Retain the t === 'dark' || t === 'light' allow-list guard when reading from localStorage — never write a raw, unvalidated localStorage value into setAttribute. This is the only untrusted-input-to-DOM path in this change.

Acceptance

  • --radius-sm, --radius-md, --radius-full present in :root
  • --shadow-sm, --shadow-md present in :root and in the dark-mode block with distinct values
  • 10-color avatar palette exported as a single TS constant from $lib/shared/avatarPalette.ts; all 10 colors pass ≥4.5:1 contrast against the initials foreground color in both light and dark themes
  • --c-tag-sand is the sole name across layout.css and the handoff doc; no --c-tag-sand-tag remains
  • Theme key is theme; handoff docs updated; boot script in src/app.html sets data-theme on <html> via a render-blocking <script> in <head> before first paint (no flash of wrong theme on hard reload with theme=dark in localStorage)
  • Visual diff at 320 / 768 / 1440 px in light and dark on Geschichten, Zeitstrahl, and Aktivitäten pages shows no unintended change
  • Purely additive — npm run lint and npm run check pass; no new raw hex or Tailwind color classes introduced in touched files

Depends on: none. Blocks: Avatar (uses the palette constant). Refs: DESIGN_RULES §1, DESIGN_RULES §5, EPIC Story 1.

**Shared component · Story 1 close-out.** Part of #853. ## Context `frontend/src/routes/layout.css` already defines ~95% of `DESIGN_RULES §1`. This issue closes the remaining gaps the audit found so the rest of the work has a complete token substrate. ## Scope (`frontend/src/routes/layout.css`, `src/app.html`) - Add `--radius-sm:2px; --radius-md:4px; --radius-full:9999px` as first-class `:root` tokens (today only in Tailwind utilities / the handoff `colors_and_type.css`). Radius is theme-invariant — no dark override needed. - Add `--shadow-sm` and `--shadow-md` as first-class `:root` tokens **with explicit dark-mode values** in the `@media (prefers-color-scheme: dark)` / `.dark` block. A navy-on-sand shadow is near-invisible on dark surfaces; dark cards need their own shadow value so the 3px-mint-top-border card signature retains its lift. - Add the §5 **10-color person/avatar palette** as a **single exported TS constant** in **`$lib/shared/`** (e.g. `$lib/shared/avatarPalette.ts`). This is the single source of truth — both `<Avatar>` (person domain) and the tag-dot colors must import from here. Do not create a parallel CSS token set for the same 10 values; a TS constant avoids drift and is boundary-safe for both `$lib/person/` and `$lib/tag/` consumers per `eslint.config.js`. - **Tag-sand naming — decided:** keep `--c-tag-sand` (the name already shipped in `layout.css`; value stays `#9a8040`). Correct the handoff doc (`colors_and_type.css`) to use `--c-tag-sand` — do not rename the live token. - **Theme storage key — decided:** keep `theme` (current `localStorage` key used by the boot script in `src/app.html` and `ThemeToggle`). Do not migrate to `fa-theme`; update the handoff docs to reflect `theme`. This avoids a client-state migration across the rolling deploy and keeps the existing `t === 'dark' || t === 'light'` allow-list guard intact (see Security note below). ## Out of Scope - **Do NOT migrate `personFormat.ts`'s existing 5-color hashing to the new 10-color palette here.** Rewiring existing avatar consumers belongs to the Avatar issue that this unblocks. - No backend changes, no Flyway migration, no new env var, no CI workflow change. - No ADR — these are additive tokens and issue-body resolutions, not irreversible architectural decisions. ## Security note (implementer) The boot script in `src/app.html` runs before Svelte hydration and writes directly to `document.documentElement`. **Retain the `t === 'dark' || t === 'light'` allow-list guard** when reading from `localStorage` — never write a raw, unvalidated localStorage value into `setAttribute`. This is the only untrusted-input-to-DOM path in this change. ## Acceptance - [ ] `--radius-sm`, `--radius-md`, `--radius-full` present in `:root` - [ ] `--shadow-sm`, `--shadow-md` present in `:root` **and** in the dark-mode block with distinct values - [ ] 10-color avatar palette exported as a single TS constant from `$lib/shared/avatarPalette.ts`; all 10 colors pass **≥4.5:1 contrast** against the initials foreground color in **both light and dark themes** - [ ] `--c-tag-sand` is the sole name across `layout.css` and the handoff doc; no `--c-tag-sand-tag` remains - [ ] Theme key is `theme`; handoff docs updated; boot script in `src/app.html` sets `data-theme` on `<html>` via a render-blocking `<script>` in `<head>` before first paint (no flash of wrong theme on hard reload with `theme=dark` in localStorage) - [ ] Visual diff at **320 / 768 / 1440 px** in **light and dark** on Geschichten, Zeitstrahl, and Aktivitäten pages shows no unintended change - [ ] Purely additive — `npm run lint` and `npm run check` pass; no new raw hex or Tailwind color classes introduced in touched files **Depends on:** none. **Blocks:** Avatar (uses the palette constant). **Refs:** `DESIGN_RULES §1`, `DESIGN_RULES §5`, EPIC Story 1.
marcel added this to the Mappe Visual Redesign milestone 2026-06-16 10:53:18 +02:00
marcel added the P1-highfeatureredesign-mappeui labels 2026-06-16 11:06:16 +02:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#854