feat(design): the scheme set, the colour contract and the token utilities (E28 S3a)

Four schemes (Midnight; Forest cyan, Slate fuchsia, Matrix lime), YouTube
and Starship retired with no data migration: normalizeTheme maps a stored
retired scheme to Midnight. A vitest contract measures every scheme x mode
(text 4.5:1, control edge 3:1, status text, the accent's 30-degree hue gap);
it caught light-mode danger at 4.43:1, now red-700. The state layers, the
strong edge, the touch target and the fades are index.css tokens and
utilities the lab now reads. Backdrops recoloured to the new accents, with
a grey overlay grain that actually breaks the banding (longest flat run
15-26px -> 4-8px). The glass tuner is retired.
This commit is contained in:
2026-09-24 04:33:13 +02:00
parent 9c4a999e54
commit ad136c3fe8
26 changed files with 326 additions and 851 deletions
+3 -3
View File
@@ -12,8 +12,8 @@ import { useQueryClient } from "@tanstack/react-query";
import { api, type Me } from "../lib/api";
import {
applyTheme,
DEFAULT_THEME,
loadLocalTheme,
normalizeTheme,
saveLocalTheme,
type ThemePrefs,
} from "../lib/theme";
@@ -28,7 +28,7 @@ const PERF_KEY = LS.perfMode;
// The four Settings-page preferences. They apply locally for instant preview and auto-save (debounced)
// to the server — there is no draft or Save button. This provider owns them (lifted out of the App god
// component) plus the last-saved "baseline" (the server values it was seeded with, then each successful
// save) used to suppress saving what the server already holds. Consumers (SettingsPanel, GlassTuner) read the PrefsController via usePrefs() (prefsContext.ts).
// save) used to suppress saving what the server already holds. Consumers (SettingsPanel) read the PrefsController via usePrefs() (prefsContext.ts).
type EditablePrefs = {
theme: ThemePrefs;
performanceMode: boolean;
@@ -44,7 +44,7 @@ export function PrefsProvider({ children }: { children: ReactNode }) {
const [seed] = useState<EditablePrefs>(() => {
const prefs = qc.getQueryData<Me | null>(qk.me())?.preferences;
return {
theme: prefs?.theme ? { ...DEFAULT_THEME, ...prefs.theme } : loadLocalTheme(),
theme: prefs?.theme ? normalizeTheme(prefs.theme) : loadLocalTheme(),
performanceMode:
typeof prefs?.performanceMode === "boolean"
? prefs.performanceMode