feat: M4 (part 2) — React reader UI with theming
- Vite + React + TS + Tailwind SPA served by FastAPI (multi-stage Docker build) - Four color schemes (Midnight default, Forest, Slate, YouTube) x dark/light, adjustable text size; persisted to user preferences and localStorage - Header search, grid/list toggle, theme menu; sidebar filters (show state, sort, include Shorts/live, language + topic tag chips with any/all matching) - Infinite-scroll feed of video cards; click opens youtube.com in a new tab; per-video watched / saved / hidden actions with optimistic updates - SPA fallback routing; login screen for unauthenticated users
This commit is contained in:
parent
9a377b7e7e
commit
e56502789f
20 changed files with 1194 additions and 4 deletions
42
frontend/src/lib/theme.ts
Normal file
42
frontend/src/lib/theme.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
export type Mode = "dark" | "light";
|
||||
export type Scheme = "midnight" | "forest" | "slate" | "youtube";
|
||||
|
||||
export const SCHEMES: { id: Scheme; name: string; swatch: string }[] = [
|
||||
{ id: "midnight", name: "Midnight", swatch: "#6d8cff" },
|
||||
{ id: "forest", name: "Forest", swatch: "#2dd4bf" },
|
||||
{ id: "slate", name: "Slate", swatch: "#e8913c" },
|
||||
{ id: "youtube", name: "YouTube", swatch: "#ff3b46" },
|
||||
];
|
||||
|
||||
export interface ThemePrefs {
|
||||
mode: Mode;
|
||||
scheme: Scheme;
|
||||
fontScale: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_THEME: ThemePrefs = {
|
||||
mode: "dark",
|
||||
scheme: "midnight",
|
||||
fontScale: 1.06,
|
||||
};
|
||||
|
||||
export function applyTheme(t: ThemePrefs): void {
|
||||
const el = document.documentElement;
|
||||
el.dataset.theme = t.mode;
|
||||
el.dataset.scheme = t.scheme;
|
||||
el.style.setProperty("--font-scale", String(t.fontScale));
|
||||
}
|
||||
|
||||
const KEY = "subfeed.theme";
|
||||
|
||||
export function loadLocalTheme(): ThemePrefs {
|
||||
try {
|
||||
return { ...DEFAULT_THEME, ...JSON.parse(localStorage.getItem(KEY) || "{}") };
|
||||
} catch {
|
||||
return DEFAULT_THEME;
|
||||
}
|
||||
}
|
||||
|
||||
export function saveLocalTheme(t: ThemePrefs): void {
|
||||
localStorage.setItem(KEY, JSON.stringify(t));
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue