mirror of
https://github.com/karatektus/pl_mail.git
synced 2026-10-25 10:04:18 +00:00
A theme is chosen by rewriting `data-theme` on <html>, which unsets the old
theme's block in one step. Anything that block declared and the new one does
not therefore falls back — to :root, to .dark, or to whatever an inline style
left there — and the user is looking at a colour nobody picked for the theme
they are in.
Three of the six themes were four-line diffs against a light-tuned base, so
most of what they painted was inherited rather than chosen, and two of the
inherited values were visibly wrong. Nord and Dusk took --rgb-info from :root,
which is the light theme's link blue, and rendered it on a near-black surface
beside a --rgb-danger, --rgb-warning and --rgb-success that had all been
lightened for that surface — info was simply forgotten when the others were
tuned. Both also took --rgb-ink-soft from .dark, a neutral grey that in Nord's
case is DARKER than its own --rgb-ink-muted, so the ink ramp ran backwards.
Solarized had a #f3f4f6 inset panel on a #fdf6e3 page.
So every palette block now declares the whole inventory: 38 variables plus
color-scheme, spelled out in :root, .dark and each of the five [data-theme]
blocks. Reading one block tells you what the theme looks like without tracing
three levels of cascade, and there is nothing left for a switch to leave over.
`light` and `system` still have no block of their own on purpose — :root is
the light palette and .dark is the dark one, and a copy under a selector would
be a second place to forget.
── The accent was a genuine remnant, and a persistent one ─────────────────
Theme::defaults() seeds settings when a theme is picked, and it named an
accent for Paper and Dark only. The accent is written inline on <html> from
the user's setting, so an inline style is exactly what survives a switch:
picking Paper and then Nord left Nord wearing Paper's clay. Not just in the
preview — save() reads the value back out of the form, so it was written to
the account and came back after the reload. The theme picker's own swatch
showed a frost blue the app never painted.
defaults() is now one expression for every case, reading swatch()[2], so there
is no case that declines to overwrite. pickTheme() also clears any seeded key
the incoming theme does not carry before applying the ones it does; that
clears nothing today, and it is what keeps this fixed if a theme ever grows a
key the others lack.
── system on a dark desktop ───────────────────────────────────────────────
The picker toggled `.dark` from data-dark, which is Theme::isDark(), and
System is not dark by that measure — it is whatever the OS says, which is the
question the no-flash script in app.html.twig asks on every load. So picking
System on a dark desktop dropped the class and painted light until the next
navigation put it back. It now asks the same question the script does.
── One mail-sheet, in the theme's own light ───────────────────────────────
app.css had two @utility mail-sheet blocks: the warm-paper redesign from
aa0edab and the white one from the first reading-view pass. @utility blocks of
the same name do not merge — the later one wins outright — and the white one
was further down the file, so the redesign had been dead code that read as
live for a fortnight. --rgb-sheet and --rgb-sheet-ink were added by that same
commit and wired to nothing.
Resolved to the redesign, keeping the one thing the white block had that it
did not: the isolate/stacking-context fix, without which Chrome re-samples the
app background through the sheet at some viewport sizes and flashes the pane
dark.
The sheet's colours now come from the theme rather than from a fixed palette.
Light is a constraint it has to meet for the mail's sake — an email's HTML was
authored against a light background, and rendering it on a near-black surface
misrepresents what was sent — but WHICH light is the theme's business: Nord
reads its mail on snow storm, Solarized on base3, Paper on the cream it is
already made of. Everything else inside the sheet derives from those two ends
of the ramp, so the hairlines, insets and fields need no channels of their own.
── Keeping it that way ────────────────────────────────────────────────────
ThemeVariableCompletenessTest parses app.css and fails when a block drifts
from the inventory, when a knob appears outside :root, when a variable in the
inventory is read by nothing, when mail-sheet is defined twice, or when a
theme seeds a different set of settings than its siblings.
theme-switch-parity.spec.ts is the acceptance criterion: it switches through
every theme IN SEQUENCE without reloading — the order matters, because it
takes a previous theme to leave a remnant — and asserts the painted palette,
the class list and color-scheme match what a cold load of that theme produces.
Under both layouts, and once more for system on a dark desktop. Both new tests
were confirmed to fail against the code they replace.
README screenshots re-recorded: the reading pane is the warm sheet the
redesign intended rather than white, and Paper's shadows and raised surfaces
are its own warm ink rather than neutral black.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
173 KiB
1440x900px
173 KiB
1440x900px