@womp/kookie-ui 0.12.0 → 0.13.0
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/cjs/hooks/appearance-script.d.ts +16 -0
- package/dist/cjs/hooks/appearance-script.d.ts.map +1 -0
- package/dist/cjs/hooks/appearance-script.js +12 -0
- package/dist/cjs/hooks/appearance-script.js.map +7 -0
- package/dist/cjs/hooks/index.d.ts +3 -0
- package/dist/cjs/hooks/index.d.ts.map +1 -1
- package/dist/cjs/hooks/index.js +12 -2
- package/dist/cjs/hooks/index.js.map +4 -4
- package/dist/cjs/hooks/use-appearance.d.ts +30 -0
- package/dist/cjs/hooks/use-appearance.d.ts.map +1 -0
- package/dist/cjs/hooks/use-appearance.js +12 -0
- package/dist/cjs/hooks/use-appearance.js.map +7 -0
- package/dist/cjs/index.js +15 -5
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/chunk-2KZ4NI3X.js +1 -0
- package/dist/esm/chunk-HT7RFHLW.js +2 -0
- package/dist/esm/chunk-HT7RFHLW.js.map +7 -0
- package/dist/esm/chunk-IUER6BNH.js +12 -0
- package/dist/esm/chunk-IUER6BNH.js.map +7 -0
- package/dist/esm/hooks/appearance-script.d.ts +16 -0
- package/dist/esm/hooks/appearance-script.d.ts.map +1 -0
- package/dist/esm/hooks/appearance-script.js +2 -0
- package/dist/esm/hooks/appearance-script.js.map +7 -0
- package/dist/esm/hooks/index.d.ts +3 -0
- package/dist/esm/hooks/index.d.ts.map +1 -1
- package/dist/esm/hooks/index.js +1 -1
- package/dist/esm/hooks/use-appearance.d.ts +30 -0
- package/dist/esm/hooks/use-appearance.d.ts.map +1 -0
- package/dist/esm/hooks/use-appearance.js +2 -0
- package/dist/esm/hooks/use-appearance.js.map +7 -0
- package/dist/esm/index.js +1 -1
- package/package.json +11 -1
- package/src/hooks/appearance-script.ts +28 -0
- package/src/hooks/index.ts +3 -0
- package/src/hooks/use-appearance.ts +119 -0
- package/dist/esm/chunk-EL7YGOTY.js +0 -1
- /package/dist/esm/{chunk-EL7YGOTY.js.map → chunk-2KZ4NI3X.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=chunk-2KZ4NI3X.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a as n}from"./chunk-IUER6BNH.js";import*as h from"react";var o=new Set;function r(){for(let e of o)e()}function c(){try{let e=globalThis.localStorage?.getItem(n);return e==="light"||e==="dark"?e:"system"}catch{return"system"}}function a(){return window.matchMedia("(prefers-color-scheme: dark)").matches?"dark":"light"}function i(e){let t=document.documentElement;t.classList.remove("light","dark"),t.classList.add(e),t.setAttribute("data-appearance",e),t.style.colorScheme=e}function u(){let e=document.documentElement;return e.classList.contains("dark")||e.classList.contains("dark-theme")?"dark":e.classList.contains("light")||e.classList.contains("light-theme")?"light":a()}var s=null;function v(){let e=c(),t=u();return(!s||s.choice!==e||s.resolved!==t)&&(s={choice:e,resolved:t}),s}var g={choice:"system",resolved:"light"};function f(e){o.add(e);let t=window.matchMedia("(prefers-color-scheme: dark)"),l=()=>{c()==="system"&&(i(a()),r())},p=m=>{if(m.key!==n)return;let d=c();i(d==="system"?a():d),r()};return t.addEventListener("change",l),window.addEventListener("storage",p),()=>{o.delete(e),t.removeEventListener("change",l),window.removeEventListener("storage",p)}}function A(e){try{e==="system"?localStorage.removeItem(n):localStorage.setItem(n,e)}catch{}i(e==="system"?a():e),r()}function S(){let{choice:e,resolved:t}=h.useSyncExternalStore(f,v,()=>g);return{choice:e,appearance:t,setAppearance:A}}export{A as a,S as b};
|
|
2
|
+
//# sourceMappingURL=chunk-HT7RFHLW.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/use-appearance.ts"],
|
|
4
|
+
"sourcesContent": ["'use client';\n\nimport * as React from 'react';\nimport { APPEARANCE_STORAGE_KEY } from './appearance-script.js';\n\n/**\n * Light and dark mode, decided before the first paint and kept on `<html>`.\n *\n * The server cannot know the visitor's mode, so `appearanceScript` runs synchronously in\n * `<head>`: the stored choice first, `prefers-color-scheme` otherwise. It writes the `light`\n * or `dark` class (which every token and component stylesheet already keys off) and\n * `data-appearance` on `<html>`. The root `<Theme>` keeps `appearance=\"inherit\"`, so it\n * writes no class of its own and `<html>` is the one source of truth. Nested themes and\n * portals inherit it from there.\n *\n * `useAppearance` is the client half: an external store over `<html>` and the same storage\n * key. It never mirrors the mode into React state that could disagree with the DOM.\n */\n\ntype AppearanceChoice = 'system' | 'light' | 'dark';\ntype ResolvedAppearance = 'light' | 'dark';\n\nconst listeners = new Set<() => void>();\n\nfunction emit() {\n for (const listener of listeners) listener();\n}\n\nfunction readChoice(): AppearanceChoice {\n try {\n const value = globalThis.localStorage?.getItem(APPEARANCE_STORAGE_KEY);\n return value === 'light' || value === 'dark' ? value : 'system';\n } catch {\n return 'system';\n }\n}\n\nfunction systemAppearance(): ResolvedAppearance {\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nfunction stamp(mode: ResolvedAppearance) {\n const root = document.documentElement;\n root.classList.remove('light', 'dark');\n root.classList.add(mode);\n root.setAttribute('data-appearance', mode);\n root.style.colorScheme = mode;\n}\n\nfunction readResolved(): ResolvedAppearance {\n const root = document.documentElement;\n if (root.classList.contains('dark') || root.classList.contains('dark-theme')) return 'dark';\n if (root.classList.contains('light') || root.classList.contains('light-theme')) return 'light';\n return systemAppearance();\n}\n\nlet snapshot: { choice: AppearanceChoice; resolved: ResolvedAppearance } | null = null;\n\nfunction getSnapshot() {\n const choice = readChoice();\n const resolved = readResolved();\n if (!snapshot || snapshot.choice !== choice || snapshot.resolved !== resolved) {\n snapshot = { choice, resolved };\n }\n return snapshot;\n}\n\nconst serverSnapshot = { choice: 'system' as AppearanceChoice, resolved: 'light' as ResolvedAppearance };\n\nfunction subscribe(listener: () => void) {\n listeners.add(listener);\n const media = window.matchMedia('(prefers-color-scheme: dark)');\n // With no stored choice, follow the system as it changes.\n const onSystemChange = () => {\n if (readChoice() === 'system') {\n stamp(systemAppearance());\n emit();\n }\n };\n // Another tab changed the choice.\n const onStorage = (event: StorageEvent) => {\n if (event.key !== APPEARANCE_STORAGE_KEY) return;\n const choice = readChoice();\n stamp(choice === 'system' ? systemAppearance() : choice);\n emit();\n };\n media.addEventListener('change', onSystemChange);\n window.addEventListener('storage', onStorage);\n return () => {\n listeners.delete(listener);\n media.removeEventListener('change', onSystemChange);\n window.removeEventListener('storage', onStorage);\n };\n}\n\n/** Applies a choice to `<html>` and stores it. The change applies even if storage is blocked. */\nfunction setAppearance(choice: AppearanceChoice) {\n try {\n if (choice === 'system') localStorage.removeItem(APPEARANCE_STORAGE_KEY);\n else localStorage.setItem(APPEARANCE_STORAGE_KEY, choice);\n } catch {\n // Storage is blocked: the mode still changes for this page.\n }\n stamp(choice === 'system' ? systemAppearance() : choice);\n emit();\n}\n\n/**\n * The page's light or dark mode. `choice` is what the person picked (`system` when they\n * haven't); `appearance` is what is showing. On the server and in the first client render it\n * reports `system` / `light`, then corrects without a hydration mismatch.\n */\nfunction useAppearance() {\n const { choice, resolved } = React.useSyncExternalStore(subscribe, getSnapshot, () => serverSnapshot);\n return { choice, appearance: resolved, setAppearance } as const;\n}\n\nexport { useAppearance, setAppearance };\nexport type { AppearanceChoice, ResolvedAppearance };\n"],
|
|
5
|
+
"mappings": "wCAEA,UAAYA,MAAW,QAoBvB,IAAMC,EAAY,IAAI,IAEtB,SAASC,GAAO,CACd,QAAWC,KAAYF,EAAWE,EAAS,CAC7C,CAEA,SAASC,GAA+B,CACtC,GAAI,CACF,IAAMC,EAAQ,WAAW,cAAc,QAAQC,CAAsB,EACrE,OAAOD,IAAU,SAAWA,IAAU,OAASA,EAAQ,QACzD,MAAQ,CACN,MAAO,QACT,CACF,CAEA,SAASE,GAAuC,CAC9C,OAAO,OAAO,WAAW,8BAA8B,EAAE,QAAU,OAAS,OAC9E,CAEA,SAASC,EAAMC,EAA0B,CACvC,IAAMC,EAAO,SAAS,gBACtBA,EAAK,UAAU,OAAO,QAAS,MAAM,EACrCA,EAAK,UAAU,IAAID,CAAI,EACvBC,EAAK,aAAa,kBAAmBD,CAAI,EACzCC,EAAK,MAAM,YAAcD,CAC3B,CAEA,SAASE,GAAmC,CAC1C,IAAMD,EAAO,SAAS,gBACtB,OAAIA,EAAK,UAAU,SAAS,MAAM,GAAKA,EAAK,UAAU,SAAS,YAAY,EAAU,OACjFA,EAAK,UAAU,SAAS,OAAO,GAAKA,EAAK,UAAU,SAAS,aAAa,EAAU,QAChFH,EAAiB,CAC1B,CAEA,IAAIK,EAA8E,KAElF,SAASC,GAAc,CACrB,IAAMC,EAASV,EAAW,EACpBW,EAAWJ,EAAa,EAC9B,OAAI,CAACC,GAAYA,EAAS,SAAWE,GAAUF,EAAS,WAAaG,KACnEH,EAAW,CAAE,OAAAE,EAAQ,SAAAC,CAAS,GAEzBH,CACT,CAEA,IAAMI,EAAiB,CAAE,OAAQ,SAA8B,SAAU,OAA8B,EAEvG,SAASC,EAAUd,EAAsB,CACvCF,EAAU,IAAIE,CAAQ,EACtB,IAAMe,EAAQ,OAAO,WAAW,8BAA8B,EAExDC,EAAiB,IAAM,CACvBf,EAAW,IAAM,WACnBI,EAAMD,EAAiB,CAAC,EACxBL,EAAK,EAET,EAEMkB,EAAaC,GAAwB,CACzC,GAAIA,EAAM,MAAQf,EAAwB,OAC1C,IAAMQ,EAASV,EAAW,EAC1BI,EAAMM,IAAW,SAAWP,EAAiB,EAAIO,CAAM,EACvDZ,EAAK,CACP,EACA,OAAAgB,EAAM,iBAAiB,SAAUC,CAAc,EAC/C,OAAO,iBAAiB,UAAWC,CAAS,EACrC,IAAM,CACXnB,EAAU,OAAOE,CAAQ,EACzBe,EAAM,oBAAoB,SAAUC,CAAc,EAClD,OAAO,oBAAoB,UAAWC,CAAS,CACjD,CACF,CAGA,SAASE,EAAcR,EAA0B,CAC/C,GAAI,CACEA,IAAW,SAAU,aAAa,WAAWR,CAAsB,EAClE,aAAa,QAAQA,EAAwBQ,CAAM,CAC1D,MAAQ,CAER,CACAN,EAAMM,IAAW,SAAWP,EAAiB,EAAIO,CAAM,EACvDZ,EAAK,CACP,CAOA,SAASqB,GAAgB,CACvB,GAAM,CAAE,OAAAT,EAAQ,SAAAC,CAAS,EAAU,uBAAqBE,EAAWJ,EAAa,IAAMG,CAAc,EACpG,MAAO,CAAE,OAAAF,EAAQ,WAAYC,EAAU,cAAAO,CAAc,CACvD",
|
|
6
|
+
"names": ["React", "listeners", "emit", "listener", "readChoice", "value", "APPEARANCE_STORAGE_KEY", "systemAppearance", "stamp", "mode", "root", "readResolved", "snapshot", "getSnapshot", "choice", "resolved", "serverSnapshot", "subscribe", "media", "onSystemChange", "onStorage", "event", "setAppearance", "useAppearance"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var e="kookie-appearance",a=`(function () {
|
|
2
|
+
var e = document.documentElement;
|
|
3
|
+
var a = null;
|
|
4
|
+
try { a = localStorage.getItem(${JSON.stringify(e)}); } catch (err) {}
|
|
5
|
+
var dark = a === "light" ? false : a === "dark" ? true : matchMedia("(prefers-color-scheme: dark)").matches;
|
|
6
|
+
var mode = dark ? "dark" : "light";
|
|
7
|
+
e.classList.remove("light", "dark");
|
|
8
|
+
e.classList.add(mode);
|
|
9
|
+
e.setAttribute("data-appearance", mode);
|
|
10
|
+
e.style.colorScheme = mode;
|
|
11
|
+
})();`;export{e as a,a as b};
|
|
12
|
+
//# sourceMappingURL=chunk-IUER6BNH.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/hooks/appearance-script.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * The pre-paint half of light and dark mode (see `use-appearance.ts`). Kept in its own module\n * with no client directive, so a server component such as a Next.js root layout can import the\n * string and inline it in `<head>`.\n */\n\nconst APPEARANCE_STORAGE_KEY = 'kookie-appearance';\n\n/**\n * Inline this in `<head>`, before any stylesheet paints, e.g.\n * `<script dangerouslySetInnerHTML={{ __html: appearanceScript }} />`.\n *\n * Storage is read inside its own try/catch: browsers that block site data throw on access,\n * and that must not skip the stamp. `matchMedia` always answers.\n */\nconst appearanceScript = `(function () {\n var e = document.documentElement;\n var a = null;\n try { a = localStorage.getItem(${JSON.stringify(APPEARANCE_STORAGE_KEY)}); } catch (err) {}\n var dark = a === \"light\" ? false : a === \"dark\" ? true : matchMedia(\"(prefers-color-scheme: dark)\").matches;\n var mode = dark ? \"dark\" : \"light\";\n e.classList.remove(\"light\", \"dark\");\n e.classList.add(mode);\n e.setAttribute(\"data-appearance\", mode);\n e.style.colorScheme = mode;\n})();`;\n\nexport { appearanceScript, APPEARANCE_STORAGE_KEY };\n"],
|
|
5
|
+
"mappings": "AAMA,IAAMA,EAAyB,oBASzBC,EAAmB;AAAA;AAAA;AAAA,mCAGU,KAAK,UAAUD,CAAsB,CAAC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
6
|
+
"names": ["APPEARANCE_STORAGE_KEY", "appearanceScript"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pre-paint half of light and dark mode (see `use-appearance.ts`). Kept in its own module
|
|
3
|
+
* with no client directive, so a server component such as a Next.js root layout can import the
|
|
4
|
+
* string and inline it in `<head>`.
|
|
5
|
+
*/
|
|
6
|
+
declare const APPEARANCE_STORAGE_KEY = "kookie-appearance";
|
|
7
|
+
/**
|
|
8
|
+
* Inline this in `<head>`, before any stylesheet paints, e.g.
|
|
9
|
+
* `<script dangerouslySetInnerHTML={{ __html: appearanceScript }} />`.
|
|
10
|
+
*
|
|
11
|
+
* Storage is read inside its own try/catch: browsers that block site data throw on access,
|
|
12
|
+
* and that must not skip the stamp. `matchMedia` always answers.
|
|
13
|
+
*/
|
|
14
|
+
declare const appearanceScript: string;
|
|
15
|
+
export { appearanceScript, APPEARANCE_STORAGE_KEY };
|
|
16
|
+
//# sourceMappingURL=appearance-script.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"appearance-script.d.ts","sourceRoot":"","sources":["../../../src/hooks/appearance-script.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,QAAA,MAAM,sBAAsB,sBAAsB,CAAC;AAEnD;;;;;;GAMG;AACH,QAAA,MAAM,gBAAgB,QAUhB,CAAC;AAEP,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -2,4 +2,7 @@ export { useLiveAnnouncer } from './use-live-announcer.js';
|
|
|
2
2
|
export { useBodyPointerEventsCleanup } from './use-body-pointer-events-cleanup.js';
|
|
3
3
|
export { useTooltipWrapper } from './use-tooltip-wrapper.js';
|
|
4
4
|
export { useToggleState } from './use-toggle-state.js';
|
|
5
|
+
export { useAppearance, setAppearance } from './use-appearance.js';
|
|
6
|
+
export { appearanceScript, APPEARANCE_STORAGE_KEY } from './appearance-script.js';
|
|
7
|
+
export type { AppearanceChoice, ResolvedAppearance } from './use-appearance.js';
|
|
5
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,2BAA2B,EAAE,MAAM,sCAAsC,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAClF,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/esm/hooks/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import"../chunk-
|
|
1
|
+
import"../chunk-2KZ4NI3X.js";import{a as g,b as h}from"../chunk-HT7RFHLW.js";import{a as e,b as f}from"../chunk-IUER6BNH.js";import{a as d}from"../chunk-VK7RNXUO.js";import{a as c}from"../chunk-EE54S7YV.js";import{a as b}from"../chunk-MHANZINI.js";import{a}from"../chunk-2N2AQNJH.js";import"../chunk-OS3FCYZY.js";export{e as APPEARANCE_STORAGE_KEY,f as appearanceScript,g as setAppearance,h as useAppearance,b as useBodyPointerEventsCleanup,c as useLiveAnnouncer,d as useToggleState,a as useTooltipWrapper};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Light and dark mode, decided before the first paint and kept on `<html>`.
|
|
3
|
+
*
|
|
4
|
+
* The server cannot know the visitor's mode, so `appearanceScript` runs synchronously in
|
|
5
|
+
* `<head>`: the stored choice first, `prefers-color-scheme` otherwise. It writes the `light`
|
|
6
|
+
* or `dark` class (which every token and component stylesheet already keys off) and
|
|
7
|
+
* `data-appearance` on `<html>`. The root `<Theme>` keeps `appearance="inherit"`, so it
|
|
8
|
+
* writes no class of its own and `<html>` is the one source of truth. Nested themes and
|
|
9
|
+
* portals inherit it from there.
|
|
10
|
+
*
|
|
11
|
+
* `useAppearance` is the client half: an external store over `<html>` and the same storage
|
|
12
|
+
* key. It never mirrors the mode into React state that could disagree with the DOM.
|
|
13
|
+
*/
|
|
14
|
+
type AppearanceChoice = 'system' | 'light' | 'dark';
|
|
15
|
+
type ResolvedAppearance = 'light' | 'dark';
|
|
16
|
+
/** Applies a choice to `<html>` and stores it. The change applies even if storage is blocked. */
|
|
17
|
+
declare function setAppearance(choice: AppearanceChoice): void;
|
|
18
|
+
/**
|
|
19
|
+
* The page's light or dark mode. `choice` is what the person picked (`system` when they
|
|
20
|
+
* haven't); `appearance` is what is showing. On the server and in the first client render it
|
|
21
|
+
* reports `system` / `light`, then corrects without a hydration mismatch.
|
|
22
|
+
*/
|
|
23
|
+
declare function useAppearance(): {
|
|
24
|
+
readonly choice: AppearanceChoice;
|
|
25
|
+
readonly appearance: ResolvedAppearance;
|
|
26
|
+
readonly setAppearance: typeof setAppearance;
|
|
27
|
+
};
|
|
28
|
+
export { useAppearance, setAppearance };
|
|
29
|
+
export type { AppearanceChoice, ResolvedAppearance };
|
|
30
|
+
//# sourceMappingURL=use-appearance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-appearance.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-appearance.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;GAYG;AAEH,KAAK,gBAAgB,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,CAAC;AACpD,KAAK,kBAAkB,GAAG,OAAO,GAAG,MAAM,CAAC;AA2E3C,iGAAiG;AACjG,iBAAS,aAAa,CAAC,MAAM,EAAE,gBAAgB,QAS9C;AAED;;;;GAIG;AACH,iBAAS,aAAa;;;;EAGrB;AAED,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,CAAC;AACxC,YAAY,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import"./chunk-
|
|
1
|
+
import"./chunk-2KZ4NI3X.js";import{a as No,b as Oo}from"./chunk-HT7RFHLW.js";import{a as Lo,b as Mo}from"./chunk-IUER6BNH.js";import"./chunk-C7EVYSWK.js";import{a as zo}from"./chunk-UYCPDGWA.js";import{b as Ao,c as Bo,d as Co,e as Do,f as Eo}from"./chunk-XLMTTSRO.js";import"./chunk-ZUOK3KFY.js";import{a as Fo}from"./chunk-CYU6BLZ5.js";import"./chunk-4WVNMGI5.js";import{a as Go}from"./chunk-YWENWZOU.js";import{c as qo}from"./chunk-ALWNS44Q.js";import"./chunk-MN3XALXD.js";import{a as so,b as uo}from"./chunk-D6PHTHF2.js";import{a as yo}from"./chunk-6G3MPL4A.js";import{a as wo}from"./chunk-VK7RNXUO.js";import{a as vo}from"./chunk-EE54S7YV.js";import{c as jo}from"./chunk-K3UAS7RW.js";import"./chunk-AUV6B4AH.js";import{h as ko}from"./chunk-36A5BYCN.js";import"./chunk-BALS3AVM.js";import{e as lo}from"./chunk-VNH7BJOA.js";import"./chunk-TZTUPQYK.js";import{a as no}from"./chunk-EPTYNVCU.js";import"./chunk-CLMOWP5T.js";import{a as go}from"./chunk-VZSQATP6.js";import{a as ho}from"./chunk-43A5GUSI.js";import"./chunk-XI5YXUCK.js";import{a as io}from"./chunk-KJUDZCYK.js";import"./chunk-AF6XKU3W.js";import{h as Io}from"./chunk-MJOXEUBT.js";import"./chunk-2EQWBT2S.js";import{p as Ho}from"./chunk-53SZ4Y23.js";import"./chunk-2CMM7MEL.js";import"./chunk-OJU5GCDZ.js";import{c as xo}from"./chunk-FG7XM7KJ.js";import"./chunk-FC2DBBJ3.js";import{h as bo}from"./chunk-VHECAEUP.js";import"./chunk-EBQZ3Z22.js";import{a as co}from"./chunk-VR7ZAFYN.js";import"./chunk-CRSHBENJ.js";import{g as Jo}from"./chunk-ZK7A2MZZ.js";import"./chunk-ADXZNG4F.js";import{a as ao}from"./chunk-OGXZHAQL.js";import"./chunk-NIX6Z3JG.js";import{c as mo}from"./chunk-2I7QRVJ5.js";import"./chunk-VLTXI4A7.js";import{a as po}from"./chunk-CIYF56YS.js";import"./chunk-STPMPY3X.js";import{a as to}from"./chunk-FHEHX7CE.js";import{a as oo}from"./chunk-XEZXKPLE.js";import{a as ro}from"./chunk-CDG5WH4U.js";import"./chunk-5XV4QEP5.js";import{a as eo}from"./chunk-GLERJXVS.js";import"./chunk-MCQCG6AS.js";import{c as fo}from"./chunk-CRTH5LNU.js";import"./chunk-OWMA6GTG.js";import{a as _}from"./chunk-YBSRX63K.js";import"./chunk-AGSSWWFI.js";import{i as Ko}from"./chunk-OIUCV5HK.js";import{e as $}from"./chunk-7UHKEJ7Q.js";import"./chunk-Y3A6ZU24.js";import{a as Y}from"./chunk-IHK3H2OM.js";import{a as X}from"./chunk-S5C5YRBS.js";import"./chunk-IBF5U4QW.js";import"./chunk-L6UXAITB.js";import"./chunk-GFOSP3PZ.js";import{a as Z}from"./chunk-CSKZZOI2.js";import"./chunk-JAMLW534.js";import{d as W}from"./chunk-MEO7MJMQ.js";import"./chunk-VJSZG35J.js";import{e as R}from"./chunk-RNTCHVFI.js";import{g as T}from"./chunk-7JJT42J2.js";import{a as S}from"./chunk-MHANZINI.js";import{o as U}from"./chunk-MZXHKM4V.js";import"./chunk-WOR3JCLX.js";import{a as V}from"./chunk-7FC7HF76.js";import"./chunk-4JQZUQY7.js";import{a as M}from"./chunk-N4GTDLRD.js";import{l as O}from"./chunk-T4OR62DQ.js";import{g as N}from"./chunk-WHFJ4JUN.js";import"./chunk-72AHPGPN.js";import"./chunk-RDAMYQFF.js";import{a as P}from"./chunk-4ME6O27S.js";import"./chunk-PPNWGWN3.js";import{n as Q}from"./chunk-PQKJNRMR.js";import"./chunk-OA7C34MJ.js";import"./chunk-NIIEIZKQ.js";import{c as J}from"./chunk-METRW4HG.js";import{a as I}from"./chunk-VX6ARMWP.js";import"./chunk-667BKCUG.js";import"./chunk-HAGKI7NA.js";import{c as K}from"./chunk-OMGZG52I.js";import"./chunk-V32EP3S7.js";import"./chunk-5SNHUVDA.js";import{a as L}from"./chunk-KFR3CK24.js";import"./chunk-UICSVJ3B.js";import"./chunk-KZWNGWIM.js";import{a as u}from"./chunk-4YFJWXL2.js";import{d as y}from"./chunk-CKMMUHKE.js";import"./chunk-OGVKSYLL.js";import{m as H}from"./chunk-EGMUQQRW.js";import{b as F,c as G}from"./chunk-L5PID6BU.js";import{a as z}from"./chunk-ISOLUTZF.js";import{a as E}from"./chunk-COWZAUZR.js";import"./chunk-5GXFBLBJ.js";import"./chunk-UBOOUDS4.js";import"./chunk-XPRPYCII.js";import"./chunk-5BWLY27A.js";import{a as A,b as B,c as C,d as D}from"./chunk-IX5HIHVI.js";import{a as w}from"./chunk-3R4YLW4A.js";import{a as v}from"./chunk-2N2AQNJH.js";import{a as k}from"./chunk-Y7LT3F6R.js";import{a as l}from"./chunk-HJU4I34X.js";import"./chunk-PS6NEG5A.js";import"./chunk-QFPHEY77.js";import{a as q}from"./chunk-KMPE7TJ6.js";import{a as n}from"./chunk-OLAWVEAI.js";import"./chunk-BPVVFHYD.js";import"./chunk-QQ62LO3X.js";import{a as s}from"./chunk-MS5J4MF6.js";import"./chunk-FGORMEAO.js";import"./chunk-ROSWI2CL.js";import{a as b}from"./chunk-ERNM3KVF.js";import{f as c}from"./chunk-Y5RCH23Z.js";import"./chunk-EDKQURDU.js";import{h}from"./chunk-IYCPYN4Z.js";import"./chunk-MJJ7XDIP.js";import{a as g}from"./chunk-5WRCK4R7.js";import"./chunk-4XKK32M4.js";import"./chunk-XKBLNVOD.js";import"./chunk-GCUGJXI2.js";import{f as j}from"./chunk-54JFIP5S.js";import{a as i}from"./chunk-JDGJSNWT.js";import"./chunk-OO652UQW.js";import{a as d}from"./chunk-GO7OHBHW.js";import"./chunk-PZ7OYATT.js";import"./chunk-G2CKYO2I.js";import"./chunk-AXUDFYLY.js";import"./chunk-KNQBAOMV.js";import"./chunk-FD2TR343.js";import"./chunk-WYADYDPN.js";import"./chunk-ZE2KAVXW.js";import"./chunk-ZWZGUBL4.js";import"./chunk-7RQNB63F.js";import{a as t,b as x,c as a}from"./chunk-SEY4NA45.js";import"./chunk-WAURCNAS.js";import"./chunk-F22ZRNWL.js";import"./chunk-XYVLQDGR.js";import"./chunk-YAQBIO2B.js";import"./chunk-AGNIFHOE.js";import"./chunk-HCNO7PX6.js";import"./chunk-GMHA5CFH.js";import"./chunk-A2XVR7GL.js";import"./chunk-BZZXXZ3O.js";import"./chunk-2LYX5S7H.js";import"./chunk-HWQ3NTJP.js";import"./chunk-3J6AUDID.js";import"./chunk-4LDVSTXI.js";import{a as f}from"./chunk-HF5WY5YE.js";import"./chunk-3KB2XWBE.js";import{a as m}from"./chunk-AGAQC4HH.js";import"./chunk-5T6K7VPP.js";import{a as e}from"./chunk-FHOCUZDR.js";import"./chunk-SMGDH54W.js";import"./chunk-SNR5VFWG.js";import{b as o,c as r}from"./chunk-FWB47YCH.js";import"./chunk-K53W3ELO.js";import"./chunk-TTFUM47A.js";import"./chunk-VX44UTQ5.js";import"./chunk-ERGJN7FJ.js";import"./chunk-5TXQLK4G.js";import"./chunk-JFWMAYEH.js";import"./chunk-KRJTW535.js";import"./chunk-F6V7GBNQ.js";import"./chunk-3KSGRY6I.js";import"./chunk-ZOY43B55.js";import{a as p}from"./chunk-E67CIG6A.js";import"./chunk-ARIV4AT5.js";import"./chunk-3XSB7YIM.js";import"./chunk-YA5DF67M.js";import"./chunk-GSTOGDAJ.js";import"./chunk-6FLLFCBU.js";import"./chunk-AX5S56DS.js";import"./chunk-E23RKOQJ.js";import"./chunk-JUY7CBSL.js";import"./chunk-WRWNN2NW.js";import"./chunk-QM3RCK5P.js";import"./chunk-RZCX6IBE.js";import"./chunk-NZJGC6ZW.js";import"./chunk-WMGIFPI2.js";import"./chunk-CFNESRYI.js";import"./chunk-ZNK6AH2G.js";import"./chunk-JSW6J6RS.js";import"./chunk-OS3FCYZY.js";export{Lo as APPEARANCE_STORAGE_KEY,b as AccessibleIcon,c as Accordion,h as AlertDialog,j as AppBar,k as AspectRatio,l as Avatar,q as Badge,s as Blockquote,u as Box,w as Button,y as Callout,i as Card,H as Chatbar,L as Checkbox,J as CheckboxCards,K as CheckboxGroup,C as ChevronDownIcon,M as Code,O as Combobox,G as Confirmation,P as Container,Q as ContextMenu,R as DataList,T as Dialog,U as DropdownMenu,V as Em,e as Flex,I as Grid,d as Heading,W as HoverCard,z as IconButton,Y as Image,Z as Inset,p as Kbd,_ as Link,Ko as MessageScroller,$ as Navbar,F as Notice,N as Popover,oo as Portal,ro as Progress,eo as Quote,po as Radio,fo as RadioCards,mo as RadioGroup,to as Reset,E as ScrollArea,ao as Section,xo as SegmentedControl,bo as Select,co as Separator,Jo as Sheet,Io as Shell,Ho as Sidebar,X as Skeleton,go as Slider,o as Slot,r as Slottable,f as Spinner,ho as Strong,io as Switch,jo as TabNav,ko as Table,lo as Tabs,g as Text,no as TextArea,qo as TextField,a as Theme,t as ThemeContext,so as ThemePanel,uo as ThemePanelContent,B as ThickCheckIcon,D as ThickChevronRightIcon,A as ThickDividerHorizontalIcon,yo as ToggleButton,zo as ToggleIconButton,Eo as Toolbar,Bo as ToolbarButton,Ao as ToolbarGroup,Co as ToolbarSeparator,Do as ToolbarTitle,n as Tooltip,Fo as UserCard,Go as VirtualMenu,m as VisuallyHidden,Mo as appearanceScript,No as setAppearance,Oo as useAppearance,S as useBodyPointerEventsCleanup,vo as useLiveAnnouncer,x as useThemeContext,wo as useToggleState,v as useTooltipWrapper};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@womp/kookie-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Kookie UI v1: a React component library with design tokens, flexible theming, a full layout shell, and comprehensive documentation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -48,6 +48,16 @@
|
|
|
48
48
|
"default": "./dist/esm/components/*.js"
|
|
49
49
|
}
|
|
50
50
|
},
|
|
51
|
+
"./appearance-script": {
|
|
52
|
+
"require": {
|
|
53
|
+
"types": "./dist/cjs/hooks/appearance-script.d.ts",
|
|
54
|
+
"default": "./dist/cjs/hooks/appearance-script.js"
|
|
55
|
+
},
|
|
56
|
+
"import": {
|
|
57
|
+
"types": "./dist/esm/hooks/appearance-script.d.ts",
|
|
58
|
+
"default": "./dist/esm/hooks/appearance-script.js"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
51
61
|
"./helpers": {
|
|
52
62
|
"require": {
|
|
53
63
|
"types": "./dist/cjs/helpers/index.d.ts",
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The pre-paint half of light and dark mode (see `use-appearance.ts`). Kept in its own module
|
|
3
|
+
* with no client directive, so a server component such as a Next.js root layout can import the
|
|
4
|
+
* string and inline it in `<head>`.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const APPEARANCE_STORAGE_KEY = 'kookie-appearance';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Inline this in `<head>`, before any stylesheet paints, e.g.
|
|
11
|
+
* `<script dangerouslySetInnerHTML={{ __html: appearanceScript }} />`.
|
|
12
|
+
*
|
|
13
|
+
* Storage is read inside its own try/catch: browsers that block site data throw on access,
|
|
14
|
+
* and that must not skip the stamp. `matchMedia` always answers.
|
|
15
|
+
*/
|
|
16
|
+
const appearanceScript = `(function () {
|
|
17
|
+
var e = document.documentElement;
|
|
18
|
+
var a = null;
|
|
19
|
+
try { a = localStorage.getItem(${JSON.stringify(APPEARANCE_STORAGE_KEY)}); } catch (err) {}
|
|
20
|
+
var dark = a === "light" ? false : a === "dark" ? true : matchMedia("(prefers-color-scheme: dark)").matches;
|
|
21
|
+
var mode = dark ? "dark" : "light";
|
|
22
|
+
e.classList.remove("light", "dark");
|
|
23
|
+
e.classList.add(mode);
|
|
24
|
+
e.setAttribute("data-appearance", mode);
|
|
25
|
+
e.style.colorScheme = mode;
|
|
26
|
+
})();`;
|
|
27
|
+
|
|
28
|
+
export { appearanceScript, APPEARANCE_STORAGE_KEY };
|
package/src/hooks/index.ts
CHANGED
|
@@ -2,3 +2,6 @@ export { useLiveAnnouncer } from './use-live-announcer.js';
|
|
|
2
2
|
export { useBodyPointerEventsCleanup } from './use-body-pointer-events-cleanup.js';
|
|
3
3
|
export { useTooltipWrapper } from './use-tooltip-wrapper.js';
|
|
4
4
|
export { useToggleState } from './use-toggle-state.js';
|
|
5
|
+
export { useAppearance, setAppearance } from './use-appearance.js';
|
|
6
|
+
export { appearanceScript, APPEARANCE_STORAGE_KEY } from './appearance-script.js';
|
|
7
|
+
export type { AppearanceChoice, ResolvedAppearance } from './use-appearance.js';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { APPEARANCE_STORAGE_KEY } from './appearance-script.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Light and dark mode, decided before the first paint and kept on `<html>`.
|
|
8
|
+
*
|
|
9
|
+
* The server cannot know the visitor's mode, so `appearanceScript` runs synchronously in
|
|
10
|
+
* `<head>`: the stored choice first, `prefers-color-scheme` otherwise. It writes the `light`
|
|
11
|
+
* or `dark` class (which every token and component stylesheet already keys off) and
|
|
12
|
+
* `data-appearance` on `<html>`. The root `<Theme>` keeps `appearance="inherit"`, so it
|
|
13
|
+
* writes no class of its own and `<html>` is the one source of truth. Nested themes and
|
|
14
|
+
* portals inherit it from there.
|
|
15
|
+
*
|
|
16
|
+
* `useAppearance` is the client half: an external store over `<html>` and the same storage
|
|
17
|
+
* key. It never mirrors the mode into React state that could disagree with the DOM.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
type AppearanceChoice = 'system' | 'light' | 'dark';
|
|
21
|
+
type ResolvedAppearance = 'light' | 'dark';
|
|
22
|
+
|
|
23
|
+
const listeners = new Set<() => void>();
|
|
24
|
+
|
|
25
|
+
function emit() {
|
|
26
|
+
for (const listener of listeners) listener();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function readChoice(): AppearanceChoice {
|
|
30
|
+
try {
|
|
31
|
+
const value = globalThis.localStorage?.getItem(APPEARANCE_STORAGE_KEY);
|
|
32
|
+
return value === 'light' || value === 'dark' ? value : 'system';
|
|
33
|
+
} catch {
|
|
34
|
+
return 'system';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function systemAppearance(): ResolvedAppearance {
|
|
39
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function stamp(mode: ResolvedAppearance) {
|
|
43
|
+
const root = document.documentElement;
|
|
44
|
+
root.classList.remove('light', 'dark');
|
|
45
|
+
root.classList.add(mode);
|
|
46
|
+
root.setAttribute('data-appearance', mode);
|
|
47
|
+
root.style.colorScheme = mode;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function readResolved(): ResolvedAppearance {
|
|
51
|
+
const root = document.documentElement;
|
|
52
|
+
if (root.classList.contains('dark') || root.classList.contains('dark-theme')) return 'dark';
|
|
53
|
+
if (root.classList.contains('light') || root.classList.contains('light-theme')) return 'light';
|
|
54
|
+
return systemAppearance();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let snapshot: { choice: AppearanceChoice; resolved: ResolvedAppearance } | null = null;
|
|
58
|
+
|
|
59
|
+
function getSnapshot() {
|
|
60
|
+
const choice = readChoice();
|
|
61
|
+
const resolved = readResolved();
|
|
62
|
+
if (!snapshot || snapshot.choice !== choice || snapshot.resolved !== resolved) {
|
|
63
|
+
snapshot = { choice, resolved };
|
|
64
|
+
}
|
|
65
|
+
return snapshot;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const serverSnapshot = { choice: 'system' as AppearanceChoice, resolved: 'light' as ResolvedAppearance };
|
|
69
|
+
|
|
70
|
+
function subscribe(listener: () => void) {
|
|
71
|
+
listeners.add(listener);
|
|
72
|
+
const media = window.matchMedia('(prefers-color-scheme: dark)');
|
|
73
|
+
// With no stored choice, follow the system as it changes.
|
|
74
|
+
const onSystemChange = () => {
|
|
75
|
+
if (readChoice() === 'system') {
|
|
76
|
+
stamp(systemAppearance());
|
|
77
|
+
emit();
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
// Another tab changed the choice.
|
|
81
|
+
const onStorage = (event: StorageEvent) => {
|
|
82
|
+
if (event.key !== APPEARANCE_STORAGE_KEY) return;
|
|
83
|
+
const choice = readChoice();
|
|
84
|
+
stamp(choice === 'system' ? systemAppearance() : choice);
|
|
85
|
+
emit();
|
|
86
|
+
};
|
|
87
|
+
media.addEventListener('change', onSystemChange);
|
|
88
|
+
window.addEventListener('storage', onStorage);
|
|
89
|
+
return () => {
|
|
90
|
+
listeners.delete(listener);
|
|
91
|
+
media.removeEventListener('change', onSystemChange);
|
|
92
|
+
window.removeEventListener('storage', onStorage);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Applies a choice to `<html>` and stores it. The change applies even if storage is blocked. */
|
|
97
|
+
function setAppearance(choice: AppearanceChoice) {
|
|
98
|
+
try {
|
|
99
|
+
if (choice === 'system') localStorage.removeItem(APPEARANCE_STORAGE_KEY);
|
|
100
|
+
else localStorage.setItem(APPEARANCE_STORAGE_KEY, choice);
|
|
101
|
+
} catch {
|
|
102
|
+
// Storage is blocked: the mode still changes for this page.
|
|
103
|
+
}
|
|
104
|
+
stamp(choice === 'system' ? systemAppearance() : choice);
|
|
105
|
+
emit();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The page's light or dark mode. `choice` is what the person picked (`system` when they
|
|
110
|
+
* haven't); `appearance` is what is showing. On the server and in the first client render it
|
|
111
|
+
* reports `system` / `light`, then corrects without a hydration mismatch.
|
|
112
|
+
*/
|
|
113
|
+
function useAppearance() {
|
|
114
|
+
const { choice, resolved } = React.useSyncExternalStore(subscribe, getSnapshot, () => serverSnapshot);
|
|
115
|
+
return { choice, appearance: resolved, setAppearance } as const;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export { useAppearance, setAppearance };
|
|
119
|
+
export type { AppearanceChoice, ResolvedAppearance };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
//# sourceMappingURL=chunk-EL7YGOTY.js.map
|
|
File without changes
|