@ttoss/fsl-theme 1.1.12 → 1.1.14
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/Types-BiBa17RL.d.cts +1427 -0
- package/dist/Types-BiBa17RL.d.mts +1427 -0
- package/dist/baseBundle-DxvXyhGa.mjs +17 -0
- package/dist/baseBundle-iEFf5nqT.cjs +22 -0
- package/dist/{esm/chunk-SE5Z52RE.js → createTheme-BLNYztZU.mjs} +76 -172
- package/dist/createTheme-Cv6RP9D6.cjs +1825 -0
- package/dist/css.cjs +48 -0
- package/dist/{css.d.ts → css.d.cts} +67 -63
- package/dist/css.d.mts +168 -0
- package/dist/css.mjs +42 -0
- package/dist/dataviz/index.cjs +45 -0
- package/dist/dataviz/{index.d.ts → index.d.cts} +9 -5
- package/dist/dataviz/index.d.mts +66 -0
- package/dist/dataviz/index.mjs +39 -0
- package/dist/dtcg.cjs +115 -0
- package/dist/{dtcg.d.ts → dtcg.d.cts} +9 -7
- package/dist/dtcg.d.mts +51 -0
- package/dist/dtcg.mjs +112 -0
- package/dist/helpers-4p4-QVt_.cjs +258 -0
- package/dist/helpers-CaswNJMy.mjs +211 -0
- package/dist/{index.d.ts → index-CsIjfw86.d.cts} +42 -34
- package/dist/index-nJrjI0BA.d.mts +94 -0
- package/dist/index.cjs +16 -0
- package/dist/index.d.cts +6 -0
- package/dist/index.d.mts +6 -0
- package/dist/index.mjs +7 -0
- package/dist/{react.d.ts → react-CGa6FlNL.d.cts} +130 -106
- package/dist/react-DnKxR2gK.d.mts +370 -0
- package/dist/react-EUwpdvY7.cjs +481 -0
- package/dist/react.cjs +12 -0
- package/dist/react.d.cts +4 -0
- package/dist/react.d.mts +4 -0
- package/dist/react.mjs +412 -0
- package/dist/runtime-entry.cjs +9 -0
- package/dist/runtime-entry.d.cts +3 -0
- package/dist/runtime-entry.d.mts +3 -0
- package/dist/runtime-entry.mjs +3 -0
- package/dist/{runtime-entry.d.ts → ssrScript-BVysxDws.d.cts} +26 -23
- package/dist/ssrScript-BVysxDws.d.mts +98 -0
- package/dist/ssrScript-CRfrN8Pm.cjs +202 -0
- package/dist/ssrScript-D3kGPQpi.mjs +179 -0
- package/dist/themes/bruttal.cjs +75 -0
- package/dist/themes/bruttal.d.cts +3 -0
- package/dist/themes/bruttal.d.mts +3 -0
- package/dist/themes/bruttal.mjs +72 -0
- package/dist/themes/corporate.cjs +34 -0
- package/dist/themes/corporate.d.cts +3 -0
- package/dist/themes/corporate.d.mts +3 -0
- package/dist/{esm/chunk-TPMN75JM.js → themes/corporate.mjs} +7 -5
- package/dist/themes/oca.cjs +34 -0
- package/dist/themes/oca.d.cts +3 -0
- package/dist/themes/oca.d.mts +3 -0
- package/dist/{esm/chunk-DU4QDQUC.js → themes/oca.mjs} +7 -5
- package/dist/themes/ventures.cjs +34 -0
- package/dist/themes/ventures.d.cts +3 -0
- package/dist/themes/ventures.d.mts +3 -0
- package/dist/{esm/chunk-BXKVVQEP.js → themes/ventures.mjs} +7 -5
- package/dist/toCssVars-CYZCe-on.mjs +286 -0
- package/dist/toCssVars-DudHKvt2.cjs +297 -0
- package/dist/{esm/chunk-4Q4P3JBB.js → tokenRegistry-DjgSN3oU.mjs} +23 -20
- package/dist/tokenRegistry-OhaJ9sPJ.cjs +199 -0
- package/dist/vars.cjs +127 -0
- package/dist/{vars.d.ts → vars.d.cts} +8 -7
- package/dist/vars.d.mts +128 -0
- package/dist/vars.mjs +123 -0
- package/dist/withDataviz-B4pVsOwV.cjs +192 -0
- package/dist/{esm/chunk-FBVUI2PK.js → withDataviz-DY5s7R51.mjs} +40 -12
- package/package.json +6 -6
- package/dist/Types-6tR0_2Ss.d.ts +0 -1452
- package/dist/esm/chunk-5PWPAQMC.js +0 -9
- package/dist/esm/chunk-HRNXVRS3.js +0 -54
- package/dist/esm/chunk-IJGA42O6.js +0 -141
- package/dist/esm/chunk-PQPQNZ73.js +0 -262
- package/dist/esm/chunk-UMRQ4OTX.js +0 -11
- package/dist/esm/chunk-VL6EGE6Z.js +0 -222
- package/dist/esm/chunk-WVQSTQD5.js +0 -192
- package/dist/esm/css.js +0 -6
- package/dist/esm/dataviz/index.js +0 -19
- package/dist/esm/dtcg.js +0 -65
- package/dist/esm/index.js +0 -10
- package/dist/esm/react.js +0 -8
- package/dist/esm/runtime-entry.js +0 -4
- package/dist/esm/themes/bruttal.js +0 -6
- package/dist/esm/themes/corporate.js +0 -6
- package/dist/esm/themes/oca.js +0 -6
- package/dist/esm/themes/ventures.js +0 -6
- package/dist/esm/vars.js +0 -28
- package/dist/themes/bruttal.d.ts +0 -5
- package/dist/themes/corporate.d.ts +0 -5
- package/dist/themes/oca.d.ts +0 -5
- package/dist/themes/ventures.d.ts +0 -5
package/dist/react.mjs
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import { r as deepMerge, s as toFlatTokens } from "./helpers-CaswNJMy.mjs";
|
|
3
|
+
import { getThemeStylesContent } from "./css.mjs";
|
|
4
|
+
import { i as createThemeRuntime, t as getThemeScriptContent } from "./ssrScript-D3kGPQpi.mjs";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
|
|
8
|
+
//#region src/react.tsx
|
|
9
|
+
const COARSE_QUERY = "(any-pointer: coarse)";
|
|
10
|
+
/**
|
|
11
|
+
* Subscribe to coarse-pointer media query changes.
|
|
12
|
+
* Returns `true` when the device has at least one coarse pointer (touch).
|
|
13
|
+
* Returns `false` on SSR or when `matchMedia` is unavailable (React Native).
|
|
14
|
+
*/
|
|
15
|
+
const useCoarsePointer = () => {
|
|
16
|
+
const [isCoarse, setIsCoarse] = React.useState(() => {
|
|
17
|
+
return typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia(COARSE_QUERY).matches : false;
|
|
18
|
+
});
|
|
19
|
+
React.useEffect(() => {
|
|
20
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return;
|
|
21
|
+
const mql = window.matchMedia(COARSE_QUERY);
|
|
22
|
+
const handler = e => {
|
|
23
|
+
return setIsCoarse(e.matches);
|
|
24
|
+
};
|
|
25
|
+
mql.addEventListener("change", handler);
|
|
26
|
+
return () => {
|
|
27
|
+
return mql.removeEventListener("change", handler);
|
|
28
|
+
};
|
|
29
|
+
}, []);
|
|
30
|
+
return isCoarse;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Apply coarse-pointer hit target overrides to a resolved token map.
|
|
34
|
+
*
|
|
35
|
+
* When `isCoarse` is true, replaces `semantic.sizing.hit.{step}` values with
|
|
36
|
+
* the raw coarse values from `core.sizing.hit.coarse.{step}`. This mirrors
|
|
37
|
+
* the `@media (any-pointer: coarse)` block that `toCssVars` emits for CSS
|
|
38
|
+
* consumers — ensuring non-CSS consumers (React Native, canvas) get
|
|
39
|
+
* touch-appropriate hit targets.
|
|
40
|
+
*/
|
|
41
|
+
const applyCoarseHitOverrides = (tokens, theme, isCoarse) => {
|
|
42
|
+
if (!isCoarse) return tokens;
|
|
43
|
+
const overrides = {
|
|
44
|
+
...tokens
|
|
45
|
+
};
|
|
46
|
+
for (const [key, value] of Object.entries(theme.core.sizing.hit.coarse)) if (typeof value === "string") overrides[`semantic.sizing.hit.${key}`] = value;
|
|
47
|
+
return overrides;
|
|
48
|
+
};
|
|
49
|
+
const SemanticTokensCtx = React.createContext(null);
|
|
50
|
+
/**
|
|
51
|
+
* Resolves semantic tokens for the given bundle + mode combination.
|
|
52
|
+
*/
|
|
53
|
+
const resolveSemanticTokens = (bundle, resolvedMode) => {
|
|
54
|
+
if (resolvedMode === bundle.baseMode || !bundle.alternate?.semantic) return bundle.base.semantic;
|
|
55
|
+
return deepMerge(bundle.base.semantic, bundle.alternate.semantic);
|
|
56
|
+
};
|
|
57
|
+
const ResolvedTokensCtx = React.createContext(null);
|
|
58
|
+
const ModeCtx = React.createContext(null);
|
|
59
|
+
/**
|
|
60
|
+
* React provider that manages theme switching via `createThemeRuntime`.
|
|
61
|
+
*
|
|
62
|
+
* Applies `data-tt-theme` and `data-tt-mode` on `<html>` (or `root`),
|
|
63
|
+
* persists to localStorage, and listens to system color scheme changes.
|
|
64
|
+
* When `theme` is provided, automatically injects CSS Custom Properties into
|
|
65
|
+
* the document `<head>` via React 19 style hoisting.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```tsx
|
|
69
|
+
* import { ThemeProvider } from '@ttoss/fsl-theme/react';
|
|
70
|
+
* import { createTheme } from '@ttoss/fsl-theme';
|
|
71
|
+
*
|
|
72
|
+
* const myTheme = createTheme();
|
|
73
|
+
*
|
|
74
|
+
* export const App = () => (
|
|
75
|
+
* <ThemeProvider theme={myTheme}>
|
|
76
|
+
* <YourApp />
|
|
77
|
+
* </ThemeProvider>
|
|
78
|
+
* );
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
const ThemeProvider = ({
|
|
82
|
+
defaultMode,
|
|
83
|
+
storageKey,
|
|
84
|
+
theme,
|
|
85
|
+
themeId,
|
|
86
|
+
onModeChange,
|
|
87
|
+
root,
|
|
88
|
+
children
|
|
89
|
+
}) => {
|
|
90
|
+
const runtimeRef = React.useRef(null);
|
|
91
|
+
const initDefaultMode = React.useRef(defaultMode);
|
|
92
|
+
const initStorageKey = React.useRef(storageKey);
|
|
93
|
+
const [state, setState] = React.useState(() => {
|
|
94
|
+
return {
|
|
95
|
+
mode: defaultMode ?? "system",
|
|
96
|
+
resolvedMode: (defaultMode ?? "system") === "system" ? "light" : defaultMode
|
|
97
|
+
};
|
|
98
|
+
});
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
const runtime = createThemeRuntime({
|
|
101
|
+
defaultTheme: themeId,
|
|
102
|
+
defaultMode: initDefaultMode.current,
|
|
103
|
+
storageKey: initStorageKey.current,
|
|
104
|
+
root
|
|
105
|
+
});
|
|
106
|
+
runtimeRef.current = runtime;
|
|
107
|
+
setState(runtime.getState());
|
|
108
|
+
const unsubscribe = runtime.subscribe(setState);
|
|
109
|
+
return () => {
|
|
110
|
+
unsubscribe();
|
|
111
|
+
runtime.destroy();
|
|
112
|
+
runtimeRef.current = null;
|
|
113
|
+
};
|
|
114
|
+
}, [root, themeId]);
|
|
115
|
+
const setMode = React.useCallback(mode => {
|
|
116
|
+
runtimeRef.current?.setMode(mode);
|
|
117
|
+
}, []);
|
|
118
|
+
const semanticTokens = React.useMemo(() => {
|
|
119
|
+
return theme ? resolveSemanticTokens(theme, state.resolvedMode) : null;
|
|
120
|
+
}, [theme, state.resolvedMode]);
|
|
121
|
+
const isCoarse = useCoarsePointer();
|
|
122
|
+
const resolvedTokens = React.useMemo(() => {
|
|
123
|
+
if (!theme || !semanticTokens) return null;
|
|
124
|
+
const all = toFlatTokens({
|
|
125
|
+
core: theme.base.core,
|
|
126
|
+
semantic: semanticTokens
|
|
127
|
+
});
|
|
128
|
+
const result = {};
|
|
129
|
+
for (const [key, value] of Object.entries(all)) if (key.startsWith("semantic.")) result[key] = value;
|
|
130
|
+
return applyCoarseHitOverrides(result, theme.base, isCoarse);
|
|
131
|
+
}, [theme, semanticTokens, isCoarse]);
|
|
132
|
+
const cssContent = React.useMemo(() => {
|
|
133
|
+
return theme ? getThemeStylesContent(theme, themeId) : null;
|
|
134
|
+
}, [theme, themeId]);
|
|
135
|
+
const onModeChangeRef = React.useRef(onModeChange);
|
|
136
|
+
React.useEffect(() => {
|
|
137
|
+
onModeChangeRef.current = onModeChange;
|
|
138
|
+
});
|
|
139
|
+
const prevModeRef = React.useRef(null);
|
|
140
|
+
React.useEffect(() => {
|
|
141
|
+
const prev = prevModeRef.current;
|
|
142
|
+
prevModeRef.current = {
|
|
143
|
+
mode: state.mode,
|
|
144
|
+
resolvedMode: state.resolvedMode
|
|
145
|
+
};
|
|
146
|
+
if (prev === null) return;
|
|
147
|
+
if (prev.mode === state.mode && prev.resolvedMode === state.resolvedMode) return;
|
|
148
|
+
onModeChangeRef.current?.(state.mode, state.resolvedMode);
|
|
149
|
+
}, [state.mode, state.resolvedMode]);
|
|
150
|
+
const modeCtxValue = React.useMemo(() => {
|
|
151
|
+
return {
|
|
152
|
+
mode: state.mode,
|
|
153
|
+
resolvedMode: state.resolvedMode,
|
|
154
|
+
setMode
|
|
155
|
+
};
|
|
156
|
+
}, [state.mode, state.resolvedMode, setMode]);
|
|
157
|
+
const coreNode = /* @__PURE__ */jsx(ModeCtx.Provider, {
|
|
158
|
+
value: modeCtxValue,
|
|
159
|
+
children
|
|
160
|
+
});
|
|
161
|
+
if (theme) return /* @__PURE__ */jsxs(Fragment, {
|
|
162
|
+
children: [/* @__PURE__ */jsx("style", {
|
|
163
|
+
precedence: "default",
|
|
164
|
+
children: cssContent
|
|
165
|
+
}), /* @__PURE__ */jsx(ResolvedTokensCtx.Provider, {
|
|
166
|
+
value: resolvedTokens,
|
|
167
|
+
children: /* @__PURE__ */jsx(SemanticTokensCtx.Provider, {
|
|
168
|
+
value: semanticTokens,
|
|
169
|
+
children: coreNode
|
|
170
|
+
})
|
|
171
|
+
})]
|
|
172
|
+
});
|
|
173
|
+
return coreNode;
|
|
174
|
+
};
|
|
175
|
+
const useColorMode = () => {
|
|
176
|
+
const context = React.useContext(ModeCtx);
|
|
177
|
+
if (!context) throw new Error("useColorMode must be used within a <ThemeProvider>");
|
|
178
|
+
return context;
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Access the current theme's **semantic tokens only** — the structural tree
|
|
182
|
+
* with **unresolved** `TokenRef` values (e.g. `'{core.colors.brand.500}'`).
|
|
183
|
+
*
|
|
184
|
+
* ### Primary use cases
|
|
185
|
+
* - Introspection and devtools
|
|
186
|
+
* - Token path comparison (e.g. checking which tokens differ between themes)
|
|
187
|
+
* - Passing to `createTheme` calls
|
|
188
|
+
*
|
|
189
|
+
* ### ✗ Do not use for styling
|
|
190
|
+
* `TokenRef` values are reference strings, not CSS values. Using them in
|
|
191
|
+
* inline styles produces silently broken rendering:
|
|
192
|
+
*
|
|
193
|
+
* ```tsx
|
|
194
|
+
* // ✗ WRONG — tokens.colors.brand.main is '{core.colors.brand.main}', not '#FF0000'
|
|
195
|
+
* <div style={{ color: tokens.colors.brand.main }} />
|
|
196
|
+
*
|
|
197
|
+
* // ✓ CSS consumers — use vars:
|
|
198
|
+
* <div style={{ color: 'var(--tt-color-brand-main)' }} />
|
|
199
|
+
*
|
|
200
|
+
* // ✓ Non-CSS consumers (React Native, canvas) — use useResolvedTokens():
|
|
201
|
+
* const resolved = useResolvedTokens();
|
|
202
|
+
* <View style={{ backgroundColor: resolved['semantic.colors.action.primary.background.default'] }} />
|
|
203
|
+
* ```
|
|
204
|
+
*
|
|
205
|
+
* Requires `<ThemeProvider theme={...}>`.
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* ```tsx
|
|
209
|
+
* import { useTokens } from '@ttoss/fsl-theme/react';
|
|
210
|
+
*
|
|
211
|
+
* const Button = () => {
|
|
212
|
+
* const tokens = useTokens(); // introspection only
|
|
213
|
+
* // tokens.colors.action.primary.background.default → '{core.colors.brand.500}'
|
|
214
|
+
* return <button style={{ background: 'var(--tt-action-primary-background-default)' }} />;
|
|
215
|
+
* };
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
const useTokens = () => {
|
|
219
|
+
const tokens = React.useContext(SemanticTokensCtx);
|
|
220
|
+
if (!React.useContext(ModeCtx)) throw new Error("useTokens must be used within a <ThemeProvider>");
|
|
221
|
+
if (tokens === null) throw new Error("useTokens requires a <ThemeProvider theme={...}>. Pass your theme bundle: <ThemeProvider theme={myTheme} />");
|
|
222
|
+
return tokens;
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Access fully resolved token values as a flat `Record<string, string | number>`.
|
|
226
|
+
*
|
|
227
|
+
* All `{ref}` indirections are resolved to their final raw values — hex colors,
|
|
228
|
+
* px sizes, unitless numbers, etc. Keys use `semantic.*` dot-path notation.
|
|
229
|
+
*
|
|
230
|
+
* ### When to use
|
|
231
|
+
* Use in non-CSS environments where CSS custom properties (`var()`) are not
|
|
232
|
+
* available: React Native, canvas renderers, PDF generation, test assertions.
|
|
233
|
+
*
|
|
234
|
+
* ### ✗ Do not use for CSS rendering
|
|
235
|
+
* CSS consumers should use `vars.*` instead for zero-JS rendering:
|
|
236
|
+
*
|
|
237
|
+
* ```tsx
|
|
238
|
+
* // ✓ CSS (browser)
|
|
239
|
+
* <div style={{ color: 'var(--tt-color-informational-primary-default)' }} />
|
|
240
|
+
*
|
|
241
|
+
* // ✓ Non-CSS (React Native, canvas)
|
|
242
|
+
* const resolved = useResolvedTokens();
|
|
243
|
+
* <View style={{ backgroundColor: resolved['semantic.colors.action.primary.background.default'] }} />
|
|
244
|
+
* ```
|
|
245
|
+
*
|
|
246
|
+
* Requires `<ThemeProvider theme={...}>`.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```tsx
|
|
250
|
+
* import { useResolvedTokens } from '@ttoss/fsl-theme/react';
|
|
251
|
+
*
|
|
252
|
+
* const ReactNativeButton = () => {
|
|
253
|
+
* const resolved = useResolvedTokens();
|
|
254
|
+
* return (
|
|
255
|
+
* <View style={{ backgroundColor: resolved['semantic.colors.action.primary.background.default'] }}>
|
|
256
|
+
* <Text>Click</Text>
|
|
257
|
+
* </View>
|
|
258
|
+
* );
|
|
259
|
+
* };
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
const useResolvedTokens = () => {
|
|
263
|
+
const resolved = React.useContext(ResolvedTokensCtx);
|
|
264
|
+
if (!React.useContext(ModeCtx)) throw new Error("useResolvedTokens must be used within a <ThemeProvider>");
|
|
265
|
+
if (resolved === null) throw new Error("useResolvedTokens requires a <ThemeProvider theme={...}>. Pass your theme bundle: <ThemeProvider theme={myTheme} />");
|
|
266
|
+
return resolved;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Renders an inline `<script>` that prevents theme flash on SSR/SSG.
|
|
270
|
+
*
|
|
271
|
+
* Place in the `<head>` before stylesheets.
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* ```tsx
|
|
275
|
+
* // Next.js app/layout.tsx
|
|
276
|
+
* import { ThemeScript } from '@ttoss/fsl-theme/react';
|
|
277
|
+
*
|
|
278
|
+
* export default function RootLayout({ children }) {
|
|
279
|
+
* return (
|
|
280
|
+
* <html lang="en">
|
|
281
|
+
* <head>
|
|
282
|
+
* <ThemeScript defaultTheme="bruttal" />
|
|
283
|
+
* </head>
|
|
284
|
+
* <body>{children}</body>
|
|
285
|
+
* </html>
|
|
286
|
+
* );
|
|
287
|
+
* }
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
const ThemeScript = ({
|
|
291
|
+
defaultTheme,
|
|
292
|
+
defaultMode,
|
|
293
|
+
storageKey,
|
|
294
|
+
nonce
|
|
295
|
+
} = {}) => {
|
|
296
|
+
return /* @__PURE__ */jsx("script", {
|
|
297
|
+
nonce,
|
|
298
|
+
dangerouslySetInnerHTML: {
|
|
299
|
+
__html: getThemeScriptContent({
|
|
300
|
+
defaultTheme,
|
|
301
|
+
defaultMode,
|
|
302
|
+
storageKey
|
|
303
|
+
})
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
};
|
|
307
|
+
/**
|
|
308
|
+
* Renders an inline `<style>` tag with all CSS Custom Properties for a theme
|
|
309
|
+
* bundle — including coarse-pointer, reduced-motion, and container query
|
|
310
|
+
* progressive enhancement blocks.
|
|
311
|
+
*
|
|
312
|
+
* Use as an escape hatch for SSR frameworks that need explicit `<head>` style
|
|
313
|
+
* injection. In most React apps, `<ThemeProvider theme={...}>` already injects
|
|
314
|
+
* styles automatically via React 19 style hoisting — no `<ThemeStyles>` needed.
|
|
315
|
+
*
|
|
316
|
+
* `dangerouslySetInnerHTML` is safe: content comes exclusively from
|
|
317
|
+
* `toCssVars()` (a pure internal function) — no user input is interpolated.
|
|
318
|
+
*
|
|
319
|
+
* @example
|
|
320
|
+
* ```tsx
|
|
321
|
+
* // SSR escape hatch — no themeId needed for canonical 1-theme model
|
|
322
|
+
* import { ThemeScript, ThemeStyles } from '@ttoss/fsl-theme/react';
|
|
323
|
+
* import { createTheme } from '@ttoss/fsl-theme';
|
|
324
|
+
*
|
|
325
|
+
* const myTheme = createTheme();
|
|
326
|
+
*
|
|
327
|
+
* export default function RootLayout({ children }) {
|
|
328
|
+
* return (
|
|
329
|
+
* <html lang="en">
|
|
330
|
+
* <head>
|
|
331
|
+
* <ThemeScript />
|
|
332
|
+
* <ThemeStyles theme={myTheme} />
|
|
333
|
+
* </head>
|
|
334
|
+
* <body>
|
|
335
|
+
* <ThemeProvider theme={myTheme}>{children}</ThemeProvider>
|
|
336
|
+
* </body>
|
|
337
|
+
* </html>
|
|
338
|
+
* );
|
|
339
|
+
* }
|
|
340
|
+
* ```
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* ```tsx
|
|
344
|
+
* // Multi-theme: explicit themeId for CSS scoping
|
|
345
|
+
* <ThemeStyles theme={brandA} themeId="brand-a" />
|
|
346
|
+
* <ThemeStyles theme={brandB} themeId="brand-b" />
|
|
347
|
+
* ```
|
|
348
|
+
*/
|
|
349
|
+
const ThemeStyles = ({
|
|
350
|
+
theme,
|
|
351
|
+
themeId,
|
|
352
|
+
nonce
|
|
353
|
+
}) => {
|
|
354
|
+
return /* @__PURE__ */jsx("style", {
|
|
355
|
+
nonce,
|
|
356
|
+
dangerouslySetInnerHTML: {
|
|
357
|
+
__html: getThemeStylesContent(theme, themeId)
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* Convenience component that renders both `<ThemeScript>` and `<ThemeStyles>`
|
|
363
|
+
* in a single line — the complete SSR `<head>` setup for flash-free theming.
|
|
364
|
+
*
|
|
365
|
+
* Use in SSR frameworks (Next.js, Remix) where you need explicit `<head>`
|
|
366
|
+
* injection. In CSR apps, `<ThemeProvider theme={...}>` handles everything.
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* ```tsx
|
|
370
|
+
* // Next.js app/layout.tsx
|
|
371
|
+
* import { ThemeHead, ThemeProvider } from '@ttoss/fsl-theme/react';
|
|
372
|
+
* import { createTheme } from '@ttoss/fsl-theme';
|
|
373
|
+
*
|
|
374
|
+
* const myTheme = createTheme();
|
|
375
|
+
*
|
|
376
|
+
* export default function RootLayout({ children }) {
|
|
377
|
+
* return (
|
|
378
|
+
* <html lang="en">
|
|
379
|
+
* <head>
|
|
380
|
+
* <ThemeHead theme={myTheme} />
|
|
381
|
+
* </head>
|
|
382
|
+
* <body>
|
|
383
|
+
* <ThemeProvider theme={myTheme}>{children}</ThemeProvider>
|
|
384
|
+
* </body>
|
|
385
|
+
* </html>
|
|
386
|
+
* );
|
|
387
|
+
* }
|
|
388
|
+
* ```
|
|
389
|
+
*/
|
|
390
|
+
const ThemeHead = ({
|
|
391
|
+
theme,
|
|
392
|
+
themeId,
|
|
393
|
+
nonce,
|
|
394
|
+
defaultMode,
|
|
395
|
+
storageKey
|
|
396
|
+
}) => {
|
|
397
|
+
return /* @__PURE__ */jsxs(Fragment, {
|
|
398
|
+
children: [/* @__PURE__ */jsx(ThemeScript, {
|
|
399
|
+
defaultTheme: themeId,
|
|
400
|
+
defaultMode,
|
|
401
|
+
storageKey,
|
|
402
|
+
nonce
|
|
403
|
+
}), /* @__PURE__ */jsx(ThemeStyles, {
|
|
404
|
+
theme,
|
|
405
|
+
themeId,
|
|
406
|
+
nonce
|
|
407
|
+
})]
|
|
408
|
+
});
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
//#endregion
|
|
412
|
+
export { ThemeHead, ThemeProvider, ThemeScript, ThemeStyles, useColorMode, useResolvedTokens, useTokens };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, {
|
|
3
|
+
value: 'Module'
|
|
4
|
+
});
|
|
5
|
+
const require_ssrScript = require('./ssrScript-CRfrN8Pm.cjs');
|
|
6
|
+
exports.DATA_MODE_ATTR = require_ssrScript.DATA_MODE_ATTR;
|
|
7
|
+
exports.DATA_THEME_ATTR = require_ssrScript.DATA_THEME_ATTR;
|
|
8
|
+
exports.createThemeRuntime = require_ssrScript.createThemeRuntime;
|
|
9
|
+
exports.getThemeScriptContent = require_ssrScript.getThemeScriptContent;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
|
|
2
|
+
import { a as ResolvedMode, c as ThemeRuntimeConfig, i as DATA_THEME_ATTR, l as ThemeState, n as getThemeScriptContent, o as ThemeMode, r as DATA_MODE_ATTR, s as ThemeRuntime, t as ThemeScriptConfig, u as createThemeRuntime } from "./ssrScript-BVysxDws.cjs";
|
|
3
|
+
export { DATA_MODE_ATTR, DATA_THEME_ATTR, type ResolvedMode, type ThemeMode, type ThemeRuntime, type ThemeRuntimeConfig, type ThemeScriptConfig, type ThemeState, createThemeRuntime, getThemeScriptContent };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
|
|
2
|
+
import { a as ResolvedMode, c as ThemeRuntimeConfig, i as DATA_THEME_ATTR, l as ThemeState, n as getThemeScriptContent, o as ThemeMode, r as DATA_MODE_ATTR, s as ThemeRuntime, t as ThemeScriptConfig, u as createThemeRuntime } from "./ssrScript-BVysxDws.mjs";
|
|
3
|
+
export { DATA_MODE_ATTR, DATA_THEME_ATTR, type ResolvedMode, type ThemeMode, type ThemeRuntime, type ThemeRuntimeConfig, type ThemeScriptConfig, type ThemeState, createThemeRuntime, getThemeScriptContent };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
/** Powered by @ttoss/config. https://ttoss.dev/docs/modules/packages/config/ */
|
|
2
|
+
import { i as createThemeRuntime, n as DATA_MODE_ATTR, r as DATA_THEME_ATTR, t as getThemeScriptContent } from "./ssrScript-D3kGPQpi.mjs";
|
|
3
|
+
export { DATA_MODE_ATTR, DATA_THEME_ATTR, createThemeRuntime, getThemeScriptContent };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/runtime.d.ts
|
|
1
3
|
declare const DATA_THEME_ATTR = "data-tt-theme";
|
|
2
4
|
declare const DATA_MODE_ATTR = "data-tt-mode";
|
|
3
5
|
/**
|
|
@@ -12,30 +14,30 @@ type ResolvedMode = 'light' | 'dark';
|
|
|
12
14
|
* Snapshot of the current theme state.
|
|
13
15
|
*/
|
|
14
16
|
interface ThemeState {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
+
mode: ThemeMode;
|
|
18
|
+
resolvedMode: ResolvedMode;
|
|
17
19
|
}
|
|
18
20
|
/**
|
|
19
21
|
* Configuration for `createThemeRuntime()`.
|
|
20
22
|
*/
|
|
21
23
|
interface ThemeRuntimeConfig {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
/** Theme identifier written to `data-tt-theme`. Provide only for MFE / multi-theme CSS scoping. When omitted, `data-tt-theme` is not written to the DOM. */
|
|
25
|
+
defaultTheme?: string;
|
|
26
|
+
/** Mode to use when no persisted value is found. @default 'system' */
|
|
27
|
+
defaultMode?: ThemeMode;
|
|
28
|
+
/** localStorage key for persistence. @default 'tt-theme' */
|
|
29
|
+
storageKey?: string;
|
|
30
|
+
/** Root element to apply data attributes to. @default document.documentElement */
|
|
31
|
+
root?: HTMLElement;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* Public API returned by `createThemeRuntime()`.
|
|
33
35
|
*/
|
|
34
36
|
interface ThemeRuntime {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
getState: () => ThemeState;
|
|
38
|
+
setMode: (mode: ThemeMode) => void;
|
|
39
|
+
subscribe: (listener: (state: ThemeState) => void) => () => void;
|
|
40
|
+
destroy: () => void;
|
|
39
41
|
}
|
|
40
42
|
/**
|
|
41
43
|
* Creates a framework-agnostic runtime that manages theme switching.
|
|
@@ -55,17 +57,18 @@ interface ThemeRuntime {
|
|
|
55
57
|
* ```
|
|
56
58
|
*/
|
|
57
59
|
declare const createThemeRuntime: (config?: ThemeRuntimeConfig) => ThemeRuntime;
|
|
58
|
-
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/ssrScript.d.ts
|
|
59
62
|
/**
|
|
60
63
|
* Configuration for the SSR bootstrap script.
|
|
61
64
|
*/
|
|
62
65
|
interface ThemeScriptConfig {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
/** Theme identifier written to `data-tt-theme`. When omitted, `data-tt-theme` is not written. */
|
|
67
|
+
defaultTheme?: string;
|
|
68
|
+
/** Mode to use when no persisted value is found. @default 'system' */
|
|
69
|
+
defaultMode?: ThemeMode;
|
|
70
|
+
/** localStorage key. Must match the runtime's `storageKey`. @default 'tt-theme' */
|
|
71
|
+
storageKey?: string;
|
|
69
72
|
}
|
|
70
73
|
/**
|
|
71
74
|
* Returns self-contained JavaScript code that prevents theme flash on SSR/SSG.
|
|
@@ -91,5 +94,5 @@ interface ThemeScriptConfig {
|
|
|
91
94
|
* ```
|
|
92
95
|
*/
|
|
93
96
|
declare const getThemeScriptContent: (config?: ThemeScriptConfig) => string;
|
|
94
|
-
|
|
95
|
-
export {
|
|
97
|
+
//#endregion
|
|
98
|
+
export { ResolvedMode as a, ThemeRuntimeConfig as c, DATA_THEME_ATTR as i, ThemeState as l, getThemeScriptContent as n, ThemeMode as o, DATA_MODE_ATTR as r, ThemeRuntime as s, ThemeScriptConfig as t, createThemeRuntime as u };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/runtime.d.ts
|
|
3
|
+
declare const DATA_THEME_ATTR = "data-tt-theme";
|
|
4
|
+
declare const DATA_MODE_ATTR = "data-tt-mode";
|
|
5
|
+
/**
|
|
6
|
+
* Theme mode. `'system'` delegates to the OS `prefers-color-scheme` preference.
|
|
7
|
+
*/
|
|
8
|
+
type ThemeMode = 'light' | 'dark' | 'system';
|
|
9
|
+
/**
|
|
10
|
+
* Resolved mode — always `'light'` or `'dark'` (never `'system'`).
|
|
11
|
+
*/
|
|
12
|
+
type ResolvedMode = 'light' | 'dark';
|
|
13
|
+
/**
|
|
14
|
+
* Snapshot of the current theme state.
|
|
15
|
+
*/
|
|
16
|
+
interface ThemeState {
|
|
17
|
+
mode: ThemeMode;
|
|
18
|
+
resolvedMode: ResolvedMode;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Configuration for `createThemeRuntime()`.
|
|
22
|
+
*/
|
|
23
|
+
interface ThemeRuntimeConfig {
|
|
24
|
+
/** Theme identifier written to `data-tt-theme`. Provide only for MFE / multi-theme CSS scoping. When omitted, `data-tt-theme` is not written to the DOM. */
|
|
25
|
+
defaultTheme?: string;
|
|
26
|
+
/** Mode to use when no persisted value is found. @default 'system' */
|
|
27
|
+
defaultMode?: ThemeMode;
|
|
28
|
+
/** localStorage key for persistence. @default 'tt-theme' */
|
|
29
|
+
storageKey?: string;
|
|
30
|
+
/** Root element to apply data attributes to. @default document.documentElement */
|
|
31
|
+
root?: HTMLElement;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Public API returned by `createThemeRuntime()`.
|
|
35
|
+
*/
|
|
36
|
+
interface ThemeRuntime {
|
|
37
|
+
getState: () => ThemeState;
|
|
38
|
+
setMode: (mode: ThemeMode) => void;
|
|
39
|
+
subscribe: (listener: (state: ThemeState) => void) => () => void;
|
|
40
|
+
destroy: () => void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Creates a framework-agnostic runtime that manages theme switching.
|
|
44
|
+
*
|
|
45
|
+
* - Sets `data-tt-mode` (and `data-tt-theme` when `defaultTheme` is provided) attributes on the root element.
|
|
46
|
+
* - Updates `style.colorScheme` for native dark/light UI.
|
|
47
|
+
* - Persists `{ mode }` to localStorage.
|
|
48
|
+
* - Listens to `prefers-color-scheme` media query when mode is `'system'`.
|
|
49
|
+
* - Pub/sub pattern for state changes.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const runtime = createThemeRuntime({ defaultMode: 'dark' });
|
|
54
|
+
* runtime.setMode('system');
|
|
55
|
+
* const unsub = runtime.subscribe(console.log);
|
|
56
|
+
* runtime.destroy();
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
declare const createThemeRuntime: (config?: ThemeRuntimeConfig) => ThemeRuntime;
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/ssrScript.d.ts
|
|
62
|
+
/**
|
|
63
|
+
* Configuration for the SSR bootstrap script.
|
|
64
|
+
*/
|
|
65
|
+
interface ThemeScriptConfig {
|
|
66
|
+
/** Theme identifier written to `data-tt-theme`. When omitted, `data-tt-theme` is not written. */
|
|
67
|
+
defaultTheme?: string;
|
|
68
|
+
/** Mode to use when no persisted value is found. @default 'system' */
|
|
69
|
+
defaultMode?: ThemeMode;
|
|
70
|
+
/** localStorage key. Must match the runtime's `storageKey`. @default 'tt-theme' */
|
|
71
|
+
storageKey?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns self-contained JavaScript code that prevents theme flash on SSR/SSG.
|
|
75
|
+
*
|
|
76
|
+
* Insert the returned string into an inline `<script>` tag in the `<head>`,
|
|
77
|
+
* before any stylesheets or the app bundle.
|
|
78
|
+
*
|
|
79
|
+
* @example
|
|
80
|
+
* ```tsx
|
|
81
|
+
* // Next.js app/layout.tsx
|
|
82
|
+
* import { getThemeScriptContent } from '@ttoss/fsl-theme/runtime';
|
|
83
|
+
*
|
|
84
|
+
* export default function RootLayout({ children }) {
|
|
85
|
+
* return (
|
|
86
|
+
* <html lang="en">
|
|
87
|
+
* <head>
|
|
88
|
+
* <script dangerouslySetInnerHTML={{ __html: getThemeScriptContent() }} />
|
|
89
|
+
* </head>
|
|
90
|
+
* <body>{children}</body>
|
|
91
|
+
* </html>
|
|
92
|
+
* );
|
|
93
|
+
* }
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
declare const getThemeScriptContent: (config?: ThemeScriptConfig) => string;
|
|
97
|
+
//#endregion
|
|
98
|
+
export { ResolvedMode as a, ThemeRuntimeConfig as c, DATA_THEME_ATTR as i, ThemeState as l, getThemeScriptContent as n, ThemeMode as o, DATA_MODE_ATTR as r, ThemeRuntime as s, ThemeScriptConfig as t, createThemeRuntime as u };
|