chore: cleanup theme stuff
This commit is contained in:
@@ -1,12 +1,3 @@
|
||||
/**
|
||||
* Theme context for SolidJS applications.
|
||||
* Provides reactive theme management with localStorage persistence and caching.
|
||||
*
|
||||
* Works in conjunction with the preload script to provide zero-FOUC theming:
|
||||
* 1. Preload script applies cached CSS immediately from localStorage
|
||||
* 2. ThemeProvider takes over, resolves theme, and updates cache
|
||||
*/
|
||||
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
@@ -24,19 +15,12 @@ import { DEFAULT_THEMES } from "./default-themes"
|
||||
export type ColorScheme = "light" | "dark" | "system"
|
||||
|
||||
interface ThemeContextValue {
|
||||
/** Currently active theme ID */
|
||||
themeId: Accessor<string>
|
||||
/** Current color scheme preference */
|
||||
colorScheme: Accessor<ColorScheme>
|
||||
/** Resolved current mode (light or dark) */
|
||||
mode: Accessor<"light" | "dark">
|
||||
/** All available themes */
|
||||
themes: Accessor<Record<string, DesktopTheme>>
|
||||
/** Set the active theme by ID */
|
||||
setTheme: (id: string) => void
|
||||
/** Set color scheme preference */
|
||||
setColorScheme: (scheme: ColorScheme) => void
|
||||
/** Register a custom theme */
|
||||
registerTheme: (theme: DesktopTheme) => void
|
||||
}
|
||||
|
||||
@@ -52,59 +36,6 @@ function getThemeCacheKey(themeId: string, mode: "light" | "dark"): string {
|
||||
return `${STORAGE_KEYS.THEME_CSS_PREFIX}-${themeId}-${mode}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Static tokens that don't change between themes
|
||||
*/
|
||||
const STATIC_TOKENS = `
|
||||
--font-family-sans: "Inter", "Inter Fallback";
|
||||
--font-family-sans--font-feature-settings: "ss03" 1;
|
||||
--font-family-mono: "IBM Plex Mono", "IBM Plex Mono Fallback";
|
||||
--font-family-mono--font-feature-settings: "ss01" 1;
|
||||
--font-size-small: 13px;
|
||||
--font-size-base: 14px;
|
||||
--font-size-large: 16px;
|
||||
--font-size-x-large: 20px;
|
||||
--font-weight-regular: 400;
|
||||
--font-weight-medium: 500;
|
||||
--line-height-large: 150%;
|
||||
--line-height-x-large: 180%;
|
||||
--line-height-2x-large: 200%;
|
||||
--letter-spacing-normal: 0;
|
||||
--letter-spacing-tight: -0.16;
|
||||
--letter-spacing-tightest: -0.32;
|
||||
--paragraph-spacing-base: 0;
|
||||
--spacing: 0.25rem;
|
||||
--breakpoint-sm: 40rem;
|
||||
--breakpoint-md: 48rem;
|
||||
--breakpoint-lg: 64rem;
|
||||
--breakpoint-xl: 80rem;
|
||||
--breakpoint-2xl: 96rem;
|
||||
--container-3xs: 16rem;
|
||||
--container-2xs: 18rem;
|
||||
--container-xs: 20rem;
|
||||
--container-sm: 24rem;
|
||||
--container-md: 28rem;
|
||||
--container-lg: 32rem;
|
||||
--container-xl: 36rem;
|
||||
--container-2xl: 42rem;
|
||||
--container-3xl: 48rem;
|
||||
--container-4xl: 56rem;
|
||||
--container-5xl: 64rem;
|
||||
--container-6xl: 72rem;
|
||||
--container-7xl: 80rem;
|
||||
--radius-xs: 0.125rem;
|
||||
--radius-sm: 0.25rem;
|
||||
--radius-md: 0.375rem;
|
||||
--radius-lg: 0.5rem;
|
||||
--radius-xl: 0.625rem;
|
||||
--shadow-xs: 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08);
|
||||
--shadow-md: 0 6px 8px -4px rgba(19, 16, 16, 0.12), 0 4px 3px -2px rgba(19, 16, 16, 0.12), 0 1px 2px -1px rgba(19, 16, 16, 0.12);
|
||||
--shadow-xs-border: 0 0 0 1px var(--border-base), 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08);
|
||||
--shadow-xs-border-base: 0 0 0 1px var(--border-weak-base), 0 1px 2px -1px rgba(19, 16, 16, 0.04), 0 1px 2px 0 rgba(19, 16, 16, 0.06), 0 1px 3px 0 rgba(19, 16, 16, 0.08);
|
||||
--shadow-xs-border-select: 0 0 0 3px var(--border-weak-selected), 0 0 0 1px var(--border-selected), 0 1px 2px -1px rgba(19, 16, 16, 0.25), 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12);
|
||||
--shadow-xs-border-focus: 0 0 0 1px var(--border-base), 0 1px 2px -1px rgba(19, 16, 16, 0.25), 0 1px 2px 0 rgba(19, 16, 16, 0.08), 0 1px 3px 0 rgba(19, 16, 16, 0.12), 0 0 0 2px var(--background-weak), 0 0 0 3px var(--border-selected);
|
||||
`
|
||||
|
||||
const THEME_STYLE_ID = "oc-theme"
|
||||
|
||||
function ensureThemeStyleElement(): HTMLStyleElement {
|
||||
@@ -118,41 +49,29 @@ function ensureThemeStyleElement(): HTMLStyleElement {
|
||||
return element
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a mode from system preference
|
||||
*/
|
||||
function getSystemMode(): "light" | "dark" {
|
||||
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply theme CSS to the document
|
||||
*/
|
||||
function applyThemeCss(theme: DesktopTheme, themeId: string, mode: "light" | "dark"): void {
|
||||
const isDark = mode === "dark"
|
||||
const variant = isDark ? theme.dark : theme.light
|
||||
const tokens = resolveThemeVariant(variant, isDark)
|
||||
const css = themeToCss(tokens)
|
||||
|
||||
// Cache to localStorage for preload script
|
||||
if (themeId !== "oc-1") {
|
||||
const cacheKey = getThemeCacheKey(themeId, mode)
|
||||
try {
|
||||
localStorage.setItem(cacheKey, css)
|
||||
} catch {
|
||||
// localStorage might be full or disabled
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
// Build full CSS
|
||||
const fullCss = `:root {
|
||||
${STATIC_TOKENS}
|
||||
color-scheme: ${mode};
|
||||
--text-mix-blend-mode: ${isDark ? "plus-lighter" : "multiply"};
|
||||
${css}
|
||||
}`
|
||||
|
||||
// Remove preload style if it exists
|
||||
const preloadStyle = document.getElementById("oc-theme-preload")
|
||||
if (preloadStyle) {
|
||||
preloadStyle.remove()
|
||||
@@ -161,14 +80,10 @@ function applyThemeCss(theme: DesktopTheme, themeId: string, mode: "light" | "da
|
||||
const themeStyleElement = ensureThemeStyleElement()
|
||||
themeStyleElement.textContent = fullCss
|
||||
|
||||
// Update data attributes
|
||||
document.documentElement.dataset.theme = themeId
|
||||
document.documentElement.dataset.colorScheme = mode
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache both light and dark variants of a theme
|
||||
*/
|
||||
function cacheThemeVariants(theme: DesktopTheme, themeId: string): void {
|
||||
if (themeId === "oc-1") return
|
||||
|
||||
@@ -180,9 +95,7 @@ function cacheThemeVariants(theme: DesktopTheme, themeId: string): void {
|
||||
const cacheKey = getThemeCacheKey(themeId, mode)
|
||||
try {
|
||||
localStorage.setItem(cacheKey, css)
|
||||
} catch {
|
||||
// localStorage might be full or disabled
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +105,6 @@ export function ThemeProvider(props: { children: JSX.Element; defaultTheme?: str
|
||||
const [colorScheme, setColorSchemeSignal] = createSignal<ColorScheme>("system")
|
||||
const [mode, setMode] = createSignal<"light" | "dark">(getSystemMode())
|
||||
|
||||
// Listen for system color scheme changes
|
||||
onMount(() => {
|
||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
|
||||
const handler = () => {
|
||||
@@ -203,29 +115,23 @@ export function ThemeProvider(props: { children: JSX.Element; defaultTheme?: str
|
||||
mediaQuery.addEventListener("change", handler)
|
||||
onCleanup(() => mediaQuery.removeEventListener("change", handler))
|
||||
|
||||
// Load saved preferences
|
||||
const savedTheme = localStorage.getItem(STORAGE_KEYS.THEME_ID)
|
||||
const savedScheme = localStorage.getItem(STORAGE_KEYS.COLOR_SCHEME) as ColorScheme | null
|
||||
|
||||
if (savedTheme && themes()[savedTheme]) {
|
||||
setThemeIdSignal(savedTheme)
|
||||
}
|
||||
|
||||
if (savedScheme) {
|
||||
setColorSchemeSignal(savedScheme)
|
||||
if (savedScheme !== "system") {
|
||||
setMode(savedScheme)
|
||||
}
|
||||
}
|
||||
|
||||
// Cache current theme variants for future preloads
|
||||
const currentTheme = themes()[themeId()]
|
||||
if (currentTheme) {
|
||||
cacheThemeVariants(currentTheme, themeId())
|
||||
}
|
||||
})
|
||||
|
||||
// Apply theme when themeId or mode changes
|
||||
createEffect(() => {
|
||||
const id = themeId()
|
||||
const m = mode()
|
||||
@@ -241,18 +147,14 @@ export function ThemeProvider(props: { children: JSX.Element; defaultTheme?: str
|
||||
console.warn(`Theme "${id}" not found`)
|
||||
return
|
||||
}
|
||||
|
||||
setThemeIdSignal(id)
|
||||
localStorage.setItem(STORAGE_KEYS.THEME_ID, id)
|
||||
|
||||
// Cache both variants for future preloads
|
||||
cacheThemeVariants(theme, id)
|
||||
}
|
||||
|
||||
const setColorSchemePref = (scheme: ColorScheme) => {
|
||||
setColorSchemeSignal(scheme)
|
||||
localStorage.setItem(STORAGE_KEYS.COLOR_SCHEME, scheme)
|
||||
|
||||
if (scheme === "system") {
|
||||
setMode(getSystemMode())
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user