@dbcdk/react-components 0.0.175 → 0.0.176

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.
Files changed (34) hide show
  1. package/README.md +17 -18
  2. package/dist/components/theme-button/ThemeButton.cjs +4 -15
  3. package/dist/components/theme-button/ThemeButton.d.ts +3 -4
  4. package/dist/components/theme-button/ThemeButton.js +4 -15
  5. package/dist/components/theme-script/ThemeScript.cjs +23 -16
  6. package/dist/components/theme-script/ThemeScript.d.ts +2 -6
  7. package/dist/components/theme-script/ThemeScript.js +24 -17
  8. package/dist/hooks/usePersistentState.cjs +6 -1
  9. package/dist/hooks/usePersistentState.d.ts +1 -1
  10. package/dist/hooks/usePersistentState.js +6 -1
  11. package/dist/hooks/useTheme.cjs +33 -96
  12. package/dist/hooks/useTheme.d.ts +3 -10
  13. package/dist/hooks/useTheme.js +34 -97
  14. package/dist/styles/themes/apply-product-theme.cjs +20 -0
  15. package/dist/styles/themes/apply-product-theme.d.ts +3 -0
  16. package/dist/styles/themes/apply-product-theme.js +18 -0
  17. package/dist/styles/themes/dbc/colors.css +1 -1
  18. package/dist/styles/themes/product-theme-tokens.cjs +39 -0
  19. package/dist/styles/themes/product-theme-tokens.d.ts +3 -0
  20. package/dist/styles/themes/product-theme-tokens.js +37 -0
  21. package/dist/styles/themes/product-themes.cjs +143 -0
  22. package/dist/styles/themes/product-themes.d.ts +11 -0
  23. package/dist/styles/themes/product-themes.js +140 -0
  24. package/dist/styles/themes/types.cjs +3 -2
  25. package/dist/styles/themes/types.d.ts +1 -13
  26. package/dist/styles/themes/types.js +3 -2
  27. package/dist/utils/sessionStorage.utils.cjs +53 -0
  28. package/dist/utils/sessionStorage.utils.d.ts +19 -0
  29. package/dist/utils/sessionStorage.utils.js +49 -0
  30. package/package.json +1 -1
  31. package/dist/styles/themes/filmstriben/theme.css +0 -108
  32. package/dist/themes/filmstriben.css +0 -4
  33. package/dist/types/assets.d.cjs +0 -2
  34. package/dist/types/assets.d.js +0 -1
package/README.md CHANGED
@@ -44,35 +44,30 @@ import '@dbcdk/react-components/styles.css'
44
44
 
45
45
  ---
46
46
 
47
- ### 3) Add the theme `<link>` in your root layout (Next.js example)
47
+ ### 3) Add `ThemeScript` in your root layout (Next.js example)
48
48
 
49
- The library uses theme CSS files that are dynamically loaded via a `<link>` tag in `<head>`.
50
- You **must** use the exported `LINK_ID` so the `useTheme()` hook can update the active theme at runtime.
49
+ The library's shared CSS foundation is still imported normally, but
50
+ product-theme switching is handled by `ThemeScript` + `useTheme()`.
51
+ `ThemeScript` restores the persisted color scheme and product theme before
52
+ paint, so the app avoids a flash of the wrong theme on first load.
51
53
 
52
54
  ```tsx
53
55
  import { ReactNode } from 'react'
54
- import { cookies } from 'next/headers'
55
56
 
56
- import { LINK_ID } from '@dbcdk/react-components'
57
+ import { ThemeScript } from '@dbcdk/react-components'
57
58
  import '@dbcdk/react-components/styles.css'
59
+ import '@dbcdk/react-components/themes/dbc.css'
58
60
 
59
- export default async function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
60
- const cookieStore = await cookies()
61
- const themeId = cookieStore.get('dbc_theme')?.value || 'light'
62
-
61
+ export default function RootLayout({ children }: Readonly<{ children: ReactNode }>) {
63
62
  return (
64
63
  <html lang="da">
65
- <head>
66
- <link id={LINK_ID} rel="stylesheet" href={`/themes/${themeId}.css`} />
67
- </head>
64
+ <head><ThemeScript defaultTheme="dbc" /></head>
68
65
  <body>{children}</body>
69
66
  </html>
70
67
  )
71
68
  }
72
69
  ```
