@weasel-js/theme 1.9.4 → 1.10.1

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/react.d.ts CHANGED
@@ -1,5 +1,6 @@
1
+ import { Palette, ColorSource } from '@weasel-js/paint';
1
2
  import React from 'react';
2
- import { a as Theme, S as Selection, o as ResolvedTheme, v as ColorList } from './definition-GS7_Kz9D.js';
3
+ import { a as Theme, S as Selection, r as ResolvedTheme, o as ColorList } from './definition-CSeERbSL.js';
3
4
  import { a as ColorModePreference, C as ColorMode } from './colorMode-CoklZomJ.js';
4
5
  export { i as isColorModePreference } from './colorMode-CoklZomJ.js';
5
6
 
@@ -35,12 +36,13 @@ interface ColorModePreferenceState {
35
36
  declare function useColorModePreference(options?: ColorModePreferenceOptions): ColorModePreferenceState;
36
37
 
37
38
  /** What `<ThemeProvider>` publishes: the theme, the full selection in force, the
38
- * fully resolved token record for that pair, and the tone list in force. */
39
+ * fully resolved token record for that pair, and the tone list and palette in force. */
39
40
  interface ThemeContextValue {
40
41
  readonly theme: Theme;
41
42
  readonly selection: Selection;
42
43
  readonly resolved: ResolvedTheme;
43
44
  readonly tones: ColorList;
45
+ readonly palette: Palette;
44
46
  }
45
47
  /** What `<ThemeProvider>` publishes. Provide `null` to hide an outer theme from a subtree, as a
46
48
  * boundary that renders its children the way a page with no provider would. */
@@ -52,6 +54,8 @@ interface ThemeProviderProps {
52
54
  readonly selection?: Selection;
53
55
  /** The colors a `tone` index picks from in this subtree. Absent: the theme's own. */
54
56
  readonly tones?: ColorList;
57
+ /** Colors a `ColorRef` names in this subtree, answered before the theme's own names. Absent: none. */
58
+ readonly palette?: Palette;
55
59
  /** Applied to the wrapper element, so it can be the layout element too. */
56
60
  readonly className?: string;
57
61
  readonly style?: React.CSSProperties;
@@ -67,7 +71,7 @@ interface ThemeProviderProps {
67
71
  * nesting your own div inside — an anonymous div between a flex parent and
68
72
  * its child breaks percentage heights.
69
73
  */
70
- declare function ThemeProvider({ theme, selection, tones, className, style, children, }: ThemeProviderProps): React.ReactElement;
74
+ declare function ThemeProvider({ theme, selection, tones, palette, className, style, children, }: ThemeProviderProps): React.ReactElement;
71
75
  /** The theme in force. Throws outside a `<ThemeProvider>`; use
72
76
  * `useThemeOptional` where the provider is not guaranteed. */
73
77
  declare function useTheme(): ThemeContextValue;
@@ -85,5 +89,12 @@ declare function useThemeOptional(): ThemeContextValue | null;
85
89
  * result as `colorAt`'s context.
86
90
  */
87
91
  declare function useTones(): ThemeContextValue;
92
+ /**
93
+ * Turns a `ColorSource` into a CSS color under the theme, tones, and palette in
94
+ * force: `colorSourceCss` against `useTones()`. Every component that takes a
95
+ * color writes it through this. `undefined` for no source, and for one nothing
96
+ * answers.
97
+ */
98
+ declare function useColorCss(): (source: ColorSource | null | undefined) => string | undefined;
88
99
 
89
- export { ColorMode, ColorModePreference, type ColorModePreferenceOptions, type ColorModePreferenceState, type ColorModeStorage, ThemeContext, type ThemeContextValue, ThemeProvider, type ThemeProviderProps, useColorModePreference, useResolvedColorMode, useTheme, useThemeOptional, useTones };
100
+ export { ColorMode, ColorModePreference, type ColorModePreferenceOptions, type ColorModePreferenceState, type ColorModeStorage, ThemeContext, type ThemeContextValue, ThemeProvider, type ThemeProviderProps, useColorCss, useColorModePreference, useResolvedColorMode, useTheme, useThemeOptional, useTones };
package/dist/react.js CHANGED
@@ -1,6 +1,10 @@
1
- import { isColorModePreference, weaselTheme, themeTones, applyTheme } from './chunk-EFGZ56CE.js';
2
- export { isColorModePreference } from './chunk-EFGZ56CE.js';
3
- import { themeAxes, fullSelection, selectionKey, resolveTheme } from './chunk-7LCRXZDY.js';
1
+ import { isColorModePreference, themeTones, applyTheme, colorSourceCss } from './chunk-5PFIA5JU.js';
2
+ export { isColorModePreference } from './chunk-5PFIA5JU.js';
3
+ import './chunk-S7MJPFDP.js';
4
+ import './chunk-M3R5MV7Q.js';
5
+ import { themeAxes, resolveTheme } from './chunk-32JKTYS2.js';
6
+ import { weaselTheme } from './chunk-JX2OC444.js';
7
+ import { fullSelection, selectionKey } from './chunk-P5F3GZV6.js';
4
8
  import { createContext, useState, useEffect, useCallback, useRef, useMemo, useLayoutEffect, useContext } from 'react';
5
9
  import { jsx } from 'react/jsx-runtime';
6
10
 
@@ -54,10 +58,12 @@ function useColorModePreference(options = {}) {
54
58
  return { preference, setPreference, mode: useResolvedColorMode(preference) };
55
59
  }
56
60
  var ThemeContext = createContext(null);
61
+ var NO_PALETTE = { entries: [] };
57
62
  function ThemeProvider({
58
63
  theme = weaselTheme,
59
64
  selection,
60
65
  tones,
66
+ palette = NO_PALETTE,
61
67
  className,
62
68
  style,
63
69
  children
@@ -67,9 +73,9 @@ function ThemeProvider({
67
73
  const full = fullSelection(axes, selection);
68
74
  const key = selectionKey(axes, full);
69
75
  const value = useMemo(
70
- () => ({ theme, selection: full, resolved: resolveTheme(theme, full), tones: tones ?? themeTones(theme) }),
76
+ () => ({ theme, selection: full, resolved: resolveTheme(theme, full), tones: tones ?? themeTones(theme), palette }),
71
77
  // eslint-disable-next-line react-hooks/exhaustive-deps
72
- [theme, key, tones]
78
+ [theme, key, tones, palette]
73
79
  );
74
80
  const release = useRef(null);
75
81
  useLayoutEffect(() => {
@@ -95,10 +101,15 @@ function useTones() {
95
101
  theme: weaselTheme,
96
102
  selection: fullSelection(themeAxes(weaselTheme)),
97
103
  resolved: resolveTheme(weaselTheme),
98
- tones: themeTones(weaselTheme)
104
+ tones: themeTones(weaselTheme),
105
+ palette: NO_PALETTE
99
106
  };
100
107
  }
108
+ function useColorCss() {
109
+ const ctx = useTones();
110
+ return useCallback((source) => source == null ? void 0 : colorSourceCss(source, ctx) ?? void 0, [ctx]);
111
+ }
101
112
 
102
- export { ThemeContext, ThemeProvider, useColorModePreference, useResolvedColorMode, useTheme, useThemeOptional, useTones };
113
+ export { ThemeContext, ThemeProvider, useColorCss, useColorModePreference, useResolvedColorMode, useTheme, useThemeOptional, useTones };
103
114
  //# sourceMappingURL=react.js.map
104
115
  //# sourceMappingURL=react.js.map
package/dist/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/useColorMode.ts","../src/react.tsx"],"names":[],"mappings":"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","file":"react.js","sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\nfunction systemQuery(): MediaQueryList | null {\n if (typeof globalThis.matchMedia !== 'function') return null;\n return globalThis.matchMedia('(prefers-color-scheme: light)');\n}\n\nconst systemMode = (): ColorMode => (systemQuery()?.matches ? 'light' : 'dark');\n\n/**\n * Resolves a preference to the mode a `ThemeProvider` selection takes.\n * `'auto'` subscribes to the OS setting and follows it live; an explicit\n * choice opts out of the subscription entirely.\n */\nexport function useResolvedColorMode(preference: ColorModePreference): ColorMode {\n const [system, setSystem] = useState<ColorMode>(systemMode);\n\n useEffect(() => {\n if (preference !== 'auto') return;\n const mq = systemQuery();\n if (!mq) return;\n // eslint-disable-next-line react-hooks/set-state-in-effect -- syncs to the OS color-scheme query\n setSystem(mq.matches ? 'light' : 'dark');\n const onChange = (e: { matches: boolean }) => setSystem(e.matches ? 'light' : 'dark');\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, [preference]);\n\n return preference === 'auto' ? system : preference;\n}\n\n/** The part of `Storage` {@link useColorModePreference} reads and writes. */\nexport type ColorModeStorage = Pick<Storage, 'getItem' | 'setItem'>;\n\n/** Options for {@link useColorModePreference}. */\nexport interface ColorModePreferenceOptions {\n /** Where the choice is remembered. Unset, it lasts as long as the component. */\n readonly storageKey?: string;\n /** The store `storageKey` names a slot in. Defaults to `localStorage`. */\n readonly storage?: ColorModeStorage;\n /** The preference before the user has chosen one. Defaults to `'auto'`. */\n readonly defaultPreference?: ColorModePreference;\n}\n\n/** What {@link useColorModePreference} returns. */\nexport interface ColorModePreferenceState {\n readonly preference: ColorModePreference;\n readonly setPreference: (next: ColorModePreference) => void;\n /** `preference` resolved against the OS — what to put in the theme's `mode` axis. */\n readonly mode: ColorMode;\n}\n\nfunction defaultStorage(): ColorModeStorage | undefined {\n try {\n return globalThis.localStorage ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * A user's color-mode choice, optionally remembered across loads, and the mode\n * it resolves to. Storage that is missing or throws (a private window, blocked\n * site data) degrades to an unremembered choice rather than an error.\n */\nexport function useColorModePreference(\n options: ColorModePreferenceOptions = {},\n): ColorModePreferenceState {\n const { storageKey, storage, defaultPreference = 'auto' } = options;\n\n const [preference, setState] = useState<ColorModePreference>(() => {\n if (storageKey === undefined) return defaultPreference;\n try {\n const stored = (storage ?? defaultStorage())?.getItem(storageKey);\n return isColorModePreference(stored) ? stored : defaultPreference;\n } catch {\n return defaultPreference;\n }\n });\n\n const setPreference = useCallback(\n (next: ColorModePreference) => {\n setState(next);\n if (storageKey === undefined) return;\n try {\n (storage ?? defaultStorage())?.setItem(storageKey, next);\n } catch {\n // Unremembered, but still applied.\n }\n },\n [storageKey, storage],\n );\n\n return { preference, setPreference, mode: useResolvedColorMode(preference) };\n}\n","import React, { createContext, useContext, useMemo, useRef, useLayoutEffect } from 'react';\nimport { applyTheme } from './applyTheme';\nimport { themeTones, type ColorList } from './colorList';\nimport { fullSelection, selectionKey, type Selection } from './axes';\nimport { resolveTheme, themeAxes, type ResolvedTheme } from './resolveTheme';\nimport { weaselTheme, type Theme } from './theme';\n\nexport {\n useColorModePreference,\n useResolvedColorMode,\n type ColorModePreferenceOptions,\n type ColorModePreferenceState,\n type ColorModeStorage,\n} from './useColorMode';\nexport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\n/** What `<ThemeProvider>` publishes: the theme, the full selection in force, the\n * fully resolved token record for that pair, and the tone list in force. */\nexport interface ThemeContextValue {\n readonly theme: Theme;\n readonly selection: Selection;\n readonly resolved: ResolvedTheme;\n readonly tones: ColorList;\n}\n\n/** What `<ThemeProvider>` publishes. Provide `null` to hide an outer theme from a subtree, as a\n * boundary that renders its children the way a page with no provider would. */\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n\n/** Props for `<ThemeProvider>`. */\nexport interface ThemeProviderProps {\n readonly theme?: Theme;\n /** Axis values, e.g. `{ mode: 'light' }`. A missing axis takes its default. */\n readonly selection?: Selection;\n /** The colors a `tone` index picks from in this subtree. Absent: the theme's own. */\n readonly tones?: ColorList;\n /** Applied to the wrapper element, so it can be the layout element too. */\n readonly className?: string;\n readonly style?: React.CSSProperties;\n readonly children: React.ReactNode;\n}\n\n/**\n * Applies a theme to a wrapper element and publishes the resolved record.\n *\n * Consumers that draw outside the DOM (the WebGL HUD) read `resolved` and\n * never touch `getComputedStyle`.\n *\n * The wrapper is a real element in the layout. Pass `className` rather than\n * nesting your own div inside — an anonymous div between a flex parent and\n * its child breaks percentage heights.\n */\nexport function ThemeProvider({\n theme = weaselTheme,\n selection,\n tones,\n className,\n style,\n children,\n}: ThemeProviderProps): React.ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const axes = themeAxes(theme);\n const full = fullSelection(axes, selection);\n // Keyed on the selection's content: callers pass a fresh object every render.\n const key = selectionKey(axes, full);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, selection: full, resolved: resolveTheme(theme, full), tones: tones ?? themeTones(theme) }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [theme, key, tones],\n );\n\n // Applying again replaces what the element held, so only the unmount releases.\n const release = useRef<(() => void) | null>(null);\n useLayoutEffect(() => {\n if (ref.current) release.current = applyTheme(ref.current, theme, value.selection);\n }, [theme, value]);\n useLayoutEffect(() => () => release.current?.(), []);\n\n const attrs = Object.fromEntries(Object.keys(axes).map((a) => [`data-wzl-${a}`, full[a]]));\n return (\n <ThemeContext.Provider value={value}>\n <div ref={ref} className={className} style={style} data-wzl-theme={theme.name} {...attrs}>\n {children}\n </div>\n </ThemeContext.Provider>\n );\n}\n\n/** The theme in force. Throws outside a `<ThemeProvider>`; use\n * `useThemeOptional` where the provider is not guaranteed. */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error('useTheme must be used inside a <ThemeProvider>');\n return ctx;\n}\n\n/**\n * The theme if one is provided, `null` otherwise.\n *\n * For kit internals that should follow a theme when the app supplies one but\n * must still work in an app that never mounted a provider — `useHud` is the\n * motivating case.\n */\nexport function useThemeOptional(): ThemeContextValue | null {\n return useContext(ThemeContext);\n}\n\nlet defaultTones: ThemeContextValue | undefined;\n\n/**\n * The tone list in force and what it resolves against — the nearest\n * `<ThemeProvider>`'s, else the built-in theme's at its defaults. Pass the\n * result as `colorAt`'s context.\n */\nexport function useTones(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (ctx) return ctx;\n return (defaultTones ??= {\n theme: weaselTheme,\n selection: fullSelection(themeAxes(weaselTheme)),\n resolved: resolveTheme(weaselTheme),\n tones: themeTones(weaselTheme),\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/useColorMode.ts","../src/react.tsx"],"names":["useCallback"],"mappings":"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","file":"react.js","sourcesContent":["import { useCallback, useEffect, useState } from 'react';\nimport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\nfunction systemQuery(): MediaQueryList | null {\n if (typeof globalThis.matchMedia !== 'function') return null;\n return globalThis.matchMedia('(prefers-color-scheme: light)');\n}\n\nconst systemMode = (): ColorMode => (systemQuery()?.matches ? 'light' : 'dark');\n\n/**\n * Resolves a preference to the mode a `ThemeProvider` selection takes.\n * `'auto'` subscribes to the OS setting and follows it live; an explicit\n * choice opts out of the subscription entirely.\n */\nexport function useResolvedColorMode(preference: ColorModePreference): ColorMode {\n const [system, setSystem] = useState<ColorMode>(systemMode);\n\n useEffect(() => {\n if (preference !== 'auto') return;\n const mq = systemQuery();\n if (!mq) return;\n // eslint-disable-next-line react-hooks/set-state-in-effect -- syncs to the OS color-scheme query\n setSystem(mq.matches ? 'light' : 'dark');\n const onChange = (e: { matches: boolean }) => setSystem(e.matches ? 'light' : 'dark');\n mq.addEventListener('change', onChange);\n return () => mq.removeEventListener('change', onChange);\n }, [preference]);\n\n return preference === 'auto' ? system : preference;\n}\n\n/** The part of `Storage` {@link useColorModePreference} reads and writes. */\nexport type ColorModeStorage = Pick<Storage, 'getItem' | 'setItem'>;\n\n/** Options for {@link useColorModePreference}. */\nexport interface ColorModePreferenceOptions {\n /** Where the choice is remembered. Unset, it lasts as long as the component. */\n readonly storageKey?: string;\n /** The store `storageKey` names a slot in. Defaults to `localStorage`. */\n readonly storage?: ColorModeStorage;\n /** The preference before the user has chosen one. Defaults to `'auto'`. */\n readonly defaultPreference?: ColorModePreference;\n}\n\n/** What {@link useColorModePreference} returns. */\nexport interface ColorModePreferenceState {\n readonly preference: ColorModePreference;\n readonly setPreference: (next: ColorModePreference) => void;\n /** `preference` resolved against the OS — what to put in the theme's `mode` axis. */\n readonly mode: ColorMode;\n}\n\nfunction defaultStorage(): ColorModeStorage | undefined {\n try {\n return globalThis.localStorage ?? undefined;\n } catch {\n return undefined;\n }\n}\n\n/**\n * A user's color-mode choice, optionally remembered across loads, and the mode\n * it resolves to. Storage that is missing or throws (a private window, blocked\n * site data) degrades to an unremembered choice rather than an error.\n */\nexport function useColorModePreference(\n options: ColorModePreferenceOptions = {},\n): ColorModePreferenceState {\n const { storageKey, storage, defaultPreference = 'auto' } = options;\n\n const [preference, setState] = useState<ColorModePreference>(() => {\n if (storageKey === undefined) return defaultPreference;\n try {\n const stored = (storage ?? defaultStorage())?.getItem(storageKey);\n return isColorModePreference(stored) ? stored : defaultPreference;\n } catch {\n return defaultPreference;\n }\n });\n\n const setPreference = useCallback(\n (next: ColorModePreference) => {\n setState(next);\n if (storageKey === undefined) return;\n try {\n (storage ?? defaultStorage())?.setItem(storageKey, next);\n } catch {\n // Unremembered, but still applied.\n }\n },\n [storageKey, storage],\n );\n\n return { preference, setPreference, mode: useResolvedColorMode(preference) };\n}\n","import type { ColorSource, Palette } from '@weasel-js/paint';\nimport React, { createContext, useCallback, useContext, useMemo, useRef, useLayoutEffect } from 'react';\nimport { applyTheme } from './applyTheme';\nimport { themeTones, type ColorList } from './colorList';\nimport { colorSourceCss } from './colorSource';\nimport { fullSelection, selectionKey, type Selection } from './axes';\nimport { resolveTheme, themeAxes, type ResolvedTheme } from './resolveTheme';\nimport { weaselTheme, type Theme } from './theme';\n\nexport {\n useColorModePreference,\n useResolvedColorMode,\n type ColorModePreferenceOptions,\n type ColorModePreferenceState,\n type ColorModeStorage,\n} from './useColorMode';\nexport { isColorModePreference, type ColorMode, type ColorModePreference } from './colorMode';\n\n/** What `<ThemeProvider>` publishes: the theme, the full selection in force, the\n * fully resolved token record for that pair, and the tone list and palette in force. */\nexport interface ThemeContextValue {\n readonly theme: Theme;\n readonly selection: Selection;\n readonly resolved: ResolvedTheme;\n readonly tones: ColorList;\n readonly palette: Palette;\n}\n\n/** What `<ThemeProvider>` publishes. Provide `null` to hide an outer theme from a subtree, as a\n * boundary that renders its children the way a page with no provider would. */\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n\nconst NO_PALETTE: Palette = { entries: [] };\n\n/** Props for `<ThemeProvider>`. */\nexport interface ThemeProviderProps {\n readonly theme?: Theme;\n /** Axis values, e.g. `{ mode: 'light' }`. A missing axis takes its default. */\n readonly selection?: Selection;\n /** The colors a `tone` index picks from in this subtree. Absent: the theme's own. */\n readonly tones?: ColorList;\n /** Colors a `ColorRef` names in this subtree, answered before the theme's own names. Absent: none. */\n readonly palette?: Palette;\n /** Applied to the wrapper element, so it can be the layout element too. */\n readonly className?: string;\n readonly style?: React.CSSProperties;\n readonly children: React.ReactNode;\n}\n\n/**\n * Applies a theme to a wrapper element and publishes the resolved record.\n *\n * Consumers that draw outside the DOM (the WebGL HUD) read `resolved` and\n * never touch `getComputedStyle`.\n *\n * The wrapper is a real element in the layout. Pass `className` rather than\n * nesting your own div inside — an anonymous div between a flex parent and\n * its child breaks percentage heights.\n */\nexport function ThemeProvider({\n theme = weaselTheme,\n selection,\n tones,\n palette = NO_PALETTE,\n className,\n style,\n children,\n}: ThemeProviderProps): React.ReactElement {\n const ref = useRef<HTMLDivElement>(null);\n const axes = themeAxes(theme);\n const full = fullSelection(axes, selection);\n // Keyed on the selection's content: callers pass a fresh object every render.\n const key = selectionKey(axes, full);\n\n const value = useMemo<ThemeContextValue>(\n () => ({ theme, selection: full, resolved: resolveTheme(theme, full), tones: tones ?? themeTones(theme), palette }),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [theme, key, tones, palette],\n );\n\n // Applying again replaces what the element held, so only the unmount releases.\n const release = useRef<(() => void) | null>(null);\n useLayoutEffect(() => {\n if (ref.current) release.current = applyTheme(ref.current, theme, value.selection);\n }, [theme, value]);\n useLayoutEffect(() => () => release.current?.(), []);\n\n const attrs = Object.fromEntries(Object.keys(axes).map((a) => [`data-wzl-${a}`, full[a]]));\n return (\n <ThemeContext.Provider value={value}>\n <div ref={ref} className={className} style={style} data-wzl-theme={theme.name} {...attrs}>\n {children}\n </div>\n </ThemeContext.Provider>\n );\n}\n\n/** The theme in force. Throws outside a `<ThemeProvider>`; use\n * `useThemeOptional` where the provider is not guaranteed. */\nexport function useTheme(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (!ctx) throw new Error('useTheme must be used inside a <ThemeProvider>');\n return ctx;\n}\n\n/**\n * The theme if one is provided, `null` otherwise.\n *\n * For kit internals that should follow a theme when the app supplies one but\n * must still work in an app that never mounted a provider — `useHud` is the\n * motivating case.\n */\nexport function useThemeOptional(): ThemeContextValue | null {\n return useContext(ThemeContext);\n}\n\nlet defaultTones: ThemeContextValue | undefined;\n\n/**\n * The tone list in force and what it resolves against — the nearest\n * `<ThemeProvider>`'s, else the built-in theme's at its defaults. Pass the\n * result as `colorAt`'s context.\n */\nexport function useTones(): ThemeContextValue {\n const ctx = useContext(ThemeContext);\n if (ctx) return ctx;\n return (defaultTones ??= {\n theme: weaselTheme,\n selection: fullSelection(themeAxes(weaselTheme)),\n resolved: resolveTheme(weaselTheme),\n tones: themeTones(weaselTheme),\n palette: NO_PALETTE,\n });\n}\n\n/**\n * Turns a `ColorSource` into a CSS color under the theme, tones, and palette in\n * force: `colorSourceCss` against `useTones()`. Every component that takes a\n * color writes it through this. `undefined` for no source, and for one nothing\n * answers.\n */\nexport function useColorCss(): (source: ColorSource | null | undefined) => string | undefined {\n const ctx = useTones();\n return useCallback((source) => (source == null ? undefined : (colorSourceCss(source, ctx) ?? undefined)), [ctx]);\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { a as Theme } from '../definition-CSeERbSL.js';
2
+ import '@weasel-js/paint';
3
+
4
+ /**
5
+ * What every classic Mac OS look shares, whatever its era: the system faces
6
+ * and their sizes, square windows and fields, centered window titles, and flat
7
+ * control faces. It names no color, so on its own it draws in the built-in
8
+ * theme's; an era's theme extends it with colors and edges.
9
+ */
10
+ declare const classicMacTheme: Theme;
11
+ /**
12
+ * Mac OS 8 and 9's Platinum appearance: light gray faces with a black outline
13
+ * and a soft two-tone edge, white fields, and pinstriped window titles. The
14
+ * faces themselves are not bundled, so the stack falls through to what the
15
+ * machine has. Opens in light mode; the dark one is this theme's own invention.
16
+ *
17
+ * Pair it with `<DefaultMotif motif={motifs.titlebar()}>` from `@weasel-js/ui`
18
+ * to title every group as a window.
19
+ */
20
+ declare const platinumTheme: Theme;
21
+
22
+ export { classicMacTheme, platinumTheme };