feat(frontend): put a light/dark/system control in the account menu
Independent Staging Quality Gate / validate (push) Successful in 10m4s
Independent Staging Quality Gate / publish (push) Successful in 24m59s

Appearance is a radio group inside the avatar menu rather than a loose
button, so arrow keys reach it and the current choice is announced.
Picking one keeps the menu open, so the change is visible where it was
made.

"跟随系统" removes data-theme instead of writing a third value — the
media query has nothing to match otherwise. A synchronous script at the
top of <head> re-applies a pinned choice before the first paint; going
through next/script with any strategy would defer it and bring the flash
straight back, so the test asserts a plain script tag. Blocked storage
degrades to following the OS instead of throwing.

The stored value is browser state, so it is read through
useSyncExternalStore rather than synced into React state in an effect,
which also trips the cascading-render lint rule. localStorage only fires
`storage` in other tabs, so a same-tab write notifies its own listeners
and every open tab stays in step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0155nFCgCHtoA7jhSDGmZmMu
This commit is contained in:
Jesse_Chen
2026-08-29 12:12:06 +00:00
co-authored by Claude Opus 5
parent c2d705ef6e
commit 187ef6ae24
7 changed files with 220 additions and 2 deletions
+11 -2
View File
@@ -115,8 +115,17 @@ must increase in lightness in floor → canvas → muted → strong order.
properly means switching antd to `theme.darkAlgorithm` as well, and that is a
separate change.
**Not built yet:** a visible light/dark/system control. The `data-theme` hook
exists so a toggle can be added without touching any of the styling above.
**The control** is a radio group inside the account menu (avatar → 外观), so it
keeps menu semantics: arrow keys reach it and the current choice is announced.
Three options, matching the three states — 浅色, 深色, 跟随系统. Picking one does
not close the menu, so the change is visible where it was made.
"跟随系统" **removes** `data-theme` rather than writing a third value; the media
query has nothing to match otherwise. The choice is stored under `jyotisha-theme`
and re-applied by a synchronous script at the top of `<head>` — it must not be
deferred, or every load flashes the other theme before hydration. Blocked storage
degrades to following the OS. The preference is per-device, not per-account, and
is read through an external store so a change in one tab reaches the others.
## 3. Typography