73
70
 
74
- > Theme files are expected to be served from `/themes/<theme>.css`.
75
-
76
71
  ---
77
72
 
78
73
  ### 4) Switching theme in your application
@@ -86,19 +81,23 @@ import { AppHeader, Button, useTheme } from '@dbcdk/react-components'
86
81
  import { Moon, Sun } from 'lucide-react'
87
82
 
88
83
  export default function Header() {
89
- const { theme, switchTheme } = useTheme()
84
+ const { theme, switchTheme } = useTheme({ defaultTheme: 'dbc' })
90
85
 
91
86
  return (
92
87
  <AppHeader>
93
- <Button variant="outlined" onClick={() => switchTheme(theme === 'light' ? 'dark' : 'light')}>
94
- {theme === 'light' ? <Moon /> : <Sun />}
88
+ <Button
89
+ variant="outlined"
90
+ onClick={() => switchTheme(theme === 'dbc' ? 'filmstriben' : 'dbc')}
91
+ >
92
+ {theme === 'dbc' ? <Moon /> : <Sun />}
95
93
  </Button>
96
94
  </AppHeader>
97
95
  )
98
96
  }
99
97
  ```
100
98
 
101
- The hook updates the `<link>` tag automatically and persists the selected theme.
99
+ The hook updates `data-product-theme`, applies the selected product theme's
100
+ token overrides, and persists the selection.
102
101
 
103
102
  ---
104
103
 
@@ -9,11 +9,8 @@ var Popover = require('../../components/popover/Popover');
9
9
  var useTheme = require('../../hooks/useTheme');
10
10
  var types = require('../../styles/themes/types');
11
11
 
12
- function ThemeMenuSection({ themes, hrefs }) {
13
- const { theme, switchTheme } = useTheme.useTheme({
14
- defaultTheme: themes[0],
15
- hrefs
16
- });
12
+ function ThemeMenuSection({ themes }) {
13
+ const { theme, switchTheme } = useTheme.useTheme({ defaultTheme: themes[0] });
17
14
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18
15
  /* @__PURE__ */ jsxRuntime.jsx(Menu.Menu.Header, { children: "Tema" }),
19
16
  themes.map((id) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -29,16 +26,8 @@ function ThemeMenuSection({ themes, hrefs }) {
29
26
  ))
30
27
  ] });
31
28
  }
32
- function ThemeButton({
33
- themes,
34
- hrefs,
35
- size,
36
- variant = "outlined"
37
- }) {
38
- const { theme, switchTheme } = useTheme.useTheme({
39
- defaultTheme: themes[0],
40
- hrefs
41
- });
29
+ function ThemeButton({ themes, size, variant = "outlined" }) {
30
+ const { theme, switchTheme } = useTheme.useTheme({ defaultTheme: themes[0] });
42
31
  return /* @__PURE__ */ jsxRuntime.jsx(
43
32
  Popover.Popover,
44
33
  {
@@ -1,14 +1,13 @@
1
1
  import type { JSX } from 'react';
2
2
  import type { ButtonSize, ButtonVariant } from '../../components/button/Button';
3
- import type { ThemeHrefs, ThemeId } from '../../hooks/useTheme';
3
+ import type { ThemeId } from '../../hooks/useTheme';
4
4
  export interface ThemeMenuSectionProps {
5
5
  /** Every theme to offer as an option. The first entry is treated as the default. */
6
6
  themes: readonly ThemeId[];
7
- hrefs?: ThemeHrefs;
8
7
  }
9
8
  export interface ThemeButtonProps extends ThemeMenuSectionProps {
10
9
  size?: ButtonSize;
11
10
  variant?: ButtonVariant;
12
11
  }
13
- export declare function ThemeMenuSection({ themes, hrefs }: ThemeMenuSectionProps): JSX.Element;
14
- export declare function ThemeButton({ themes, hrefs, size, variant, }: ThemeButtonProps): JSX.Element;
12
+ export declare function ThemeMenuSection({ themes }: ThemeMenuSectionProps): JSX.Element;
13
+ export declare function ThemeButton({ themes, size, variant }: ThemeButtonProps): JSX.Element;
@@ -7,11 +7,8 @@ import { Popover } from '../../components/popover/Popover';
7
7
  import { useTheme } from '../../hooks/useTheme';
8
8
  import { THEME_LABELS } from '../../styles/themes/types';
9
9
 
10
- function ThemeMenuSection({ themes, hrefs }) {
11
- const { theme, switchTheme } = useTheme({
12
- defaultTheme: themes[0],
13
- hrefs
14
- });
10
+ function ThemeMenuSection({ themes }) {
11
+ const { theme, switchTheme } = useTheme({ defaultTheme: themes[0] });
15
12
  return /* @__PURE__ */ jsxs(Fragment, { children: [
16
13
  /* @__PURE__ */ jsx(Menu.Header, { children: "Tema" }),
17
14
  themes.map((id) => /* @__PURE__ */ jsx(
@@ -27,16 +24,8 @@ function ThemeMenuSection({ themes, hrefs }) {
27
24
  ))
28
25
  ] });
29
26
  }
30
- function ThemeButton({
31
- themes,
32
- hrefs,
33
- size,
34
- variant = "outlined"
35
- }) {
36
- const { theme, switchTheme } = useTheme({
37
- defaultTheme: themes[0],
38
- hrefs
39
- });
27
+ function ThemeButton({ themes, size, variant = "outlined" }) {
28
+ const { theme, switchTheme } = useTheme({ defaultTheme: themes[0] });
40
29
  return /* @__PURE__ */ jsx(
41
30
  Popover,
42
31
  {
@@ -1,30 +1,39 @@
1
1
  'use strict';
2
2
 
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
+ var productThemes = require('../../styles/themes/product-themes');
4
5
  var colorScheme_constants = require('../../utils/color-scheme.constants');
5
6
  var theme_constants = require('../../utils/theme.constants');
6
7
 
7
- function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey) {
8
- const hrefById = {};
9
- for (const [id, href] of Object.entries(hrefs)) {
10
- if (id !== defaultTheme && href) hrefById[id] = href;
11
- }
8
+ function buildInitScript(defaultColorScheme, defaultTheme, storageKey) {
9
+ const productThemes$1 = JSON.stringify(productThemes.PRODUCT_THEMES);
12
10
  const themeBlock = defaultTheme === void 0 ? "" : `try{
13
11
  var THEME_KEY=${JSON.stringify(storageKey)};
14
12
  var THEME_DEFAULT=${JSON.stringify(defaultTheme)};
15
- var HREFS=${JSON.stringify(hrefById)};
16
- var LINK_ID=${JSON.stringify(linkId)};
13
+ var PRODUCT_THEMES=${productThemes$1};
17
14
  var resolvedTheme=getCookie(THEME_KEY);
18
15
  if(!resolvedTheme){try{resolvedTheme=localStorage.getItem(THEME_KEY)}catch(e){}}
19
16
  if(!resolvedTheme)resolvedTheme=THEME_DEFAULT;
20
- if(resolvedTheme!==THEME_DEFAULT&&HREFS[resolvedTheme]){
21
- var link=document.createElement('link');
22
- link.id=LINK_ID;
23
- link.rel='stylesheet';
24
- link.href=HREFS[resolvedTheme];
25
- document.head.appendChild(link);
17
+ function applyProductTheme(themeId, scheme){
18
+ var target=document.documentElement;
19
+ var definition=PRODUCT_THEMES[themeId]||{};
20
+ var tokens={};
21
+ var source=scheme==='dark'?definition.dark:definition.light;
22
+ if(definition.base){for(var baseKey in definition.base){tokens[baseKey]=definition.base[baseKey];}}
23
+ if(source){for(var sourceKey in source){tokens[sourceKey]=source[sourceKey];}}
24
+ var knownThemeIds=Object.keys(PRODUCT_THEMES);
25
+ for(var i=0;i<knownThemeIds.length;i++){
26
+ var knownDefinition=PRODUCT_THEMES[knownThemeIds[i]]||{};
27
+ for(var sectionIndex=0;sectionIndex<3;sectionIndex++){
28
+ var section=sectionIndex===0?knownDefinition.base:sectionIndex===1?knownDefinition.light:knownDefinition.dark;
29
+ if(!section)continue;
30
+ for(var clearKey in section){target.style.removeProperty(clearKey);}
31
+ }
32
+ }
33
+ for(var token in tokens){target.style.setProperty(token,tokens[token]);}
26
34
  }
27
35
  document.documentElement.dataset.productTheme=resolvedTheme;
36
+ applyProductTheme(resolvedTheme, document.documentElement.dataset.theme);
28
37
  }catch(e){}`;
29
38
  return `(function(){
30
39
  function getCookie(name){var m=document.cookie.match(new RegExp('(?:^|; )'+name+'=([^;]*)'));return m?decodeURIComponent(m[1]):null}
@@ -43,15 +52,13 @@ function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storag
43
52
  function ThemeScript({
44
53
  defaultColorScheme = "system",
45
54
  defaultTheme,
46
- hrefs = {},
47
- linkId = theme_constants.THEME_LINK_ID,
48
55
  storageKey = theme_constants.THEME_STORAGE_KEY
49
56
  }) {
50
57
  return /* @__PURE__ */ jsxRuntime.jsx(
51
58
  "script",
52
59
  {
53
60
  dangerouslySetInnerHTML: {
54
- __html: buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey)
61
+ __html: buildInitScript(defaultColorScheme, defaultTheme, storageKey)
55
62
  }
56
63
  }
57
64
  );
@@ -1,4 +1,4 @@
1
- import type { ThemeHrefs, ThemeId } from '../../styles/themes/types';
1
+ import type { ThemeId } from '../../styles/themes/types';
2
2
  import type { ColorScheme } from '../../utils/color-scheme.constants';
3
3
  export interface ThemeScriptProps {
4
4
  /** Color scheme applied when nothing is stored yet. Defaults to 'system'. */
@@ -9,10 +9,6 @@ export interface ThemeScriptProps {
9
9
  * `data-product-theme` handling happens at all in that case.
10
10
  */
11
11
  defaultTheme?: ThemeId;
12
- /** Per-theme CSS URL, for any theme not already loaded some other way. */
13
- hrefs?: ThemeHrefs;
14
- /** DOM id for the `<link>` this script (and useTheme) manage. */
15
- linkId?: string;
16
12
  storageKey?: string;
17
13
  }
18
14
  /**
@@ -45,4 +41,4 @@ export interface ThemeScriptProps {
45
41
  * </Head>
46
42
  * ```
47
43
  */
48
- export declare function ThemeScript({ defaultColorScheme, defaultTheme, hrefs, linkId, storageKey, }: ThemeScriptProps): import("react/jsx-runtime").JSX.Element;
44
+ export declare function ThemeScript({ defaultColorScheme, defaultTheme, storageKey, }: ThemeScriptProps): import("react/jsx-runtime").JSX.Element;
@@ -1,28 +1,37 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
+ import { PRODUCT_THEMES } from '../../styles/themes/product-themes';
2
3
  import { COLOR_SCHEME_STORAGE_KEY, COLOR_SCHEMES } from '../../utils/color-scheme.constants';
3
- import { THEME_LINK_ID, THEME_STORAGE_KEY } from '../../utils/theme.constants';
4
+ import { THEME_STORAGE_KEY } from '../../utils/theme.constants';
4
5
 
5
- function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey) {
6
- const hrefById = {};
7
- for (const [id, href] of Object.entries(hrefs)) {
8
- if (id !== defaultTheme && href) hrefById[id] = href;
9
- }
6
+ function buildInitScript(defaultColorScheme, defaultTheme, storageKey) {
7
+ const productThemes = JSON.stringify(PRODUCT_THEMES);
10
8
  const themeBlock = defaultTheme === void 0 ? "" : `try{
11
9
  var THEME_KEY=${JSON.stringify(storageKey)};
12
10
  var THEME_DEFAULT=${JSON.stringify(defaultTheme)};
13
- var HREFS=${JSON.stringify(hrefById)};
14
- var LINK_ID=${JSON.stringify(linkId)};
11
+ var PRODUCT_THEMES=${productThemes};
15
12
  var resolvedTheme=getCookie(THEME_KEY);
16
13
  if(!resolvedTheme){try{resolvedTheme=localStorage.getItem(THEME_KEY)}catch(e){}}
17
14
  if(!resolvedTheme)resolvedTheme=THEME_DEFAULT;
18
- if(resolvedTheme!==THEME_DEFAULT&&HREFS[resolvedTheme]){
19
- var link=document.createElement('link');
20
- link.id=LINK_ID;
21
- link.rel='stylesheet';
22
- link.href=HREFS[resolvedTheme];
23
- document.head.appendChild(link);
15
+ function applyProductTheme(themeId, scheme){
16
+ var target=document.documentElement;
17
+ var definition=PRODUCT_THEMES[themeId]||{};
18
+ var tokens={};
19
+ var source=scheme==='dark'?definition.dark:definition.light;
20
+ if(definition.base){for(var baseKey in definition.base){tokens[baseKey]=definition.base[baseKey];}}
21
+ if(source){for(var sourceKey in source){tokens[sourceKey]=source[sourceKey];}}
22
+ var knownThemeIds=Object.keys(PRODUCT_THEMES);
23
+ for(var i=0;i<knownThemeIds.length;i++){
24
+ var knownDefinition=PRODUCT_THEMES[knownThemeIds[i]]||{};
25
+ for(var sectionIndex=0;sectionIndex<3;sectionIndex++){
26
+ var section=sectionIndex===0?knownDefinition.base:sectionIndex===1?knownDefinition.light:knownDefinition.dark;
27
+ if(!section)continue;
28
+ for(var clearKey in section){target.style.removeProperty(clearKey);}
29
+ }
30
+ }
31
+ for(var token in tokens){target.style.setProperty(token,tokens[token]);}
24
32
  }
25
33
  document.documentElement.dataset.productTheme=resolvedTheme;
34
+ applyProductTheme(resolvedTheme, document.documentElement.dataset.theme);
26
35
  }catch(e){}`;
27
36
  return `(function(){
28
37
  function getCookie(name){var m=document.cookie.match(new RegExp('(?:^|; )'+name+'=([^;]*)'));return m?decodeURIComponent(m[1]):null}
@@ -41,15 +50,13 @@ function buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storag
41
50
  function ThemeScript({
42
51
  defaultColorScheme = "system",
43
52
  defaultTheme,
44
- hrefs = {},
45
- linkId = THEME_LINK_ID,
46
53
  storageKey = THEME_STORAGE_KEY
47
54
  }) {
48
55
  return /* @__PURE__ */ jsx(
49
56
  "script",
50
57
  {
51
58
  dangerouslySetInnerHTML: {
52
- __html: buildInitScript(defaultColorScheme, hrefs, defaultTheme, linkId, storageKey)
59
+ __html: buildInitScript(defaultColorScheme, defaultTheme, storageKey)
53
60
  }
54
61
  }
55
62
  );
@@ -4,13 +4,18 @@
4
4
  var react = require('react');
5
5
  var cookie_utils = require('../utils/cookie.utils');
6
6
  var localStorage_utils = require('../utils/localStorage.utils');
7
+ var sessionStorage_utils = require('../utils/sessionStorage.utils');
7
8
 
8
9
  function readStorage(storage, key) {
9
- return storage === "cookie" ? cookie_utils.readCookie(key) : localStorage_utils.readLocalStorage(key);
10
+ if (storage === "cookie") return cookie_utils.readCookie(key);
11
+ if (storage === "sessionStorage") return sessionStorage_utils.readSessionStorage(key);
12
+ return localStorage_utils.readLocalStorage(key);
10
13
  }
11
14
  function writeStorage(storage, key, value, maxAgeSeconds) {
12
15
  if (storage === "cookie") {
13
16
  cookie_utils.writeCookie(key, value, maxAgeSeconds);
17
+ } else if (storage === "sessionStorage") {
18
+ sessionStorage_utils.writeSessionStorage(key, value);
14
19
  } else {
15
20
  localStorage_utils.writeLocalStorage(key, value);
16
21
  }
@@ -1,5 +1,5 @@
1
1
  import { Dispatch, SetStateAction } from 'react';
2
- export type PersistentStateStorage = 'localStorage' | 'cookie';
2
+ export type PersistentStateStorage = 'localStorage' | 'sessionStorage' | 'cookie';
3
3
  /**
4
4
  * 'effect' (default): the stored value is read in a useEffect after mount,
5
5
  * so `value` starts as `initialValue` on every render — including the
@@ -2,13 +2,18 @@
2
2
  import { useRef, useState, useEffect } from 'react';
3
3
  import { readCookie, writeCookie } from '../utils/cookie.utils';
4
4
  import { readLocalStorage, writeLocalStorage } from '../utils/localStorage.utils';
5
+ import { readSessionStorage, writeSessionStorage } from '../utils/sessionStorage.utils';
5
6
 
6
7
  function readStorage(storage, key) {
7
- return storage === "cookie" ? readCookie(key) : readLocalStorage(key);
8
+ if (storage === "cookie") return readCookie(key);
9
+ if (storage === "sessionStorage") return readSessionStorage(key);
10
+ return readLocalStorage(key);
8
11
  }
9
12
  function writeStorage(storage, key, value, maxAgeSeconds) {
10
13
  if (storage === "cookie") {
11
14
  writeCookie(key, value, maxAgeSeconds);
15
+ } else if (storage === "sessionStorage") {
16
+ writeSessionStorage(key, value);
12
17
  } else {
13
18
  writeLocalStorage(key, value);
14
19
  }
@@ -2,11 +2,11 @@
2
2
  'use strict';
3
3
 
4
4
  var react = require('react');
5
+ var applyProductTheme = require('../styles/themes/apply-product-theme');
5
6
  var cookie_utils = require('../utils/cookie.utils');
6
7
  var localStorage_utils = require('../utils/localStorage.utils');
7
8
  var theme_constants = require('../utils/theme.constants');
8
9
 
9
- const PRELOAD_ID_PREFIX = "dbc-theme-preload-";
10
10
  function persistTheme(storageKey, id) {
11
11
  localStorage_utils.writeLocalStorage(storageKey, id);
12
12
  cookie_utils.writeCookie(storageKey, id);
@@ -14,47 +14,16 @@ function persistTheme(storageKey, id) {
14
14
  function getDomTheme() {
15
15
  return document.documentElement.dataset.productTheme;
16
16
  }
17
+ function getDomColorScheme() {
18
+ return document.documentElement.dataset.theme;
19
+ }
17
20
  function applyTheme(id) {
18
21
  document.documentElement.dataset.productTheme = id;
19
- }
20
- function findLink(linkId) {
21
- return document.getElementById(linkId);
22
- }
23
- function findPreloadLink(id) {
24
- return document.getElementById(`${PRELOAD_ID_PREFIX}${id}`);
25
- }
26
- function ensureStylesheet(id, href, linkId, onReady) {
27
- var _a;
28
- if (!href) {
29
- onReady();
30
- return;
31
- }
32
- const active = findLink(linkId);
33
- if (active && active.getAttribute("href") === href && active.rel === "stylesheet") {
34
- onReady();
35
- return;
36
- }
37
- const preload = findPreloadLink(id);
38
- const link = (_a = active != null ? active : preload) != null ? _a : document.createElement("link");
39
- const wasConnected = link.isConnected;
40
- link.id = linkId;
41
- link.rel = "stylesheet";
42
- link.removeAttribute("as");
43
- link.href = href;
44
- if (preload && preload !== link) preload.remove();
45
- let settled = false;
46
- const finish = () => {
47
- if (settled) return;
48
- settled = true;
49
- onReady();
50
- };
51
- link.addEventListener("load", finish, { once: true });
52
- link.addEventListener("error", finish, { once: true });
53
- if (!wasConnected) document.head.appendChild(link);
22
+ applyProductTheme.applyProductTheme(id, getDomColorScheme());
54
23
  }
55
24
  let currentTheme = null;
56
25
  let resolved = false;
57
- let preloadScheduled = false;
26
+ let observerAttached = false;
58
27
  const listeners = /* @__PURE__ */ new Set();
59
28
  function notify() {
60
29
  listeners.forEach((listener) => listener());
@@ -80,75 +49,43 @@ function resolveInitialTheme(storageKey, defaultTheme) {
80
49
  if (fromStorage) return fromStorage;
81
50
  return defaultTheme;
82
51
  }
83
- function ensureResolved(hrefs, defaultTheme, storageKey, linkId) {
52
+ function ensureResolved(defaultTheme, storageKey) {
84
53
  if (resolved) return;
85
54
  resolved = true;
86
55
  const value = resolveInitialTheme(storageKey, defaultTheme);
87
- const finish = () => {
88
- applyTheme(value);
89
- persistTheme(storageKey, value);
90
- currentTheme = value;
91
- notify();
92
- };
93
- if (value === defaultTheme) {
94
- finish();
95
- return;
96
- }
97
- ensureStylesheet(value, hrefs[value], linkId, finish);
56
+ applyTheme(value);
57
+ persistTheme(storageKey, value);
58
+ currentTheme = value;
59
+ notify();
98
60
  }
99
- function schedulePreload(hrefs, defaultTheme) {
100
- if (preloadScheduled) return;
101
- preloadScheduled = true;
102
- const run = () => {
103
- var _a;
104
- for (const [id, href] of Object.entries(hrefs)) {
105
- if (id === defaultTheme || !href) continue;
106
- if (((_a = findLink(theme_constants.THEME_LINK_ID)) == null ? void 0 : _a.getAttribute("href")) === href) continue;
107
- if (findPreloadLink(id)) continue;
108
- const link = document.createElement("link");
109
- link.id = `${PRELOAD_ID_PREFIX}${id}`;
110
- link.rel = "preload";
111
- link.as = "style";
112
- link.href = href;
113
- document.head.appendChild(link);
114
- }
115
- };
116
- const requestIdle = window.requestIdleCallback;
117
- if (typeof requestIdle === "function") {
118
- requestIdle(run);
119
- } else {
120
- setTimeout(run, 1);
121
- }
61
+ function ensureColorSchemeObserver() {
62
+ if (observerAttached) return;
63
+ observerAttached = true;
64
+ const observer = new MutationObserver(() => {
65
+ if (!currentTheme) return;
66
+ applyProductTheme.applyProductTheme(currentTheme, getDomColorScheme());
67
+ });
68
+ observer.observe(document.documentElement, {
69
+ attributes: true,
70
+ attributeFilter: ["data-theme"]
71
+ });
122
72
  }
123
- function switchThemeInternal(hrefs, defaultTheme, storageKey, linkId, id) {
124
- const finish = () => {
125
- applyTheme(id);
126
- persistTheme(storageKey, id);
127
- currentTheme = id;
128
- resolved = true;
129
- notify();
130
- };
131
- if (id === defaultTheme) {
132
- finish();
133
- return;
134
- }
135
- ensureStylesheet(id, hrefs[id], linkId, finish);
73
+ function switchThemeInternal(storageKey, id) {
74
+ applyTheme(id);
75
+ persistTheme(storageKey, id);
76
+ currentTheme = id;
77
+ resolved = true;
78
+ notify();
136
79
  }
137
80
  function useTheme(options) {
138
- const {
139
- defaultTheme,
140
- hrefs = {},
141
- linkId = theme_constants.THEME_LINK_ID,
142
- preloadOthers = true,
143
- storageKey = theme_constants.THEME_STORAGE_KEY
144
- } = options;
81
+ const { defaultTheme, storageKey = theme_constants.THEME_STORAGE_KEY } = options;
145
82
  react.useEffect(() => {
146
- ensureResolved(hrefs, defaultTheme, storageKey, linkId);
147
- if (preloadOthers) schedulePreload(hrefs, defaultTheme);
148
- }, []);
83
+ ensureColorSchemeObserver();
84
+ ensureResolved(defaultTheme, storageKey);
85
+ }, [defaultTheme, storageKey]);
149
86
  const theme = react.useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
150
87
  function switchTheme(id) {
151
- switchThemeInternal(hrefs, defaultTheme, storageKey, linkId, id);
88
+ switchThemeInternal(storageKey, id);
152
89
  }
153
90
  return { theme, switchTheme };
154
91
  }
@@ -1,15 +1,8 @@
1
- import type { ThemeHrefs, ThemeId } from '../styles/themes/types';
2
- export type { ThemeHrefs, ThemeId };
1
+ import type { ThemeId } from '../styles/themes/types';
2
+ export type { ThemeId };
3
3
  export interface UseThemeOptions {
4
- /** The theme the app already loads via a normal static import never
5
- * fetched/linked dynamically, just applied via the attribute. */
4
+ /** The theme the app should fall back to when nothing is stored yet. */
6
5
  defaultTheme: ThemeId;
7
- /** Per-theme CSS URL, for any theme not already loaded some other way. */
8
- hrefs?: ThemeHrefs;
9
- /** DOM id for the `<link>` element used for the currently active non-default theme. */
10
- linkId?: string;
11
- /** Warm the browser cache for every other theme at idle time. Default true. */
12
- preloadOthers?: boolean;
13
6
  storageKey?: string;
14
7
  }
15
8
  export declare function useTheme(options: UseThemeOptions): {