@sigx/lynx-daisyui 0.4.1 → 0.4.3

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.
@@ -2,81 +2,141 @@
2
2
  * `<ThemeProvider>` and `useTheme()` — daisyui theme switching for
3
3
  * `@sigx/lynx-daisyui`.
4
4
  *
5
- * The package ships two color themes (`daisy-light`, `daisy-dark`) plus
6
- * style modifier themes (`daisy-rounded`, `daisy-flat`). Each is a CSS
7
- * class containing scoped `--color-*` / `--radius-*` / `--border-*`
8
- * variable definitions; descendants of an element with the class
9
- * inherit those variables (Lynx has `enableCSSInheritance: true` in
10
- * its layout-pipeline defaults), and the daisyui components are built
11
- * to read those vars directly.
5
+ * Themes are CSS classes containing scoped `--color-*` / `--radius-*`
6
+ * variable definitions; descendants of an element with the class inherit
7
+ * those variables (Lynx has `enableCSSInheritance: true` in its
8
+ * layout-pipeline defaults), and the daisyui components are built to read
9
+ * those vars directly.
10
+ *
11
+ * Six color themes ship in the box (`daisy-light`, `daisy-dark`,
12
+ * `daisy-cupcake`, `daisy-emerald`, `daisy-synthwave`, `daisy-dracula`)
13
+ * plus style modifier themes (`daisy-rounded`, `daisy-flat`). Custom themes
14
+ * register their light/dark variant via `registerTheme()` in
15
+ * `./registry.ts` so `followSystem` and `toggle()` know what to pick.
12
16
  *
13
17
  * Usage:
14
18
  *
15
19
  * ```tsx
16
20
  * import { ThemeProvider, useTheme } from '@sigx/lynx-daisyui';
17
21
  *
22
+ * // System-aware (default): picks daisy-light or daisy-dark from the OS,
23
+ * // live-flips when the user toggles dark mode.
18
24
  * defineApp(() => () => (
19
- * <ThemeProvider initial="daisy-light">
25
+ * <ThemeProvider>
20
26
  * <App />
21
27
  * </ThemeProvider>
22
28
  * ));
23
29
  *
24
- * // Anywhere inside:
25
- * const theme = useTheme();
26
- * theme.toggle(); // daisy-light ↔ daisy-dark
27
- * theme.set('daisy-dark'); // explicit
28
- * theme.name; // 'daisy-light' | 'daisy-dark' | custom string
29
- * ```
30
+ * // Pin a specific theme — ignores system appearance.
31
+ * <ThemeProvider initial="daisy-light">…</ThemeProvider>
30
32
  *
