feat(theme+comments): runtime colour theme and COMMENTS_ENABLED switch
Colour theme is configurable at runtime from two seed colours (brand + accent); neutrals stay fixed for contrast safety. Tailwind v4 var()-based tokens let a :root:root override recolour everything with no rebuild; the 50->950 ramps are derived via a color-mix ladder. Config lives in the DB config table (admin UI: Config > Farbschema, presets + custom pickers + live preview), served on the public /event endpoint with env defaults (THEME_PRESET/PRIMARY/ACCENT), propagated live via event-updated SSE, and cached in localStorage for a no-flash boot. The keepsake export mirrors the same ladder in Rust so offline archives match the event theme. COMMENTS_ENABLED (env, default true) is a boot-time kill-switch: the backend rejects new comments with 403 and the frontend hides the comment button (feed card) and panel/composer (lightbox). Existing comments stay in the DB, hidden, and return when re-enabled. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
60
frontend/src/lib/event-config-store.ts
Normal file
60
frontend/src/lib/event-config-store.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Public event presentation config: the colour theme and the comments feature flag,
|
||||
* served unauthenticated from `GET /api/v1/event`. Loaded once on boot (and refreshed
|
||||
* on the `event-updated` SSE) so the theme applies before the user has even joined and
|
||||
* the comment UI reflects the instance setting.
|
||||
*
|
||||
* The resolved theme CSS is cached in localStorage so the boot script in app.html can
|
||||
* paint the right colours before the JS bundle loads (no flash of the default palette).
|
||||
*/
|
||||
import { writable } from 'svelte/store';
|
||||
import { buildPaletteCss, applyPaletteCss, type ThemeConfig } from './theme/palette';
|
||||
|
||||
export const PALETTE_CACHE_KEY = 'eventsnap_palette_css';
|
||||
|
||||
export type EventConfig = {
|
||||
name: string;
|
||||
slug: string;
|
||||
commentsEnabled: boolean;
|
||||
theme: ThemeConfig;
|
||||
};
|
||||
|
||||
export const eventConfig = writable<EventConfig | null>(null);
|
||||
/** Convenience flag for the comment UI. Optimistic `true` until /event resolves. */
|
||||
export const commentsEnabled = writable<boolean>(true);
|
||||
|
||||
/** Fetch /event, apply the theme, and cache the resolved CSS for the next boot. */
|
||||
export async function loadEventConfig(): Promise<void> {
|
||||
try {
|
||||
const res = await fetch('/api/v1/event');
|
||||
if (!res.ok) return;
|
||||
const b = await res.json();
|
||||
const theme: ThemeConfig = {
|
||||
preset: b.theme_preset ?? 'champagne-gold',
|
||||
primary: b.theme_primary ?? '#8a6a2b',
|
||||
accent: b.theme_accent ?? '#8a6a2b'
|
||||
};
|
||||
eventConfig.set({
|
||||
name: b.name,
|
||||
slug: b.slug,
|
||||
commentsEnabled: b.comments_enabled ?? true,
|
||||
theme
|
||||
});
|
||||
commentsEnabled.set(b.comments_enabled ?? true);
|
||||
|
||||
const css = buildPaletteCss(theme);
|
||||
applyPaletteCss(css);
|
||||
try {
|
||||
localStorage.setItem(PALETTE_CACHE_KEY, css);
|
||||
} catch {
|
||||
/* private mode / quota — the theme still applied for this session */
|
||||
}
|
||||
} catch {
|
||||
/* offline / server down — keep whatever the boot script already painted */
|
||||
}
|
||||
}
|
||||
|
||||
/** Apply a theme immediately without persisting — used by the admin live preview. */
|
||||
export function previewTheme(theme: ThemeConfig): void {
|
||||
applyPaletteCss(buildPaletteCss(theme));
|
||||
}
|
||||
Reference in New Issue
Block a user