@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.
- package/README.md +117 -1
- package/dist/index.d.ts +10 -2
- package/dist/index.js +11 -1
- package/dist/navigation/NavDrawer.d.ts +62 -0
- package/dist/navigation/NavDrawer.js +205 -0
- package/dist/navigation/NavHeader.d.ts +12 -1
- package/dist/navigation/NavHeader.js +13 -1
- package/dist/navigation/NavTabBar.js +34 -2
- package/dist/navigation/SwiperIndicator.d.ts +59 -0
- package/dist/navigation/SwiperIndicator.js +232 -0
- package/dist/shared/styles.d.ts +1 -1
- package/dist/styles/components/typography.css +36 -2
- package/dist/styles/index.css +8 -4
- package/dist/styles/themes/shapes.css +2 -1
- package/dist/styles/themes/{dark.css → tokens.css} +9 -33
- package/dist/theme/StatusBarSync.d.ts +41 -0
- package/dist/theme/StatusBarSync.js +88 -0
- package/dist/theme/ThemeProvider.d.ts +97 -37
- package/dist/theme/ThemeProvider.js +164 -46
- package/dist/theme/registry.d.ts +101 -0
- package/dist/theme/registry.js +185 -0
- package/dist/theme/theme-state.d.ts +28 -0
- package/dist/theme/theme-state.js +72 -0
- package/dist/theme/use-screen-theme.d.ts +3 -0
- package/dist/theme/use-screen-theme.js +42 -0
- package/package.json +9 -6
- package/dist/styles/themes/light.css +0 -95
|
@@ -2,81 +2,141 @@
|
|
|
2
2
|
* `<ThemeProvider>` and `useTheme()` — daisyui theme switching for
|
|
3
3
|
* `@sigx/lynx-daisyui`.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
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
|
|
25
|
+
* <ThemeProvider>
|
|
20
26
|
* <App />
|
|
21
27
|
* </ThemeProvider>
|
|
22
28
|
* ));
|
|
23
29
|
*
|
|
24
|
-
* //
|
|
25
|
-
*
|
|
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
|
-
*
|
|
32
|
-
*
|
|
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
|
|
37
|
-
*
|
|
38
|
-
*
|
|
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
|
-
/**
|
|
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
|
|
49
|
-
*
|
|
50
|
-
*
|
|
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
|
|
56
|
-
*
|
|
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
|
-
/**
|
|
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
|
-
*
|
|
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
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
|
26
|
+
* <ThemeProvider>
|
|
21
27
|
* <App />
|
|
22
28
|
* </ThemeProvider>
|
|
23
29
|
* ));
|
|
24
30
|
*
|
|
25
|
-
* //
|
|
26
|
-
*
|
|
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
|
-
*
|
|
33
|
-
*
|
|
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
|
|
38
|
-
*
|
|
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
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
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
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
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
|
-
*
|
|
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
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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;
|