31
- * For multi-class compositions (color + modifier), set a custom string:
32
- * `theme.set('daisy-light daisy-rounded')`.
33
+ * // Custom light/dark pair under followSystem.
34
+ * <ThemeProvider light="daisy-cupcake" dark="daisy-synthwave">…</ThemeProvider>
35
+ * ```
33
36
  */
34
37
  import { type Define } from '@sigx/lynx';
38
+ import type { DaisyColor } from '../shared/styles.js';
39
+ /**
40
+ * Declaration-merge extension: add a typed `variant` prop to `<Icon>`,
41
+ * `<FaSolidIcon>`, `<LucideIcon>`, etc. Daisy owns the entire concept
42
+ * — `@sigx/lynx-icons` has no notion of variants. Without this merge
43
+ * being in scope (i.e. an app that doesn't depend on daisy), `<Icon
44
+ * variant="…">` is a compile error: the property doesn't exist.
45
+ *
46
+ * The merge fires the moment any consumer imports anything from
47
+ * `@sigx/lynx-daisyui`. No subpath, no extra import dance.
48
+ */
49
+ declare module '@sigx/lynx-icons' {
50
+ interface IconPropsExtensions {
51
+ /**
52
+ * Daisy color token applied as the icon's `fill`. Resolved at
53
+ * runtime through `useIconColorResolver` (provided by
54
+ * `<ThemeProvider>`) to the current theme's hex value.
55
+ */
56
+ variant?: DaisyColor;
57
+ }
58
+ }
35
59
  /**
36
- * Theme class applied to the provider's host view. The two built-ins
37
- * (`daisy-light` / `daisy-dark`) get autocomplete; arbitrary strings
38
- * are accepted for custom themes or multi-class compositions like
39
- * `'daisy-light daisy-rounded'`.
60
+ * Theme class applied to the provider's host view. The six color themes
61
+ * get autocomplete; arbitrary strings are accepted for custom themes or
62
+ * multi-class compositions like `'daisy-light daisy-rounded'`.
40
63
  */
41
- export type DaisyTheme = 'daisy-light' | 'daisy-dark' | (string & {});
64
+ export type DaisyTheme = 'daisy-light' | 'daisy-dark' | 'daisy-cupcake' | 'daisy-emerald' | 'daisy-synthwave' | 'daisy-dracula' | (string & {});
42
65
  export interface ThemeController {
43
66
  /** Current theme class. Reactive — read inside render/effect to track. */
44
67
  readonly name: DaisyTheme;
45
- /** Replace the active theme. */
68
+ /**
69
+ * Whether the theme is currently being driven by the system color
70
+ * scheme (true when no `initial` was passed and `set()` hasn't been
71
+ * called since mount). UI like a settings screen can read this to show
72
+ * a "Follow system" indicator.
73
+ */
74
+ readonly followingSystem: boolean;
75
+ /**
76
+ * Replace the active theme. Pins the choice — subsequent system
77
+ * appearance changes won't override it (until `followSystem()` is called).
78
+ */
46
79
  set(name: DaisyTheme): void;
47
80
  /**
48
- * Flip between `daisy-light` and `daisy-dark`. When the active
49
- * theme is neither (custom / multi-class), defaults to
50
- * `daisy-dark` on first call.
81
+ * Flip to the paired theme — for built-ins, light dark; for custom
82
+ * themes, follows the `pair` declared in `registerTheme()`, or the
83
+ * first theme of the opposite variant.
51
84
  */
52
85
  toggle(): void;
86
+ /**
87
+ * Resume following system appearance. Equivalent to mounting fresh
88
+ * with no `initial` prop. Useful for a "Reset to system" button.
89
+ */
90
+ followSystem(): void;
53
91
  }
54
92
  /**
55
- * Access the enclosing daisyui theme controller. Throws when used
56
- * outside `<ThemeProvider>` install a provider at your app root.
93
+ * Access the active daisyui theme controller. Resolves to the nearest
94
+ * `<ThemeProvider>`'s controller (a content sub-scope), or — at the app root
95
+ * and in *headless* code with no provider mounted — the global controller
96
+ * (`themeController`). Never throws: theme control is reachable from anywhere
97
+ * (issue #113). For control that must always target the app/OS theme
98
+ * regardless of scope (e.g. a status-bar sync), import `themeController`.
57
99
  */
58
100
  export declare const useTheme: import("@sigx/runtime-core").InjectableFunction<ThemeController>;
59
101
  export type ThemeProviderProps =
60
- /** Initial theme class. Defaults to `daisy-light`. */
102
+ /**
103
+ * Pin the initial theme. When set, the provider ignores system
104
+ * appearance until `controller.followSystem()` is called. When
105
+ * omitted, the provider follows the OS color scheme and live-flips
106
+ * with it.
107
+ */
61
108
  Define.Prop<'initial', DaisyTheme, false>
109
+ /**
110
+ * Theme to use when the system color scheme is `'light'`. Defaults to
111
+ * the first registered light theme (`daisy-light`). Only consulted
112
+ * while `followingSystem` is true.
113
+ */
114
+ & Define.Prop<'light', DaisyTheme, false>
115
+ /**
116
+ * Theme to use when the system color scheme is `'dark'`. Defaults to
117
+ * the first registered dark theme (`daisy-dark`). Only consulted
118
+ * while `followingSystem` is true.
119
+ */
120
+ & Define.Prop<'dark', DaisyTheme, false>
62
121
  /** Extra classes appended to the theme class on the host view. */
63
122
  & Define.Prop<'class', string, false>
64
123
  /** Extra inline style on the host view. Merged after the base flex-fill defaults. */
65
124
  & Define.Prop<'style', Record<string, string | number>, false> & Define.Slot<'default'>;
66
125
  /**
67
- * Wraps children in a `<view class={theme}>` so the daisyui CSS
68
- * variables defined inside `.daisy-light` / `.daisy-dark` inherit
69
- * down to every descendant.
126
+ * Wraps children in a `<view class={theme}>` so the daisyui CSS variables
127
+ * defined inside the theme class inherit down to every descendant.
70
128
  *
71
- * The host view defaults to flex-fill long-form so the wrapper doesn't
72
- * collapse between ancestors that flex (e.g. `<SafeAreaProvider>`) and
73
- * descendants that need a sized parent (`<SafeAreaView>`). Consumers
74
- * override the layout via `style`.
129
+ * Layout: defaults to flex-fill long-form so the wrapper doesn't collapse
130
+ * between ancestors that flex (e.g. `<SafeAreaProvider>`) and descendants
131
+ * that need a sized parent (`<SafeAreaView>`). Consumers override via
132
+ * `style`.
75
133
  *
76
134
  * Theme name is held in an *object* signal (not a primitive) so the
77
- * literal-union type survives — `signal<T>` widens primitive literals
78
- * to plain `string` via `Widen<T>`.
135
+ * literal-union type survives — `signal<T>` widens primitive literals to
136
+ * plain `string` via `Widen<T>`.
79
137
  */
80
138
  export declare const ThemeProvider: import("@sigx/runtime-core").ComponentFactory<ThemeProviderProps, void, {
81
139
  default: () => import("@sigx/runtime-core").JSXElement | import("@sigx/runtime-core").JSXElement[] | null;
82
140
  }>;
141
+ export { listThemes, registerTheme, extendTheme, pickThemeFor, pairOf, variantOf, colorsOf, radiusOf, } from './registry.js';
142
+ export type { Theme, ThemePalette, ThemeRadius, ThemeVariant } from './registry.js';
@@ -3,81 +3,199 @@ import { jsx as _jsx } from "@sigx/lynx/jsx-runtime";
3
3
  * `<ThemeProvider>` and `useTheme()` — daisyui theme switching for
4
4
  * `@sigx/lynx-daisyui`.
5
5
  *
6
- * The package ships two color themes (`daisy-light`, `daisy-dark`) plus
7
- * style modifier themes (`daisy-rounded`, `daisy-flat`). Each is a CSS
8
- * class containing scoped `--color-*` / `--radius-*` / `--border-*`
9
- * variable definitions; descendants of an element with the class
10
- * inherit those variables (Lynx has `enableCSSInheritance: true` in
11
- * its layout-pipeline defaults), and the daisyui components are built
12
- * to read those vars directly.
6
+ * Themes are CSS classes containing scoped `--color-*` / `--radius-*`
7
+ * variable definitions; descendants of an element with the class inherit
8
+ * those variables (Lynx has `enableCSSInheritance: true` in its
9
+ * layout-pipeline defaults), and the daisyui components are built to read
10
+ * those vars directly.
11
+ *
12
+ * Six color themes ship in the box (`daisy-light`, `daisy-dark`,
13
+ * `daisy-cupcake`, `daisy-emerald`, `daisy-synthwave`, `daisy-dracula`)
14
+ * plus style modifier themes (`daisy-rounded`, `daisy-flat`). Custom themes
15
+ * register their light/dark variant via `registerTheme()` in
16
+ * `./registry.ts` so `followSystem` and `toggle()` know what to pick.
13
17
  *
14
18
  * Usage:
15
19
  *
16
20
  * ```tsx
