feat(decnet_web/theme-lab): light theme tokens + dev toggle
Adds html[data-theme="light"] block to index.css overriding the core six tokens (bg, matrix, violet, panel, border, alert), the matrix/violet/alert tints, and the foreground opacity ramp to a cream-on-ink palette anchored on #dbdad6. Glows are no-op'd — light mode trades neon haloes for hard 1px borders. Lab page gets a Dark/Light toggle that flips html.dataset.theme and persists to sessionStorage (decnet_theme_lab) — intentionally tab-scoped, not user-facing. App.tsx hydrates the same key on boot so a tab reload keeps the dev's chosen theme. The user-facing localStorage toggle ships later via Config.
This commit is contained in:
57
decnet_web/src/components/ThemeLab/ThemeToggle.tsx
Normal file
57
decnet_web/src/components/ThemeLab/ThemeToggle.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
/* Dev-scoped Dark/Light theme toggle for the theme lab.
|
||||
*
|
||||
* Flips `document.documentElement.dataset.theme` and persists to
|
||||
* **sessionStorage** intentionally — the lab is a tab-scoped
|
||||
* exploration tool. Global persistence (across reloads, all users)
|
||||
* is the user-facing Config toggle that ships in Task 6. */
|
||||
|
||||
export const THEME_SESSION_KEY = 'decnet_theme_lab';
|
||||
|
||||
export type Theme = 'dark' | 'light';
|
||||
|
||||
export function readLabTheme(): Theme {
|
||||
try {
|
||||
const v = sessionStorage.getItem(THEME_SESSION_KEY);
|
||||
return v === 'light' ? 'light' : 'dark';
|
||||
} catch {
|
||||
return 'dark';
|
||||
}
|
||||
}
|
||||
|
||||
export function applyTheme(theme: Theme): void {
|
||||
document.documentElement.dataset.theme = theme;
|
||||
}
|
||||
|
||||
const ThemeToggle: React.FC = () => {
|
||||
const [theme, setTheme] = useState<Theme>(() => readLabTheme());
|
||||
|
||||
useEffect(() => {
|
||||
applyTheme(theme);
|
||||
try { sessionStorage.setItem(THEME_SESSION_KEY, theme); } catch { /* ignore */ }
|
||||
}, [theme]);
|
||||
|
||||
return (
|
||||
<div className="lab-theme-toggle" role="group" aria-label="Theme">
|
||||
<button
|
||||
type="button"
|
||||
className={`btn small ${theme === 'dark' ? '' : 'ghost'}`}
|
||||
onClick={() => setTheme('dark')}
|
||||
aria-pressed={theme === 'dark'}
|
||||
>
|
||||
DARK
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`btn small ${theme === 'light' ? '' : 'ghost'}`}
|
||||
onClick={() => setTheme('light')}
|
||||
aria-pressed={theme === 'light'}
|
||||
>
|
||||
LIGHT
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ThemeToggle;
|
||||
Reference in New Issue
Block a user