Files
Levyraati26_go/docs/theme.md
T
Esa Kataja f33f4fa4d6 Apply the theme: tokens, Oswald, song cards, toasts
The theme handoff encoded as CSS custom properties rather than a Tailwind
config, since there is no Tailwind here. Palette, spacing, radii, shadows and
motion follow it as written; docs/theme.md lists what differs and why.

- Oswald vendored as a 21KB variable woff2, latin subset, no CDN. Its phantom
  weight 900 resolved to 700 — loading a weight you don't have is what made
  the brand render differently per platform
- color-scheme: dark makes the native audio element fit the palette, which
  was the handoff's complaint about it
- Songs are text cards rather than artwork tiles, because there is no
  artwork. The unreviewed state keeps its red-brown border and gains a badge,
  so it is never carried by colour alone
- Nav is the three-column grid; the mobile menu is <details>, no JS
- Flash messages became bottom-right toasts

Favicon carried over from the Nuxt project.
2026-07-31 22:16:29 +03:00

3.5 KiB
Raw Blame History

Theme

Dark-only. Rock/metal club poster, not SaaS dashboard: near-black surfaces, warm bronze/amber accents, condensed uppercase display type, one-tone-lighter surfaces instead of borders everywhere. Restrained motion — 150 ms, one easing curve, no bounce. There is no light theme and none is wanted.

The tokens themselves live in static/style.css as CSS custom properties, and that file is the source of truth. This page records the decisions behind them and the places the implementation deliberately differs from the theme handoff it came from.

Rules

  • Nothing outside :root invents a value. No colour, spacing step, radius or duration appears in a rule unless it is declared as a token first. Six spacing steps (432 px), one radius (4 px, plus 6 px for toasts and a pill), one duration, one curve.
  • Headings step downward in brightness with level — h1 lightest gold, h3 the primary bronze.
  • Status colours are desaturated on purpose. A pure red error would break the palette.
  • color-scheme: dark is set on :root, which is what keeps the native <audio> element, checkboxes, range inputs and scrollbars from rendering as white slabs.
  • One focus treatment, everywhere: a soft gold ring via box-shadow on :focus-visible only. Not optional — keyboard navigation is the only way through some admin tables.
  • prefers-reduced-motion drops the card lift and the panel slide, and keeps the fades.

Type

Body is a system stack; no webfont for body text. Display is Oswald, vendored as a variable .woff2 (latin subset, 21 KB) in static/fonts/ — no CDN, matching the no-npm rule for HTMX.

The handoff asked for weight 900 in five places while loading only 400/600/700, so browsers were synthesising a fake bold that differed per platform. Resolved as 700 being the top weight. The variable font covers 400700 and nothing asks for more.

The unreviewed state

A song the viewer has not reviewed gets a red-brown border — it is the single most important state in the app, since it is how you see what still needs a review. It is never carried by colour alone: the card also shows an arvostelematta badge.

Deviations from the handoff

Handoff said Here Why
Tailwind theme config, utility classes CSS custom properties, semantic classes Decisions 4 and 8 — no Tailwind, no bundler, no npm
Song cards are 16:9 tiles with cover artwork and a gradient scrim Text cards, same borders, badges, hover glow and score badge Songs have no artwork. There is no upload for one and nothing to derive it from
Fixed bottom audio player bar Player inline on the song page Nothing plays across navigation, so a persistent bar would be an empty bar on every other page
A styleguide page rendering every variant Not built The real pages cover every component; a second copy of them would drift
Custom audio player skin Native <audio controls> Decision 14. It is keyboard-operable, screen-reader labelled and media-key aware for free, and replacing it later is one template partial — see later.md
Nav dropdown for the user block, hamburger with animated bars User block is a plain row; mobile menu is <details> No JS for either. The app has no dropdown-worthy menu yet: logout is one button

Everything else — the palette, spacing, radii, shadows, motion, the 1450 px content width, the 420 px auth column, badges, review cards, the admin section cards, bottom-right toasts — follows the handoff as written.