17
21
  * import { ThemeProvider, useTheme } from '@sigx/lynx-daisyui';
18
22
  *
23
+ * // System-aware (default): picks daisy-light or daisy-dark from the OS,
24
+ * // live-flips when the user toggles dark mode.
19
25
  * defineApp(() => () => (
20
- * <ThemeProvider initial="daisy-light">
26
+ * <ThemeProvider>
21
27
  * <App />
22
28
  * </ThemeProvider>
23
29
  * ));
24
30
  *
25
- * // Anywhere inside:
26
- * const theme = useTheme();
27
- * theme.toggle(); // daisy-light ↔ daisy-dark
28
- * theme.set('daisy-dark'); // explicit
29
- * theme.name; // 'daisy-light' | 'daisy-dark' | custom string
30
- * ```
31
+ * // Pin a specific theme — ignores system appearance.
32
+ * <ThemeProvider initial="daisy-light">…</ThemeProvider>
31
33
  *
32
- * For multi-class compositions (color + modifier), set a custom string:
33
- * `theme.set('daisy-light daisy-rounded')`.
34
+ * // Custom light/dark pair under followSystem.
35
+ * <ThemeProvider light="daisy-cupcake" dark="daisy-synthwave">…</ThemeProvider>
36
+ * ```
34
37
  */
