Agora's colors and fonts are now hardcoded in the bundle. There is no
runtime CSS-variable injection, no remote-loaded theme, no font-family
override from event data, no background image, no recolored favicon.
Switching themes only toggles the .dark class on <html>.
Removed:
- src/themes.ts: ThemeConfig, ThemesConfig, CoreThemeColors, ThemeFont,
ThemeBackground, themePresets (22 presets), buildThemeCssFromCore,
deriveTokensFromCore, the 'custom' Theme variant. Now exports only
resolveTheme(theme) -> 'light' | 'dark'.
- src/lib/fontLoader.ts: deleted. Mounted arbitrary @font-face rules with
event-sourced URLs and font-family overrides — the primary CSS-injection
vector. sanitizeCssString moved to src/lib/cssSanitize.ts (still used by
the Letter feature).
- AppProvider hooks useApplyFonts / useApplyBackground / useApplyFavicon.
- useTheme.applyCustomTheme — the entrypoint that wrote external palettes
to global theme state.
- ColorMomentEyeButton + its callers in NoteCard and PostDetailPage. Color
Moments (kind 3367) still render as palette art, but the 'Set as theme'
button is gone.
- LetterAttachment in LetterDetailSheet — the gift-box UI that applied an
attached color moment as the recipient's theme.
- paletteToTheme() from colorMomentUtils — only getColors() remains.
- customTheme / themes fields from AppConfig, AppConfigSchema,
EncryptedSettings, EncryptedSettingsSchema. Existing customTheme values
in localStorage and encrypted NIP-78 settings are now ignored.
- 14 unused @fontsource packages. Only the 10 letter fonts and the 2 base
UI fonts (Inter Variable, Bebas Neue) remain. noto-sans-nushu is kept
for the encrypted-letter obfuscation indicator.
Added:
- Static :root {} and .dark {} blocks in src/index.css with the 19 shadcn
tokens hardcoded. These were previously injected at runtime by
AppProvider; without them the app would lose all colors when the runtime
injector was removed.
- src/lib/cssSanitize.ts holding the sanitizeCssString helper for the
Letter feature's font-family interpolation.
Simplified:
- public/theme.js: no longer reads customTheme or themes from localStorage,
just resolves system/light/dark with hardcoded built-ins. Must stay in
sync with src/index.css colors.
- src/lib/fonts.ts: trimmed to only the 10 letter fonts. findBundledFont
and resolveCssFamily removed (they were only used by fontLoader).
The preloader script was reading customTheme.background/primary directly,
but the app now stores colors under customTheme.colors.{background,primary}.
Also fixes the light theme builtin colors, adds support for configured
theme overrides (cfg.themes), and removes obsolete legacy preset handling
(legacy themes are migrated to 'custom' by AppProvider on load).
Replace the 19-token ThemeTokens interface with a 4-field CoreThemeColors
for config storage, Nostr events, and encrypted settings. All other
Tailwind CSS tokens (card, muted, border, etc.) are now derived
automatically via deriveTokensFromCore().
- Add CoreThemeColors interface: { background, text, primary, secondary }
- Keep ThemeTokens as internal type for CSS variable injection
- Update builtinThemes and themePresets to store CoreThemeColors
- Add backward-compatible parsing for legacy 19-token format in Nostr
events, encrypted settings, and localStorage
- Simplify ThemeBuilderPage: remove Surface/UI color sections, just 4
color pickers with auto-derivation always on
- Update all consumers: ThemeSelector, ScopedTheme, ThemeUpdateCard,
MobileDrawer, LeftSidebar, ProfilePage, InitialSyncGate, etc.
Restructure the theme architecture so 'light', 'dark', and 'system' are
builtin themes with build-time colors (customizable by self-hosters),
while 'black', 'pink', and future themes become presets that apply to a
single 'custom' theme slot.
Key changes:
- Theme type is now 'light' | 'dark' | 'system' | 'custom'
- builtinThemes (light/dark) and themePresets (black/pink/etc.) are
separate maps in themes.ts
- AppConfig gains optional customTheme (ThemeTokens) field
- useTheme exposes applyCustomTheme(tokens) for applying presets
- Custom theme tokens sync across devices via encrypted settings
- Backward compat: legacy 'black'/'pink' values in localStorage and
encrypted settings auto-migrate to custom + preset tokens
- All UI surfaces updated: ThemeSelector, LeftSidebar dropdown,
MobileDrawer cycle, and onboarding ThemeStep
Defer body inline style removal until CSS variables are injected by
useApplyTheme, preventing a one-frame flash of unstyled background.
Replace preloader logo <img> with CSS mask technique matching the React
DittoLogo component, so the logo uses the theme's primary color instead
of filter:invert() which turned purple into green.
Read the user's theme from localStorage before first paint so the
preloader background and spinner colors match dark/light/black/pink.
Uses a small blocking script (public/theme.js) that runs before
the preloader is visible.