@irisui/ui 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +85 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/theme-script.cjs.map +1 -1
- package/dist/theme-script.js.map +1 -1
- package/package.json +3 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 theteqlab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# @irisui/ui
|
|
2
|
+
|
|
3
|
+
Accessible React components in the _Verre irisé_ style, from
|
|
4
|
+
[iris-ui](https://github.com/theteqlab/iris-ui).
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```sh
|
|
9
|
+
npm install @irisui/ui @irisui/tokens
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`react` and `react-dom` (18 or 19) are peer dependencies.
|
|
13
|
+
|
|
14
|
+
Import the tokens once in your global CSS (see
|
|
15
|
+
[`@irisui/tokens`](https://www.npmjs.com/package/@irisui/tokens)):
|
|
16
|
+
|
|
17
|
+
```css
|
|
18
|
+
@import "tailwindcss";
|
|
19
|
+
@import "@irisui/tokens/tailwind.css";
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Theme
|
|
23
|
+
|
|
24
|
+
The theme is driven by two attributes on `<html>`: `data-palette` (`nacre`,
|
|
25
|
+
`aurore`, `couchant`, `glacier`) and `data-mode` (`light`, `dark`, `system`).
|
|
26
|
+
Setting them by hand is enough; the helpers below are optional.
|
|
27
|
+
|
|
28
|
+
### `ThemeProvider` and `useTheme`
|
|
29
|
+
|
|
30
|
+
`ThemeProvider` sets the attributes on `<html>` and remembers the user's choice
|
|
31
|
+
in `localStorage` (synced across tabs).
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { ThemeProvider, useTheme, PALETTES, type Palette } from "@irisui/ui";
|
|
35
|
+
|
|
36
|
+
function PaletteSwitcher() {
|
|
37
|
+
const { palette, setPalette } = useTheme();
|
|
38
|
+
return (
|
|
39
|
+
<select value={palette} onChange={(e) => setPalette(e.target.value as Palette)}>
|
|
40
|
+
{PALETTES.map((p) => (
|
|
41
|
+
<option key={p}>{p}</option>
|
|
42
|
+
))}
|
|
43
|
+
</select>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
<ThemeProvider defaultPalette="nacre" defaultMode="system">
|
|
48
|
+
<App />
|
|
49
|
+
</ThemeProvider>;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Props: `palette` / `mode` (controlled), `defaultPalette` / `defaultMode`,
|
|
53
|
+
`onPaletteChange` / `onModeChange`, and `storageKey` (default
|
|
54
|
+
`"@irisui/theme"`, `null` disables persistence).
|
|
55
|
+
|
|
56
|
+
`useTheme()` returns `{ palette, mode, resolvedMode, setPalette, setMode }`;
|
|
57
|
+
`resolvedMode` is `light` or `dark`, with `system` resolved.
|
|
58
|
+
|
|
59
|
+
### Avoiding a flash of the default theme
|
|
60
|
+
|
|
61
|
+
Render `ThemeScript` in `<head>` so the stored theme is applied before the
|
|
62
|
+
first paint:
|
|
63
|
+
|
|
64
|
+
```tsx
|
|
65
|
+
import { ThemeScript } from "@irisui/ui";
|
|
66
|
+
|
|
67
|
+
<head>
|
|
68
|
+
<ThemeScript />
|
|
69
|
+
</head>;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Outside React (a Vite plugin, a server template, a Server Component), use
|
|
73
|
+
`getThemeScript()` from the server-safe entry point; it returns the script as a
|
|
74
|
+
string:
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { getThemeScript } from "@irisui/ui/theme-script";
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Pass the same `storageKey`, `defaultPalette` and `defaultMode` as to
|
|
81
|
+
`ThemeProvider`.
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
[MIT](./LICENSE)
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/theme/theme-provider.tsx","../src/theme/constants.ts","../src/theme/theme-script.ts"],"sourcesContent":["export * from \"./theme\";\r\n","import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useLayoutEffect,\r\n useMemo,\r\n useState,\r\n useSyncExternalStore,\r\n type ReactNode,\r\n} from \"react\";\r\nimport {\r\n DEFAULT_MODE,\r\n DEFAULT_PALETTE,\r\n DEFAULT_STORAGE_KEY,\r\n isMode,\r\n isPalette,\r\n type Mode,\r\n type Palette,\r\n type ResolvedMode,\r\n} from \"./constants\";\r\nimport { getThemeScript, type ThemeScriptOptions } from \"./theme-script\";\r\n\r\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\r\n\r\nexport interface ThemeContextValue {\r\n palette: Palette;\r\n /** Mode as chosen: `light`, `dark` or `system`. */\r\n mode: Mode;\r\n /** Mode actually displayed, `system` resolved through `prefers-color-scheme`. */\r\n resolvedMode: ResolvedMode;\r\n setPalette: (palette: Palette) => void;\r\n setMode: (mode: Mode) => void;\r\n}\r\n\r\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\r\n\r\nexport interface ThemeProviderProps {\r\n children?: ReactNode;\r\n /** Controlled palette. */\r\n palette?: Palette;\r\n /** Initial palette when uncontrolled and nothing is stored. */\r\n defaultPalette?: Palette;\r\n onPaletteChange?: (palette: Palette) => void;\r\n /** Controlled mode. */\r\n mode?: Mode;\r\n /** Initial mode when uncontrolled and nothing is stored. */\r\n defaultMode?: Mode;\r\n onModeChange?: (mode: Mode) => void;\r\n /** localStorage key used to remember the user's choice. `null` disables persistence. */\r\n storageKey?: string | null;\r\n}\r\n\r\ninterface StoredTheme {\r\n palette?: unknown;\r\n mode?: unknown;\r\n}\r\n\r\nfunction readStorage(key: string): StoredTheme {\r\n try {\r\n const parsed: unknown = JSON.parse(window.localStorage.getItem(key) ?? \"{}\");\r\n return parsed && typeof parsed === \"object\" ? (parsed as StoredTheme) : {};\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\nfunction writeStorage(key: string, value: { palette: Palette; mode: Mode }): void {\r\n try {\r\n window.localStorage.setItem(key, JSON.stringify(value));\r\n } catch {\r\n // Storage unavailable (private mode, quota): the choice is simply not remembered.\r\n }\r\n}\r\n\r\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\r\n\r\nfunction subscribeColorScheme(onChange: () => void): () => void {\r\n const media = window.matchMedia(DARK_QUERY);\r\n media.addEventListener(\"change\", onChange);\r\n return () => media.removeEventListener(\"change\", onChange);\r\n}\r\n\r\nfunction useSystemMode(): ResolvedMode {\r\n return useSyncExternalStore(\r\n subscribeColorScheme,\r\n () => (window.matchMedia(DARK_QUERY).matches ? \"dark\" : \"light\"),\r\n () => \"light\",\r\n );\r\n}\r\n\r\n/**\r\n * Optional. Sets `data-palette` and `data-mode` on `<html>` and remembers the\r\n * user's choice. Without it, the HTML attributes alone are enough: the\r\n * `system` mode is handled in CSS by the tokens.\r\n */\r\nexport function ThemeProvider({\r\n children,\r\n palette: paletteProp,\r\n defaultPalette = DEFAULT_PALETTE,\r\n onPaletteChange,\r\n mode: modeProp,\r\n defaultMode = DEFAULT_MODE,\r\n onModeChange,\r\n storageKey = DEFAULT_STORAGE_KEY,\r\n}: ThemeProviderProps) {\r\n const [paletteState, setPaletteState] = useState<Palette>(defaultPalette);\r\n const [modeState, setModeState] = useState<Mode>(defaultMode);\r\n // Stored values are read after mount so that server and client render the same tree.\r\n const [restored, setRestored] = useState(false);\r\n\r\n const palette = paletteProp ?? paletteState;\r\n const mode = modeProp ?? modeState;\r\n const systemMode = useSystemMode();\r\n const resolvedMode: ResolvedMode = mode === \"system\" ? systemMode : mode;\r\n\r\n useIsomorphicLayoutEffect(() => {\r\n if (storageKey !== null) {\r\n const stored = readStorage(storageKey);\r\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\r\n if (isMode(stored.mode)) setModeState(stored.mode);\r\n }\r\n setRestored(true);\r\n }, [storageKey]);\r\n\r\n useIsomorphicLayoutEffect(() => {\r\n if (!restored) return;\r\n const root = document.documentElement;\r\n root.setAttribute(\"data-palette\", palette);\r\n root.setAttribute(\"data-mode\", mode);\r\n }, [restored, palette, mode]);\r\n\r\n // Keep several tabs in sync.\r\n useEffect(() => {\r\n if (storageKey === null) return;\r\n const onStorage = (event: StorageEvent) => {\r\n if (event.key !== storageKey) return;\r\n const stored = readStorage(storageKey);\r\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\r\n if (isMode(stored.mode)) setModeState(stored.mode);\r\n };\r\n window.addEventListener(\"storage\", onStorage);\r\n return () => window.removeEventListener(\"storage\", onStorage);\r\n }, [storageKey]);\r\n\r\n const setPalette = useCallback(\r\n (next: Palette) => {\r\n if (paletteProp === undefined) setPaletteState(next);\r\n if (storageKey !== null) writeStorage(storageKey, { palette: next, mode });\r\n onPaletteChange?.(next);\r\n },\r\n [paletteProp, storageKey, mode, onPaletteChange],\r\n );\r\n\r\n const setMode = useCallback(\r\n (next: Mode) => {\r\n if (modeProp === undefined) setModeState(next);\r\n if (storageKey !== null) writeStorage(storageKey, { palette, mode: next });\r\n onModeChange?.(next);\r\n },\r\n [modeProp, storageKey, palette, onModeChange],\r\n );\r\n\r\n const value = useMemo<ThemeContextValue>(\r\n () => ({ palette, mode, resolvedMode, setPalette, setMode }),\r\n [palette, mode, resolvedMode, setPalette, setMode],\r\n );\r\n\r\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\r\n}\r\n\r\n/** Reads and changes the current theme. Must be used inside `ThemeProvider`. */\r\nexport function useTheme(): ThemeContextValue {\r\n const context = useContext(ThemeContext);\r\n if (context === null) {\r\n throw new Error(\"useTheme must be used within a <ThemeProvider>.\");\r\n }\r\n return context;\r\n}\r\n\r\nexport interface ThemeScriptProps extends ThemeScriptOptions {\r\n nonce?: string;\r\n}\r\n\r\n/** Renders the anti-flash script; place it in `<head>`. */\r\nexport function ThemeScript({ nonce, ...options }: ThemeScriptProps) {\r\n return (\r\n <script\r\n nonce={nonce}\r\n suppressHydrationWarning\r\n dangerouslySetInnerHTML={{ __html: getThemeScript(options) }}\r\n />\r\n );\r\n}\r\n","export const PALETTES = [\"nacre\", \"aurore\", \"couchant\", \"glacier\"] as const;\r\nexport const MODES = [\"light\", \"dark\", \"system\"] as const;\r\n\r\nexport type Palette = (typeof PALETTES)[number];\r\nexport type Mode = (typeof MODES)[number];\r\nexport type ResolvedMode = Exclude<Mode, \"system\">;\r\n\r\nexport const DEFAULT_PALETTE: Palette = \"nacre\";\r\nexport const DEFAULT_MODE: Mode = \"system\";\r\nexport const DEFAULT_STORAGE_KEY = \"@irisui/theme\";\r\n\r\nexport const isPalette = (value: unknown): value is Palette =>\r\n (PALETTES as readonly unknown[]).includes(value);\r\nexport const isMode = (value: unknown): value is Mode =>\r\n (MODES as readonly unknown[]).includes(value);\r\n","import {\r\n DEFAULT_MODE,\r\n DEFAULT_PALETTE,\r\n DEFAULT_STORAGE_KEY,\r\n MODES,\r\n PALETTES,\r\n type Mode,\r\n type Palette,\r\n} from \"./constants\";\r\n\r\nexport interface ThemeScriptOptions {\r\n storageKey?: string;\r\n defaultPalette?: Palette;\r\n defaultMode?: Mode;\r\n}\r\n\r\n/**\r\n * Returns an inline script that applies the stored palette and mode to\r\n * `<html>` before the first paint, to avoid a flash of the default theme.\r\n * Optional: without it the theme still works from the HTML attributes alone.\r\n */\r\nexport function getThemeScript({\r\n storageKey = DEFAULT_STORAGE_KEY,\r\n defaultPalette = DEFAULT_PALETTE,\r\n defaultMode = DEFAULT_MODE,\r\n}: ThemeScriptOptions = {}): string {\r\n const args = [storageKey, PALETTES, MODES, defaultPalette, defaultMode]\r\n .map((arg) => JSON.stringify(arg))\r\n .join(\",\");\r\n return `(function(k,P,M,dp,dm){var d=document.documentElement,s={};try{s=JSON.parse(localStorage.getItem(k)||\"{}\")||{}}catch(e){}d.setAttribute(\"data-palette\",P.indexOf(s.palette)>-1?s.palette:dp);d.setAttribute(\"data-mode\",M.indexOf(s.mode)>-1?s.mode:dm)})(${args})`;\r\n}\r\n\r\nexport {\r\n DEFAULT_MODE,\r\n DEFAULT_PALETTE,\r\n DEFAULT_STORAGE_KEY,\r\n MODES,\r\n PALETTES,\r\n type Mode,\r\n type Palette,\r\n type ResolvedMode,\r\n} from \"./constants\";\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/theme/theme-provider.tsx","../src/theme/constants.ts","../src/theme/theme-script.ts"],"sourcesContent":["export * from \"./theme\";\n","import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\nimport {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n isMode,\n isPalette,\n type Mode,\n type Palette,\n type ResolvedMode,\n} from \"./constants\";\nimport { getThemeScript, type ThemeScriptOptions } from \"./theme-script\";\n\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nexport interface ThemeContextValue {\n palette: Palette;\n /** Mode as chosen: `light`, `dark` or `system`. */\n mode: Mode;\n /** Mode actually displayed, `system` resolved through `prefers-color-scheme`. */\n resolvedMode: ResolvedMode;\n setPalette: (palette: Palette) => void;\n setMode: (mode: Mode) => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children?: ReactNode;\n /** Controlled palette. */\n palette?: Palette;\n /** Initial palette when uncontrolled and nothing is stored. */\n defaultPalette?: Palette;\n onPaletteChange?: (palette: Palette) => void;\n /** Controlled mode. */\n mode?: Mode;\n /** Initial mode when uncontrolled and nothing is stored. */\n defaultMode?: Mode;\n onModeChange?: (mode: Mode) => void;\n /** localStorage key used to remember the user's choice. `null` disables persistence. */\n storageKey?: string | null;\n}\n\ninterface StoredTheme {\n palette?: unknown;\n mode?: unknown;\n}\n\nfunction readStorage(key: string): StoredTheme {\n try {\n const parsed: unknown = JSON.parse(window.localStorage.getItem(key) ?? \"{}\");\n return parsed && typeof parsed === \"object\" ? (parsed as StoredTheme) : {};\n } catch {\n return {};\n }\n}\n\nfunction writeStorage(key: string, value: { palette: Palette; mode: Mode }): void {\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // Storage unavailable (private mode, quota): the choice is simply not remembered.\n }\n}\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\nfunction subscribeColorScheme(onChange: () => void): () => void {\n const media = window.matchMedia(DARK_QUERY);\n media.addEventListener(\"change\", onChange);\n return () => media.removeEventListener(\"change\", onChange);\n}\n\nfunction useSystemMode(): ResolvedMode {\n return useSyncExternalStore(\n subscribeColorScheme,\n () => (window.matchMedia(DARK_QUERY).matches ? \"dark\" : \"light\"),\n () => \"light\",\n );\n}\n\n/**\n * Optional. Sets `data-palette` and `data-mode` on `<html>` and remembers the\n * user's choice. Without it, the HTML attributes alone are enough: the\n * `system` mode is handled in CSS by the tokens.\n */\nexport function ThemeProvider({\n children,\n palette: paletteProp,\n defaultPalette = DEFAULT_PALETTE,\n onPaletteChange,\n mode: modeProp,\n defaultMode = DEFAULT_MODE,\n onModeChange,\n storageKey = DEFAULT_STORAGE_KEY,\n}: ThemeProviderProps) {\n const [paletteState, setPaletteState] = useState<Palette>(defaultPalette);\n const [modeState, setModeState] = useState<Mode>(defaultMode);\n // Stored values are read after mount so that server and client render the same tree.\n const [restored, setRestored] = useState(false);\n\n const palette = paletteProp ?? paletteState;\n const mode = modeProp ?? modeState;\n const systemMode = useSystemMode();\n const resolvedMode: ResolvedMode = mode === \"system\" ? systemMode : mode;\n\n useIsomorphicLayoutEffect(() => {\n if (storageKey !== null) {\n const stored = readStorage(storageKey);\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\n if (isMode(stored.mode)) setModeState(stored.mode);\n }\n setRestored(true);\n }, [storageKey]);\n\n useIsomorphicLayoutEffect(() => {\n if (!restored) return;\n const root = document.documentElement;\n root.setAttribute(\"data-palette\", palette);\n root.setAttribute(\"data-mode\", mode);\n }, [restored, palette, mode]);\n\n // Keep several tabs in sync.\n useEffect(() => {\n if (storageKey === null) return;\n const onStorage = (event: StorageEvent) => {\n if (event.key !== storageKey) return;\n const stored = readStorage(storageKey);\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\n if (isMode(stored.mode)) setModeState(stored.mode);\n };\n window.addEventListener(\"storage\", onStorage);\n return () => window.removeEventListener(\"storage\", onStorage);\n }, [storageKey]);\n\n const setPalette = useCallback(\n (next: Palette) => {\n if (paletteProp === undefined) setPaletteState(next);\n if (storageKey !== null) writeStorage(storageKey, { palette: next, mode });\n onPaletteChange?.(next);\n },\n [paletteProp, storageKey, mode, onPaletteChange],\n );\n\n const setMode = useCallback(\n (next: Mode) => {\n if (modeProp === undefined) setModeState(next);\n if (storageKey !== null) writeStorage(storageKey, { palette, mode: next });\n onModeChange?.(next);\n },\n [modeProp, storageKey, palette, onModeChange],\n );\n\n const value = useMemo<ThemeContextValue>(\n () => ({ palette, mode, resolvedMode, setPalette, setMode }),\n [palette, mode, resolvedMode, setPalette, setMode],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/** Reads and changes the current theme. Must be used inside `ThemeProvider`. */\nexport function useTheme(): ThemeContextValue {\n const context = useContext(ThemeContext);\n if (context === null) {\n throw new Error(\"useTheme must be used within a <ThemeProvider>.\");\n }\n return context;\n}\n\nexport interface ThemeScriptProps extends ThemeScriptOptions {\n nonce?: string;\n}\n\n/** Renders the anti-flash script; place it in `<head>`. */\nexport function ThemeScript({ nonce, ...options }: ThemeScriptProps) {\n return (\n <script\n nonce={nonce}\n suppressHydrationWarning\n dangerouslySetInnerHTML={{ __html: getThemeScript(options) }}\n />\n );\n}\n","export const PALETTES = [\"nacre\", \"aurore\", \"couchant\", \"glacier\"] as const;\nexport const MODES = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Palette = (typeof PALETTES)[number];\nexport type Mode = (typeof MODES)[number];\nexport type ResolvedMode = Exclude<Mode, \"system\">;\n\nexport const DEFAULT_PALETTE: Palette = \"nacre\";\nexport const DEFAULT_MODE: Mode = \"system\";\nexport const DEFAULT_STORAGE_KEY = \"@irisui/theme\";\n\nexport const isPalette = (value: unknown): value is Palette =>\n (PALETTES as readonly unknown[]).includes(value);\nexport const isMode = (value: unknown): value is Mode =>\n (MODES as readonly unknown[]).includes(value);\n","import {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n} from \"./constants\";\n\nexport interface ThemeScriptOptions {\n storageKey?: string;\n defaultPalette?: Palette;\n defaultMode?: Mode;\n}\n\n/**\n * Returns an inline script that applies the stored palette and mode to\n * `<html>` before the first paint, to avoid a flash of the default theme.\n * Optional: without it the theme still works from the HTML attributes alone.\n */\nexport function getThemeScript({\n storageKey = DEFAULT_STORAGE_KEY,\n defaultPalette = DEFAULT_PALETTE,\n defaultMode = DEFAULT_MODE,\n}: ThemeScriptOptions = {}): string {\n const args = [storageKey, PALETTES, MODES, defaultPalette, defaultMode]\n .map((arg) => JSON.stringify(arg))\n .join(\",\");\n return `(function(k,P,M,dp,dm){var d=document.documentElement,s={};try{s=JSON.parse(localStorage.getItem(k)||\"{}\")||{}}catch(e){}d.setAttribute(\"data-palette\",P.indexOf(s.palette)>-1?s.palette:dp);d.setAttribute(\"data-mode\",M.indexOf(s.mode)>-1?s.mode:dm)})(${args})`;\n}\n\nexport {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n type ResolvedMode,\n} from \"./constants\";\n"],"mappings":"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","names":[]}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/theme/theme-provider.tsx","../src/theme/constants.ts","../src/theme/theme-script.ts"],"sourcesContent":["import {\r\n createContext,\r\n useCallback,\r\n useContext,\r\n useEffect,\r\n useLayoutEffect,\r\n useMemo,\r\n useState,\r\n useSyncExternalStore,\r\n type ReactNode,\r\n} from \"react\";\r\nimport {\r\n DEFAULT_MODE,\r\n DEFAULT_PALETTE,\r\n DEFAULT_STORAGE_KEY,\r\n isMode,\r\n isPalette,\r\n type Mode,\r\n type Palette,\r\n type ResolvedMode,\r\n} from \"./constants\";\r\nimport { getThemeScript, type ThemeScriptOptions } from \"./theme-script\";\r\n\r\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\r\n\r\nexport interface ThemeContextValue {\r\n palette: Palette;\r\n /** Mode as chosen: `light`, `dark` or `system`. */\r\n mode: Mode;\r\n /** Mode actually displayed, `system` resolved through `prefers-color-scheme`. */\r\n resolvedMode: ResolvedMode;\r\n setPalette: (palette: Palette) => void;\r\n setMode: (mode: Mode) => void;\r\n}\r\n\r\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\r\n\r\nexport interface ThemeProviderProps {\r\n children?: ReactNode;\r\n /** Controlled palette. */\r\n palette?: Palette;\r\n /** Initial palette when uncontrolled and nothing is stored. */\r\n defaultPalette?: Palette;\r\n onPaletteChange?: (palette: Palette) => void;\r\n /** Controlled mode. */\r\n mode?: Mode;\r\n /** Initial mode when uncontrolled and nothing is stored. */\r\n defaultMode?: Mode;\r\n onModeChange?: (mode: Mode) => void;\r\n /** localStorage key used to remember the user's choice. `null` disables persistence. */\r\n storageKey?: string | null;\r\n}\r\n\r\ninterface StoredTheme {\r\n palette?: unknown;\r\n mode?: unknown;\r\n}\r\n\r\nfunction readStorage(key: string): StoredTheme {\r\n try {\r\n const parsed: unknown = JSON.parse(window.localStorage.getItem(key) ?? \"{}\");\r\n return parsed && typeof parsed === \"object\" ? (parsed as StoredTheme) : {};\r\n } catch {\r\n return {};\r\n }\r\n}\r\n\r\nfunction writeStorage(key: string, value: { palette: Palette; mode: Mode }): void {\r\n try {\r\n window.localStorage.setItem(key, JSON.stringify(value));\r\n } catch {\r\n // Storage unavailable (private mode, quota): the choice is simply not remembered.\r\n }\r\n}\r\n\r\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\r\n\r\nfunction subscribeColorScheme(onChange: () => void): () => void {\r\n const media = window.matchMedia(DARK_QUERY);\r\n media.addEventListener(\"change\", onChange);\r\n return () => media.removeEventListener(\"change\", onChange);\r\n}\r\n\r\nfunction useSystemMode(): ResolvedMode {\r\n return useSyncExternalStore(\r\n subscribeColorScheme,\r\n () => (window.matchMedia(DARK_QUERY).matches ? \"dark\" : \"light\"),\r\n () => \"light\",\r\n );\r\n}\r\n\r\n/**\r\n * Optional. Sets `data-palette` and `data-mode` on `<html>` and remembers the\r\n * user's choice. Without it, the HTML attributes alone are enough: the\r\n * `system` mode is handled in CSS by the tokens.\r\n */\r\nexport function ThemeProvider({\r\n children,\r\n palette: paletteProp,\r\n defaultPalette = DEFAULT_PALETTE,\r\n onPaletteChange,\r\n mode: modeProp,\r\n defaultMode = DEFAULT_MODE,\r\n onModeChange,\r\n storageKey = DEFAULT_STORAGE_KEY,\r\n}: ThemeProviderProps) {\r\n const [paletteState, setPaletteState] = useState<Palette>(defaultPalette);\r\n const [modeState, setModeState] = useState<Mode>(defaultMode);\r\n // Stored values are read after mount so that server and client render the same tree.\r\n const [restored, setRestored] = useState(false);\r\n\r\n const palette = paletteProp ?? paletteState;\r\n const mode = modeProp ?? modeState;\r\n const systemMode = useSystemMode();\r\n const resolvedMode: ResolvedMode = mode === \"system\" ? systemMode : mode;\r\n\r\n useIsomorphicLayoutEffect(() => {\r\n if (storageKey !== null) {\r\n const stored = readStorage(storageKey);\r\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\r\n if (isMode(stored.mode)) setModeState(stored.mode);\r\n }\r\n setRestored(true);\r\n }, [storageKey]);\r\n\r\n useIsomorphicLayoutEffect(() => {\r\n if (!restored) return;\r\n const root = document.documentElement;\r\n root.setAttribute(\"data-palette\", palette);\r\n root.setAttribute(\"data-mode\", mode);\r\n }, [restored, palette, mode]);\r\n\r\n // Keep several tabs in sync.\r\n useEffect(() => {\r\n if (storageKey === null) return;\r\n const onStorage = (event: StorageEvent) => {\r\n if (event.key !== storageKey) return;\r\n const stored = readStorage(storageKey);\r\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\r\n if (isMode(stored.mode)) setModeState(stored.mode);\r\n };\r\n window.addEventListener(\"storage\", onStorage);\r\n return () => window.removeEventListener(\"storage\", onStorage);\r\n }, [storageKey]);\r\n\r\n const setPalette = useCallback(\r\n (next: Palette) => {\r\n if (paletteProp === undefined) setPaletteState(next);\r\n if (storageKey !== null) writeStorage(storageKey, { palette: next, mode });\r\n onPaletteChange?.(next);\r\n },\r\n [paletteProp, storageKey, mode, onPaletteChange],\r\n );\r\n\r\n const setMode = useCallback(\r\n (next: Mode) => {\r\n if (modeProp === undefined) setModeState(next);\r\n if (storageKey !== null) writeStorage(storageKey, { palette, mode: next });\r\n onModeChange?.(next);\r\n },\r\n [modeProp, storageKey, palette, onModeChange],\r\n );\r\n\r\n const value = useMemo<ThemeContextValue>(\r\n () => ({ palette, mode, resolvedMode, setPalette, setMode }),\r\n [palette, mode, resolvedMode, setPalette, setMode],\r\n );\r\n\r\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\r\n}\r\n\r\n/** Reads and changes the current theme. Must be used inside `ThemeProvider`. */\r\nexport function useTheme(): ThemeContextValue {\r\n const context = useContext(ThemeContext);\r\n if (context === null) {\r\n throw new Error(\"useTheme must be used within a <ThemeProvider>.\");\r\n }\r\n return context;\r\n}\r\n\r\nexport interface ThemeScriptProps extends ThemeScriptOptions {\r\n nonce?: string;\r\n}\r\n\r\n/** Renders the anti-flash script; place it in `<head>`. */\r\nexport function ThemeScript({ nonce, ...options }: ThemeScriptProps) {\r\n return (\r\n <script\r\n nonce={nonce}\r\n suppressHydrationWarning\r\n dangerouslySetInnerHTML={{ __html: getThemeScript(options) }}\r\n />\r\n );\r\n}\r\n","export const PALETTES = [\"nacre\", \"aurore\", \"couchant\", \"glacier\"] as const;\r\nexport const MODES = [\"light\", \"dark\", \"system\"] as const;\r\n\r\nexport type Palette = (typeof PALETTES)[number];\r\nexport type Mode = (typeof MODES)[number];\r\nexport type ResolvedMode = Exclude<Mode, \"system\">;\r\n\r\nexport const DEFAULT_PALETTE: Palette = \"nacre\";\r\nexport const DEFAULT_MODE: Mode = \"system\";\r\nexport const DEFAULT_STORAGE_KEY = \"@irisui/theme\";\r\n\r\nexport const isPalette = (value: unknown): value is Palette =>\r\n (PALETTES as readonly unknown[]).includes(value);\r\nexport const isMode = (value: unknown): value is Mode =>\r\n (MODES as readonly unknown[]).includes(value);\r\n","import {\r\n DEFAULT_MODE,\r\n DEFAULT_PALETTE,\r\n DEFAULT_STORAGE_KEY,\r\n MODES,\r\n PALETTES,\r\n type Mode,\r\n type Palette,\r\n} from \"./constants\";\r\n\r\nexport interface ThemeScriptOptions {\r\n storageKey?: string;\r\n defaultPalette?: Palette;\r\n defaultMode?: Mode;\r\n}\r\n\r\n/**\r\n * Returns an inline script that applies the stored palette and mode to\r\n * `<html>` before the first paint, to avoid a flash of the default theme.\r\n * Optional: without it the theme still works from the HTML attributes alone.\r\n */\r\nexport function getThemeScript({\r\n storageKey = DEFAULT_STORAGE_KEY,\r\n defaultPalette = DEFAULT_PALETTE,\r\n defaultMode = DEFAULT_MODE,\r\n}: ThemeScriptOptions = {}): string {\r\n const args = [storageKey, PALETTES, MODES, defaultPalette, defaultMode]\r\n .map((arg) => JSON.stringify(arg))\r\n .join(\",\");\r\n return `(function(k,P,M,dp,dm){var d=document.documentElement,s={};try{s=JSON.parse(localStorage.getItem(k)||\"{}\")||{}}catch(e){}d.setAttribute(\"data-palette\",P.indexOf(s.palette)>-1?s.palette:dp);d.setAttribute(\"data-mode\",M.indexOf(s.mode)>-1?s.mode:dm)})(${args})`;\r\n}\r\n\r\nexport {\r\n DEFAULT_MODE,\r\n DEFAULT_PALETTE,\r\n DEFAULT_STORAGE_KEY,\r\n MODES,\r\n PALETTES,\r\n type Mode,\r\n type Palette,\r\n type ResolvedMode,\r\n} from \"./constants\";\r\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/theme/theme-provider.tsx","../src/theme/constants.ts","../src/theme/theme-script.ts"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useLayoutEffect,\n useMemo,\n useState,\n useSyncExternalStore,\n type ReactNode,\n} from \"react\";\nimport {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n isMode,\n isPalette,\n type Mode,\n type Palette,\n type ResolvedMode,\n} from \"./constants\";\nimport { getThemeScript, type ThemeScriptOptions } from \"./theme-script\";\n\nconst useIsomorphicLayoutEffect = typeof window === \"undefined\" ? useEffect : useLayoutEffect;\n\nexport interface ThemeContextValue {\n palette: Palette;\n /** Mode as chosen: `light`, `dark` or `system`. */\n mode: Mode;\n /** Mode actually displayed, `system` resolved through `prefers-color-scheme`. */\n resolvedMode: ResolvedMode;\n setPalette: (palette: Palette) => void;\n setMode: (mode: Mode) => void;\n}\n\nconst ThemeContext = createContext<ThemeContextValue | null>(null);\n\nexport interface ThemeProviderProps {\n children?: ReactNode;\n /** Controlled palette. */\n palette?: Palette;\n /** Initial palette when uncontrolled and nothing is stored. */\n defaultPalette?: Palette;\n onPaletteChange?: (palette: Palette) => void;\n /** Controlled mode. */\n mode?: Mode;\n /** Initial mode when uncontrolled and nothing is stored. */\n defaultMode?: Mode;\n onModeChange?: (mode: Mode) => void;\n /** localStorage key used to remember the user's choice. `null` disables persistence. */\n storageKey?: string | null;\n}\n\ninterface StoredTheme {\n palette?: unknown;\n mode?: unknown;\n}\n\nfunction readStorage(key: string): StoredTheme {\n try {\n const parsed: unknown = JSON.parse(window.localStorage.getItem(key) ?? \"{}\");\n return parsed && typeof parsed === \"object\" ? (parsed as StoredTheme) : {};\n } catch {\n return {};\n }\n}\n\nfunction writeStorage(key: string, value: { palette: Palette; mode: Mode }): void {\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // Storage unavailable (private mode, quota): the choice is simply not remembered.\n }\n}\n\nconst DARK_QUERY = \"(prefers-color-scheme: dark)\";\n\nfunction subscribeColorScheme(onChange: () => void): () => void {\n const media = window.matchMedia(DARK_QUERY);\n media.addEventListener(\"change\", onChange);\n return () => media.removeEventListener(\"change\", onChange);\n}\n\nfunction useSystemMode(): ResolvedMode {\n return useSyncExternalStore(\n subscribeColorScheme,\n () => (window.matchMedia(DARK_QUERY).matches ? \"dark\" : \"light\"),\n () => \"light\",\n );\n}\n\n/**\n * Optional. Sets `data-palette` and `data-mode` on `<html>` and remembers the\n * user's choice. Without it, the HTML attributes alone are enough: the\n * `system` mode is handled in CSS by the tokens.\n */\nexport function ThemeProvider({\n children,\n palette: paletteProp,\n defaultPalette = DEFAULT_PALETTE,\n onPaletteChange,\n mode: modeProp,\n defaultMode = DEFAULT_MODE,\n onModeChange,\n storageKey = DEFAULT_STORAGE_KEY,\n}: ThemeProviderProps) {\n const [paletteState, setPaletteState] = useState<Palette>(defaultPalette);\n const [modeState, setModeState] = useState<Mode>(defaultMode);\n // Stored values are read after mount so that server and client render the same tree.\n const [restored, setRestored] = useState(false);\n\n const palette = paletteProp ?? paletteState;\n const mode = modeProp ?? modeState;\n const systemMode = useSystemMode();\n const resolvedMode: ResolvedMode = mode === \"system\" ? systemMode : mode;\n\n useIsomorphicLayoutEffect(() => {\n if (storageKey !== null) {\n const stored = readStorage(storageKey);\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\n if (isMode(stored.mode)) setModeState(stored.mode);\n }\n setRestored(true);\n }, [storageKey]);\n\n useIsomorphicLayoutEffect(() => {\n if (!restored) return;\n const root = document.documentElement;\n root.setAttribute(\"data-palette\", palette);\n root.setAttribute(\"data-mode\", mode);\n }, [restored, palette, mode]);\n\n // Keep several tabs in sync.\n useEffect(() => {\n if (storageKey === null) return;\n const onStorage = (event: StorageEvent) => {\n if (event.key !== storageKey) return;\n const stored = readStorage(storageKey);\n if (isPalette(stored.palette)) setPaletteState(stored.palette);\n if (isMode(stored.mode)) setModeState(stored.mode);\n };\n window.addEventListener(\"storage\", onStorage);\n return () => window.removeEventListener(\"storage\", onStorage);\n }, [storageKey]);\n\n const setPalette = useCallback(\n (next: Palette) => {\n if (paletteProp === undefined) setPaletteState(next);\n if (storageKey !== null) writeStorage(storageKey, { palette: next, mode });\n onPaletteChange?.(next);\n },\n [paletteProp, storageKey, mode, onPaletteChange],\n );\n\n const setMode = useCallback(\n (next: Mode) => {\n if (modeProp === undefined) setModeState(next);\n if (storageKey !== null) writeStorage(storageKey, { palette, mode: next });\n onModeChange?.(next);\n },\n [modeProp, storageKey, palette, onModeChange],\n );\n\n const value = useMemo<ThemeContextValue>(\n () => ({ palette, mode, resolvedMode, setPalette, setMode }),\n [palette, mode, resolvedMode, setPalette, setMode],\n );\n\n return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;\n}\n\n/** Reads and changes the current theme. Must be used inside `ThemeProvider`. */\nexport function useTheme(): ThemeContextValue {\n const context = useContext(ThemeContext);\n if (context === null) {\n throw new Error(\"useTheme must be used within a <ThemeProvider>.\");\n }\n return context;\n}\n\nexport interface ThemeScriptProps extends ThemeScriptOptions {\n nonce?: string;\n}\n\n/** Renders the anti-flash script; place it in `<head>`. */\nexport function ThemeScript({ nonce, ...options }: ThemeScriptProps) {\n return (\n <script\n nonce={nonce}\n suppressHydrationWarning\n dangerouslySetInnerHTML={{ __html: getThemeScript(options) }}\n />\n );\n}\n","export const PALETTES = [\"nacre\", \"aurore\", \"couchant\", \"glacier\"] as const;\nexport const MODES = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Palette = (typeof PALETTES)[number];\nexport type Mode = (typeof MODES)[number];\nexport type ResolvedMode = Exclude<Mode, \"system\">;\n\nexport const DEFAULT_PALETTE: Palette = \"nacre\";\nexport const DEFAULT_MODE: Mode = \"system\";\nexport const DEFAULT_STORAGE_KEY = \"@irisui/theme\";\n\nexport const isPalette = (value: unknown): value is Palette =>\n (PALETTES as readonly unknown[]).includes(value);\nexport const isMode = (value: unknown): value is Mode =>\n (MODES as readonly unknown[]).includes(value);\n","import {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n} from \"./constants\";\n\nexport interface ThemeScriptOptions {\n storageKey?: string;\n defaultPalette?: Palette;\n defaultMode?: Mode;\n}\n\n/**\n * Returns an inline script that applies the stored palette and mode to\n * `<html>` before the first paint, to avoid a flash of the default theme.\n * Optional: without it the theme still works from the HTML attributes alone.\n */\nexport function getThemeScript({\n storageKey = DEFAULT_STORAGE_KEY,\n defaultPalette = DEFAULT_PALETTE,\n defaultMode = DEFAULT_MODE,\n}: ThemeScriptOptions = {}): string {\n const args = [storageKey, PALETTES, MODES, defaultPalette, defaultMode]\n .map((arg) => JSON.stringify(arg))\n .join(\",\");\n return `(function(k,P,M,dp,dm){var d=document.documentElement,s={};try{s=JSON.parse(localStorage.getItem(k)||\"{}\")||{}}catch(e){}d.setAttribute(\"data-palette\",P.indexOf(s.palette)>-1?s.palette:dp);d.setAttribute(\"data-mode\",M.indexOf(s.mode)>-1?s.mode:dm)})(${args})`;\n}\n\nexport {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n type ResolvedMode,\n} from \"./constants\";\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/theme/theme-script.ts","../src/theme/constants.ts"],"sourcesContent":["import {\
|
|
1
|
+
{"version":3,"sources":["../src/theme/theme-script.ts","../src/theme/constants.ts"],"sourcesContent":["import {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n} from \"./constants\";\n\nexport interface ThemeScriptOptions {\n storageKey?: string;\n defaultPalette?: Palette;\n defaultMode?: Mode;\n}\n\n/**\n * Returns an inline script that applies the stored palette and mode to\n * `<html>` before the first paint, to avoid a flash of the default theme.\n * Optional: without it the theme still works from the HTML attributes alone.\n */\nexport function getThemeScript({\n storageKey = DEFAULT_STORAGE_KEY,\n defaultPalette = DEFAULT_PALETTE,\n defaultMode = DEFAULT_MODE,\n}: ThemeScriptOptions = {}): string {\n const args = [storageKey, PALETTES, MODES, defaultPalette, defaultMode]\n .map((arg) => JSON.stringify(arg))\n .join(\",\");\n return `(function(k,P,M,dp,dm){var d=document.documentElement,s={};try{s=JSON.parse(localStorage.getItem(k)||\"{}\")||{}}catch(e){}d.setAttribute(\"data-palette\",P.indexOf(s.palette)>-1?s.palette:dp);d.setAttribute(\"data-mode\",M.indexOf(s.mode)>-1?s.mode:dm)})(${args})`;\n}\n\nexport {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n type ResolvedMode,\n} from \"./constants\";\n","export const PALETTES = [\"nacre\", \"aurore\", \"couchant\", \"glacier\"] as const;\nexport const MODES = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Palette = (typeof PALETTES)[number];\nexport type Mode = (typeof MODES)[number];\nexport type ResolvedMode = Exclude<Mode, \"system\">;\n\nexport const DEFAULT_PALETTE: Palette = \"nacre\";\nexport const DEFAULT_MODE: Mode = \"system\";\nexport const DEFAULT_STORAGE_KEY = \"@irisui/theme\";\n\nexport const isPalette = (value: unknown): value is Palette =>\n (PALETTES as readonly unknown[]).includes(value);\nexport const isMode = (value: unknown): value is Mode =>\n (MODES as readonly unknown[]).includes(value);\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAO,IAAM,WAAW,CAAC,SAAS,UAAU,YAAY,SAAS;AAC1D,IAAM,QAAQ,CAAC,SAAS,QAAQ,QAAQ;AAMxC,IAAM,kBAA2B;AACjC,IAAM,eAAqB;AAC3B,IAAM,sBAAsB;;;ADY5B,SAAS,eAAe;AAAA,EAC7B,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,cAAc;AAChB,IAAwB,CAAC,GAAW;AAClC,QAAM,OAAO,CAAC,YAAY,UAAU,OAAO,gBAAgB,WAAW,EACnE,IAAI,CAAC,QAAQ,KAAK,UAAU,GAAG,CAAC,EAChC,KAAK,GAAG;AACX,SAAO,6PAA6P,IAAI;AAC1Q;","names":[]}
|
package/dist/theme-script.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/theme/constants.ts","../src/theme/theme-script.ts"],"sourcesContent":["export const PALETTES = [\"nacre\", \"aurore\", \"couchant\", \"glacier\"] as const;\
|
|
1
|
+
{"version":3,"sources":["../src/theme/constants.ts","../src/theme/theme-script.ts"],"sourcesContent":["export const PALETTES = [\"nacre\", \"aurore\", \"couchant\", \"glacier\"] as const;\nexport const MODES = [\"light\", \"dark\", \"system\"] as const;\n\nexport type Palette = (typeof PALETTES)[number];\nexport type Mode = (typeof MODES)[number];\nexport type ResolvedMode = Exclude<Mode, \"system\">;\n\nexport const DEFAULT_PALETTE: Palette = \"nacre\";\nexport const DEFAULT_MODE: Mode = \"system\";\nexport const DEFAULT_STORAGE_KEY = \"@irisui/theme\";\n\nexport const isPalette = (value: unknown): value is Palette =>\n (PALETTES as readonly unknown[]).includes(value);\nexport const isMode = (value: unknown): value is Mode =>\n (MODES as readonly unknown[]).includes(value);\n","import {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n} from \"./constants\";\n\nexport interface ThemeScriptOptions {\n storageKey?: string;\n defaultPalette?: Palette;\n defaultMode?: Mode;\n}\n\n/**\n * Returns an inline script that applies the stored palette and mode to\n * `<html>` before the first paint, to avoid a flash of the default theme.\n * Optional: without it the theme still works from the HTML attributes alone.\n */\nexport function getThemeScript({\n storageKey = DEFAULT_STORAGE_KEY,\n defaultPalette = DEFAULT_PALETTE,\n defaultMode = DEFAULT_MODE,\n}: ThemeScriptOptions = {}): string {\n const args = [storageKey, PALETTES, MODES, defaultPalette, defaultMode]\n .map((arg) => JSON.stringify(arg))\n .join(\",\");\n return `(function(k,P,M,dp,dm){var d=document.documentElement,s={};try{s=JSON.parse(localStorage.getItem(k)||\"{}\")||{}}catch(e){}d.setAttribute(\"data-palette\",P.indexOf(s.palette)>-1?s.palette:dp);d.setAttribute(\"data-mode\",M.indexOf(s.mode)>-1?s.mode:dm)})(${args})`;\n}\n\nexport {\n DEFAULT_MODE,\n DEFAULT_PALETTE,\n DEFAULT_STORAGE_KEY,\n MODES,\n PALETTES,\n type Mode,\n type Palette,\n type ResolvedMode,\n} from \"./constants\";\n"],"mappings":";AAAO,IAAM,WAAW,CAAC,SAAS,UAAU,YAAY,SAAS;AAC1D,IAAM,QAAQ,CAAC,SAAS,QAAQ,QAAQ;AAMxC,IAAM,kBAA2B;AACjC,IAAM,eAAqB;AAC3B,IAAM,sBAAsB;;;ACY5B,SAAS,eAAe;AAAA,EAC7B,aAAa;AAAA,EACb,iBAAiB;AAAA,EACjB,cAAc;AAChB,IAAwB,CAAC,GAAW;AAClC,QAAM,OAAO,CAAC,YAAY,UAAU,OAAO,gBAAgB,WAAW,EACnE,IAAI,CAAC,QAAQ,KAAK,UAAU,GAAG,CAAC,EAChC,KAAK,GAAG;AACX,SAAO,6PAA6P,IAAI;AAC1Q;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irisui/ui",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Accessible React components in the Verre irisé style.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@irisui/
|
|
38
|
-
"@irisui/
|
|
37
|
+
"@irisui/tokens": "0.1.2",
|
|
38
|
+
"@irisui/utils": "0.1.1"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"react": "^18.2.0 || ^19.0.0",
|