@gospore/theme 1.0.150 → 1.0.152
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/theme.cjs.js +1 -1
- package/dist/theme.cjs.js.map +1 -1
- package/dist/theme.es.js +1 -1
- package/dist/theme.es.js.map +1 -1
- package/dist/theme.umd.js +1 -1
- package/dist/theme.umd.js.map +1 -1
- package/package.json +4 -4
package/dist/theme.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.es.js","names":[],"sources":["../src/modeStorage.ts","../src/portalHost.ts","../src/Theme.vue","../src/Theme.vue","../src/ThemeSwitcher.vue","../src/ThemeSwitcher.vue"],"sourcesContent":["const VALID_THEME_MODES = [\"auto\", \"light\", \"dark\"] as const;\nexport type ThemeMode = (typeof VALID_THEME_MODES)[number];\nconst STORAGE_KEY = \"gospore-theme-mode\";\nconst ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;\nconst DARK_MODE_QUERY = \"(prefers-color-scheme: dark)\";\n\nexport function readStoredMode(): ThemeMode | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n const stored = getCookie(STORAGE_KEY);\n return isThemeMode(stored) ? stored : null;\n}\n\nexport function writeStoredMode(mode: ThemeMode) {\n if (typeof document === \"undefined\" || !VALID_THEME_MODES.includes(mode)) {\n return;\n }\n const hostname = typeof window !== \"undefined\" ? window.location.hostname : \"\";\n const protocol = typeof window !== \"undefined\" ? window.location.protocol : \"\";\n const domain = getCookieDomain(hostname);\n const secure = protocol === \"https:\";\n const attributes = [\"path=/\", `max-age=${ONE_YEAR_IN_SECONDS}`, \"SameSite=Lax\"];\n if (domain) {\n attributes.push(`domain=${domain}`);\n }\n if (secure) {\n attributes.push(\"Secure\");\n }\n document.cookie = [\n `${encodeURIComponent(STORAGE_KEY)}=${encodeURIComponent(mode)}`,\n ...attributes,\n ].join(\"; \");\n}\n\nexport function applyStoredModeToDocument(mode?: string | null) {\n if (typeof document === \"undefined\") {\n return;\n }\n const resolvedMode = isThemeMode(mode) ? mode : (readStoredMode() ?? \"auto\");\n console.log(\"Applying theme mode:\", resolvedMode);\n const darkMode = resolveDarkMode(resolvedMode);\n document.documentElement.setAttribute(\"dark-mode\", String(darkMode));\n}\n\nexport { VALID_THEME_MODES, STORAGE_KEY };\n\nfunction getCookie(name: string) {\n if (typeof document === \"undefined\" || !document.cookie) {\n return null;\n }\n const encodedName = `${encodeURIComponent(name)}=`;\n const cookies = document.cookie.split(\"; \");\n for (const cookie of cookies) {\n if (cookie.startsWith(encodedName)) {\n return decodeURIComponent(cookie.slice(encodedName.length));\n }\n }\n return null;\n}\n\nfunction getCookieDomain(hostname: string) {\n if (!hostname || hostname === \"localhost\") {\n return null;\n }\n const isIpAddress = /^\\d+(?:\\.\\d+){3}$/.test(hostname);\n if (isIpAddress) {\n return hostname;\n }\n const parts = hostname.split(\".\");\n if (parts.length <= 1) {\n return null;\n }\n if (parts.length === 2) {\n return hostname;\n }\n return parts.slice(-2).join(\".\");\n}\n\nfunction resolveDarkMode(mode: string) {\n if (mode === \"dark\") {\n return true;\n }\n if (mode === \"light\") {\n return false;\n }\n if (typeof window === \"undefined\") {\n return false;\n }\n try {\n const mediaQueryList =\n typeof window.matchMedia === \"function\" ? window.matchMedia(DARK_MODE_QUERY) : null;\n return mediaQueryList ? mediaQueryList.matches : false;\n } catch {\n return false;\n }\n}\n\nfunction isThemeMode(value: string | null | undefined): value is ThemeMode {\n return typeof value === \"string\" && (VALID_THEME_MODES as readonly string[]).includes(value);\n}\n","import { computed, inject } from \"vue\";\nimport type { InjectionKey, Ref } from \"vue\";\n\nexport type ThemePortalTarget = HTMLElement | string;\n\nexport const themePortalHostKey = Symbol.for(\"gospore.theme.portalHost\") as InjectionKey<\n Ref<HTMLElement | null>\n>;\n\nexport function useThemePortalTarget() {\n const hostRef = inject(themePortalHostKey, null);\n console.log(\"Using theme portal host:\", hostRef);\n const fallbackTarget: ThemePortalTarget =\n typeof document !== \"undefined\" ? document.body : \"body\";\n\n return computed<ThemePortalTarget>(() => hostRef?.value ?? fallbackTarget);\n}\n","<script lang=\"ts\">\ninterface GlobalAccentEntry {\n vars: Record<string, string>;\n enabled: boolean;\n}\n\nconst globalAccentStack: GlobalAccentEntry[] = [];\nconst appliedGlobalAccentKeys = new Set<string>();\n\nconst DARK_MODE_QUERY = \"(prefers-color-scheme: dark)\"\nconst PREFERS_REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\"\nconst THEME_TRANSITION_DURATION = 200\nconst BRAND_MIN_CONTRAST = 3.8\nconst BRAND_MIN_SURFACE_CONTRAST = 1.8\nconst BRAND_MIN_SUBTLE_SURFACE_CONTRAST = 1.15\nconst BRAND_MIN_SOFT_SURFACE_CONTRAST = 1.08\nconst BRAND_UI_TEXT_MIN_CONTRAST = 2.5\nconst BRAND_LIGHT_TEXT_SWITCH_DELTA = 3\nconst LIGHT_CANVAS_HEX = \"#ffffff\"\nconst DARK_CANVAS_HEX = \"#1f2228\"</script>\n\n<template>\n <div class=\"theme\" :style=\"brandVars\">\n <slot />\n <div ref=\"portalHost\" class=\"theme__portal-host\"></div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { provide, ref, watch, onBeforeUnmount, computed } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { applyStoredModeToDocument, readStoredMode, type ThemeMode } from \"./modeStorage\";\nimport \"what-input\";\nimport { themePortalHostKey } from \"./portalHost\";\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\ninterface OklchColor {\n l: number;\n c: number;\n h: number;\n}\n\ntype BrandRamp = Record<number, string>;\ntype BrandVars = Record<string, string>;\n\ninterface ReadableTextOptions {\n minContrast?: number;\n preferDark?: boolean;\n chromaScale?: number;\n initialLightness?: number;\n step?: number;\n}\n\ninterface ForegroundOptions extends ReadableTextOptions {\n darkCandidate?: string;\n lightCandidate?: string;\n preferredCandidate?: \"dark\" | \"light\";\n preferredMinContrast?: number;\n preferredSwitchDelta?: number;\n}\n\ndefineOptions({\n name: \"Theme\",\n});\ndefineSlots<{\n /** Application content that receives theme tokens and portal context. */\n default?: () => unknown;\n}>();\nconst props = defineProps({\n /**\n * Controls which color mode is active. auto follows the OS preference (and persisted storage\n * when used with ThemeSwitcher).\n */\n mode: {\n type: String as PropType<ThemeMode | undefined>,\n default: undefined,\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n /**\n * Optional hex accent color (\"#RRGGBB\" or \"#RGB\"). When provided, Theme derives contrast-safe\n * semantic brand tokens and exposes them on the root .theme element (and the global document\n * for portals).\n */\n accentColor: {\n type: String,\n required: false,\n default: undefined,\n },\n /**\n * When true the brand vars are also exposed on <html>, allowing portals attached outside the\n * Theme subtree to inherit the accent colors.\n */\n exposeGlobalAccent: {\n type: Boolean,\n default: false,\n },\n});\n\nfunction applyGlobalAccentFromStack() {\n const root = document.documentElement;\n if (!root) {\n return;\n }\n\n appliedGlobalAccentKeys.forEach((key) => {\n root.style.removeProperty(key);\n });\n appliedGlobalAccentKeys.clear();\n\n const activeEntry = [...globalAccentStack]\n .reverse()\n .find((entry) => entry.enabled && entry.vars && Object.keys(entry.vars).length > 0);\n\n if (!activeEntry) {\n return;\n }\n\n Object.entries(activeEntry.vars).forEach(([key, value]) => {\n root.style.setProperty(key, value);\n appliedGlobalAccentKeys.add(key);\n });\n}\n\n\n\n\nconst darkModeMedia = window.matchMedia(DARK_MODE_QUERY);\nconst prefersReducedMotionMedia = window.matchMedia(PREFERS_REDUCED_MOTION_QUERY);\n\nconst initialMode = getEffectiveMode();\napplyStoredModeToDocument(initialMode);\n\nconst currentMode = ref(initialMode);\n\nconst theme = ref({\n darkMode: resolveDarkMode(initialMode),\n});\nconst portalHost = ref<HTMLElement | null>(null);\n\ndarkModeMedia.addEventListener(\"change\", handleSystemPreferenceChange);\n\nwatch(\n () => props.mode,\n () => {\n syncDarkMode();\n },\n { immediate: true },\n);\n\nwatch(\n () => theme.value.darkMode,\n (darkMode, previousValue) => {\n if (previousValue === undefined) {\n setDocumentDarkModeAttribute(darkMode);\n return;\n }\n withThemeTransition(() => {\n setDocumentDarkModeAttribute(darkMode);\n });\n },\n { immediate: true },\n);\n\nonBeforeUnmount(() => {\n darkModeMedia.removeEventListener(\"change\", handleSystemPreferenceChange);\n const index = globalAccentStack.indexOf(accentStackEntry);\n if (index !== -1) {\n globalAccentStack.splice(index, 1);\n }\n applyGlobalAccentFromStack();\n});\n\nfunction syncDarkMode() {\n const mode = getEffectiveMode();\n currentMode.value = mode;\n theme.value.darkMode = resolveDarkMode(mode);\n}\n\nfunction resolveDarkMode(mode: ThemeMode) {\n if (mode === \"dark\") {\n return true;\n }\n if (mode === \"light\") {\n return false;\n }\n return darkModeMedia.matches;\n}\n\nfunction handleSystemPreferenceChange(event: MediaQueryListEvent) {\n if (!event || currentMode.value !== \"auto\") {\n return;\n }\n theme.value.darkMode = event.matches;\n}\n\nfunction setDocumentDarkModeAttribute(darkMode: boolean) {\n document.documentElement.setAttribute(\"dark-mode\", String(darkMode));\n}\n\nfunction withThemeTransition(applyTheme: () => void) {\n if (prefersReducedMotionMedia.matches) {\n applyTheme();\n return;\n }\n const startViewTransition = document.startViewTransition?.bind(document);\n if (startViewTransition) {\n startViewTransition(() => {\n applyTheme();\n });\n return;\n }\n applyTheme();\n const element = document.documentElement;\n if (typeof element.animate !== \"function\") {\n return;\n }\n window.requestAnimationFrame(() => {\n element.animate([{ opacity: 0.85 }, { opacity: 1 }], {\n duration: THEME_TRANSITION_DURATION,\n easing: \"ease-in-out\",\n });\n });\n}\n\nfunction getEffectiveMode(): ThemeMode {\n return props.mode ?? readStoredMode() ?? \"auto\";\n}\n\nprovide(\"theme\", theme);\nprovide(themePortalHostKey, portalHost);\n\n// --- Accent helpers -------------------------------------------------------\n\n\nfunction normalizeHex(hex: string | undefined) {\n if (!hex) return null;\n const h = hex.trim().toLowerCase();\n const m = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.exec(h);\n if (!m) return null;\n if (h.length === 4) {\n // expand #rgb -> #rrggbb\n const r = h[1],\n g = h[2],\n b = h[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return h;\n}\n\nfunction hexToRgb(hex: string | undefined): RgbColor | null {\n const h = normalizeHex(hex);\n if (!h) return null;\n const r = parseInt(h.slice(1, 3), 16);\n const g = parseInt(h.slice(3, 5), 16);\n const b = parseInt(h.slice(5, 7), 16);\n return { r, g, b };\n}\n\nfunction rgbToHex({ r, g, b }: RgbColor) {\n const toHex = (value: number) => value.toString(16).padStart(2, \"0\");\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction srgbChannelToLinear(channel: number) {\n if (channel <= 0.04045) {\n return channel / 12.92;\n }\n return Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearChannelToSrgb(channel: number) {\n const normalized = clamp01(channel);\n if (normalized <= 0.0031308) {\n return 12.92 * normalized;\n }\n return 1.055 * Math.pow(normalized, 1 / 2.4) - 0.055;\n}\n\nfunction rgbToOklch({ r, g, b }: RgbColor): OklchColor {\n const rNorm = r / 255;\n const gNorm = g / 255;\n const bNorm = b / 255;\n\n const rLin = srgbChannelToLinear(rNorm);\n const gLin = srgbChannelToLinear(gNorm);\n const bLin = srgbChannelToLinear(bNorm);\n\n const l = 0.4122214708 * rLin + 0.5363325363 * gLin + 0.0514459929 * bLin;\n const m = 0.2119034982 * rLin + 0.6806995451 * gLin + 0.1073969566 * bLin;\n const s = 0.0883024619 * rLin + 0.2817188376 * gLin + 0.6299787005 * bLin;\n\n const lRoot = Math.cbrt(l);\n const mRoot = Math.cbrt(m);\n const sRoot = Math.cbrt(s);\n\n const oklabL = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;\n const oklabA = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;\n const oklabB = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;\n\n const chroma = Math.sqrt(oklabA * oklabA + oklabB * oklabB);\n let hue = Math.atan2(oklabB, oklabA) * (180 / Math.PI);\n if (hue < 0) {\n hue += 360;\n }\n\n return { l: oklabL, c: chroma, h: hue };\n}\n\nfunction tryOklchToSrgb(l: number, c: number, h: number, clampLinear = false): RgbColor | null {\n const angle = (h * Math.PI) / 180;\n const a = c * Math.cos(angle);\n const b = c * Math.sin(angle);\n\n const lComponent = l + 0.3963377774 * a + 0.2158037573 * b;\n const mComponent = l - 0.1055613458 * a - 0.0638541728 * b;\n const sComponent = l - 0.0894841775 * a - 1.291485548 * b;\n\n const lCube = clampLinear ? clamp01(lComponent) : lComponent;\n const mCube = clampLinear ? clamp01(mComponent) : mComponent;\n const sCube = clampLinear ? clamp01(sComponent) : sComponent;\n\n if (!clampLinear) {\n const outOfRange =\n lCube < -0.0001 ||\n lCube > 1.0001 ||\n mCube < -0.0001 ||\n mCube > 1.0001 ||\n sCube < -0.0001 ||\n sCube > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const lLin = lCube * lCube * lCube;\n const mLin = mCube * mCube * mCube;\n const sLin = sCube * sCube * sCube;\n\n let rLin = 4.0767416621 * lLin - 3.3077115913 * mLin + 0.2309699292 * sLin;\n let gLin = -1.2684380046 * lLin + 2.6097574011 * mLin - 0.3413193965 * sLin;\n let bLin = -0.0041960863 * lLin - 0.7034186147 * mLin + 1.707614701 * sLin;\n\n if (clampLinear) {\n rLin = clamp01(rLin);\n gLin = clamp01(gLin);\n bLin = clamp01(bLin);\n } else {\n const outOfRange =\n rLin < -0.0001 ||\n rLin > 1.0001 ||\n gLin < -0.0001 ||\n gLin > 1.0001 ||\n bLin < -0.0001 ||\n bLin > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const r = clamp01(linearChannelToSrgb(rLin));\n const g = clamp01(linearChannelToSrgb(gLin));\n const bValue = clamp01(linearChannelToSrgb(bLin));\n\n return {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(bValue * 255),\n };\n}\n\nfunction oklchToSrgb(l: number, c: number, h: number): RgbColor {\n let chroma = c;\n for (let attempt = 0; attempt < 8; attempt += 1) {\n const rgb = tryOklchToSrgb(l, chroma, h);\n if (rgb) {\n return rgb;\n }\n chroma *= 0.9;\n }\n return tryOklchToSrgb(l, clamp01(chroma), h, true) || { r: 0, g: 0, b: 0 };\n}\n\nfunction relativeLuminance(hex: string) {\n const rgb = hexToRgb(hex);\n if (!rgb) {\n return 0;\n }\n const r = srgbChannelToLinear(rgb.r / 255);\n const g = srgbChannelToLinear(rgb.g / 255);\n const b = srgbChannelToLinear(rgb.b / 255);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\nfunction contrastRatio(hexA: string, hexB: string) {\n const lumA = relativeLuminance(hexA);\n const lumB = relativeLuminance(hexB);\n const lighter = Math.max(lumA, lumB);\n const darker = Math.min(lumA, lumB);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction makeReadableTextOnSurface(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ReadableTextOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const preferDark = options.preferDark !== false;\n const textChroma = Math.max(\n 0.01,\n Math.min(0.06, (baseOklch.c || 0.12) * (options.chromaScale ?? 0.25)),\n );\n const initialLightness = options.initialLightness ?? (preferDark ? 0.24 : 0.94);\n const direction = preferDark ? -1 : 1;\n const step = options.step ?? 0.03;\n let lightness = clamp01(initialLightness);\n let bestHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n let bestContrast = contrastRatio(surfaceHex, bestHex);\n\n for (let attempt = 0; attempt < 48; attempt += 1) {\n const textHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n const ratio = contrastRatio(surfaceHex, textHex);\n\n if (ratio > bestContrast) {\n bestContrast = ratio;\n bestHex = textHex;\n }\n if (ratio >= minContrast) {\n return textHex;\n }\n\n lightness = clamp01(lightness + direction * step);\n if (lightness === 0 || lightness === 1) {\n break;\n }\n }\n\n const fallbackDark = \"#101214\";\n const fallbackLight = \"#ffffff\";\n const darkRatio = contrastRatio(surfaceHex, fallbackDark);\n const lightRatio = contrastRatio(surfaceHex, fallbackLight);\n\n if (darkRatio >= minContrast || lightRatio >= minContrast) {\n return darkRatio >= lightRatio ? fallbackDark : fallbackLight;\n }\n\n return bestHex;\n}\n\nfunction pickForegroundColor(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ForegroundOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const darkCandidate = options.darkCandidate ?? \"#101214\";\n const lightCandidate = options.lightCandidate ?? \"#ffffff\";\n const preferredCandidate = options.preferredCandidate;\n const preferredMinContrast = options.preferredMinContrast ?? minContrast;\n const preferredSwitchDelta = options.preferredSwitchDelta ?? 0;\n const darkRatio = contrastRatio(surfaceHex, darkCandidate);\n const lightRatio = contrastRatio(surfaceHex, lightCandidate);\n const darkPasses = darkRatio >= minContrast;\n const lightPasses = lightRatio >= minContrast;\n const preferredLightPasses = lightRatio >= preferredMinContrast;\n const preferredDarkPasses = darkRatio >= preferredMinContrast;\n\n if (darkPasses || lightPasses) {\n if (preferredCandidate === \"light\" && preferredLightPasses) {\n return lightCandidate;\n }\n if (preferredCandidate === \"dark\" && preferredDarkPasses) {\n return darkCandidate;\n }\n\n if (preferredCandidate === \"light\" && lightPasses) {\n if (!darkPasses || darkRatio - lightRatio <= preferredSwitchDelta) {\n return lightCandidate;\n }\n return darkCandidate;\n }\n if (preferredCandidate === \"dark\" && darkPasses) {\n if (!lightPasses || lightRatio - darkRatio <= preferredSwitchDelta) {\n return darkCandidate;\n }\n return lightCandidate;\n }\n\n if (darkPasses && lightPasses) {\n return darkRatio >= lightRatio ? darkCandidate : lightCandidate;\n }\n return darkPasses ? darkCandidate : lightCandidate;\n }\n\n const preferDark = darkRatio >= lightRatio;\n return makeReadableTextOnSurface(baseOklch, surfaceHex, {\n minContrast,\n preferDark,\n initialLightness: preferDark ? 0.2 : 0.96,\n chromaScale: 0.1,\n });\n}\n\nconst BRAND_STOPS = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];\nconst BRAND_LIGHTNESS_CURVE = [0.9, 0.84, 0.78, 0.72, 0.67, 0.62, 0.56, 0.5, 0.44, 0.38];\nconst BRAND_CHROMA_MULTIPLIERS = [0.28, 0.4, 0.54, 0.7, 0.86, 1, 1.08, 1.14, 1.2, 1.24];\n\n\n\n\n\n\n\n\nfunction pickRampStopForContrast(\n ramp: BrandRamp,\n stopCandidates: number[],\n referenceHex: string,\n minContrast: number,\n) {\n const fallbackStop = stopCandidates[stopCandidates.length - 1];\n for (const stop of stopCandidates) {\n const candidate = ramp[stop];\n if (!candidate) {\n continue;\n }\n if (contrastRatio(candidate, referenceHex) >= minContrast) {\n return stop;\n }\n }\n return fallbackStop;\n}\n\nfunction makeBrandRampFromSeed(baseOklch: OklchColor): BrandRamp {\n const lightnessOffset = baseOklch.l - BRAND_LIGHTNESS_CURVE[5];\n const minSeedChroma = baseOklch.c > 0.02 ? 0.02 : 0;\n\n return BRAND_STOPS.reduce<BrandRamp>((acc, stop, index) => {\n const lightness = clamp01(BRAND_LIGHTNESS_CURVE[index] + lightnessOffset);\n const chroma = Math.min(\n 0.4,\n Math.max(minSeedChroma, baseOklch.c * BRAND_CHROMA_MULTIPLIERS[index]),\n );\n acc[stop] = rgbToHex(oklchToSrgb(lightness, chroma, baseOklch.h));\n return acc;\n }, {});\n}\n\nfunction makeSemanticBrandTokens(baseOklch: OklchColor, ramp: BrandRamp): BrandVars {\n const lightSoftStops = [100, 200, 300, 400, 500, 600, 700];\n const lightSoftStop = pickRampStopForContrast(\n ramp,\n lightSoftStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const lightSoftIndex = lightSoftStops.indexOf(lightSoftStop);\n const lightSoftHoveredStop =\n lightSoftStops[Math.min(lightSoftIndex + 1, lightSoftStops.length - 1)];\n const lightSoftPressedStop =\n lightSoftStops[Math.min(lightSoftIndex + 2, lightSoftStops.length - 1)];\n\n const darkSoftStops = [1000, 900, 800, 700, 600, 500];\n const darkSoftStop = pickRampStopForContrast(\n ramp,\n darkSoftStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const darkSoftIndex = darkSoftStops.indexOf(darkSoftStop);\n const darkSoftHoveredStop =\n darkSoftStops[Math.min(darkSoftIndex + 1, darkSoftStops.length - 1)];\n const darkSoftPressedStop =\n darkSoftStops[Math.min(darkSoftIndex + 2, darkSoftStops.length - 1)];\n\n const lightSubtleStops = [200, 300, 400, 500, 600, 700, 800];\n const lightSubtleStop = pickRampStopForContrast(\n ramp,\n lightSubtleStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n const darkSubtleStops = [900, 800, 700, 600, 500, 400];\n const darkSubtleStop = pickRampStopForContrast(\n ramp,\n darkSubtleStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n\n const lightStrongStops = [600, 700, 800, 900, 1000];\n const lightStrongStop = pickRampStopForContrast(\n ramp,\n lightStrongStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const lightStrongIndex = lightStrongStops.indexOf(lightStrongStop);\n const lightStrongHoveredStop =\n lightStrongStops[Math.min(lightStrongIndex + 1, lightStrongStops.length - 1)];\n const lightStrongPressedStop =\n lightStrongStops[Math.min(lightStrongIndex + 2, lightStrongStops.length - 1)];\n\n const darkStrongStops = [400, 300, 200, 100];\n const darkStrongStop = pickRampStopForContrast(\n ramp,\n darkStrongStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const darkStrongIndex = darkStrongStops.indexOf(darkStrongStop);\n const darkStrongHoveredStop =\n darkStrongStops[Math.min(darkStrongIndex + 1, darkStrongStops.length - 1)];\n const darkStrongPressedStop =\n darkStrongStops[Math.min(darkStrongIndex + 2, darkStrongStops.length - 1)];\n\n const tokens: BrandVars = {\n \"--color-brand-surface-soft-light\": ramp[lightSoftStop],\n \"--color-brand-surface-soft-hovered-light\": ramp[lightSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-light\": ramp[lightSoftPressedStop],\n \"--color-brand-surface-soft-dark\": ramp[darkSoftStop],\n \"--color-brand-surface-soft-hovered-dark\": ramp[darkSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-dark\": ramp[darkSoftPressedStop],\n \"--color-brand-surface-subtle-light\": ramp[lightSubtleStop],\n \"--color-brand-surface-subtle-dark\": ramp[darkSubtleStop],\n \"--color-brand-surface-strong-light\": ramp[lightStrongStop],\n \"--color-brand-surface-strong-hovered-light\": ramp[lightStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-light\": ramp[lightStrongPressedStop],\n \"--color-brand-surface-strong-dark\": ramp[darkStrongStop],\n \"--color-brand-surface-strong-hovered-dark\": ramp[darkStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-dark\": ramp[darkStrongPressedStop],\n };\n\n const preferredSoftLightForeground = ramp[600];\n const softLightContrast = contrastRatio(\n tokens[\"--color-brand-surface-soft-light\"],\n preferredSoftLightForeground,\n );\n tokens[\"--color-brand-on-soft-light\"] =\n softLightContrast >= BRAND_MIN_CONTRAST\n ? preferredSoftLightForeground\n : pickForegroundColor(baseOklch, tokens[\"--color-brand-surface-soft-light\"], {\n minContrast: BRAND_MIN_CONTRAST,\n });\n tokens[\"--color-brand-on-soft-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-soft-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-subtle-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-subtle-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-strong-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-strong-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n\n tokens[\"--color-brand-text-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-text-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-icon-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-icon-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-border-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-border-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n\n return tokens;\n}\n\nfunction makeBrandTokens(baseHex: string | undefined): BrandVars | null {\n const norm = normalizeHex(baseHex);\n if (!norm) {\n return null;\n }\n\n const baseRgb = hexToRgb(norm);\n if (!baseRgb) {\n return null;\n }\n\n const baseOklch = rgbToOklch(baseRgb);\n const ramp = makeBrandRampFromSeed(baseOklch);\n // Keep the incoming seed exact for debugging and transparency.\n ramp[600] = norm;\n\n return {\n \"--color-brand-seed\": norm,\n \"--color-brand-100\": ramp[100],\n \"--color-brand-200\": ramp[200],\n \"--color-brand-300\": ramp[300],\n \"--color-brand-400\": ramp[400],\n \"--color-brand-500\": ramp[500],\n \"--color-brand-600\": ramp[600],\n \"--color-brand-700\": ramp[700],\n \"--color-brand-800\": ramp[800],\n \"--color-brand-900\": ramp[900],\n \"--color-brand-1000\": ramp[1000],\n ...makeSemanticBrandTokens(baseOklch, ramp),\n };\n}\n\nconst brandVars = computed(() => makeBrandTokens(props.accentColor) ?? {});\n\nconst accentStackEntry = {\n vars: {},\n enabled: Boolean(props.exposeGlobalAccent),\n};\nglobalAccentStack.push(accentStackEntry);\n\nwatch(\n () => props.exposeGlobalAccent,\n (enabled) => {\n accentStackEntry.enabled = Boolean(enabled);\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n\nwatch(\n brandVars,\n (vars) => {\n if (vars && Object.keys(vars).length > 0) {\n accentStackEntry.vars = { ...vars };\n } else {\n accentStackEntry.vars = {};\n }\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n</script>\n\n<style lang=\"less\">\n@import \"index.less\";\n</style>\n","<script lang=\"ts\">\ninterface GlobalAccentEntry {\n vars: Record<string, string>;\n enabled: boolean;\n}\n\nconst globalAccentStack: GlobalAccentEntry[] = [];\nconst appliedGlobalAccentKeys = new Set<string>();\n</script>\n\n<template>\n <div class=\"theme\" :style=\"brandVars\">\n <slot />\n <div ref=\"portalHost\" class=\"theme__portal-host\"></div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { provide, ref, watch, onBeforeUnmount, computed } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { applyStoredModeToDocument, readStoredMode, type ThemeMode } from \"./modeStorage\";\nimport \"what-input\";\nimport { themePortalHostKey } from \"./portalHost\";\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\ninterface OklchColor {\n l: number;\n c: number;\n h: number;\n}\n\ntype BrandRamp = Record<number, string>;\ntype BrandVars = Record<string, string>;\n\ninterface ReadableTextOptions {\n minContrast?: number;\n preferDark?: boolean;\n chromaScale?: number;\n initialLightness?: number;\n step?: number;\n}\n\ninterface ForegroundOptions extends ReadableTextOptions {\n darkCandidate?: string;\n lightCandidate?: string;\n preferredCandidate?: \"dark\" | \"light\";\n preferredMinContrast?: number;\n preferredSwitchDelta?: number;\n}\n\ndefineOptions({\n name: \"Theme\",\n});\ndefineSlots<{\n /** Application content that receives theme tokens and portal context. */\n default?: () => unknown;\n}>();\nconst props = defineProps({\n /**\n * Controls which color mode is active. auto follows the OS preference (and persisted storage\n * when used with ThemeSwitcher).\n */\n mode: {\n type: String as PropType<ThemeMode | undefined>,\n default: undefined,\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n /**\n * Optional hex accent color (\"#RRGGBB\" or \"#RGB\"). When provided, Theme derives contrast-safe\n * semantic brand tokens and exposes them on the root .theme element (and the global document\n * for portals).\n */\n accentColor: {\n type: String,\n required: false,\n default: undefined,\n },\n /**\n * When true the brand vars are also exposed on <html>, allowing portals attached outside the\n * Theme subtree to inherit the accent colors.\n */\n exposeGlobalAccent: {\n type: Boolean,\n default: false,\n },\n});\n\nfunction applyGlobalAccentFromStack() {\n const root = document.documentElement;\n if (!root) {\n return;\n }\n\n appliedGlobalAccentKeys.forEach((key) => {\n root.style.removeProperty(key);\n });\n appliedGlobalAccentKeys.clear();\n\n const activeEntry = [...globalAccentStack]\n .reverse()\n .find((entry) => entry.enabled && entry.vars && Object.keys(entry.vars).length > 0);\n\n if (!activeEntry) {\n return;\n }\n\n Object.entries(activeEntry.vars).forEach(([key, value]) => {\n root.style.setProperty(key, value);\n appliedGlobalAccentKeys.add(key);\n });\n}\n\nconst DARK_MODE_QUERY = \"(prefers-color-scheme: dark)\";\nconst PREFERS_REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\";\nconst THEME_TRANSITION_DURATION = 200;\nconst darkModeMedia = window.matchMedia(DARK_MODE_QUERY);\nconst prefersReducedMotionMedia = window.matchMedia(PREFERS_REDUCED_MOTION_QUERY);\n\nconst initialMode = getEffectiveMode();\napplyStoredModeToDocument(initialMode);\n\nconst currentMode = ref(initialMode);\n\nconst theme = ref({\n darkMode: resolveDarkMode(initialMode),\n});\nconst portalHost = ref<HTMLElement | null>(null);\n\ndarkModeMedia.addEventListener(\"change\", handleSystemPreferenceChange);\n\nwatch(\n () => props.mode,\n () => {\n syncDarkMode();\n },\n { immediate: true },\n);\n\nwatch(\n () => theme.value.darkMode,\n (darkMode, previousValue) => {\n if (previousValue === undefined) {\n setDocumentDarkModeAttribute(darkMode);\n return;\n }\n withThemeTransition(() => {\n setDocumentDarkModeAttribute(darkMode);\n });\n },\n { immediate: true },\n);\n\nonBeforeUnmount(() => {\n darkModeMedia.removeEventListener(\"change\", handleSystemPreferenceChange);\n const index = globalAccentStack.indexOf(accentStackEntry);\n if (index !== -1) {\n globalAccentStack.splice(index, 1);\n }\n applyGlobalAccentFromStack();\n});\n\nfunction syncDarkMode() {\n const mode = getEffectiveMode();\n currentMode.value = mode;\n theme.value.darkMode = resolveDarkMode(mode);\n}\n\nfunction resolveDarkMode(mode: ThemeMode) {\n if (mode === \"dark\") {\n return true;\n }\n if (mode === \"light\") {\n return false;\n }\n return darkModeMedia.matches;\n}\n\nfunction handleSystemPreferenceChange(event: MediaQueryListEvent) {\n if (!event || currentMode.value !== \"auto\") {\n return;\n }\n theme.value.darkMode = event.matches;\n}\n\nfunction setDocumentDarkModeAttribute(darkMode: boolean) {\n document.documentElement.setAttribute(\"dark-mode\", String(darkMode));\n}\n\nfunction withThemeTransition(applyTheme: () => void) {\n if (prefersReducedMotionMedia.matches) {\n applyTheme();\n return;\n }\n const startViewTransition = document.startViewTransition?.bind(document);\n if (startViewTransition) {\n startViewTransition(() => {\n applyTheme();\n });\n return;\n }\n applyTheme();\n const element = document.documentElement;\n if (typeof element.animate !== \"function\") {\n return;\n }\n window.requestAnimationFrame(() => {\n element.animate([{ opacity: 0.85 }, { opacity: 1 }], {\n duration: THEME_TRANSITION_DURATION,\n easing: \"ease-in-out\",\n });\n });\n}\n\nfunction getEffectiveMode(): ThemeMode {\n return props.mode ?? readStoredMode() ?? \"auto\";\n}\n\nprovide(\"theme\", theme);\nprovide(themePortalHostKey, portalHost);\n\n// --- Accent helpers -------------------------------------------------------\nconst BRAND_MIN_CONTRAST = 3.8;\n\nfunction normalizeHex(hex: string | undefined) {\n if (!hex) return null;\n const h = hex.trim().toLowerCase();\n const m = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.exec(h);\n if (!m) return null;\n if (h.length === 4) {\n // expand #rgb -> #rrggbb\n const r = h[1],\n g = h[2],\n b = h[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return h;\n}\n\nfunction hexToRgb(hex: string | undefined): RgbColor | null {\n const h = normalizeHex(hex);\n if (!h) return null;\n const r = parseInt(h.slice(1, 3), 16);\n const g = parseInt(h.slice(3, 5), 16);\n const b = parseInt(h.slice(5, 7), 16);\n return { r, g, b };\n}\n\nfunction rgbToHex({ r, g, b }: RgbColor) {\n const toHex = (value: number) => value.toString(16).padStart(2, \"0\");\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction srgbChannelToLinear(channel: number) {\n if (channel <= 0.04045) {\n return channel / 12.92;\n }\n return Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearChannelToSrgb(channel: number) {\n const normalized = clamp01(channel);\n if (normalized <= 0.0031308) {\n return 12.92 * normalized;\n }\n return 1.055 * Math.pow(normalized, 1 / 2.4) - 0.055;\n}\n\nfunction rgbToOklch({ r, g, b }: RgbColor): OklchColor {\n const rNorm = r / 255;\n const gNorm = g / 255;\n const bNorm = b / 255;\n\n const rLin = srgbChannelToLinear(rNorm);\n const gLin = srgbChannelToLinear(gNorm);\n const bLin = srgbChannelToLinear(bNorm);\n\n const l = 0.4122214708 * rLin + 0.5363325363 * gLin + 0.0514459929 * bLin;\n const m = 0.2119034982 * rLin + 0.6806995451 * gLin + 0.1073969566 * bLin;\n const s = 0.0883024619 * rLin + 0.2817188376 * gLin + 0.6299787005 * bLin;\n\n const lRoot = Math.cbrt(l);\n const mRoot = Math.cbrt(m);\n const sRoot = Math.cbrt(s);\n\n const oklabL = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;\n const oklabA = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;\n const oklabB = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;\n\n const chroma = Math.sqrt(oklabA * oklabA + oklabB * oklabB);\n let hue = Math.atan2(oklabB, oklabA) * (180 / Math.PI);\n if (hue < 0) {\n hue += 360;\n }\n\n return { l: oklabL, c: chroma, h: hue };\n}\n\nfunction tryOklchToSrgb(l: number, c: number, h: number, clampLinear = false): RgbColor | null {\n const angle = (h * Math.PI) / 180;\n const a = c * Math.cos(angle);\n const b = c * Math.sin(angle);\n\n const lComponent = l + 0.3963377774 * a + 0.2158037573 * b;\n const mComponent = l - 0.1055613458 * a - 0.0638541728 * b;\n const sComponent = l - 0.0894841775 * a - 1.291485548 * b;\n\n const lCube = clampLinear ? clamp01(lComponent) : lComponent;\n const mCube = clampLinear ? clamp01(mComponent) : mComponent;\n const sCube = clampLinear ? clamp01(sComponent) : sComponent;\n\n if (!clampLinear) {\n const outOfRange =\n lCube < -0.0001 ||\n lCube > 1.0001 ||\n mCube < -0.0001 ||\n mCube > 1.0001 ||\n sCube < -0.0001 ||\n sCube > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const lLin = lCube * lCube * lCube;\n const mLin = mCube * mCube * mCube;\n const sLin = sCube * sCube * sCube;\n\n let rLin = 4.0767416621 * lLin - 3.3077115913 * mLin + 0.2309699292 * sLin;\n let gLin = -1.2684380046 * lLin + 2.6097574011 * mLin - 0.3413193965 * sLin;\n let bLin = -0.0041960863 * lLin - 0.7034186147 * mLin + 1.707614701 * sLin;\n\n if (clampLinear) {\n rLin = clamp01(rLin);\n gLin = clamp01(gLin);\n bLin = clamp01(bLin);\n } else {\n const outOfRange =\n rLin < -0.0001 ||\n rLin > 1.0001 ||\n gLin < -0.0001 ||\n gLin > 1.0001 ||\n bLin < -0.0001 ||\n bLin > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const r = clamp01(linearChannelToSrgb(rLin));\n const g = clamp01(linearChannelToSrgb(gLin));\n const bValue = clamp01(linearChannelToSrgb(bLin));\n\n return {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(bValue * 255),\n };\n}\n\nfunction oklchToSrgb(l: number, c: number, h: number): RgbColor {\n let chroma = c;\n for (let attempt = 0; attempt < 8; attempt += 1) {\n const rgb = tryOklchToSrgb(l, chroma, h);\n if (rgb) {\n return rgb;\n }\n chroma *= 0.9;\n }\n return tryOklchToSrgb(l, clamp01(chroma), h, true) || { r: 0, g: 0, b: 0 };\n}\n\nfunction relativeLuminance(hex: string) {\n const rgb = hexToRgb(hex);\n if (!rgb) {\n return 0;\n }\n const r = srgbChannelToLinear(rgb.r / 255);\n const g = srgbChannelToLinear(rgb.g / 255);\n const b = srgbChannelToLinear(rgb.b / 255);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\nfunction contrastRatio(hexA: string, hexB: string) {\n const lumA = relativeLuminance(hexA);\n const lumB = relativeLuminance(hexB);\n const lighter = Math.max(lumA, lumB);\n const darker = Math.min(lumA, lumB);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction makeReadableTextOnSurface(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ReadableTextOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const preferDark = options.preferDark !== false;\n const textChroma = Math.max(\n 0.01,\n Math.min(0.06, (baseOklch.c || 0.12) * (options.chromaScale ?? 0.25)),\n );\n const initialLightness = options.initialLightness ?? (preferDark ? 0.24 : 0.94);\n const direction = preferDark ? -1 : 1;\n const step = options.step ?? 0.03;\n let lightness = clamp01(initialLightness);\n let bestHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n let bestContrast = contrastRatio(surfaceHex, bestHex);\n\n for (let attempt = 0; attempt < 48; attempt += 1) {\n const textHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n const ratio = contrastRatio(surfaceHex, textHex);\n\n if (ratio > bestContrast) {\n bestContrast = ratio;\n bestHex = textHex;\n }\n if (ratio >= minContrast) {\n return textHex;\n }\n\n lightness = clamp01(lightness + direction * step);\n if (lightness === 0 || lightness === 1) {\n break;\n }\n }\n\n const fallbackDark = \"#101214\";\n const fallbackLight = \"#ffffff\";\n const darkRatio = contrastRatio(surfaceHex, fallbackDark);\n const lightRatio = contrastRatio(surfaceHex, fallbackLight);\n\n if (darkRatio >= minContrast || lightRatio >= minContrast) {\n return darkRatio >= lightRatio ? fallbackDark : fallbackLight;\n }\n\n return bestHex;\n}\n\nfunction pickForegroundColor(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ForegroundOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const darkCandidate = options.darkCandidate ?? \"#101214\";\n const lightCandidate = options.lightCandidate ?? \"#ffffff\";\n const preferredCandidate = options.preferredCandidate;\n const preferredMinContrast = options.preferredMinContrast ?? minContrast;\n const preferredSwitchDelta = options.preferredSwitchDelta ?? 0;\n const darkRatio = contrastRatio(surfaceHex, darkCandidate);\n const lightRatio = contrastRatio(surfaceHex, lightCandidate);\n const darkPasses = darkRatio >= minContrast;\n const lightPasses = lightRatio >= minContrast;\n const preferredLightPasses = lightRatio >= preferredMinContrast;\n const preferredDarkPasses = darkRatio >= preferredMinContrast;\n\n if (darkPasses || lightPasses) {\n if (preferredCandidate === \"light\" && preferredLightPasses) {\n return lightCandidate;\n }\n if (preferredCandidate === \"dark\" && preferredDarkPasses) {\n return darkCandidate;\n }\n\n if (preferredCandidate === \"light\" && lightPasses) {\n if (!darkPasses || darkRatio - lightRatio <= preferredSwitchDelta) {\n return lightCandidate;\n }\n return darkCandidate;\n }\n if (preferredCandidate === \"dark\" && darkPasses) {\n if (!lightPasses || lightRatio - darkRatio <= preferredSwitchDelta) {\n return darkCandidate;\n }\n return lightCandidate;\n }\n\n if (darkPasses && lightPasses) {\n return darkRatio >= lightRatio ? darkCandidate : lightCandidate;\n }\n return darkPasses ? darkCandidate : lightCandidate;\n }\n\n const preferDark = darkRatio >= lightRatio;\n return makeReadableTextOnSurface(baseOklch, surfaceHex, {\n minContrast,\n preferDark,\n initialLightness: preferDark ? 0.2 : 0.96,\n chromaScale: 0.1,\n });\n}\n\nconst BRAND_STOPS = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];\nconst BRAND_LIGHTNESS_CURVE = [0.9, 0.84, 0.78, 0.72, 0.67, 0.62, 0.56, 0.5, 0.44, 0.38];\nconst BRAND_CHROMA_MULTIPLIERS = [0.28, 0.4, 0.54, 0.7, 0.86, 1, 1.08, 1.14, 1.2, 1.24];\nconst BRAND_MIN_SURFACE_CONTRAST = 1.8;\nconst BRAND_MIN_SUBTLE_SURFACE_CONTRAST = 1.15;\nconst BRAND_MIN_SOFT_SURFACE_CONTRAST = 1.08;\nconst BRAND_UI_TEXT_MIN_CONTRAST = 2.5;\nconst BRAND_LIGHT_TEXT_SWITCH_DELTA = 3;\nconst LIGHT_CANVAS_HEX = \"#ffffff\";\nconst DARK_CANVAS_HEX = \"#1f2228\";\n\nfunction pickRampStopForContrast(\n ramp: BrandRamp,\n stopCandidates: number[],\n referenceHex: string,\n minContrast: number,\n) {\n const fallbackStop = stopCandidates[stopCandidates.length - 1];\n for (const stop of stopCandidates) {\n const candidate = ramp[stop];\n if (!candidate) {\n continue;\n }\n if (contrastRatio(candidate, referenceHex) >= minContrast) {\n return stop;\n }\n }\n return fallbackStop;\n}\n\nfunction makeBrandRampFromSeed(baseOklch: OklchColor): BrandRamp {\n const lightnessOffset = baseOklch.l - BRAND_LIGHTNESS_CURVE[5];\n const minSeedChroma = baseOklch.c > 0.02 ? 0.02 : 0;\n\n return BRAND_STOPS.reduce<BrandRamp>((acc, stop, index) => {\n const lightness = clamp01(BRAND_LIGHTNESS_CURVE[index] + lightnessOffset);\n const chroma = Math.min(\n 0.4,\n Math.max(minSeedChroma, baseOklch.c * BRAND_CHROMA_MULTIPLIERS[index]),\n );\n acc[stop] = rgbToHex(oklchToSrgb(lightness, chroma, baseOklch.h));\n return acc;\n }, {});\n}\n\nfunction makeSemanticBrandTokens(baseOklch: OklchColor, ramp: BrandRamp): BrandVars {\n const lightSoftStops = [100, 200, 300, 400, 500, 600, 700];\n const lightSoftStop = pickRampStopForContrast(\n ramp,\n lightSoftStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const lightSoftIndex = lightSoftStops.indexOf(lightSoftStop);\n const lightSoftHoveredStop =\n lightSoftStops[Math.min(lightSoftIndex + 1, lightSoftStops.length - 1)];\n const lightSoftPressedStop =\n lightSoftStops[Math.min(lightSoftIndex + 2, lightSoftStops.length - 1)];\n\n const darkSoftStops = [1000, 900, 800, 700, 600, 500];\n const darkSoftStop = pickRampStopForContrast(\n ramp,\n darkSoftStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const darkSoftIndex = darkSoftStops.indexOf(darkSoftStop);\n const darkSoftHoveredStop =\n darkSoftStops[Math.min(darkSoftIndex + 1, darkSoftStops.length - 1)];\n const darkSoftPressedStop =\n darkSoftStops[Math.min(darkSoftIndex + 2, darkSoftStops.length - 1)];\n\n const lightSubtleStops = [200, 300, 400, 500, 600, 700, 800];\n const lightSubtleStop = pickRampStopForContrast(\n ramp,\n lightSubtleStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n const darkSubtleStops = [900, 800, 700, 600, 500, 400];\n const darkSubtleStop = pickRampStopForContrast(\n ramp,\n darkSubtleStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n\n const lightStrongStops = [600, 700, 800, 900, 1000];\n const lightStrongStop = pickRampStopForContrast(\n ramp,\n lightStrongStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const lightStrongIndex = lightStrongStops.indexOf(lightStrongStop);\n const lightStrongHoveredStop =\n lightStrongStops[Math.min(lightStrongIndex + 1, lightStrongStops.length - 1)];\n const lightStrongPressedStop =\n lightStrongStops[Math.min(lightStrongIndex + 2, lightStrongStops.length - 1)];\n\n const darkStrongStops = [400, 300, 200, 100];\n const darkStrongStop = pickRampStopForContrast(\n ramp,\n darkStrongStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const darkStrongIndex = darkStrongStops.indexOf(darkStrongStop);\n const darkStrongHoveredStop =\n darkStrongStops[Math.min(darkStrongIndex + 1, darkStrongStops.length - 1)];\n const darkStrongPressedStop =\n darkStrongStops[Math.min(darkStrongIndex + 2, darkStrongStops.length - 1)];\n\n const tokens: BrandVars = {\n \"--color-brand-surface-soft-light\": ramp[lightSoftStop],\n \"--color-brand-surface-soft-hovered-light\": ramp[lightSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-light\": ramp[lightSoftPressedStop],\n \"--color-brand-surface-soft-dark\": ramp[darkSoftStop],\n \"--color-brand-surface-soft-hovered-dark\": ramp[darkSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-dark\": ramp[darkSoftPressedStop],\n \"--color-brand-surface-subtle-light\": ramp[lightSubtleStop],\n \"--color-brand-surface-subtle-dark\": ramp[darkSubtleStop],\n \"--color-brand-surface-strong-light\": ramp[lightStrongStop],\n \"--color-brand-surface-strong-hovered-light\": ramp[lightStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-light\": ramp[lightStrongPressedStop],\n \"--color-brand-surface-strong-dark\": ramp[darkStrongStop],\n \"--color-brand-surface-strong-hovered-dark\": ramp[darkStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-dark\": ramp[darkStrongPressedStop],\n };\n\n const preferredSoftLightForeground = ramp[600];\n const softLightContrast = contrastRatio(\n tokens[\"--color-brand-surface-soft-light\"],\n preferredSoftLightForeground,\n );\n tokens[\"--color-brand-on-soft-light\"] =\n softLightContrast >= BRAND_MIN_CONTRAST\n ? preferredSoftLightForeground\n : pickForegroundColor(baseOklch, tokens[\"--color-brand-surface-soft-light\"], {\n minContrast: BRAND_MIN_CONTRAST,\n });\n tokens[\"--color-brand-on-soft-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-soft-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-subtle-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-subtle-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-strong-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-strong-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n\n tokens[\"--color-brand-text-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-text-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-icon-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-icon-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-border-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-border-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n\n return tokens;\n}\n\nfunction makeBrandTokens(baseHex: string | undefined): BrandVars | null {\n const norm = normalizeHex(baseHex);\n if (!norm) {\n return null;\n }\n\n const baseRgb = hexToRgb(norm);\n if (!baseRgb) {\n return null;\n }\n\n const baseOklch = rgbToOklch(baseRgb);\n const ramp = makeBrandRampFromSeed(baseOklch);\n // Keep the incoming seed exact for debugging and transparency.\n ramp[600] = norm;\n\n return {\n \"--color-brand-seed\": norm,\n \"--color-brand-100\": ramp[100],\n \"--color-brand-200\": ramp[200],\n \"--color-brand-300\": ramp[300],\n \"--color-brand-400\": ramp[400],\n \"--color-brand-500\": ramp[500],\n \"--color-brand-600\": ramp[600],\n \"--color-brand-700\": ramp[700],\n \"--color-brand-800\": ramp[800],\n \"--color-brand-900\": ramp[900],\n \"--color-brand-1000\": ramp[1000],\n ...makeSemanticBrandTokens(baseOklch, ramp),\n };\n}\n\nconst brandVars = computed(() => makeBrandTokens(props.accentColor) ?? {});\n\nconst accentStackEntry = {\n vars: {},\n enabled: Boolean(props.exposeGlobalAccent),\n};\nglobalAccentStack.push(accentStackEntry);\n\nwatch(\n () => props.exposeGlobalAccent,\n (enabled) => {\n accentStackEntry.enabled = Boolean(enabled);\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n\nwatch(\n brandVars,\n (vars) => {\n if (vars && Object.keys(vars).length > 0) {\n accentStackEntry.vars = { ...vars };\n } else {\n accentStackEntry.vars = {};\n }\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n</script>\n\n<style lang=\"less\">\n@import \"index.less\";\n</style>\n","<template>\n <DropdownMenu v-model:open=\"open\" text=\"Theme\" aria-label=\"Select theme mode\">\n <DropdownItemGroup title=\"Theme\" item-role=\"menuitemradio\">\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"auto\"\n :label=\"MODE_LABELS.auto\"\n :aria-label=\"`${MODE_LABELS.auto} mode`\"\n />\n <hr class=\"theme-switcher-divider\" />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"light\"\n :label=\"MODE_LABELS.light\"\n :aria-label=\"`${MODE_LABELS.light} mode`\"\n />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"dark\"\n :label=\"MODE_LABELS.dark\"\n :aria-label=\"`${MODE_LABELS.dark} mode`\"\n />\n </DropdownItemGroup>\n </DropdownMenu>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { DropdownMenu, DropdownItemGroup, DropdownItemRadio } from \"@gospore/dropdown-menu\";\nimport {\n readStoredMode,\n writeStoredMode,\n VALID_THEME_MODES,\n applyStoredModeToDocument,\n type ThemeMode,\n} from \"./modeStorage\";\n\nconst MODE_OPTIONS: { value: ThemeMode; label: string }[] = [\n { value: \"auto\", label: \"System\" },\n { value: \"light\", label: \"Light\" },\n { value: \"dark\", label: \"Dark\" },\n];\nconst MODE_LABELS = MODE_OPTIONS.reduce<Record<ThemeMode, string>>(\n (map, option) => {\n map[option.value] = option.label;\n return map;\n },\n {} as Record<ThemeMode, string>,\n);\nconst VALID_MODES = VALID_THEME_MODES;\n\nconst props = defineProps({\n /** Selected theme mode. Supports v-model:mode and persists changes to local storage. */\n mode: {\n type: String as PropType<ThemeMode>,\n default: \"auto\",\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n});\nconst emit = defineEmits([\"update:mode\"]);\n\napplyStoredModeToDocument();\n\nconst open = ref(false);\nconst initialMode = readStoredMode() ?? props.mode;\nconst internalMode = ref<ThemeMode>(VALID_MODES.includes(initialMode) ? initialMode : \"auto\");\nwatch(\n () => props.mode,\n (mode) => {\n if (VALID_MODES.includes(mode) && mode !== internalMode.value) {\n internalMode.value = mode;\n }\n },\n);\n\nwatch(\n internalMode,\n (mode) => {\n if (!VALID_MODES.includes(mode)) {\n internalMode.value = \"auto\";\n return;\n }\n open.value = false;\n emit(\"update:mode\", mode);\n writeStoredMode(mode);\n applyStoredModeToDocument(mode);\n },\n { immediate: true },\n);\n</script>\n\n<style scoped lang=\"less\">\n.theme-switcher-divider {\n margin: 4px 0;\n}\n</style>\n","<template>\n <DropdownMenu v-model:open=\"open\" text=\"Theme\" aria-label=\"Select theme mode\">\n <DropdownItemGroup title=\"Theme\" item-role=\"menuitemradio\">\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"auto\"\n :label=\"MODE_LABELS.auto\"\n :aria-label=\"`${MODE_LABELS.auto} mode`\"\n />\n <hr class=\"theme-switcher-divider\" />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"light\"\n :label=\"MODE_LABELS.light\"\n :aria-label=\"`${MODE_LABELS.light} mode`\"\n />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"dark\"\n :label=\"MODE_LABELS.dark\"\n :aria-label=\"`${MODE_LABELS.dark} mode`\"\n />\n </DropdownItemGroup>\n </DropdownMenu>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { DropdownMenu, DropdownItemGroup, DropdownItemRadio } from \"@gospore/dropdown-menu\";\nimport {\n readStoredMode,\n writeStoredMode,\n VALID_THEME_MODES,\n applyStoredModeToDocument,\n type ThemeMode,\n} from \"./modeStorage\";\n\nconst MODE_OPTIONS: { value: ThemeMode; label: string }[] = [\n { value: \"auto\", label: \"System\" },\n { value: \"light\", label: \"Light\" },\n { value: \"dark\", label: \"Dark\" },\n];\nconst MODE_LABELS = MODE_OPTIONS.reduce<Record<ThemeMode, string>>(\n (map, option) => {\n map[option.value] = option.label;\n return map;\n },\n {} as Record<ThemeMode, string>,\n);\nconst VALID_MODES = VALID_THEME_MODES;\n\nconst props = defineProps({\n /** Selected theme mode. Supports v-model:mode and persists changes to local storage. */\n mode: {\n type: String as PropType<ThemeMode>,\n default: \"auto\",\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n});\nconst emit = defineEmits<{\n \"update:mode\": [value: ThemeMode];\n}>();\n\napplyStoredModeToDocument();\n\nconst open = ref(false);\nconst initialMode = readStoredMode() ?? props.mode;\nconst internalMode = ref<ThemeMode>(VALID_MODES.includes(initialMode) ? initialMode : \"auto\");\nwatch(\n () => props.mode,\n (mode) => {\n if (VALID_MODES.includes(mode) && mode !== internalMode.value) {\n internalMode.value = mode;\n }\n },\n);\n\nwatch(\n internalMode,\n (mode) => {\n if (!VALID_MODES.includes(mode)) {\n internalMode.value = \"auto\";\n return;\n }\n open.value = false;\n emit(\"update:mode\", mode);\n writeStoredMode(mode);\n applyStoredModeToDocument(mode);\n },\n { immediate: true },\n);\n</script>\n\n<style scoped lang=\"less\">\n.theme-switcher-divider {\n margin: 4px 0;\n}\n</style>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"theme.es.js","names":[],"sources":["../src/modeStorage.ts","../src/portalHost.ts","../src/Theme.vue","../src/Theme.vue","../src/ThemeSwitcher.vue","../src/ThemeSwitcher.vue"],"sourcesContent":["const VALID_THEME_MODES = [\"auto\", \"light\", \"dark\"] as const;\nexport type ThemeMode = (typeof VALID_THEME_MODES)[number];\nconst STORAGE_KEY = \"gospore-theme-mode\";\nconst ONE_YEAR_IN_SECONDS = 60 * 60 * 24 * 365;\nconst DARK_MODE_QUERY = \"(prefers-color-scheme: dark)\";\n\nexport function readStoredMode(): ThemeMode | null {\n if (typeof document === \"undefined\") {\n return null;\n }\n const stored = getCookie(STORAGE_KEY);\n return isThemeMode(stored) ? stored : null;\n}\n\nexport function writeStoredMode(mode: ThemeMode) {\n if (typeof document === \"undefined\" || !VALID_THEME_MODES.includes(mode)) {\n return;\n }\n const hostname = typeof window !== \"undefined\" ? window.location.hostname : \"\";\n const protocol = typeof window !== \"undefined\" ? window.location.protocol : \"\";\n const domain = getCookieDomain(hostname);\n const secure = protocol === \"https:\";\n const attributes = [\"path=/\", `max-age=${ONE_YEAR_IN_SECONDS}`, \"SameSite=Lax\"];\n if (domain) {\n attributes.push(`domain=${domain}`);\n }\n if (secure) {\n attributes.push(\"Secure\");\n }\n document.cookie = [\n `${encodeURIComponent(STORAGE_KEY)}=${encodeURIComponent(mode)}`,\n ...attributes,\n ].join(\"; \");\n}\n\nexport function applyStoredModeToDocument(mode?: string | null) {\n if (typeof document === \"undefined\") {\n return;\n }\n const resolvedMode = isThemeMode(mode) ? mode : (readStoredMode() ?? \"auto\");\n console.log(\"Applying theme mode:\", resolvedMode);\n const darkMode = resolveDarkMode(resolvedMode);\n document.documentElement.setAttribute(\"dark-mode\", String(darkMode));\n}\n\nexport { VALID_THEME_MODES, STORAGE_KEY };\n\nfunction getCookie(name: string) {\n if (typeof document === \"undefined\" || !document.cookie) {\n return null;\n }\n const encodedName = `${encodeURIComponent(name)}=`;\n const cookies = document.cookie.split(\"; \");\n for (const cookie of cookies) {\n if (cookie.startsWith(encodedName)) {\n return decodeURIComponent(cookie.slice(encodedName.length));\n }\n }\n return null;\n}\n\nfunction getCookieDomain(hostname: string) {\n if (!hostname || hostname === \"localhost\") {\n return null;\n }\n const isIpAddress = /^\\d+(?:\\.\\d+){3}$/.test(hostname);\n if (isIpAddress) {\n return hostname;\n }\n const parts = hostname.split(\".\");\n if (parts.length <= 1) {\n return null;\n }\n if (parts.length === 2) {\n return hostname;\n }\n return parts.slice(-2).join(\".\");\n}\n\nfunction resolveDarkMode(mode: string) {\n if (mode === \"dark\") {\n return true;\n }\n if (mode === \"light\") {\n return false;\n }\n if (typeof window === \"undefined\") {\n return false;\n }\n try {\n const mediaQueryList =\n typeof window.matchMedia === \"function\" ? window.matchMedia(DARK_MODE_QUERY) : null;\n return mediaQueryList ? mediaQueryList.matches : false;\n } catch {\n return false;\n }\n}\n\nfunction isThemeMode(value: string | null | undefined): value is ThemeMode {\n return typeof value === \"string\" && (VALID_THEME_MODES as readonly string[]).includes(value);\n}\n","import { computed, inject } from \"vue\";\nimport type { InjectionKey, Ref } from \"vue\";\n\nexport type ThemePortalTarget = HTMLElement | string;\n\nexport const themePortalHostKey = Symbol.for(\"gospore.theme.portalHost\") as InjectionKey<\n Ref<HTMLElement | null>\n>;\n\nexport function useThemePortalTarget() {\n const hostRef = inject(themePortalHostKey, null);\n console.log(\"Using theme portal host:\", hostRef);\n const fallbackTarget: ThemePortalTarget =\n typeof document !== \"undefined\" ? document.body : \"body\";\n\n return computed<ThemePortalTarget>(() => hostRef?.value ?? fallbackTarget);\n}\n","<script lang=\"ts\">\ninterface GlobalAccentEntry {\n vars: Record<string, string>;\n enabled: boolean;\n}\n\nconst globalAccentStack: GlobalAccentEntry[] = [];\nconst appliedGlobalAccentKeys = new Set<string>();\n\nconst DARK_MODE_QUERY = \"(prefers-color-scheme: dark)\"\nconst PREFERS_REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\"\nconst THEME_TRANSITION_DURATION = 200\nconst BRAND_MIN_CONTRAST = 3.8\nconst BRAND_MIN_SURFACE_CONTRAST = 1.8\nconst BRAND_MIN_SUBTLE_SURFACE_CONTRAST = 1.15\nconst BRAND_MIN_SOFT_SURFACE_CONTRAST = 1.08\nconst BRAND_UI_TEXT_MIN_CONTRAST = 2.5\nconst BRAND_LIGHT_TEXT_SWITCH_DELTA = 3\nconst LIGHT_CANVAS_HEX = \"#ffffff\"\nconst DARK_CANVAS_HEX = \"#1f2228\"</script>\n\n<template>\n <div class=\"theme\" :style=\"brandVars\">\n <slot />\n <div ref=\"portalHost\" class=\"theme__portal-host\"></div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { provide, ref, watch, onBeforeUnmount, computed } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { applyStoredModeToDocument, readStoredMode, type ThemeMode } from \"./modeStorage\";\nimport \"what-input\";\nimport { themePortalHostKey } from \"./portalHost\";\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\ninterface OklchColor {\n l: number;\n c: number;\n h: number;\n}\n\ntype BrandRamp = Record<number, string>;\ntype BrandVars = Record<string, string>;\n\ninterface ReadableTextOptions {\n minContrast?: number;\n preferDark?: boolean;\n chromaScale?: number;\n initialLightness?: number;\n step?: number;\n}\n\ninterface ForegroundOptions extends ReadableTextOptions {\n darkCandidate?: string;\n lightCandidate?: string;\n preferredCandidate?: \"dark\" | \"light\";\n preferredMinContrast?: number;\n preferredSwitchDelta?: number;\n}\n\ndefineOptions({\n name: \"Theme\",\n});\ndefineSlots<{\n /** Application content that receives theme tokens and portal context. */\n default?: () => unknown;\n}>();\nconst props = defineProps({\n /**\n * Controls which color mode is active. auto follows the OS preference (and persisted storage\n * when used with ThemeSwitcher).\n */\n mode: {\n type: String as PropType<ThemeMode | undefined>,\n default: undefined,\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n /**\n * Optional hex accent color (\"#RRGGBB\" or \"#RGB\"). When provided, Theme derives contrast-safe\n * semantic brand tokens and exposes them on the root .theme element (and the global document\n * for portals).\n */\n accentColor: {\n type: String,\n required: false,\n default: undefined,\n },\n /**\n * When true the brand vars are also exposed on <html>, allowing portals attached outside the\n * Theme subtree to inherit the accent colors.\n */\n exposeGlobalAccent: {\n type: Boolean,\n default: false,\n },\n});\n\nfunction applyGlobalAccentFromStack() {\n const root = document.documentElement;\n if (!root) {\n return;\n }\n\n appliedGlobalAccentKeys.forEach((key) => {\n root.style.removeProperty(key);\n });\n appliedGlobalAccentKeys.clear();\n\n const activeEntry = [...globalAccentStack]\n .reverse()\n .find((entry) => entry.enabled && entry.vars && Object.keys(entry.vars).length > 0);\n\n if (!activeEntry) {\n return;\n }\n\n Object.entries(activeEntry.vars).forEach(([key, value]) => {\n root.style.setProperty(key, value);\n appliedGlobalAccentKeys.add(key);\n });\n}\n\n\n\n\nconst darkModeMedia = window.matchMedia(DARK_MODE_QUERY);\nconst prefersReducedMotionMedia = window.matchMedia(PREFERS_REDUCED_MOTION_QUERY);\n\nconst initialMode = getEffectiveMode();\napplyStoredModeToDocument(initialMode);\n\nconst currentMode = ref(initialMode);\n\nconst theme = ref({\n darkMode: resolveDarkMode(initialMode),\n});\nconst portalHost = ref<HTMLElement | null>(null);\n\ndarkModeMedia.addEventListener(\"change\", handleSystemPreferenceChange);\n\nwatch(\n () => props.mode,\n () => {\n syncDarkMode();\n },\n { immediate: true },\n);\n\nwatch(\n () => theme.value.darkMode,\n (darkMode, previousValue) => {\n if (previousValue === undefined) {\n setDocumentDarkModeAttribute(darkMode);\n return;\n }\n withThemeTransition(() => {\n setDocumentDarkModeAttribute(darkMode);\n });\n },\n { immediate: true },\n);\n\nonBeforeUnmount(() => {\n darkModeMedia.removeEventListener(\"change\", handleSystemPreferenceChange);\n const index = globalAccentStack.indexOf(accentStackEntry);\n if (index !== -1) {\n globalAccentStack.splice(index, 1);\n }\n applyGlobalAccentFromStack();\n});\n\nfunction syncDarkMode() {\n const mode = getEffectiveMode();\n currentMode.value = mode;\n theme.value.darkMode = resolveDarkMode(mode);\n}\n\nfunction resolveDarkMode(mode: ThemeMode) {\n if (mode === \"dark\") {\n return true;\n }\n if (mode === \"light\") {\n return false;\n }\n return darkModeMedia.matches;\n}\n\nfunction handleSystemPreferenceChange(event: MediaQueryListEvent) {\n if (!event || currentMode.value !== \"auto\") {\n return;\n }\n theme.value.darkMode = event.matches;\n}\n\nfunction setDocumentDarkModeAttribute(darkMode: boolean) {\n document.documentElement.setAttribute(\"dark-mode\", String(darkMode));\n}\n\nfunction withThemeTransition(applyTheme: () => void) {\n if (prefersReducedMotionMedia.matches) {\n applyTheme();\n return;\n }\n const startViewTransition = document.startViewTransition?.bind(document);\n if (startViewTransition) {\n startViewTransition(() => {\n applyTheme();\n });\n return;\n }\n applyTheme();\n const element = document.documentElement;\n if (typeof element.animate !== \"function\") {\n return;\n }\n window.requestAnimationFrame(() => {\n element.animate([{ opacity: 0.85 }, { opacity: 1 }], {\n duration: THEME_TRANSITION_DURATION,\n easing: \"ease-in-out\",\n });\n });\n}\n\nfunction getEffectiveMode(): ThemeMode {\n return props.mode ?? readStoredMode() ?? \"auto\";\n}\n\nprovide(\"theme\", theme);\nprovide(themePortalHostKey, portalHost);\n\n// --- Accent helpers -------------------------------------------------------\n\n\nfunction normalizeHex(hex: string | undefined) {\n if (!hex) return null;\n const h = hex.trim().toLowerCase();\n const m = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.exec(h);\n if (!m) return null;\n if (h.length === 4) {\n // expand #rgb -> #rrggbb\n const r = h[1],\n g = h[2],\n b = h[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return h;\n}\n\nfunction hexToRgb(hex: string | undefined): RgbColor | null {\n const h = normalizeHex(hex);\n if (!h) return null;\n const r = parseInt(h.slice(1, 3), 16);\n const g = parseInt(h.slice(3, 5), 16);\n const b = parseInt(h.slice(5, 7), 16);\n return { r, g, b };\n}\n\nfunction rgbToHex({ r, g, b }: RgbColor) {\n const toHex = (value: number) => value.toString(16).padStart(2, \"0\");\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction srgbChannelToLinear(channel: number) {\n if (channel <= 0.04045) {\n return channel / 12.92;\n }\n return Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearChannelToSrgb(channel: number) {\n const normalized = clamp01(channel);\n if (normalized <= 0.0031308) {\n return 12.92 * normalized;\n }\n return 1.055 * Math.pow(normalized, 1 / 2.4) - 0.055;\n}\n\nfunction rgbToOklch({ r, g, b }: RgbColor): OklchColor {\n const rNorm = r / 255;\n const gNorm = g / 255;\n const bNorm = b / 255;\n\n const rLin = srgbChannelToLinear(rNorm);\n const gLin = srgbChannelToLinear(gNorm);\n const bLin = srgbChannelToLinear(bNorm);\n\n const l = 0.4122214708 * rLin + 0.5363325363 * gLin + 0.0514459929 * bLin;\n const m = 0.2119034982 * rLin + 0.6806995451 * gLin + 0.1073969566 * bLin;\n const s = 0.0883024619 * rLin + 0.2817188376 * gLin + 0.6299787005 * bLin;\n\n const lRoot = Math.cbrt(l);\n const mRoot = Math.cbrt(m);\n const sRoot = Math.cbrt(s);\n\n const oklabL = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;\n const oklabA = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;\n const oklabB = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;\n\n const chroma = Math.sqrt(oklabA * oklabA + oklabB * oklabB);\n let hue = Math.atan2(oklabB, oklabA) * (180 / Math.PI);\n if (hue < 0) {\n hue += 360;\n }\n\n return { l: oklabL, c: chroma, h: hue };\n}\n\nfunction tryOklchToSrgb(l: number, c: number, h: number, clampLinear = false): RgbColor | null {\n const angle = (h * Math.PI) / 180;\n const a = c * Math.cos(angle);\n const b = c * Math.sin(angle);\n\n const lComponent = l + 0.3963377774 * a + 0.2158037573 * b;\n const mComponent = l - 0.1055613458 * a - 0.0638541728 * b;\n const sComponent = l - 0.0894841775 * a - 1.291485548 * b;\n\n const lCube = clampLinear ? clamp01(lComponent) : lComponent;\n const mCube = clampLinear ? clamp01(mComponent) : mComponent;\n const sCube = clampLinear ? clamp01(sComponent) : sComponent;\n\n if (!clampLinear) {\n const outOfRange =\n lCube < -0.0001 ||\n lCube > 1.0001 ||\n mCube < -0.0001 ||\n mCube > 1.0001 ||\n sCube < -0.0001 ||\n sCube > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const lLin = lCube * lCube * lCube;\n const mLin = mCube * mCube * mCube;\n const sLin = sCube * sCube * sCube;\n\n let rLin = 4.0767416621 * lLin - 3.3077115913 * mLin + 0.2309699292 * sLin;\n let gLin = -1.2684380046 * lLin + 2.6097574011 * mLin - 0.3413193965 * sLin;\n let bLin = -0.0041960863 * lLin - 0.7034186147 * mLin + 1.707614701 * sLin;\n\n if (clampLinear) {\n rLin = clamp01(rLin);\n gLin = clamp01(gLin);\n bLin = clamp01(bLin);\n } else {\n const outOfRange =\n rLin < -0.0001 ||\n rLin > 1.0001 ||\n gLin < -0.0001 ||\n gLin > 1.0001 ||\n bLin < -0.0001 ||\n bLin > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const r = clamp01(linearChannelToSrgb(rLin));\n const g = clamp01(linearChannelToSrgb(gLin));\n const bValue = clamp01(linearChannelToSrgb(bLin));\n\n return {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(bValue * 255),\n };\n}\n\nfunction oklchToSrgb(l: number, c: number, h: number): RgbColor {\n let chroma = c;\n for (let attempt = 0; attempt < 8; attempt += 1) {\n const rgb = tryOklchToSrgb(l, chroma, h);\n if (rgb) {\n return rgb;\n }\n chroma *= 0.9;\n }\n return tryOklchToSrgb(l, clamp01(chroma), h, true) || { r: 0, g: 0, b: 0 };\n}\n\nfunction relativeLuminance(hex: string) {\n const rgb = hexToRgb(hex);\n if (!rgb) {\n return 0;\n }\n const r = srgbChannelToLinear(rgb.r / 255);\n const g = srgbChannelToLinear(rgb.g / 255);\n const b = srgbChannelToLinear(rgb.b / 255);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\nfunction contrastRatio(hexA: string, hexB: string) {\n const lumA = relativeLuminance(hexA);\n const lumB = relativeLuminance(hexB);\n const lighter = Math.max(lumA, lumB);\n const darker = Math.min(lumA, lumB);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction makeReadableTextOnSurface(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ReadableTextOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const preferDark = options.preferDark !== false;\n const textChroma = Math.max(\n 0.01,\n Math.min(0.06, (baseOklch.c || 0.12) * (options.chromaScale ?? 0.25)),\n );\n const initialLightness = options.initialLightness ?? (preferDark ? 0.24 : 0.94);\n const direction = preferDark ? -1 : 1;\n const step = options.step ?? 0.03;\n let lightness = clamp01(initialLightness);\n let bestHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n let bestContrast = contrastRatio(surfaceHex, bestHex);\n\n for (let attempt = 0; attempt < 48; attempt += 1) {\n const textHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n const ratio = contrastRatio(surfaceHex, textHex);\n\n if (ratio > bestContrast) {\n bestContrast = ratio;\n bestHex = textHex;\n }\n if (ratio >= minContrast) {\n return textHex;\n }\n\n lightness = clamp01(lightness + direction * step);\n if (lightness === 0 || lightness === 1) {\n break;\n }\n }\n\n const fallbackDark = \"#101214\";\n const fallbackLight = \"#ffffff\";\n const darkRatio = contrastRatio(surfaceHex, fallbackDark);\n const lightRatio = contrastRatio(surfaceHex, fallbackLight);\n\n if (darkRatio >= minContrast || lightRatio >= minContrast) {\n return darkRatio >= lightRatio ? fallbackDark : fallbackLight;\n }\n\n return bestHex;\n}\n\nfunction pickForegroundColor(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ForegroundOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const darkCandidate = options.darkCandidate ?? \"#101214\";\n const lightCandidate = options.lightCandidate ?? \"#ffffff\";\n const preferredCandidate = options.preferredCandidate;\n const preferredMinContrast = options.preferredMinContrast ?? minContrast;\n const preferredSwitchDelta = options.preferredSwitchDelta ?? 0;\n const darkRatio = contrastRatio(surfaceHex, darkCandidate);\n const lightRatio = contrastRatio(surfaceHex, lightCandidate);\n const darkPasses = darkRatio >= minContrast;\n const lightPasses = lightRatio >= minContrast;\n const preferredLightPasses = lightRatio >= preferredMinContrast;\n const preferredDarkPasses = darkRatio >= preferredMinContrast;\n\n if (darkPasses || lightPasses) {\n if (preferredCandidate === \"light\" && preferredLightPasses) {\n return lightCandidate;\n }\n if (preferredCandidate === \"dark\" && preferredDarkPasses) {\n return darkCandidate;\n }\n\n if (preferredCandidate === \"light\" && lightPasses) {\n if (!darkPasses || darkRatio - lightRatio <= preferredSwitchDelta) {\n return lightCandidate;\n }\n return darkCandidate;\n }\n if (preferredCandidate === \"dark\" && darkPasses) {\n if (!lightPasses || lightRatio - darkRatio <= preferredSwitchDelta) {\n return darkCandidate;\n }\n return lightCandidate;\n }\n\n if (darkPasses && lightPasses) {\n return darkRatio >= lightRatio ? darkCandidate : lightCandidate;\n }\n return darkPasses ? darkCandidate : lightCandidate;\n }\n\n const preferDark = darkRatio >= lightRatio;\n return makeReadableTextOnSurface(baseOklch, surfaceHex, {\n minContrast,\n preferDark,\n initialLightness: preferDark ? 0.2 : 0.96,\n chromaScale: 0.1,\n });\n}\n\nconst BRAND_STOPS = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];\nconst BRAND_LIGHTNESS_CURVE = [0.9, 0.84, 0.78, 0.72, 0.67, 0.62, 0.56, 0.5, 0.44, 0.38];\nconst BRAND_CHROMA_MULTIPLIERS = [0.28, 0.4, 0.54, 0.7, 0.86, 1, 1.08, 1.14, 1.2, 1.24];\n\n\n\n\n\n\n\n\nfunction pickRampStopForContrast(\n ramp: BrandRamp,\n stopCandidates: number[],\n referenceHex: string,\n minContrast: number,\n) {\n const fallbackStop = stopCandidates[stopCandidates.length - 1];\n for (const stop of stopCandidates) {\n const candidate = ramp[stop];\n if (!candidate) {\n continue;\n }\n if (contrastRatio(candidate, referenceHex) >= minContrast) {\n return stop;\n }\n }\n return fallbackStop;\n}\n\nfunction makeBrandRampFromSeed(baseOklch: OklchColor): BrandRamp {\n const lightnessOffset = baseOklch.l - BRAND_LIGHTNESS_CURVE[5];\n const minSeedChroma = baseOklch.c > 0.02 ? 0.02 : 0;\n\n return BRAND_STOPS.reduce<BrandRamp>((acc, stop, index) => {\n const lightness = clamp01(BRAND_LIGHTNESS_CURVE[index] + lightnessOffset);\n const chroma = Math.min(\n 0.4,\n Math.max(minSeedChroma, baseOklch.c * BRAND_CHROMA_MULTIPLIERS[index]),\n );\n acc[stop] = rgbToHex(oklchToSrgb(lightness, chroma, baseOklch.h));\n return acc;\n }, {});\n}\n\nfunction makeSemanticBrandTokens(baseOklch: OklchColor, ramp: BrandRamp): BrandVars {\n const lightSoftStops = [100, 200, 300, 400, 500, 600, 700];\n const lightSoftStop = pickRampStopForContrast(\n ramp,\n lightSoftStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const lightSoftIndex = lightSoftStops.indexOf(lightSoftStop);\n const lightSoftHoveredStop =\n lightSoftStops[Math.min(lightSoftIndex + 1, lightSoftStops.length - 1)];\n const lightSoftPressedStop =\n lightSoftStops[Math.min(lightSoftIndex + 2, lightSoftStops.length - 1)];\n\n const darkSoftStops = [1000, 900, 800, 700, 600, 500];\n const darkSoftStop = pickRampStopForContrast(\n ramp,\n darkSoftStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const darkSoftIndex = darkSoftStops.indexOf(darkSoftStop);\n const darkSoftHoveredStop =\n darkSoftStops[Math.min(darkSoftIndex + 1, darkSoftStops.length - 1)];\n const darkSoftPressedStop =\n darkSoftStops[Math.min(darkSoftIndex + 2, darkSoftStops.length - 1)];\n\n const lightSubtleStops = [200, 300, 400, 500, 600, 700, 800];\n const lightSubtleStop = pickRampStopForContrast(\n ramp,\n lightSubtleStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n const darkSubtleStops = [900, 800, 700, 600, 500, 400];\n const darkSubtleStop = pickRampStopForContrast(\n ramp,\n darkSubtleStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n\n const lightStrongStops = [600, 700, 800, 900, 1000];\n const lightStrongStop = pickRampStopForContrast(\n ramp,\n lightStrongStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const lightStrongIndex = lightStrongStops.indexOf(lightStrongStop);\n const lightStrongHoveredStop =\n lightStrongStops[Math.min(lightStrongIndex + 1, lightStrongStops.length - 1)];\n const lightStrongPressedStop =\n lightStrongStops[Math.min(lightStrongIndex + 2, lightStrongStops.length - 1)];\n\n const darkStrongStops = [400, 300, 200, 100];\n const darkStrongStop = pickRampStopForContrast(\n ramp,\n darkStrongStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const darkStrongIndex = darkStrongStops.indexOf(darkStrongStop);\n const darkStrongHoveredStop =\n darkStrongStops[Math.min(darkStrongIndex + 1, darkStrongStops.length - 1)];\n const darkStrongPressedStop =\n darkStrongStops[Math.min(darkStrongIndex + 2, darkStrongStops.length - 1)];\n\n const tokens: BrandVars = {\n \"--color-brand-surface-soft-light\": ramp[lightSoftStop],\n \"--color-brand-surface-soft-hovered-light\": ramp[lightSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-light\": ramp[lightSoftPressedStop],\n \"--color-brand-surface-soft-dark\": ramp[darkSoftStop],\n \"--color-brand-surface-soft-hovered-dark\": ramp[darkSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-dark\": ramp[darkSoftPressedStop],\n \"--color-brand-surface-subtle-light\": ramp[lightSubtleStop],\n \"--color-brand-surface-subtle-dark\": ramp[darkSubtleStop],\n \"--color-brand-surface-strong-light\": ramp[lightStrongStop],\n \"--color-brand-surface-strong-hovered-light\": ramp[lightStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-light\": ramp[lightStrongPressedStop],\n \"--color-brand-surface-strong-dark\": ramp[darkStrongStop],\n \"--color-brand-surface-strong-hovered-dark\": ramp[darkStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-dark\": ramp[darkStrongPressedStop],\n };\n\n const preferredSoftLightForeground = ramp[600];\n const softLightContrast = contrastRatio(\n tokens[\"--color-brand-surface-soft-light\"],\n preferredSoftLightForeground,\n );\n tokens[\"--color-brand-on-soft-light\"] =\n softLightContrast >= BRAND_MIN_CONTRAST\n ? preferredSoftLightForeground\n : pickForegroundColor(baseOklch, tokens[\"--color-brand-surface-soft-light\"], {\n minContrast: BRAND_MIN_CONTRAST,\n });\n tokens[\"--color-brand-on-soft-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-soft-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-subtle-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-subtle-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-strong-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-strong-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n\n tokens[\"--color-brand-text-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-text-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-icon-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-icon-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-border-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-border-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n\n return tokens;\n}\n\nfunction makeBrandTokens(baseHex: string | undefined): BrandVars | null {\n const norm = normalizeHex(baseHex);\n if (!norm) {\n return null;\n }\n\n const baseRgb = hexToRgb(norm);\n if (!baseRgb) {\n return null;\n }\n\n const baseOklch = rgbToOklch(baseRgb);\n const ramp = makeBrandRampFromSeed(baseOklch);\n // Keep the incoming seed exact for debugging and transparency.\n ramp[600] = norm;\n\n return {\n \"--color-brand-seed\": norm,\n \"--color-brand-100\": ramp[100],\n \"--color-brand-200\": ramp[200],\n \"--color-brand-300\": ramp[300],\n \"--color-brand-400\": ramp[400],\n \"--color-brand-500\": ramp[500],\n \"--color-brand-600\": ramp[600],\n \"--color-brand-700\": ramp[700],\n \"--color-brand-800\": ramp[800],\n \"--color-brand-900\": ramp[900],\n \"--color-brand-1000\": ramp[1000],\n ...makeSemanticBrandTokens(baseOklch, ramp),\n };\n}\n\nconst brandVars = computed(() => makeBrandTokens(props.accentColor) ?? {});\n\nconst accentStackEntry = {\n vars: {},\n enabled: Boolean(props.exposeGlobalAccent),\n};\nglobalAccentStack.push(accentStackEntry);\n\nwatch(\n () => props.exposeGlobalAccent,\n (enabled) => {\n accentStackEntry.enabled = Boolean(enabled);\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n\nwatch(\n brandVars,\n (vars) => {\n if (vars && Object.keys(vars).length > 0) {\n accentStackEntry.vars = { ...vars };\n } else {\n accentStackEntry.vars = {};\n }\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n</script>\n\n<style lang=\"less\">\n@import \"index.less\";\n</style>\n","<script lang=\"ts\">\ninterface GlobalAccentEntry {\n vars: Record<string, string>;\n enabled: boolean;\n}\n\nconst globalAccentStack: GlobalAccentEntry[] = [];\nconst appliedGlobalAccentKeys = new Set<string>();\n</script>\n\n<template>\n <div class=\"theme\" :style=\"brandVars\">\n <slot />\n <div ref=\"portalHost\" class=\"theme__portal-host\"></div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { provide, ref, watch, onBeforeUnmount, computed } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { applyStoredModeToDocument, readStoredMode, type ThemeMode } from \"./modeStorage\";\nimport \"what-input\";\nimport { themePortalHostKey } from \"./portalHost\";\n\ninterface RgbColor {\n r: number;\n g: number;\n b: number;\n}\n\ninterface OklchColor {\n l: number;\n c: number;\n h: number;\n}\n\ntype BrandRamp = Record<number, string>;\ntype BrandVars = Record<string, string>;\n\ninterface ReadableTextOptions {\n minContrast?: number;\n preferDark?: boolean;\n chromaScale?: number;\n initialLightness?: number;\n step?: number;\n}\n\ninterface ForegroundOptions extends ReadableTextOptions {\n darkCandidate?: string;\n lightCandidate?: string;\n preferredCandidate?: \"dark\" | \"light\";\n preferredMinContrast?: number;\n preferredSwitchDelta?: number;\n}\n\ndefineOptions({\n name: \"Theme\",\n});\ndefineSlots<{\n /** Application content that receives theme tokens and portal context. */\n default?: () => unknown;\n}>();\nconst props = defineProps({\n /**\n * Controls which color mode is active. auto follows the OS preference (and persisted storage\n * when used with ThemeSwitcher).\n */\n mode: {\n type: String as PropType<ThemeMode | undefined>,\n default: undefined,\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n /**\n * Optional hex accent color (\"#RRGGBB\" or \"#RGB\"). When provided, Theme derives contrast-safe\n * semantic brand tokens and exposes them on the root .theme element (and the global document\n * for portals).\n */\n accentColor: {\n type: String,\n required: false,\n default: undefined,\n },\n /**\n * When true the brand vars are also exposed on <html>, allowing portals attached outside the\n * Theme subtree to inherit the accent colors.\n */\n exposeGlobalAccent: {\n type: Boolean,\n default: false,\n },\n});\n\nfunction applyGlobalAccentFromStack() {\n const root = document.documentElement;\n if (!root) {\n return;\n }\n\n appliedGlobalAccentKeys.forEach((key) => {\n root.style.removeProperty(key);\n });\n appliedGlobalAccentKeys.clear();\n\n const activeEntry = [...globalAccentStack]\n .reverse()\n .find((entry) => entry.enabled && entry.vars && Object.keys(entry.vars).length > 0);\n\n if (!activeEntry) {\n return;\n }\n\n Object.entries(activeEntry.vars).forEach(([key, value]) => {\n root.style.setProperty(key, value);\n appliedGlobalAccentKeys.add(key);\n });\n}\n\nconst DARK_MODE_QUERY = \"(prefers-color-scheme: dark)\";\nconst PREFERS_REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\";\nconst THEME_TRANSITION_DURATION = 200;\nconst darkModeMedia = window.matchMedia(DARK_MODE_QUERY);\nconst prefersReducedMotionMedia = window.matchMedia(PREFERS_REDUCED_MOTION_QUERY);\n\nconst initialMode = getEffectiveMode();\napplyStoredModeToDocument(initialMode);\n\nconst currentMode = ref(initialMode);\n\nconst theme = ref({\n darkMode: resolveDarkMode(initialMode),\n});\nconst portalHost = ref<HTMLElement | null>(null);\n\ndarkModeMedia.addEventListener(\"change\", handleSystemPreferenceChange);\n\nwatch(\n () => props.mode,\n () => {\n syncDarkMode();\n },\n { immediate: true },\n);\n\nwatch(\n () => theme.value.darkMode,\n (darkMode, previousValue) => {\n if (previousValue === undefined) {\n setDocumentDarkModeAttribute(darkMode);\n return;\n }\n withThemeTransition(() => {\n setDocumentDarkModeAttribute(darkMode);\n });\n },\n { immediate: true },\n);\n\nonBeforeUnmount(() => {\n darkModeMedia.removeEventListener(\"change\", handleSystemPreferenceChange);\n const index = globalAccentStack.indexOf(accentStackEntry);\n if (index !== -1) {\n globalAccentStack.splice(index, 1);\n }\n applyGlobalAccentFromStack();\n});\n\nfunction syncDarkMode() {\n const mode = getEffectiveMode();\n currentMode.value = mode;\n theme.value.darkMode = resolveDarkMode(mode);\n}\n\nfunction resolveDarkMode(mode: ThemeMode) {\n if (mode === \"dark\") {\n return true;\n }\n if (mode === \"light\") {\n return false;\n }\n return darkModeMedia.matches;\n}\n\nfunction handleSystemPreferenceChange(event: MediaQueryListEvent) {\n if (!event || currentMode.value !== \"auto\") {\n return;\n }\n theme.value.darkMode = event.matches;\n}\n\nfunction setDocumentDarkModeAttribute(darkMode: boolean) {\n document.documentElement.setAttribute(\"dark-mode\", String(darkMode));\n}\n\nfunction withThemeTransition(applyTheme: () => void) {\n if (prefersReducedMotionMedia.matches) {\n applyTheme();\n return;\n }\n const startViewTransition = document.startViewTransition?.bind(document);\n if (startViewTransition) {\n startViewTransition(() => {\n applyTheme();\n });\n return;\n }\n applyTheme();\n const element = document.documentElement;\n if (typeof element.animate !== \"function\") {\n return;\n }\n window.requestAnimationFrame(() => {\n element.animate([{ opacity: 0.85 }, { opacity: 1 }], {\n duration: THEME_TRANSITION_DURATION,\n easing: \"ease-in-out\",\n });\n });\n}\n\nfunction getEffectiveMode(): ThemeMode {\n return props.mode ?? readStoredMode() ?? \"auto\";\n}\n\nprovide(\"theme\", theme);\nprovide(themePortalHostKey, portalHost);\n\n// --- Accent helpers -------------------------------------------------------\nconst BRAND_MIN_CONTRAST = 3.8;\n\nfunction normalizeHex(hex: string | undefined) {\n if (!hex) return null;\n const h = hex.trim().toLowerCase();\n const m = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.exec(h);\n if (!m) return null;\n if (h.length === 4) {\n // expand #rgb -> #rrggbb\n const r = h[1],\n g = h[2],\n b = h[3];\n return `#${r}${r}${g}${g}${b}${b}`;\n }\n return h;\n}\n\nfunction hexToRgb(hex: string | undefined): RgbColor | null {\n const h = normalizeHex(hex);\n if (!h) return null;\n const r = parseInt(h.slice(1, 3), 16);\n const g = parseInt(h.slice(3, 5), 16);\n const b = parseInt(h.slice(5, 7), 16);\n return { r, g, b };\n}\n\nfunction rgbToHex({ r, g, b }: RgbColor) {\n const toHex = (value: number) => value.toString(16).padStart(2, \"0\");\n return `#${toHex(r)}${toHex(g)}${toHex(b)}`;\n}\n\nfunction clamp01(value: number) {\n return Math.max(0, Math.min(1, value));\n}\n\nfunction srgbChannelToLinear(channel: number) {\n if (channel <= 0.04045) {\n return channel / 12.92;\n }\n return Math.pow((channel + 0.055) / 1.055, 2.4);\n}\n\nfunction linearChannelToSrgb(channel: number) {\n const normalized = clamp01(channel);\n if (normalized <= 0.0031308) {\n return 12.92 * normalized;\n }\n return 1.055 * Math.pow(normalized, 1 / 2.4) - 0.055;\n}\n\nfunction rgbToOklch({ r, g, b }: RgbColor): OklchColor {\n const rNorm = r / 255;\n const gNorm = g / 255;\n const bNorm = b / 255;\n\n const rLin = srgbChannelToLinear(rNorm);\n const gLin = srgbChannelToLinear(gNorm);\n const bLin = srgbChannelToLinear(bNorm);\n\n const l = 0.4122214708 * rLin + 0.5363325363 * gLin + 0.0514459929 * bLin;\n const m = 0.2119034982 * rLin + 0.6806995451 * gLin + 0.1073969566 * bLin;\n const s = 0.0883024619 * rLin + 0.2817188376 * gLin + 0.6299787005 * bLin;\n\n const lRoot = Math.cbrt(l);\n const mRoot = Math.cbrt(m);\n const sRoot = Math.cbrt(s);\n\n const oklabL = 0.2104542553 * lRoot + 0.793617785 * mRoot - 0.0040720468 * sRoot;\n const oklabA = 1.9779984951 * lRoot - 2.428592205 * mRoot + 0.4505937099 * sRoot;\n const oklabB = 0.0259040371 * lRoot + 0.7827717662 * mRoot - 0.808675766 * sRoot;\n\n const chroma = Math.sqrt(oklabA * oklabA + oklabB * oklabB);\n let hue = Math.atan2(oklabB, oklabA) * (180 / Math.PI);\n if (hue < 0) {\n hue += 360;\n }\n\n return { l: oklabL, c: chroma, h: hue };\n}\n\nfunction tryOklchToSrgb(l: number, c: number, h: number, clampLinear = false): RgbColor | null {\n const angle = (h * Math.PI) / 180;\n const a = c * Math.cos(angle);\n const b = c * Math.sin(angle);\n\n const lComponent = l + 0.3963377774 * a + 0.2158037573 * b;\n const mComponent = l - 0.1055613458 * a - 0.0638541728 * b;\n const sComponent = l - 0.0894841775 * a - 1.291485548 * b;\n\n const lCube = clampLinear ? clamp01(lComponent) : lComponent;\n const mCube = clampLinear ? clamp01(mComponent) : mComponent;\n const sCube = clampLinear ? clamp01(sComponent) : sComponent;\n\n if (!clampLinear) {\n const outOfRange =\n lCube < -0.0001 ||\n lCube > 1.0001 ||\n mCube < -0.0001 ||\n mCube > 1.0001 ||\n sCube < -0.0001 ||\n sCube > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const lLin = lCube * lCube * lCube;\n const mLin = mCube * mCube * mCube;\n const sLin = sCube * sCube * sCube;\n\n let rLin = 4.0767416621 * lLin - 3.3077115913 * mLin + 0.2309699292 * sLin;\n let gLin = -1.2684380046 * lLin + 2.6097574011 * mLin - 0.3413193965 * sLin;\n let bLin = -0.0041960863 * lLin - 0.7034186147 * mLin + 1.707614701 * sLin;\n\n if (clampLinear) {\n rLin = clamp01(rLin);\n gLin = clamp01(gLin);\n bLin = clamp01(bLin);\n } else {\n const outOfRange =\n rLin < -0.0001 ||\n rLin > 1.0001 ||\n gLin < -0.0001 ||\n gLin > 1.0001 ||\n bLin < -0.0001 ||\n bLin > 1.0001;\n if (outOfRange) {\n return null;\n }\n }\n\n const r = clamp01(linearChannelToSrgb(rLin));\n const g = clamp01(linearChannelToSrgb(gLin));\n const bValue = clamp01(linearChannelToSrgb(bLin));\n\n return {\n r: Math.round(r * 255),\n g: Math.round(g * 255),\n b: Math.round(bValue * 255),\n };\n}\n\nfunction oklchToSrgb(l: number, c: number, h: number): RgbColor {\n let chroma = c;\n for (let attempt = 0; attempt < 8; attempt += 1) {\n const rgb = tryOklchToSrgb(l, chroma, h);\n if (rgb) {\n return rgb;\n }\n chroma *= 0.9;\n }\n return tryOklchToSrgb(l, clamp01(chroma), h, true) || { r: 0, g: 0, b: 0 };\n}\n\nfunction relativeLuminance(hex: string) {\n const rgb = hexToRgb(hex);\n if (!rgb) {\n return 0;\n }\n const r = srgbChannelToLinear(rgb.r / 255);\n const g = srgbChannelToLinear(rgb.g / 255);\n const b = srgbChannelToLinear(rgb.b / 255);\n return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n}\n\nfunction contrastRatio(hexA: string, hexB: string) {\n const lumA = relativeLuminance(hexA);\n const lumB = relativeLuminance(hexB);\n const lighter = Math.max(lumA, lumB);\n const darker = Math.min(lumA, lumB);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\nfunction makeReadableTextOnSurface(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ReadableTextOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const preferDark = options.preferDark !== false;\n const textChroma = Math.max(\n 0.01,\n Math.min(0.06, (baseOklch.c || 0.12) * (options.chromaScale ?? 0.25)),\n );\n const initialLightness = options.initialLightness ?? (preferDark ? 0.24 : 0.94);\n const direction = preferDark ? -1 : 1;\n const step = options.step ?? 0.03;\n let lightness = clamp01(initialLightness);\n let bestHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n let bestContrast = contrastRatio(surfaceHex, bestHex);\n\n for (let attempt = 0; attempt < 48; attempt += 1) {\n const textHex = rgbToHex(oklchToSrgb(lightness, textChroma, baseOklch.h));\n const ratio = contrastRatio(surfaceHex, textHex);\n\n if (ratio > bestContrast) {\n bestContrast = ratio;\n bestHex = textHex;\n }\n if (ratio >= minContrast) {\n return textHex;\n }\n\n lightness = clamp01(lightness + direction * step);\n if (lightness === 0 || lightness === 1) {\n break;\n }\n }\n\n const fallbackDark = \"#101214\";\n const fallbackLight = \"#ffffff\";\n const darkRatio = contrastRatio(surfaceHex, fallbackDark);\n const lightRatio = contrastRatio(surfaceHex, fallbackLight);\n\n if (darkRatio >= minContrast || lightRatio >= minContrast) {\n return darkRatio >= lightRatio ? fallbackDark : fallbackLight;\n }\n\n return bestHex;\n}\n\nfunction pickForegroundColor(\n baseOklch: OklchColor,\n surfaceHex: string,\n options: ForegroundOptions = {},\n) {\n const minContrast = options.minContrast ?? BRAND_MIN_CONTRAST;\n const darkCandidate = options.darkCandidate ?? \"#101214\";\n const lightCandidate = options.lightCandidate ?? \"#ffffff\";\n const preferredCandidate = options.preferredCandidate;\n const preferredMinContrast = options.preferredMinContrast ?? minContrast;\n const preferredSwitchDelta = options.preferredSwitchDelta ?? 0;\n const darkRatio = contrastRatio(surfaceHex, darkCandidate);\n const lightRatio = contrastRatio(surfaceHex, lightCandidate);\n const darkPasses = darkRatio >= minContrast;\n const lightPasses = lightRatio >= minContrast;\n const preferredLightPasses = lightRatio >= preferredMinContrast;\n const preferredDarkPasses = darkRatio >= preferredMinContrast;\n\n if (darkPasses || lightPasses) {\n if (preferredCandidate === \"light\" && preferredLightPasses) {\n return lightCandidate;\n }\n if (preferredCandidate === \"dark\" && preferredDarkPasses) {\n return darkCandidate;\n }\n\n if (preferredCandidate === \"light\" && lightPasses) {\n if (!darkPasses || darkRatio - lightRatio <= preferredSwitchDelta) {\n return lightCandidate;\n }\n return darkCandidate;\n }\n if (preferredCandidate === \"dark\" && darkPasses) {\n if (!lightPasses || lightRatio - darkRatio <= preferredSwitchDelta) {\n return darkCandidate;\n }\n return lightCandidate;\n }\n\n if (darkPasses && lightPasses) {\n return darkRatio >= lightRatio ? darkCandidate : lightCandidate;\n }\n return darkPasses ? darkCandidate : lightCandidate;\n }\n\n const preferDark = darkRatio >= lightRatio;\n return makeReadableTextOnSurface(baseOklch, surfaceHex, {\n minContrast,\n preferDark,\n initialLightness: preferDark ? 0.2 : 0.96,\n chromaScale: 0.1,\n });\n}\n\nconst BRAND_STOPS = [100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];\nconst BRAND_LIGHTNESS_CURVE = [0.9, 0.84, 0.78, 0.72, 0.67, 0.62, 0.56, 0.5, 0.44, 0.38];\nconst BRAND_CHROMA_MULTIPLIERS = [0.28, 0.4, 0.54, 0.7, 0.86, 1, 1.08, 1.14, 1.2, 1.24];\nconst BRAND_MIN_SURFACE_CONTRAST = 1.8;\nconst BRAND_MIN_SUBTLE_SURFACE_CONTRAST = 1.15;\nconst BRAND_MIN_SOFT_SURFACE_CONTRAST = 1.08;\nconst BRAND_UI_TEXT_MIN_CONTRAST = 2.5;\nconst BRAND_LIGHT_TEXT_SWITCH_DELTA = 3;\nconst LIGHT_CANVAS_HEX = \"#ffffff\";\nconst DARK_CANVAS_HEX = \"#1f2228\";\n\nfunction pickRampStopForContrast(\n ramp: BrandRamp,\n stopCandidates: number[],\n referenceHex: string,\n minContrast: number,\n) {\n const fallbackStop = stopCandidates[stopCandidates.length - 1];\n for (const stop of stopCandidates) {\n const candidate = ramp[stop];\n if (!candidate) {\n continue;\n }\n if (contrastRatio(candidate, referenceHex) >= minContrast) {\n return stop;\n }\n }\n return fallbackStop;\n}\n\nfunction makeBrandRampFromSeed(baseOklch: OklchColor): BrandRamp {\n const lightnessOffset = baseOklch.l - BRAND_LIGHTNESS_CURVE[5];\n const minSeedChroma = baseOklch.c > 0.02 ? 0.02 : 0;\n\n return BRAND_STOPS.reduce<BrandRamp>((acc, stop, index) => {\n const lightness = clamp01(BRAND_LIGHTNESS_CURVE[index] + lightnessOffset);\n const chroma = Math.min(\n 0.4,\n Math.max(minSeedChroma, baseOklch.c * BRAND_CHROMA_MULTIPLIERS[index]),\n );\n acc[stop] = rgbToHex(oklchToSrgb(lightness, chroma, baseOklch.h));\n return acc;\n }, {});\n}\n\nfunction makeSemanticBrandTokens(baseOklch: OklchColor, ramp: BrandRamp): BrandVars {\n const lightSoftStops = [100, 200, 300, 400, 500, 600, 700];\n const lightSoftStop = pickRampStopForContrast(\n ramp,\n lightSoftStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const lightSoftIndex = lightSoftStops.indexOf(lightSoftStop);\n const lightSoftHoveredStop =\n lightSoftStops[Math.min(lightSoftIndex + 1, lightSoftStops.length - 1)];\n const lightSoftPressedStop =\n lightSoftStops[Math.min(lightSoftIndex + 2, lightSoftStops.length - 1)];\n\n const darkSoftStops = [1000, 900, 800, 700, 600, 500];\n const darkSoftStop = pickRampStopForContrast(\n ramp,\n darkSoftStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SOFT_SURFACE_CONTRAST,\n );\n const darkSoftIndex = darkSoftStops.indexOf(darkSoftStop);\n const darkSoftHoveredStop =\n darkSoftStops[Math.min(darkSoftIndex + 1, darkSoftStops.length - 1)];\n const darkSoftPressedStop =\n darkSoftStops[Math.min(darkSoftIndex + 2, darkSoftStops.length - 1)];\n\n const lightSubtleStops = [200, 300, 400, 500, 600, 700, 800];\n const lightSubtleStop = pickRampStopForContrast(\n ramp,\n lightSubtleStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n const darkSubtleStops = [900, 800, 700, 600, 500, 400];\n const darkSubtleStop = pickRampStopForContrast(\n ramp,\n darkSubtleStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SUBTLE_SURFACE_CONTRAST,\n );\n\n const lightStrongStops = [600, 700, 800, 900, 1000];\n const lightStrongStop = pickRampStopForContrast(\n ramp,\n lightStrongStops,\n LIGHT_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const lightStrongIndex = lightStrongStops.indexOf(lightStrongStop);\n const lightStrongHoveredStop =\n lightStrongStops[Math.min(lightStrongIndex + 1, lightStrongStops.length - 1)];\n const lightStrongPressedStop =\n lightStrongStops[Math.min(lightStrongIndex + 2, lightStrongStops.length - 1)];\n\n const darkStrongStops = [400, 300, 200, 100];\n const darkStrongStop = pickRampStopForContrast(\n ramp,\n darkStrongStops,\n DARK_CANVAS_HEX,\n BRAND_MIN_SURFACE_CONTRAST,\n );\n const darkStrongIndex = darkStrongStops.indexOf(darkStrongStop);\n const darkStrongHoveredStop =\n darkStrongStops[Math.min(darkStrongIndex + 1, darkStrongStops.length - 1)];\n const darkStrongPressedStop =\n darkStrongStops[Math.min(darkStrongIndex + 2, darkStrongStops.length - 1)];\n\n const tokens: BrandVars = {\n \"--color-brand-surface-soft-light\": ramp[lightSoftStop],\n \"--color-brand-surface-soft-hovered-light\": ramp[lightSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-light\": ramp[lightSoftPressedStop],\n \"--color-brand-surface-soft-dark\": ramp[darkSoftStop],\n \"--color-brand-surface-soft-hovered-dark\": ramp[darkSoftHoveredStop],\n \"--color-brand-surface-soft-pressed-dark\": ramp[darkSoftPressedStop],\n \"--color-brand-surface-subtle-light\": ramp[lightSubtleStop],\n \"--color-brand-surface-subtle-dark\": ramp[darkSubtleStop],\n \"--color-brand-surface-strong-light\": ramp[lightStrongStop],\n \"--color-brand-surface-strong-hovered-light\": ramp[lightStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-light\": ramp[lightStrongPressedStop],\n \"--color-brand-surface-strong-dark\": ramp[darkStrongStop],\n \"--color-brand-surface-strong-hovered-dark\": ramp[darkStrongHoveredStop],\n \"--color-brand-surface-strong-pressed-dark\": ramp[darkStrongPressedStop],\n };\n\n const preferredSoftLightForeground = ramp[600];\n const softLightContrast = contrastRatio(\n tokens[\"--color-brand-surface-soft-light\"],\n preferredSoftLightForeground,\n );\n tokens[\"--color-brand-on-soft-light\"] =\n softLightContrast >= BRAND_MIN_CONTRAST\n ? preferredSoftLightForeground\n : pickForegroundColor(baseOklch, tokens[\"--color-brand-surface-soft-light\"], {\n minContrast: BRAND_MIN_CONTRAST,\n });\n tokens[\"--color-brand-on-soft-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-soft-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-subtle-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-subtle-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-subtle-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n tokens[\"--color-brand-on-strong-light\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-light\"],\n {\n minContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredCandidate: \"light\",\n preferredMinContrast: BRAND_UI_TEXT_MIN_CONTRAST,\n preferredSwitchDelta: BRAND_LIGHT_TEXT_SWITCH_DELTA,\n },\n );\n tokens[\"--color-brand-on-strong-dark\"] = pickForegroundColor(\n baseOklch,\n tokens[\"--color-brand-surface-strong-dark\"],\n {\n minContrast: BRAND_MIN_CONTRAST,\n },\n );\n\n tokens[\"--color-brand-text-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-text-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-icon-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-icon-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n tokens[\"--color-brand-border-light\"] = tokens[\"--color-brand-surface-strong-light\"];\n tokens[\"--color-brand-border-dark\"] = tokens[\"--color-brand-surface-strong-dark\"];\n\n return tokens;\n}\n\nfunction makeBrandTokens(baseHex: string | undefined): BrandVars | null {\n const norm = normalizeHex(baseHex);\n if (!norm) {\n return null;\n }\n\n const baseRgb = hexToRgb(norm);\n if (!baseRgb) {\n return null;\n }\n\n const baseOklch = rgbToOklch(baseRgb);\n const ramp = makeBrandRampFromSeed(baseOklch);\n // Keep the incoming seed exact for debugging and transparency.\n ramp[600] = norm;\n\n return {\n \"--color-brand-seed\": norm,\n \"--color-brand-100\": ramp[100],\n \"--color-brand-200\": ramp[200],\n \"--color-brand-300\": ramp[300],\n \"--color-brand-400\": ramp[400],\n \"--color-brand-500\": ramp[500],\n \"--color-brand-600\": ramp[600],\n \"--color-brand-700\": ramp[700],\n \"--color-brand-800\": ramp[800],\n \"--color-brand-900\": ramp[900],\n \"--color-brand-1000\": ramp[1000],\n ...makeSemanticBrandTokens(baseOklch, ramp),\n };\n}\n\nconst brandVars = computed(() => makeBrandTokens(props.accentColor) ?? {});\n\nconst accentStackEntry = {\n vars: {},\n enabled: Boolean(props.exposeGlobalAccent),\n};\nglobalAccentStack.push(accentStackEntry);\n\nwatch(\n () => props.exposeGlobalAccent,\n (enabled) => {\n accentStackEntry.enabled = Boolean(enabled);\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n\nwatch(\n brandVars,\n (vars) => {\n if (vars && Object.keys(vars).length > 0) {\n accentStackEntry.vars = { ...vars };\n } else {\n accentStackEntry.vars = {};\n }\n applyGlobalAccentFromStack();\n },\n { immediate: true },\n);\n</script>\n\n<style lang=\"less\">\n@import \"index.less\";\n</style>\n","<template>\n <DropdownMenu v-model:open=\"open\" text=\"Theme\" aria-label=\"Select theme mode\">\n <DropdownItemGroup title=\"Theme\" item-role=\"menuitemradio\">\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"auto\"\n :label=\"MODE_LABELS.auto\"\n :aria-label=\"`${MODE_LABELS.auto} mode`\"\n />\n <hr class=\"theme-switcher-divider\" />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"light\"\n :label=\"MODE_LABELS.light\"\n :aria-label=\"`${MODE_LABELS.light} mode`\"\n />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"dark\"\n :label=\"MODE_LABELS.dark\"\n :aria-label=\"`${MODE_LABELS.dark} mode`\"\n />\n </DropdownItemGroup>\n </DropdownMenu>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { DropdownMenu, DropdownItemGroup, DropdownItemRadio } from \"@gospore/dropdown-menu\";\nimport {\n readStoredMode,\n writeStoredMode,\n VALID_THEME_MODES,\n applyStoredModeToDocument,\n type ThemeMode,\n} from \"./modeStorage\";\n\nconst MODE_OPTIONS: { value: ThemeMode; label: string }[] = [\n { value: \"auto\", label: \"System\" },\n { value: \"light\", label: \"Light\" },\n { value: \"dark\", label: \"Dark\" },\n];\nconst MODE_LABELS = MODE_OPTIONS.reduce<Record<ThemeMode, string>>(\n (map, option) => {\n map[option.value] = option.label;\n return map;\n },\n {} as Record<ThemeMode, string>,\n);\nconst VALID_MODES = VALID_THEME_MODES;\n\nconst props = defineProps({\n /** Selected theme mode. Supports v-model:mode and persists changes to local storage. */\n mode: {\n type: String as PropType<ThemeMode>,\n default: \"auto\",\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n});\nconst emit = defineEmits([\"update:mode\"]);\n\napplyStoredModeToDocument();\n\nconst open = ref(false);\nconst initialMode = readStoredMode() ?? props.mode;\nconst internalMode = ref<ThemeMode>(VALID_MODES.includes(initialMode) ? initialMode : \"auto\");\nwatch(\n () => props.mode,\n (mode) => {\n if (VALID_MODES.includes(mode) && mode !== internalMode.value) {\n internalMode.value = mode;\n }\n },\n);\n\nwatch(\n internalMode,\n (mode) => {\n if (!VALID_MODES.includes(mode)) {\n internalMode.value = \"auto\";\n return;\n }\n open.value = false;\n emit(\"update:mode\", mode);\n writeStoredMode(mode);\n applyStoredModeToDocument(mode);\n },\n { immediate: true },\n);\n</script>\n\n<style scoped lang=\"less\">\n.theme-switcher-divider {\n margin: 4px 0;\n}\n</style>\n","<template>\n <DropdownMenu v-model:open=\"open\" text=\"Theme\" aria-label=\"Select theme mode\">\n <DropdownItemGroup title=\"Theme\" item-role=\"menuitemradio\">\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"auto\"\n :label=\"MODE_LABELS.auto\"\n :aria-label=\"`${MODE_LABELS.auto} mode`\"\n />\n <hr class=\"theme-switcher-divider\" />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"light\"\n :label=\"MODE_LABELS.light\"\n :aria-label=\"`${MODE_LABELS.light} mode`\"\n />\n <DropdownItemRadio\n v-model:checked=\"internalMode\"\n value=\"dark\"\n :label=\"MODE_LABELS.dark\"\n :aria-label=\"`${MODE_LABELS.dark} mode`\"\n />\n </DropdownItemGroup>\n </DropdownMenu>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, watch } from \"vue\";\nimport type { PropType } from \"vue\";\nimport { DropdownMenu, DropdownItemGroup, DropdownItemRadio } from \"@gospore/dropdown-menu\";\nimport {\n readStoredMode,\n writeStoredMode,\n VALID_THEME_MODES,\n applyStoredModeToDocument,\n type ThemeMode,\n} from \"./modeStorage\";\n\nconst MODE_OPTIONS: { value: ThemeMode; label: string }[] = [\n { value: \"auto\", label: \"System\" },\n { value: \"light\", label: \"Light\" },\n { value: \"dark\", label: \"Dark\" },\n];\nconst MODE_LABELS = MODE_OPTIONS.reduce<Record<ThemeMode, string>>(\n (map, option) => {\n map[option.value] = option.label;\n return map;\n },\n {} as Record<ThemeMode, string>,\n);\nconst VALID_MODES = VALID_THEME_MODES;\n\nconst props = defineProps({\n /** Selected theme mode. Supports v-model:mode and persists changes to local storage. */\n mode: {\n type: String as PropType<ThemeMode>,\n default: \"auto\",\n validator: (value: string) => [\"auto\", \"light\", \"dark\"].includes(value),\n },\n});\nconst emit = defineEmits<{\n \"update:mode\": [value: ThemeMode];\n}>();\n\napplyStoredModeToDocument();\n\nconst open = ref(false);\nconst initialMode = readStoredMode() ?? props.mode;\nconst internalMode = ref<ThemeMode>(VALID_MODES.includes(initialMode) ? initialMode : \"auto\");\nwatch(\n () => props.mode,\n (mode) => {\n if (VALID_MODES.includes(mode) && mode !== internalMode.value) {\n internalMode.value = mode;\n }\n },\n);\n\nwatch(\n internalMode,\n (mode) => {\n if (!VALID_MODES.includes(mode)) {\n internalMode.value = \"auto\";\n return;\n }\n open.value = false;\n emit(\"update:mode\", mode);\n writeStoredMode(mode);\n applyStoredModeToDocument(mode);\n },\n { immediate: true },\n);\n</script>\n\n<style scoped lang=\"less\">\n.theme-switcher-divider {\n margin: 4px 0;\n}\n</style>\n"],"mappings":"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"}
|
package/dist/theme.umd.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`vue`),require(`what-input`),require(`@gospore/dropdown-menu`)):typeof define==`function`&&define.amd?define([`exports`,`vue`,`what-input`,`@gospore/dropdown-menu`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.theme={},e.Vue,e.whatInput,e.DropdownMenu))})(this,function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var i=[`auto`,`light`,`dark`],a=`gospore-theme-mode`,o=3600*24*365,s=`(prefers-color-scheme: dark)`;function c(){if(typeof document>`u`)return null;let e=d(a);return m(e)?e:null}function l(e){if(typeof document>`u`||!i.includes(e))return;let t=typeof window<`u`?window.location.hostname:``,n=typeof window<`u`?window.location.protocol:``,r=f(t),s=n===`https:`,c=[`path=/`,`max-age=${o}`,`SameSite=Lax`];r&&c.push(`domain=${r}`),s&&c.push(`Secure`),document.cookie=[`${encodeURIComponent(a)}=${encodeURIComponent(e)}`,...c].join(`; `)}function u(e){if(typeof document>`u`)return;let t=m(e)?e:c()??`auto`;console.log(`Applying theme mode:`,t);let n=p(t);document.documentElement.setAttribute(`dark-mode`,String(n))}function d(e){if(typeof document>`u`||!document.cookie)return null;let t=`${encodeURIComponent(e)}=`,n=document.cookie.split(`; `);for(let e of n)if(e.startsWith(t))return decodeURIComponent(e.slice(t.length));return null}function f(e){if(!e||e===`localhost`)return null;if(/^\d+(?:\.\d+){3}$/.test(e))return e;let t=e.split(`.`);return t.length<=1?null:t.length===2?e:t.slice(-2).join(`.`)}function p(e){if(e===`dark`)return!0;if(e===`light`||typeof window>`u`)return!1;try{let e=typeof window.matchMedia==`function`?window.matchMedia(s):null;return e?e.matches:!1}catch{return!1}}function m(e){return typeof e==`string`&&i.includes(e)}var h=Symbol.for(`gospore.theme.portalHost`);function g(){let e=(0,t.inject)(h,null);console.log(`Using theme portal host:`,e);let n=typeof document<`u`?document.body:`body`;return(0,t.computed)(()=>e?.value??n)}var _=[],v=new Set,y=`(prefers-color-scheme: dark)`,b=`(prefers-reduced-motion: reduce)`,x=200,S=3.8,C=1.8,w=1.15,T=1.08,E=2.5,D=3,O=`#ffffff`,k=`#1f2228`,A=(0,t.defineComponent)({name:`Theme`,__name:`Theme`,props:{mode:{type:String,default:void 0,validator:e=>[`auto`,`light`,`dark`].includes(e)},accentColor:{type:String,required:!1,default:void 0},exposeGlobalAccent:{type:Boolean,default:!1}},setup(e){let n=e;function r(){let e=document.documentElement;if(!e)return;v.forEach(t=>{e.style.removeProperty(t)}),v.clear();let t=[..._].reverse().find(e=>e.enabled&&e.vars&&Object.keys(e.vars).length>0);t&&Object.entries(t.vars).forEach(([t,n])=>{e.style.setProperty(t,n),v.add(t)})}let i=window.matchMedia(y),a=window.matchMedia(b),o=j();u(o);let s=(0,t.ref)(o),l=(0,t.ref)({darkMode:p(o)}),d=(0,t.ref)(null);i.addEventListener(`change`,m),(0,t.watch)(()=>n.mode,()=>{f()},{immediate:!0}),(0,t.watch)(()=>l.value.darkMode,(e,t)=>{if(t===void 0){g(e);return}A(()=>{g(e)})},{immediate:!0}),(0,t.onBeforeUnmount)(()=>{i.removeEventListener(`change`,m);let e=_.indexOf($);e!==-1&&_.splice(e,1),r()});function f(){let e=j();s.value=e,l.value.darkMode=p(e)}function p(e){return e===`dark`?!0:e===`light`?!1:i.matches}function m(e){!e||s.value!==`auto`||(l.value.darkMode=e.matches)}function g(e){document.documentElement.setAttribute(`dark-mode`,String(e))}function A(e){if(a.matches){e();return}let t=document.startViewTransition?.bind(document);if(t){t(()=>{e()});return}e();let n=document.documentElement;typeof n.animate==`function`&&window.requestAnimationFrame(()=>{n.animate([{opacity:.85},{opacity:1}],{duration:x,easing:`ease-in-out`})})}function j(){return n.mode??c()??`auto`}(0,t.provide)(`theme`,l),(0,t.provide)(h,d);function M(e){if(!e)return null;let t=e.trim().toLowerCase();if(!/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.exec(t))return null;if(t.length===4){let e=t[1],n=t[2],r=t[3];return`#${e}${e}${n}${n}${r}${r}`}return t}function N(e){let t=M(e);return t?{r:parseInt(t.slice(1,3),16),g:parseInt(t.slice(3,5),16),b:parseInt(t.slice(5,7),16)}:null}function P({r:e,g:t,b:n}){let r=e=>e.toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}function F(e){return Math.max(0,Math.min(1,e))}function I(e){return e<=.04045?e/12.92:((e+.055)/1.055)**2.4}function L(e){let t=F(e);return t<=.0031308?12.92*t:1.055*t**(1/2.4)-.055}function R({r:e,g:t,b:n}){let r=e/255,i=t/255,a=n/255,o=I(r),s=I(i),c=I(a),l=.4122214708*o+.5363325363*s+.0514459929*c,u=.2119034982*o+.6806995451*s+.1073969566*c,d=.0883024619*o+.2817188376*s+.6299787005*c,f=Math.cbrt(l),p=Math.cbrt(u),m=Math.cbrt(d),h=.2104542553*f+.793617785*p-.0040720468*m,g=1.9779984951*f-2.428592205*p+.4505937099*m,_=.0259040371*f+.7827717662*p-.808675766*m,v=Math.sqrt(g*g+_*_),y=180/Math.PI*Math.atan2(_,g);return y<0&&(y+=360),{l:h,c:v,h:y}}function z(e,t,n,r=!1){let i=n*Math.PI/180,a=t*Math.cos(i),o=t*Math.sin(i),s=e+.3963377774*a+.2158037573*o,c=e-.1055613458*a-.0638541728*o,l=e-.0894841775*a-1.291485548*o,u=r?F(s):s,d=r?F(c):c,f=r?F(l):l;if(!r&&(u<-1e-4||u>1.0001||d<-1e-4||d>1.0001||f<-1e-4||f>1.0001))return null;let p=u*u*u,m=d*d*d,h=f*f*f,g=4.0767416621*p-3.3077115913*m+.2309699292*h,_=-1.2684380046*p+2.6097574011*m-.3413193965*h,v=-.0041960863*p-.7034186147*m+1.707614701*h;if(r)g=F(g),_=F(_),v=F(v);else if(g<-1e-4||g>1.0001||_<-1e-4||_>1.0001||v<-1e-4||v>1.0001)return null;let y=F(L(g)),b=F(L(_)),x=F(L(v));return{r:Math.round(y*255),g:Math.round(b*255),b:Math.round(x*255)}}function B(e,t,n){let r=t;for(let t=0;t<8;t+=1){let t=z(e,r,n);if(t)return t;r*=.9}return z(e,F(r),n,!0)||{r:0,g:0,b:0}}function V(e){let t=N(e);if(!t)return 0;let n=I(t.r/255),r=I(t.g/255),i=I(t.b/255);return .2126*n+.7152*r+.0722*i}function H(e,t){let n=V(e),r=V(t),i=Math.max(n,r),a=Math.min(n,r);return(i+.05)/(a+.05)}function U(e,t,n={}){let r=n.minContrast??S,i=n.preferDark!==!1,a=Math.max(.01,Math.min(.06,(e.c||.12)*(n.chromaScale??.25))),o=n.initialLightness??(i?.24:.94),s=i?-1:1,c=n.step??.03,l=F(o),u=P(B(l,a,e.h)),d=H(t,u);for(let n=0;n<48;n+=1){let n=P(B(l,a,e.h)),i=H(t,n);if(i>d&&(d=i,u=n),i>=r)return n;if(l=F(l+s*c),l===0||l===1)break}let f=`#101214`,p=`#ffffff`,m=H(t,f),h=H(t,p);return m>=r||h>=r?m>=h?f:p:u}function W(e,t,n={}){let r=n.minContrast??S,i=n.darkCandidate??`#101214`,a=n.lightCandidate??`#ffffff`,o=n.preferredCandidate,s=n.preferredMinContrast??r,c=n.preferredSwitchDelta??0,l=H(t,i),u=H(t,a),d=l>=r,f=u>=r,p=u>=s,m=l>=s;if(d||f)return o===`light`&&p?a:o===`dark`&&m?i:o===`light`&&f?!d||l-u<=c?a:i:o===`dark`&&d?!f||u-l<=c?i:a:d&&f?l>=u?i:a:d?i:a;let h=l>=u;return U(e,t,{minContrast:r,preferDark:h,initialLightness:h?.2:.96,chromaScale:.1})}let G=[100,200,300,400,500,600,700,800,900,1e3],K=[.9,.84,.78,.72,.67,.62,.56,.5,.44,.38],q=[.28,.4,.54,.7,.86,1,1.08,1.14,1.2,1.24];function J(e,t,n,r){let i=t[t.length-1];for(let i of t){let t=e[i];if(t&&H(t,n)>=r)return i}return i}function Y(e){let t=e.l-K[5],n=e.c>.02?.02:0;return G.reduce((r,i,a)=>(r[i]=P(B(F(K[a]+t),Math.min(.4,Math.max(n,e.c*q[a])),e.h)),r),{})}function X(e,t){let n=[100,200,300,400,500,600,700],r=J(t,n,O,T),i=n.indexOf(r),a=n[Math.min(i+1,n.length-1)],o=n[Math.min(i+2,n.length-1)],s=[1e3,900,800,700,600,500],c=J(t,s,k,T),l=s.indexOf(c),u=s[Math.min(l+1,s.length-1)],d=s[Math.min(l+2,s.length-1)],f=J(t,[200,300,400,500,600,700,800],O,w),p=J(t,[900,800,700,600,500,400],k,w),m=[600,700,800,900,1e3],h=J(t,m,O,C),g=m.indexOf(h),_=m[Math.min(g+1,m.length-1)],v=m[Math.min(g+2,m.length-1)],y=[400,300,200,100],b=J(t,y,k,C),x=y.indexOf(b),A=y[Math.min(x+1,y.length-1)],j=y[Math.min(x+2,y.length-1)],M={"--color-brand-surface-soft-light":t[r],"--color-brand-surface-soft-hovered-light":t[a],"--color-brand-surface-soft-pressed-light":t[o],"--color-brand-surface-soft-dark":t[c],"--color-brand-surface-soft-hovered-dark":t[u],"--color-brand-surface-soft-pressed-dark":t[d],"--color-brand-surface-subtle-light":t[f],"--color-brand-surface-subtle-dark":t[p],"--color-brand-surface-strong-light":t[h],"--color-brand-surface-strong-hovered-light":t[_],"--color-brand-surface-strong-pressed-light":t[v],"--color-brand-surface-strong-dark":t[b],"--color-brand-surface-strong-hovered-dark":t[A],"--color-brand-surface-strong-pressed-dark":t[j]},N=t[600];return M[`--color-brand-on-soft-light`]=H(M[`--color-brand-surface-soft-light`],N)>=S?N:W(e,M[`--color-brand-surface-soft-light`],{minContrast:S}),M[`--color-brand-on-soft-dark`]=W(e,M[`--color-brand-surface-soft-dark`],{minContrast:S}),M[`--color-brand-on-subtle-light`]=W(e,M[`--color-brand-surface-subtle-light`],{minContrast:E,preferredCandidate:`light`,preferredMinContrast:E,preferredSwitchDelta:D}),M[`--color-brand-on-subtle-dark`]=W(e,M[`--color-brand-surface-subtle-dark`],{minContrast:S}),M[`--color-brand-on-strong-light`]=W(e,M[`--color-brand-surface-strong-light`],{minContrast:E,preferredCandidate:`light`,preferredMinContrast:E,preferredSwitchDelta:D}),M[`--color-brand-on-strong-dark`]=W(e,M[`--color-brand-surface-strong-dark`],{minContrast:S}),M[`--color-brand-text-light`]=M[`--color-brand-surface-strong-light`],M[`--color-brand-text-dark`]=M[`--color-brand-surface-strong-dark`],M[`--color-brand-icon-light`]=M[`--color-brand-surface-strong-light`],M[`--color-brand-icon-dark`]=M[`--color-brand-surface-strong-dark`],M[`--color-brand-border-light`]=M[`--color-brand-surface-strong-light`],M[`--color-brand-border-dark`]=M[`--color-brand-surface-strong-dark`],M}function Z(e){let t=M(e);if(!t)return null;let n=N(t);if(!n)return null;let r=R(n),i=Y(r);return i[600]=t,{"--color-brand-seed":t,"--color-brand-100":i[100],"--color-brand-200":i[200],"--color-brand-300":i[300],"--color-brand-400":i[400],"--color-brand-500":i[500],"--color-brand-600":i[600],"--color-brand-700":i[700],"--color-brand-800":i[800],"--color-brand-900":i[900],"--color-brand-1000":i[1e3],...X(r,i)}}let Q=(0,t.computed)(()=>Z(n.accentColor)??{}),$={vars:{},enabled:!!n.exposeGlobalAccent};return _.push($),(0,t.watch)(()=>n.exposeGlobalAccent,e=>{$.enabled=!!e,r()},{immediate:!0}),(0,t.watch)(Q,e=>{e&&Object.keys(e).length>0?$.vars={...e}:$.vars={},r()},{immediate:!0}),(e,n)=>((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{class:`theme`,style:(0,t.normalizeStyle)(Q.value)},[(0,t.renderSlot)(e.$slots,`default`),(0,t.createElementVNode)(`div`,{ref_key:`portalHost`,ref:d,class:`theme__portal-host`},null,512)],4))}}),j=((e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n})((0,t.defineComponent)({__name:`ThemeSwitcher`,props:{mode:{type:String,default:`auto`,validator:e=>[`auto`,`light`,`dark`].includes(e)}},emits:[`update:mode`],setup(e,{emit:n}){let a=[{value:`auto`,label:`System`},{value:`light`,label:`Light`},{value:`dark`,label:`Dark`}].reduce((e,t)=>(e[t.value]=t.label,e),{}),o=i,s=e,d=n;u();let f=(0,t.ref)(!1),p=c()??s.mode,m=(0,t.ref)(o.includes(p)?p:`auto`);return(0,t.watch)(()=>s.mode,e=>{o.includes(e)&&e!==m.value&&(m.value=e)}),(0,t.watch)(m,e=>{if(!o.includes(e)){m.value=`auto`;return}f.value=!1,d(`update:mode`,e),l(e),u(e)},{immediate:!0}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.unref)(r.DropdownMenu),{open:f.value,"onUpdate:open":n[3]||=e=>f.value=e,text:`Theme`,"aria-label":`Select theme mode`},{default:(0,t.withCtx)(()=>[(0,t.createVNode)((0,t.unref)(r.DropdownItemGroup),{title:`Theme`,"item-role":`menuitemradio`},{default:(0,t.withCtx)(()=>[(0,t.createVNode)((0,t.unref)(r.DropdownItemRadio),{checked:m.value,"onUpdate:checked":n[0]||=e=>m.value=e,value:`auto`,label:(0,t.unref)(a).auto,"aria-label":`${(0,t.unref)(a).auto} mode`},null,8,[`checked`,`label`,`aria-label`]),n[4]||=(0,t.createElementVNode)(`hr`,{class:`theme-switcher-divider`},null,-1),(0,t.createVNode)((0,t.unref)(r.DropdownItemRadio),{checked:m.value,"onUpdate:checked":n[1]||=e=>m.value=e,value:`light`,label:(0,t.unref)(a).light,"aria-label":`${(0,t.unref)(a).light} mode`},null,8,[`checked`,`label`,`aria-label`]),(0,t.createVNode)((0,t.unref)(r.DropdownItemRadio),{checked:m.value,"onUpdate:checked":n[2]||=e=>m.value=e,value:`dark`,label:(0,t.unref)(a).dark,"aria-label":`${(0,t.unref)(a).dark} mode`},null,8,[`checked`,`label`,`aria-label`])]),_:1})]),_:1},8,[`open`]))}}),[[`__scopeId`,`data-v-be9d7090`]]);e.Theme=A,e.ThemeSwitcher=j,e.themePortalHostKey=h,e.useThemePortalTarget=g});
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require("vue"),require("what-input"),require("@gospore/dropdown-menu")):typeof define==`function`&&define.amd?define([`exports`,`vue`,`what-input`,`@gospore/dropdown-menu`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.theme={},e.Vue,e.whatInput,e.DropdownMenu))})(this,function(e,t,n,r){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var i=[`auto`,`light`,`dark`],a=`gospore-theme-mode`,o=3600*24*365,s=`(prefers-color-scheme: dark)`;function c(){if(typeof document>`u`)return null;let e=d(a);return m(e)?e:null}function l(e){if(typeof document>`u`||!i.includes(e))return;let t=typeof window<`u`?window.location.hostname:``,n=typeof window<`u`?window.location.protocol:``,r=f(t),s=n===`https:`,c=[`path=/`,`max-age=${o}`,`SameSite=Lax`];r&&c.push(`domain=${r}`),s&&c.push(`Secure`),document.cookie=[`${encodeURIComponent(a)}=${encodeURIComponent(e)}`,...c].join(`; `)}function u(e){if(typeof document>`u`)return;let t=m(e)?e:c()??`auto`;console.log(`Applying theme mode:`,t);let n=p(t);document.documentElement.setAttribute(`dark-mode`,String(n))}function d(e){if(typeof document>`u`||!document.cookie)return null;let t=`${encodeURIComponent(e)}=`,n=document.cookie.split(`; `);for(let e of n)if(e.startsWith(t))return decodeURIComponent(e.slice(t.length));return null}function f(e){if(!e||e===`localhost`)return null;if(/^\d+(?:\.\d+){3}$/.test(e))return e;let t=e.split(`.`);return t.length<=1?null:t.length===2?e:t.slice(-2).join(`.`)}function p(e){if(e===`dark`)return!0;if(e===`light`||typeof window>`u`)return!1;try{let e=typeof window.matchMedia==`function`?window.matchMedia(s):null;return e?e.matches:!1}catch{return!1}}function m(e){return typeof e==`string`&&i.includes(e)}var h=Symbol.for(`gospore.theme.portalHost`);function g(){let e=(0,t.inject)(h,null);console.log(`Using theme portal host:`,e);let n=typeof document<`u`?document.body:`body`;return(0,t.computed)(()=>e?.value??n)}var _=[],v=new Set,y=`(prefers-color-scheme: dark)`,b=`(prefers-reduced-motion: reduce)`,x=200,S=3.8,C=1.8,w=1.15,T=1.08,E=2.5,D=3,O=`#ffffff`,k=`#1f2228`,A=(0,t.defineComponent)({name:`Theme`,__name:`Theme`,props:{mode:{type:String,default:void 0,validator:e=>[`auto`,`light`,`dark`].includes(e)},accentColor:{type:String,required:!1,default:void 0},exposeGlobalAccent:{type:Boolean,default:!1}},setup(e){let n=e;function r(){let e=document.documentElement;if(!e)return;v.forEach(t=>{e.style.removeProperty(t)}),v.clear();let t=[..._].reverse().find(e=>e.enabled&&e.vars&&Object.keys(e.vars).length>0);t&&Object.entries(t.vars).forEach(([t,n])=>{e.style.setProperty(t,n),v.add(t)})}let i=window.matchMedia(y),a=window.matchMedia(b),o=j();u(o);let s=(0,t.ref)(o),l=(0,t.ref)({darkMode:p(o)}),d=(0,t.ref)(null);i.addEventListener(`change`,m),(0,t.watch)(()=>n.mode,()=>{f()},{immediate:!0}),(0,t.watch)(()=>l.value.darkMode,(e,t)=>{if(t===void 0){g(e);return}A(()=>{g(e)})},{immediate:!0}),(0,t.onBeforeUnmount)(()=>{i.removeEventListener(`change`,m);let e=_.indexOf($);e!==-1&&_.splice(e,1),r()});function f(){let e=j();s.value=e,l.value.darkMode=p(e)}function p(e){return e===`dark`?!0:e===`light`?!1:i.matches}function m(e){!e||s.value!==`auto`||(l.value.darkMode=e.matches)}function g(e){document.documentElement.setAttribute(`dark-mode`,String(e))}function A(e){if(a.matches){e();return}let t=document.startViewTransition?.bind(document);if(t){t(()=>{e()});return}e();let n=document.documentElement;typeof n.animate==`function`&&window.requestAnimationFrame(()=>{n.animate([{opacity:.85},{opacity:1}],{duration:x,easing:`ease-in-out`})})}function j(){return n.mode??c()??`auto`}(0,t.provide)(`theme`,l),(0,t.provide)(h,d);function M(e){if(!e)return null;let t=e.trim().toLowerCase();if(!/^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.exec(t))return null;if(t.length===4){let e=t[1],n=t[2],r=t[3];return`#${e}${e}${n}${n}${r}${r}`}return t}function N(e){let t=M(e);return t?{r:parseInt(t.slice(1,3),16),g:parseInt(t.slice(3,5),16),b:parseInt(t.slice(5,7),16)}:null}function P({r:e,g:t,b:n}){let r=e=>e.toString(16).padStart(2,`0`);return`#${r(e)}${r(t)}${r(n)}`}function F(e){return Math.max(0,Math.min(1,e))}function I(e){return e<=.04045?e/12.92:((e+.055)/1.055)**2.4}function L(e){let t=F(e);return t<=.0031308?12.92*t:1.055*t**(1/2.4)-.055}function R({r:e,g:t,b:n}){let r=e/255,i=t/255,a=n/255,o=I(r),s=I(i),c=I(a),l=.4122214708*o+.5363325363*s+.0514459929*c,u=.2119034982*o+.6806995451*s+.1073969566*c,d=.0883024619*o+.2817188376*s+.6299787005*c,f=Math.cbrt(l),p=Math.cbrt(u),m=Math.cbrt(d),h=.2104542553*f+.793617785*p-.0040720468*m,g=1.9779984951*f-2.428592205*p+.4505937099*m,_=.0259040371*f+.7827717662*p-.808675766*m,v=Math.sqrt(g*g+_*_),y=180/Math.PI*Math.atan2(_,g);return y<0&&(y+=360),{l:h,c:v,h:y}}function z(e,t,n,r=!1){let i=n*Math.PI/180,a=t*Math.cos(i),o=t*Math.sin(i),s=e+.3963377774*a+.2158037573*o,c=e-.1055613458*a-.0638541728*o,l=e-.0894841775*a-1.291485548*o,u=r?F(s):s,d=r?F(c):c,f=r?F(l):l;if(!r&&(u<-1e-4||u>1.0001||d<-1e-4||d>1.0001||f<-1e-4||f>1.0001))return null;let p=u*u*u,m=d*d*d,h=f*f*f,g=4.0767416621*p-3.3077115913*m+.2309699292*h,_=-1.2684380046*p+2.6097574011*m-.3413193965*h,v=-.0041960863*p-.7034186147*m+1.707614701*h;if(r)g=F(g),_=F(_),v=F(v);else if(g<-1e-4||g>1.0001||_<-1e-4||_>1.0001||v<-1e-4||v>1.0001)return null;let y=F(L(g)),b=F(L(_)),x=F(L(v));return{r:Math.round(y*255),g:Math.round(b*255),b:Math.round(x*255)}}function B(e,t,n){let r=t;for(let t=0;t<8;t+=1){let t=z(e,r,n);if(t)return t;r*=.9}return z(e,F(r),n,!0)||{r:0,g:0,b:0}}function V(e){let t=N(e);if(!t)return 0;let n=I(t.r/255),r=I(t.g/255),i=I(t.b/255);return .2126*n+.7152*r+.0722*i}function H(e,t){let n=V(e),r=V(t),i=Math.max(n,r),a=Math.min(n,r);return(i+.05)/(a+.05)}function U(e,t,n={}){let r=n.minContrast??S,i=n.preferDark!==!1,a=Math.max(.01,Math.min(.06,(e.c||.12)*(n.chromaScale??.25))),o=n.initialLightness??(i?.24:.94),s=i?-1:1,c=n.step??.03,l=F(o),u=P(B(l,a,e.h)),d=H(t,u);for(let n=0;n<48;n+=1){let n=P(B(l,a,e.h)),i=H(t,n);if(i>d&&(d=i,u=n),i>=r)return n;if(l=F(l+s*c),l===0||l===1)break}let f=`#101214`,p=`#ffffff`,m=H(t,f),h=H(t,p);return m>=r||h>=r?m>=h?f:p:u}function W(e,t,n={}){let r=n.minContrast??S,i=n.darkCandidate??`#101214`,a=n.lightCandidate??`#ffffff`,o=n.preferredCandidate,s=n.preferredMinContrast??r,c=n.preferredSwitchDelta??0,l=H(t,i),u=H(t,a),d=l>=r,f=u>=r,p=u>=s,m=l>=s;if(d||f)return o===`light`&&p?a:o===`dark`&&m?i:o===`light`&&f?!d||l-u<=c?a:i:o===`dark`&&d?!f||u-l<=c?i:a:d&&f?l>=u?i:a:d?i:a;let h=l>=u;return U(e,t,{minContrast:r,preferDark:h,initialLightness:h?.2:.96,chromaScale:.1})}let G=[100,200,300,400,500,600,700,800,900,1e3],K=[.9,.84,.78,.72,.67,.62,.56,.5,.44,.38],q=[.28,.4,.54,.7,.86,1,1.08,1.14,1.2,1.24];function J(e,t,n,r){let i=t[t.length-1];for(let i of t){let t=e[i];if(t&&H(t,n)>=r)return i}return i}function Y(e){let t=e.l-K[5],n=e.c>.02?.02:0;return G.reduce((r,i,a)=>(r[i]=P(B(F(K[a]+t),Math.min(.4,Math.max(n,e.c*q[a])),e.h)),r),{})}function X(e,t){let n=[100,200,300,400,500,600,700],r=J(t,n,O,T),i=n.indexOf(r),a=n[Math.min(i+1,n.length-1)],o=n[Math.min(i+2,n.length-1)],s=[1e3,900,800,700,600,500],c=J(t,s,k,T),l=s.indexOf(c),u=s[Math.min(l+1,s.length-1)],d=s[Math.min(l+2,s.length-1)],f=J(t,[200,300,400,500,600,700,800],O,w),p=J(t,[900,800,700,600,500,400],k,w),m=[600,700,800,900,1e3],h=J(t,m,O,C),g=m.indexOf(h),_=m[Math.min(g+1,m.length-1)],v=m[Math.min(g+2,m.length-1)],y=[400,300,200,100],b=J(t,y,k,C),x=y.indexOf(b),A=y[Math.min(x+1,y.length-1)],j=y[Math.min(x+2,y.length-1)],M={"--color-brand-surface-soft-light":t[r],"--color-brand-surface-soft-hovered-light":t[a],"--color-brand-surface-soft-pressed-light":t[o],"--color-brand-surface-soft-dark":t[c],"--color-brand-surface-soft-hovered-dark":t[u],"--color-brand-surface-soft-pressed-dark":t[d],"--color-brand-surface-subtle-light":t[f],"--color-brand-surface-subtle-dark":t[p],"--color-brand-surface-strong-light":t[h],"--color-brand-surface-strong-hovered-light":t[_],"--color-brand-surface-strong-pressed-light":t[v],"--color-brand-surface-strong-dark":t[b],"--color-brand-surface-strong-hovered-dark":t[A],"--color-brand-surface-strong-pressed-dark":t[j]},N=t[600];return M[`--color-brand-on-soft-light`]=H(M[`--color-brand-surface-soft-light`],N)>=S?N:W(e,M[`--color-brand-surface-soft-light`],{minContrast:S}),M[`--color-brand-on-soft-dark`]=W(e,M[`--color-brand-surface-soft-dark`],{minContrast:S}),M[`--color-brand-on-subtle-light`]=W(e,M[`--color-brand-surface-subtle-light`],{minContrast:E,preferredCandidate:`light`,preferredMinContrast:E,preferredSwitchDelta:D}),M[`--color-brand-on-subtle-dark`]=W(e,M[`--color-brand-surface-subtle-dark`],{minContrast:S}),M[`--color-brand-on-strong-light`]=W(e,M[`--color-brand-surface-strong-light`],{minContrast:E,preferredCandidate:`light`,preferredMinContrast:E,preferredSwitchDelta:D}),M[`--color-brand-on-strong-dark`]=W(e,M[`--color-brand-surface-strong-dark`],{minContrast:S}),M[`--color-brand-text-light`]=M[`--color-brand-surface-strong-light`],M[`--color-brand-text-dark`]=M[`--color-brand-surface-strong-dark`],M[`--color-brand-icon-light`]=M[`--color-brand-surface-strong-light`],M[`--color-brand-icon-dark`]=M[`--color-brand-surface-strong-dark`],M[`--color-brand-border-light`]=M[`--color-brand-surface-strong-light`],M[`--color-brand-border-dark`]=M[`--color-brand-surface-strong-dark`],M}function Z(e){let t=M(e);if(!t)return null;let n=N(t);if(!n)return null;let r=R(n),i=Y(r);return i[600]=t,{"--color-brand-seed":t,"--color-brand-100":i[100],"--color-brand-200":i[200],"--color-brand-300":i[300],"--color-brand-400":i[400],"--color-brand-500":i[500],"--color-brand-600":i[600],"--color-brand-700":i[700],"--color-brand-800":i[800],"--color-brand-900":i[900],"--color-brand-1000":i[1e3],...X(r,i)}}let Q=(0,t.computed)(()=>Z(n.accentColor)??{}),$={vars:{},enabled:!!n.exposeGlobalAccent};return _.push($),(0,t.watch)(()=>n.exposeGlobalAccent,e=>{$.enabled=!!e,r()},{immediate:!0}),(0,t.watch)(Q,e=>{e&&Object.keys(e).length>0?$.vars={...e}:$.vars={},r()},{immediate:!0}),(e,n)=>((0,t.openBlock)(),(0,t.createElementBlock)(`div`,{class:`theme`,style:(0,t.normalizeStyle)(Q.value)},[(0,t.renderSlot)(e.$slots,`default`),(0,t.createElementVNode)(`div`,{ref_key:`portalHost`,ref:d,class:`theme__portal-host`},null,512)],4))}}),j=((e,t)=>{let n=e.__vccOpts||e;for(let[e,r]of t)n[e]=r;return n})((0,t.defineComponent)({__name:`ThemeSwitcher`,props:{mode:{type:String,default:`auto`,validator:e=>[`auto`,`light`,`dark`].includes(e)}},emits:[`update:mode`],setup(e,{emit:n}){let a=[{value:`auto`,label:`System`},{value:`light`,label:`Light`},{value:`dark`,label:`Dark`}].reduce((e,t)=>(e[t.value]=t.label,e),{}),o=i,s=e,d=n;u();let f=(0,t.ref)(!1),p=c()??s.mode,m=(0,t.ref)(o.includes(p)?p:`auto`);return(0,t.watch)(()=>s.mode,e=>{o.includes(e)&&e!==m.value&&(m.value=e)}),(0,t.watch)(m,e=>{if(!o.includes(e)){m.value=`auto`;return}f.value=!1,d(`update:mode`,e),l(e),u(e)},{immediate:!0}),(e,n)=>((0,t.openBlock)(),(0,t.createBlock)((0,t.unref)(r.DropdownMenu),{open:f.value,"onUpdate:open":n[3]||=e=>f.value=e,text:`Theme`,"aria-label":`Select theme mode`},{default:(0,t.withCtx)(()=>[(0,t.createVNode)((0,t.unref)(r.DropdownItemGroup),{title:`Theme`,"item-role":`menuitemradio`},{default:(0,t.withCtx)(()=>[(0,t.createVNode)((0,t.unref)(r.DropdownItemRadio),{checked:m.value,"onUpdate:checked":n[0]||=e=>m.value=e,value:`auto`,label:(0,t.unref)(a).auto,"aria-label":`${(0,t.unref)(a).auto} mode`},null,8,[`checked`,`label`,`aria-label`]),n[4]||=(0,t.createElementVNode)(`hr`,{class:`theme-switcher-divider`},null,-1),(0,t.createVNode)((0,t.unref)(r.DropdownItemRadio),{checked:m.value,"onUpdate:checked":n[1]||=e=>m.value=e,value:`light`,label:(0,t.unref)(a).light,"aria-label":`${(0,t.unref)(a).light} mode`},null,8,[`checked`,`label`,`aria-label`]),(0,t.createVNode)((0,t.unref)(r.DropdownItemRadio),{checked:m.value,"onUpdate:checked":n[2]||=e=>m.value=e,value:`dark`,label:(0,t.unref)(a).dark,"aria-label":`${(0,t.unref)(a).dark} mode`},null,8,[`checked`,`label`,`aria-label`])]),_:1})]),_:1},8,[`open`]))}}),[[`__scopeId`,`data-v-be9d7090`]]);e.Theme=A,e.ThemeSwitcher=j,e.themePortalHostKey=h,e.useThemePortalTarget=g});
|
|
2
2
|
//# sourceMappingURL=theme.umd.js.map
|