diff --git a/frontend/src/sidebar/SidebarContext.tsx b/frontend/src/sidebar/SidebarContext.tsx index 7bccde3..402ba68 100644 --- a/frontend/src/sidebar/SidebarContext.tsx +++ b/frontend/src/sidebar/SidebarContext.tsx @@ -37,6 +37,7 @@ const DEFAULT_NEUTRAL_CHROMA = 0.44; const DEFAULT_NEUTRAL_CONTRAST = 0.56; const DEFAULT_THEME_MODE = 'system'; const THEME_MODES = ['system', 'light', 'dark']; +const DARK_MODE_MEDIA_QUERY = '(prefers-color-scheme: dark)'; const loadInitialThemeSettings = () => { const defaults = { @@ -144,6 +145,12 @@ export const SidebarProvider = ({ initialCollapsed = false, children }) => { const [neutralChroma, setNeutralChromaState] = useState(initialTheme.neutralChroma); const [neutralContrast, setNeutralContrastState] = useState(initialTheme.neutralContrast); const [themeMode, setThemeModeState] = useState(initialTheme.mode); + const [systemPrefersDark, setSystemPrefersDark] = useState(() => { + if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { + return false; + } + return window.matchMedia(DARK_MODE_MEDIA_QUERY).matches; + }); useEffect(() => { document.documentElement.style.setProperty('--neutral-hue', `${neutralHue}deg`); @@ -157,14 +164,34 @@ export const SidebarProvider = ({ initialCollapsed = false, children }) => { document.documentElement.style.setProperty('--neutral-contrast', String(neutralContrast)); }, [neutralContrast]); + useEffect(() => { + if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') { + return () => undefined; + } + const mediaQuery = window.matchMedia(DARK_MODE_MEDIA_QUERY); + const handler = (event: MediaQueryListEvent) => { + setSystemPrefersDark(event.matches); + }; + if (typeof mediaQuery.addEventListener === 'function') { + mediaQuery.addEventListener('change', handler); + return () => mediaQuery.removeEventListener('change', handler); + } + mediaQuery.addListener(handler); + return () => mediaQuery.removeListener(handler); + }, []); + useEffect(() => { const root = document.documentElement; if (themeMode === 'system') { - root.removeAttribute('data-theme'); - } else { - root.setAttribute('data-theme', themeMode); + if (systemPrefersDark) { + root.setAttribute('data-theme', 'dark'); + } else { + root.removeAttribute('data-theme'); + } + return; } - }, [themeMode]); + root.setAttribute('data-theme', themeMode); + }, [themeMode, systemPrefersDark]); useEffect(() => { try { diff --git a/frontend/src/styles/base/theme.css b/frontend/src/styles/base/theme.css index ff71512..0d1b894 100644 --- a/frontend/src/styles/base/theme.css +++ b/frontend/src/styles/base/theme.css @@ -20,6 +20,7 @@ --neutral-contrast-baseline: 1.125; --neutral-contrast-delta: calc(var(--neutral-contrast-amount) - var(--neutral-contrast-baseline)); --neutral-chroma-scale: calc(clamp(0.0001, var(--neutral-chroma-effective), 1) / 0.1); + --tag-chip-default-l: 0.9; --tag-chip-bg-lighten: 0.4; --tag-chip-bg-chroma-scale: 0.75; --tag-chip-outline-lighten: 0.12; @@ -117,10 +118,6 @@ --sidebar-width: 20em; --documents-grid-title-size: 0.8rem; - --tag-chip-bg-lighten: 0.4; - --tag-chip-bg-chroma-scale: 0.75; - --tag-chip-outline-lighten: 0.12; - --tag-chip-outline-chroma-scale: 0.75; } :root[data-theme='light'] { @@ -178,12 +175,13 @@ --overlay-backdrop: color-mix(in oklch, oklch(0.1 0.015 var(--dark-neutral-hue)) 70%, transparent); --overlay-shadow: color-mix(in oklch, oklch(0.12 0.015 var(--dark-neutral-hue)) 55%, transparent); - --tag-chip-bg-lighten: 0.2; - --tag-chip-bg-chroma-scale: 0.65; - --tag-chip-outline-lighten: 0.08; - --tag-chip-outline-chroma-scale: 0.85; - --tag-chip-text-lighten: 0.35; - --tag-chip-text-chroma-scale: 0.6; + --tag-chip-default-l: 0.2; + --tag-chip-bg-lighten: -1.8; + --tag-chip-bg-chroma-scale: 0.95; + --tag-chip-outline-lighten: 0.8; + --tag-chip-outline-chroma-scale: 2.5; + --tag-chip-text-lighten: 0.5; + --tag-chip-text-chroma-scale: 0.9; --success: oklch(0.62 0.16 var(--success-hue)); --warning: oklch(0.68 0.17 var(--warning-hue)); @@ -208,89 +206,6 @@ --preview-nav-fg: var(--fg); --text-on-dark: color-mix(in oklch, white 92%, transparent); --surface-ink-soft: color-mix(in oklch, white 12%, transparent); - - --tag-chip-bg-lighten: -1.2; - --tag-chip-bg-chroma-scale: 1.5; - --tag-chip-outline-lighten: 0; - --tag-chip-outline-chroma-scale: 1.2; -} - -@media (prefers-color-scheme: dark) { - :root:not([data-theme='light']) { - color-scheme: dark; - - --dark-neutral-hue: calc(var(--neutral-hue) + var(--dark-hue-offset)); - --dark-foreground-hue: calc(var(--neutral-hue) + var(--dark-foreground-hue-offset)); - --bg: oklch(calc(0.5 - 0.35 * var(--neutral-contrast-amount)) calc(var(--neutral-chroma-effective) * 0.1) calc(var(--dark-neutral-hue) + var(--surface-hue-shift))); - --surface: oklch(calc(0.5 - 0.31 * var(--neutral-contrast-amount)) calc(var(--neutral-chroma-effective) * 0.12) calc(var(--dark-neutral-hue) + var(--surface-hue-shift))); - --surface-subtle: oklch(calc(0.5 - 0.27 * var(--neutral-contrast-amount)) calc(var(--neutral-chroma-effective) * 0.12) calc(var(--dark-neutral-hue) + var(--surface-hue-shift))); - --fg: oklch(calc(0.5 + 0.39 * var(--neutral-contrast-amount)) calc(var(--neutral-chroma-effective) * 0.04) var(--dark-foreground-hue)); - --muted: oklch(calc(0.5 + 0.22 * var(--neutral-contrast-amount)) calc(var(--neutral-chroma-effective) * 0.06) var(--dark-foreground-hue)); - --muted-subtle: color-mix(in oklch, var(--muted) 45%, var(--border)); - --sidebar-fg: oklch(calc(0.5 + 0.28 * var(--neutral-contrast-amount)) calc(var(--neutral-chroma-effective) * 0.06) var(--dark-foreground-hue)); - --border: oklch(calc(0.5 - 0.17 * var(--neutral-contrast-amount)) calc(var(--neutral-chroma-effective) * 0.03) var(--dark-foreground-hue)); - - --accent: oklch(0.75 calc(0.16 * var(--neutral-chroma-scale)) calc(var(--dark-foreground-hue) + var(--accent-hue-offset))); - --accent-hover: oklch(0.82 calc(0.13 * var(--neutral-chroma-scale)) calc(var(--dark-foreground-hue) + var(--accent-hue-offset))); - --on-accent: oklch(0.15 0.015 var(--dark-foreground-hue)); - - --folder-icon-back: color-mix(in oklch, var(--accent) 72%, black 12%); - --folder-icon-mid: color-mix(in oklch, var(--accent) 50%, white 24%); - --folder-icon-front: color-mix(in oklch, var(--accent) 30%, white 50%); - - --accent-soft: color-mix(in oklch, var(--accent) 22%, transparent); - --accent-elevated: color-mix(in oklch, var(--accent) 28%, transparent); - --accent-elevated-strong: color-mix(in oklch, var(--accent) 38%, transparent); - --accent-outline: color-mix(in oklch, var(--accent) 55%, transparent); - --accent-outline-strong: color-mix(in oklch, var(--accent) 80%, transparent); - --accent-focus: color-mix(in oklch, var(--accent) 72%, transparent); - --surface-overlay: color-mix(in oklch, black 60%, transparent); - - --selection: oklch(0.44 0.04 220deg); - --selection-soft: color-mix(in oklch, var(--selection) 18%, transparent); - - --shadow-faint: color-mix(in oklch, black 35%, transparent); - --shadow-soft: color-mix(in oklch, black 50%, transparent); - --shadow-medium: color-mix(in oklch, black 65%, transparent); - --shadow-strong: color-mix(in oklch, black 80%, transparent); - --shadow-deep: color-mix(in oklch, black 90%, transparent); - --shadow-pop: color-mix(in oklch, black 70%, transparent); - --outline-subtle: color-mix(in oklch, white 10%, transparent); - --overlay-dark: color-mix(in oklch, black 70%, transparent); - --overlay-dim: color-mix(in oklch, oklch(0.42 0.04 var(--dark-neutral-hue)) 35%, transparent); - --overlay-darker: color-mix(in oklch, black 85%, transparent); - --surface-danger-subtle: color-mix(in oklch, var(--danger) 26%, transparent); - --overlay-accent-subtle: color-mix(in oklch, var(--accent) 32%, transparent); - --border-strong: color-mix(in oklch, var(--fg) 30%, transparent); - --surface-hover: color-mix(in oklch, white 6%, transparent); - --overlay-accent-strong: color-mix(in oklch, var(--accent) 55%, transparent); - --overlay-backdrop: color-mix(in oklch, oklch(0.1 0.015 var(--dark-neutral-hue)) 70%, transparent); - --overlay-shadow: color-mix(in oklch, oklch(0.12 0.015 var(--dark-neutral-hue)) 55%, transparent); - - --success: oklch(0.62 0.16 var(--success-hue)); - --warning: oklch(0.68 0.17 var(--warning-hue)); - --danger: oklch(0.60 0.2 var(--danger-hue)); - - --success-subtle: color-mix(in oklch, var(--success) 20%, transparent); - --warning-subtle: color-mix(in oklch, var(--warning) 20%, transparent); - --danger-border: color-mix(in oklch, var(--danger) 45%, transparent); - --danger-soft: color-mix(in oklch, var(--danger) 24%, transparent); - --danger-subtle: color-mix(in oklch, var(--danger) 18%, transparent); - - --row-hover-bg: color-mix(in oklch, white 4%, transparent); - --row-active-bg: color-mix(in oklch, var(--selection) 20%, transparent); - --sidebar-hover-bg: color-mix(in oklch, white 6%, transparent); - --sidebar-active-bg: color-mix(in oklch, var(--selection) 26%, transparent); - --selection-ring: oklch(0.68 0.16 var(--dark-foreground-hue)); - --sidebar-active-pill-border: color-mix(in oklch, var(--accent) 72%, transparent); - --link: color-mix(in oklch, var(--accent) 92%, transparent); - --link-visited: color-mix(in oklch, var(--accent) 70%, white 20%); - --preview-nav-bg: oklch(0.3 0.04 var(--dark-neutral-hue)); - --preview-nav-bg-hover: oklch(0.35 0.04 var(--dark-neutral-hue)); - --preview-nav-fg: var(--fg); - --text-on-dark: color-mix(in oklch, white 92%, transparent); - --surface-ink-soft: color-mix(in oklch, white 12%, transparent); - } } html, diff --git a/frontend/src/styles/documents/listing.css b/frontend/src/styles/documents/listing.css index 7975677..65dd4ba 100644 --- a/frontend/src/styles/documents/listing.css +++ b/frontend/src/styles/documents/listing.css @@ -758,7 +758,7 @@ } .tag-chip { - --tag-chip-resolved-l: var(--tag-chip-base-l, 0.9); + --tag-chip-resolved-l: var(--tag-chip-base-l, var(--tag-chip-default-l, 0.9)); --tag-chip-resolved-c: var(--tag-chip-base-c, 0); --tag-chip-resolved-h: var(--tag-chip-base-h, 0deg); gap: 0.25rem;