35
- import { component, defineInjectable, defineProvide, signal, } from '@sigx/lynx';
38
+ import { component, defineInjectable, defineProvide, effect, onMounted, onUnmounted, signal, untrack, } from '@sigx/lynx';
39
+ import { useIconColorResolver } from '@sigx/lynx-icons';
40
+ import { useSystemColorScheme } from '@sigx/lynx-appearance';
41
+ import { colorsOf, pickThemeFor, radiusOf } from './registry.js';
42
+ import { globalThemeState, makeThemeController, themeController, } from './theme-state.js';
36
43
  /**
37
- * Access the enclosing daisyui theme controller. Throws when used
38
- * outside `<ThemeProvider>` install a provider at your app root.
44
+ * Access the active daisyui theme controller. Resolves to the nearest
45
+ * `<ThemeProvider>`'s controller (a content sub-scope), or — at the app root
46
+ * and in *headless* code with no provider mounted — the global controller
47
+ * (`themeController`). Never throws: theme control is reachable from anywhere
48
+ * (issue #113). For control that must always target the app/OS theme
49
+ * regardless of scope (e.g. a status-bar sync), import `themeController`.
39
50
  */
40
- export const useTheme = defineInjectable(() => {
41
- throw new Error('[lynx-daisyui] useTheme() called outside <ThemeProvider>. Wrap your app root with `<ThemeProvider initial="daisy-light">…</ThemeProvider>`.');
42
- });
51
+ export const useTheme = defineInjectable(() => themeController);
43
52
  /**
44
- * Wraps children in a `<view class={theme}>` so the daisyui CSS
45
- * variables defined inside `.daisy-light` / `.daisy-dark` inherit
46
- * down to every descendant.
53
+ * Nesting-depth marker. The outermost `<ThemeProvider>` sees depth 0 and binds
54
+ * the global singleton (so headless `themeController` mutations render and the
55
+ * OS bars track it); a nested provider sees >= 1 and creates its own local
56
+ * state — a content sub-scope that recolors its subtree without touching the
57
+ * global theme or the system bars.
58
+ */
59
+ const useThemeDepth = defineInjectable(() => 0);
60
+ /**
61
+ * Wraps children in a `<view class={theme}>` so the daisyui CSS variables
62
+ * defined inside the theme class inherit down to every descendant.
47
63
  *
48
- * The host view defaults to flex-fill long-form so the wrapper doesn't
49
- * collapse between ancestors that flex (e.g. `<SafeAreaProvider>`) and
50
- * descendants that need a sized parent (`<SafeAreaView>`). Consumers
51
- * override the layout via `style`.
64
+ * Layout: defaults to flex-fill long-form so the wrapper doesn't collapse
65
+ * between ancestors that flex (e.g. `<SafeAreaProvider>`) and descendants
66
+ * that need a sized parent (`<SafeAreaView>`). Consumers override via
67
+ * `style`.
52
68
  *
53
69
  * Theme name is held in an *object* signal (not a primitive) so the
54
- * literal-union type survives — `signal<T>` widens primitive literals
55
- * to plain `string` via `Widen<T>`.
70
+ * literal-union type survives — `signal<T>` widens primitive literals to
71
+ * plain `string` via `Widen<T>`.
56
72
  */
57
73
  export const ThemeProvider = component(({ props, slots }) => {
58
- const state = signal({ name: props.initial ?? 'daisy-light' });
59
- const controller = {
60
- get name() { return state.name; },
61
- set(next) { state.name = next; },
62
- toggle() {
63
- if (state.name === 'daisy-light')
64
- state.name = 'daisy-dark';
65
- else if (state.name === 'daisy-dark')
66
- state.name = 'daisy-light';
67
- else
68
- state.name = 'daisy-dark';
69
- },
70
- };
74
+ const systemScheme = useSystemColorScheme();
75
+ // The underlying signal widens to PrimitiveSignal<string> via Widen<T>;
76
+ // cast at read sites to keep the narrow union throughout the component.
77
+ const readScheme = () => systemScheme.value;
78
+ // Root vs. nested. The outermost provider (depth 0) binds the global
79
+ // singleton so headless `themeController` mutations render here and the OS
80
+ // bars (via StatusBarSync) follow this theme. A nested provider gets its own
81
+ // local state: a content sub-scope that overrides its subtree only.
82
+ const depth = useThemeDepth();
83
+ const isRoot = depth === 0;
84
+ defineProvide(useThemeDepth, () => depth + 1);
85
+ const state = isRoot
86
+ ? globalThemeState
87
+ : signal(props.initial
88
+ ? { name: props.initial, following: false }
89
+ : {
90
+ name: readScheme() === 'dark'
91
+ ? (props.dark ?? pickThemeFor('dark'))
92
+ : (props.light ?? pickThemeFor('light')),
93
+ following: true,
94
+ });
95
+ // Seed the root from props/system. An explicit `initial` pin is author
96
+ // intent and wins. With no `initial`, reflect the current system scheme into
97
+ // the first render — but only while `following`, so a theme a headless
98
+ // caller set before this mounted is respected, not clobbered. The follow
99
+ // effect below keeps it in sync afterwards.
100
+ if (isRoot) {
101
+ if (props.initial) {
102
+ state.name = props.initial;
103
+ state.following = false;
104
+ }
105
+ else if (state.following) {
106
+ state.name = readScheme() === 'dark'
107
+ ? (props.dark ?? pickThemeFor('dark'))
108
+ : (props.light ?? pickThemeFor('light'));
109
+ }
110
+ }
111
+ const controller = isRoot
112
+ ? themeController
113
+ : makeThemeController(state);
71
114
  defineProvide(useTheme, () => controller);
115
+ // Wire the daisy color resolver into `@sigx/lynx-icons`'s injectable
116
+ // so any `<Icon variant="primary">` rendered inside this subtree gets
117
+ // the daisy primary hex automatically. Reading `state.name` inside
118
+ // the resolver makes every icon's render re-run when the theme flips.
119
+ const resolver = (iconProps) => {
120
+ const variant = iconProps.variant;
121
+ if (!variant)
122
+ return undefined;
123
+ // Every theme's palette lives in the registry; fall back to daisy-light
124
+ // if the active theme isn't registered. SVG fills can't read CSS vars,
125
+ // so the resolved hex/rgb is substituted into the fill at render time.
126
+ const palette = colorsOf(state.name) ?? colorsOf('daisy-light');
127
+ return palette?.[variant];
128
+ };
129
+ defineProvide(useIconColorResolver, () => resolver);
130
+ // Follow the system color scheme while `following`. Reactive: re-runs when
131
+ // `following` flips true (e.g. `controller.followSystem()`, including the
132
+ // headless `themeController`) or when the OS scheme changes, and writes the
133
+ // matching theme. Reading `state.following` and `systemScheme.value` tracks
134
+ // them; the `name` write is `untrack`ed so it can't re-trigger the effect.
135
+ // Created on mount (the native publisher may populate the scheme between
136
+ // setup and mount) and torn down on unmount.
137
+ let follow;
138
+ onMounted(() => {
139
+ follow = effect(() => {
140
+ const following = state.following;
141
+ const scheme = readScheme();
142
+ if (!following)
143
+ return;
144
+ const next = scheme === 'dark'
145
+ ? (props.dark ?? pickThemeFor('dark'))
146
+ : (props.light ?? pickThemeFor('light'));
147
+ untrack(() => {
148
+ if (state.name !== next)
149
+ state.name = next;
150
+ });
151
+ });
152
+ });
153
+ onUnmounted(() => {
154
+ follow?.stop();
155
+ follow = undefined;
156
+ });
72
157
  return () => {
73
- const baseStyle = {
158
+ // Every theme is data. Apply its color tokens as inline CSS custom
159
+ // properties — Lynx inherits custom properties to descendants, so
160
+ // component classes resolve `var(--color-*)` against these (the same
161
+ // mechanism SafeAreaProvider uses for `--sat`/`--sal`). The `daisy`
162
+ // base class supplies theme-agnostic structural tokens (radius,
163
+ // sizing); a theme may override roundness via `radius`. The root
164
+ // background/text are painted from the palette literals (inline
165
+ // `var()` values don't resolve in Lynx).
166
+ const palette = colorsOf(state.name) ?? colorsOf('daisy-light');
167
+ const radius = radiusOf(state.name);
168
+ const style = {
74
169
  flexGrow: 1,
75
170
  flexShrink: 1,
76
171
  flexBasis: 0,
77
172
  minHeight: 0,
78
173
  display: 'flex',
79
174
  flexDirection: 'column',
175
+ backgroundColor: palette['base-100'],
176
+ color: palette['base-content'],
80
177
  };
81
- return (_jsx("view", { class: `${state.name}${props.class ? ' ' + props.class : ''}`, style: props.style ? { ...baseStyle, ...props.style } : baseStyle, children: slots.default?.() }));
178
+ for (const key in palette) {
179
+ style[`--color-${key}`] = palette[key];
180
+ }
181
+ if (radius) {
182
+ if (radius.box)
183
+ style['--rounded-box'] = radius.box;
184
+ if (radius.btn)
185
+ style['--rounded-btn'] = radius.btn;
186
+ if (radius.badge)
187
+ style['--rounded-badge'] = radius.badge;
188
+ if (radius.tab)
189
+ style['--rounded-tab'] = radius.tab;
190
+ if (radius.selector)
191
+ style['--rounded-selector'] = radius.selector;
192
+ if (radius.toggle)
193
+ style['--rounded-toggle'] = radius.toggle;
194
+ }
195
+ if (props.style)
196
+ Object.assign(style, props.style);
197
+ return (_jsx("view", { class: `daisy${props.class ? ' ' + props.class : ''}`, style: style, children: slots.default?.() }));
82
198
  };
83
199
  });
200
+ // Re-export registry helpers so consumers only need `@sigx/lynx-daisyui`.
201
+ export { listThemes, registerTheme, extendTheme, pickThemeFor, pairOf, variantOf, colorsOf, radiusOf, } from './registry.js';
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Theme registry — the single source of truth for daisy themes.
3
+ *
4
+ * A theme is *data*: a name, a light/dark variant, and a full color palette
5
+ * (plus an optional toggle `pair` and roundness overrides). Both rendering and
6
+ * icon tinting read from here — `<ThemeProvider>` applies a theme's `colors`
7
+ * as inline CSS custom properties on its host view (Lynx inherits custom
8
+ * properties to descendants, so component classes resolve `var(--color-*)`),
9
+ * and the icon color resolver reads the same palette for SVG fills (parsed SVG
10
+ * content can't read CSS vars). There is no per-theme CSS or parallel JS
11
+ * palette to keep in sync.
12
+ *
13
+ * The six built-ins are seeded below. Register more — including tenant themes
14
+ * fetched at runtime — with `registerTheme()`, or `extendTheme()` to derive
15
+ * one from a base. Order matters for `pickThemeFor()`: the first theme of a
16
+ * given variant is the follow-system default for that variant.
17
+ *
18
+ * Structural tokens (radius, sizing, component dimensions) are theme-agnostic
19
+ * and ship once in the bundled `.daisy` base class (`styles/themes/tokens.css`);
20
+ * a theme may override roundness via `radius`.
21
+ *
22
+ * Colors are engine-safe strings — hex or `rgb()`. Lynx's CSS engine does not
23
+ * parse `oklch()`, so convert before registering.
24
+ */
25
+ import type { DaisyColor } from '../shared/styles.js';
26
+ export type ThemeVariant = 'light' | 'dark';
27
+ /** Full daisy color palette — every semantic token, no holes. */
28
+ export type ThemePalette = Record<DaisyColor, string>;
29
+ /** Roundness token overrides. Defaults live in the bundled `.daisy` base. */
30
+ export interface ThemeRadius {
31
+ box?: string;
32
+ btn?: string;
33
+ badge?: string;
34
+ tab?: string;
35
+ selector?: string;
36
+ toggle?: string;
37
+ }
38
+ export interface Theme {
39
+ /** Unique id — also the value of `theme.name`. */
40
+ name: string;
41
+ /** Light or dark — drives follow-system selection and status-bar tint. */
42
+ variant: ThemeVariant;
43
+ /** Complete color palette (all 20 semantic tokens). */
44
+ colors: ThemePalette;
45
+ /**
46
+ * Which theme `toggle()` flips to. Defaults to the first registered theme of
47
+ * the opposite variant.
48
+ */
49
+ pair?: string;
50
+ /** Optional roundness overrides; unspecified tokens fall back to `.daisy`. */
51
+ radius?: ThemeRadius;
52
+ }
53
+ /**
54
+ * All registered themes in insertion order. Returns a shallow copy so callers
55
+ * can't mutate the internal registry — re-registration goes through
56
+ * `registerTheme()`. Each entry is a full `Theme` (name, variant, palette),
57
+ * so consumers can render swatches in a picker.
58
+ */
59
+ export declare function listThemes(): readonly Theme[];
60
+ /**
61
+ * Register (or replace, by `name`) a theme. Call at module-load time before
62
+ * mounting `<ThemeProvider>` so it shows up in `listThemes()` / `pickThemeFor()`.
63
+ */
64
+ export declare function registerTheme(theme: Theme): void;
65
+ /**
66
+ * Derive a new theme from a registered base, overriding any colors / roundness.
67
+ * Ergonomic for "tenant tweaks a few tokens": the result is a full `Theme` you
68
+ * pass to `registerTheme()`. Throws if `base` isn't registered.
69
+ *
70
+ * ```ts
71
+ * registerTheme(extendTheme('daisy-dark', {
72
+ * name: 'acme-dark',
73
+ * colors: { primary: '#fb7185' },
74
+ * }));
75
+ * ```
76
+ */
77
+ export declare function extendTheme(base: string, patch: {
78
+ name: string;
79
+ variant?: ThemeVariant;
80
+ pair?: string;
81
+ colors?: Partial<ThemePalette>;
82
+ radius?: ThemeRadius;
83
+ }): Theme;
84
+ /** The variant of a registered theme, or `undefined` if not registered. */
85
+ export declare function variantOf(name: string | undefined): ThemeVariant | undefined;
86
+ /** The color palette of a registered theme, or `undefined` if not registered. */
87
+ export declare function colorsOf(name: string | undefined): ThemePalette | undefined;
88
+ /** The roundness overrides of a registered theme, if any. */
89
+ export declare function radiusOf(name: string | undefined): ThemeRadius | undefined;
90
+ /**
91
+ * Pick a default theme for a given system color scheme — the first registered
92
+ * theme of that variant (`daisy-light` / `daisy-dark` under the seeded
93
+ * registry). Falls back to `'daisy-light'` if none of that variant exists.
94
+ */
95
+ export declare function pickThemeFor(scheme: ThemeVariant): string;
96
+ /**
97
+ * Resolve the paired theme of a given name — used by `theme.toggle()`. Follows
98
+ * `pair` if set, otherwise the first theme of the opposite variant. Returns the
99
+ * input unchanged when the theme isn't registered.
100
+ */
101
+ export declare function pairOf(name: string): string;