@zen-ui-capri/system 1.1.0 → 1.1.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zen-ui-capri/system",
3
- "version": "1.1.0",
3
+ "version": "1.1.4",
4
4
  "description": "zeN-UI // CAPRI — Universal Multi-Framework Design System (51 Primitives, 40 Vector Icons, 8 Dark/Light Themes & Capri 5-Subroutine AI Co-Pilot)",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -54,27 +54,57 @@
54
54
  "prepublishOnly": "npm run capri -- all && npm run build",
55
55
  "publish:npm": "node bin/capri.mjs publish-npm"
56
56
  },
57
- "dependencies": {
57
+ "peerDependencies": {
58
+ "@react-three/fiber": ">=8.0.0",
59
+ "@react-three/postprocessing": ">=2.0.0",
60
+ "lucide-react": ">=0.300.0",
61
+ "postprocessing": ">=6.0.0",
62
+ "react": ">=18.0.0",
63
+ "react-dom": ">=18.0.0",
64
+ "three": ">=0.150.0"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "@react-three/fiber": {
68
+ "optional": true
69
+ },
70
+ "@react-three/postprocessing": {
71
+ "optional": true
72
+ },
73
+ "lucide-react": {
74
+ "optional": true
75
+ },
76
+ "postprocessing": {
77
+ "optional": true
78
+ },
79
+ "react": {
80
+ "optional": true
81
+ },
82
+ "react-dom": {
83
+ "optional": true
84
+ },
85
+ "three": {
86
+ "optional": true
87
+ }
88
+ },
89
+ "devDependencies": {
58
90
  "@gsap/react": "^2.1.2",
91
+ "@playwright/test": "^1.51.0",
59
92
  "@react-three/fiber": "^9.5.0",
60
93
  "@react-three/postprocessing": "^3.2.2",
61
94
  "@tailwindcss/vite": "^4.1.0",
95
+ "@types/node": "^22.10.0",
96
+ "@types/react": "^19.0.10",
97
+ "@types/react-dom": "^19.0.4",
98
+ "@types/three": "^0.182.0",
99
+ "@vitejs/plugin-react": "^4.3.4",
62
100
  "gsap": "^3.13.0",
63
101
  "lucide-react": "^0.562.0",
64
102
  "postprocessing": "^6.39.6",
65
103
  "react": "^19.2.3",
66
104
  "react-dom": "^19.2.3",
67
105
  "tailwindcss": "^4.1.0",
68
- "three": "^0.182.0"
69
- },
70
- "devDependencies": {
71
- "@playwright/test": "^1.51.0",
72
- "@types/node": "^22.10.0",
73
- "@types/react": "^19.0.10",
74
- "@types/react-dom": "^19.0.4",
75
- "@types/three": "^0.182.0",
76
- "@vitejs/plugin-react": "^4.3.4",
106
+ "three": "^0.182.0",
77
107
  "typescript": "~5.8.2",
78
108
  "vite": "^6.2.0"
79
109
  }
80
- }
110
+ }
@@ -1,114 +1,114 @@
1
- import { Volume2, VolumeX, Disc3 } from 'lucide-react';
2
- import type { ZenAudioSynthController, ZenSynthPresetId } from './useZenAudioSynth';
3
- import { ZenButton } from '../components/ZenButton';
4
- import { ZenSegmentedControl } from '../components/ZenSegmentedControl';
5
-
6
- export interface ZenAudioControllerProps {
7
- readonly synth: ZenAudioSynthController;
8
- readonly mode?: 'pill' | 'deck';
9
- readonly activeLabel?: string;
10
- readonly idleLabel?: string;
11
- readonly className?: string;
12
- }
13
-
14
- export function ZenAudioController({
15
- synth,
16
- mode = 'pill',
17
- activeLabel = 'Lo-Fi Radio: ON',
18
- idleLabel = 'Lo-Fi Background',
19
- className = '',
20
- }: ZenAudioControllerProps) {
21
- if (mode === 'pill') {
22
- return (
23
- <ZenButton
24
- variant="secondary"
25
- size="sm"
26
- mono
27
- pressed={synth.active}
28
- onClick={synth.toggle}
29
- aria-label="Toggle procedural ambient synthesizer"
30
- className={className}
31
- iconLeft={
32
- synth.active ? (
33
- <Volume2 className="w-3.5 h-3.5 animate-pulse" />
34
- ) : (
35
- <VolumeX className="w-3.5 h-3.5 text-[var(--zen-accent-primary)]" />
36
- )
37
- }
38
- >
39
- {synth.active ? activeLabel : idleLabel}
40
- </ZenButton>
41
- );
42
- }
43
-
44
- return (
45
- <div
46
- data-zen-occlude="true"
47
- className={`p-5 rounded-3xl zen-surface-static flex flex-col gap-4 ${className}`.trim()}
48
- >
49
- <div className="flex flex-wrap items-center justify-between gap-3">
50
- <div className="flex items-center gap-3">
51
- <Disc3
52
- className={`w-6 h-6 text-[var(--zen-accent-primary)] ${
53
- synth.active ? 'animate-spin' : ''
54
- }`}
55
- />
56
- <div>
57
- <div className="zen-telemetry-label text-[var(--zen-accent-primary)]">
58
- WEB AUDIO PROCEDURAL SYNTHESIZER
59
- </div>
60
- <div className="zen-font-display font-bold text-base text-[var(--zen-ink-primary)]">
61
- Zero-Dependency Acoustic Engine
62
- </div>
63
- </div>
64
- </div>
65
-
66
- <ZenButton
67
- variant={synth.active ? 'primary' : 'secondary'}
68
- size="sm"
69
- mono
70
- pressed={synth.active}
71
- onClick={synth.toggle}
72
- iconLeft={
73
- synth.active ? (
74
- <Volume2 className="w-3.5 h-3.5" />
75
- ) : (
76
- <VolumeX className="w-3.5 h-3.5" />
77
- )
78
- }
79
- >
80
- {synth.active ? 'SYNTH ACTIVE' : 'START SYNTH'}
81
- </ZenButton>
82
- </div>
83
-
84
- <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pt-2 border-t border-[var(--zen-border-subtle)]">
85
- <ZenSegmentedControl<ZenSynthPresetId>
86
- ariaLabel="Select synthesizer voicing preset"
87
- size="sm"
88
- value={synth.presetId}
89
- onChange={synth.setPresetId}
90
- items={synth.presets.map((p) => ({
91
- value: p.id,
92
- label: p.label.split(' (')[0],
93
- }))}
94
- />
95
-
96
- <div className="flex items-center gap-1.5" aria-label="8-step bar sequencer">
97
- {Array.from({ length: 8 }).map((_, idx) => {
98
- const isBeat = synth.active && synth.currentStep === idx;
99
- return (
100
- <span
101
- key={idx}
102
- className={`h-2.5 w-2.5 rounded-full transition-all ${
103
- isBeat
104
- ? 'bg-[var(--zen-accent-primary)] scale-125'
105
- : 'bg-[var(--zen-border-default)]'
106
- }`}
107
- />
108
- );
109
- })}
110
- </div>
111
- </div>
112
- </div>
113
- );
114
- }
1
+ import { Volume2, VolumeX, Disc3 } from 'lucide-react';
2
+ import type { ZenAudioSynthController, ZenSynthPresetId } from './useZenAudioSynth';
3
+ import { ZenButton } from '../components/ZenButton';
4
+ import { ZenSegmentedControl } from '../components/ZenSegmentedControl';
5
+
6
+ export interface ZenAudioControllerProps {
7
+ readonly synth: ZenAudioSynthController;
8
+ readonly mode?: 'pill' | 'deck';
9
+ readonly activeLabel?: string;
10
+ readonly idleLabel?: string;
11
+ readonly className?: string;
12
+ }
13
+
14
+ export function ZenAudioController({
15
+ synth,
16
+ mode = 'pill',
17
+ activeLabel = 'Lo-Fi Radio: ON',
18
+ idleLabel = 'Lo-Fi Background',
19
+ className = '',
20
+ }: ZenAudioControllerProps) {
21
+ if (mode === 'pill') {
22
+ return (
23
+ <ZenButton
24
+ variant="secondary"
25
+ size="sm"
26
+ mono
27
+ pressed={synth.active}
28
+ onClick={synth.toggle}
29
+ aria-label="Toggle procedural ambient synthesizer"
30
+ className={className}
31
+ iconLeft={
32
+ synth.active ? (
33
+ <Volume2 className="w-3.5 h-3.5 animate-pulse" />
34
+ ) : (
35
+ <VolumeX className="w-3.5 h-3.5 text-[var(--zen-accent-primary)]" />
36
+ )
37
+ }
38
+ >
39
+ {synth.active ? activeLabel : idleLabel}
40
+ </ZenButton>
41
+ );
42
+ }
43
+
44
+ return (
45
+ <div
46
+ data-zen-occlude="true"
47
+ className={`p-5 rounded-3xl zen-surface-static flex flex-col gap-4 ${className}`.trim()}
48
+ >
49
+ <div className="flex flex-wrap items-center justify-between gap-3">
50
+ <div className="flex items-center gap-3">
51
+ <Disc3
52
+ className={`w-6 h-6 text-[var(--zen-accent-primary)] ${
53
+ synth.active ? 'animate-spin' : ''
54
+ }`}
55
+ />
56
+ <div>
57
+ <div className="zen-telemetry-label text-[var(--zen-accent-primary)]">
58
+ WEB AUDIO PROCEDURAL SYNTHESIZER
59
+ </div>
60
+ <div className="zen-font-display font-bold text-base text-[var(--zen-ink-primary)]">
61
+ Zero-Dependency Acoustic Engine
62
+ </div>
63
+ </div>
64
+ </div>
65
+
66
+ <ZenButton
67
+ variant={synth.active ? 'primary' : 'secondary'}
68
+ size="sm"
69
+ mono
70
+ pressed={synth.active}
71
+ onClick={synth.toggle}
72
+ iconLeft={
73
+ synth.active ? (
74
+ <Volume2 className="w-3.5 h-3.5" />
75
+ ) : (
76
+ <VolumeX className="w-3.5 h-3.5" />
77
+ )
78
+ }
79
+ >
80
+ {synth.active ? 'SYNTH ACTIVE' : 'START SYNTH'}
81
+ </ZenButton>
82
+ </div>
83
+
84
+ <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pt-2 border-t border-[var(--zen-border-subtle)]">
85
+ <ZenSegmentedControl<ZenSynthPresetId>
86
+ ariaLabel="Select synthesizer voicing preset"
87
+ size="sm"
88
+ value={synth.presetId}
89
+ onChange={synth.setPresetId}
90
+ items={synth.presets.map((p) => ({
91
+ value: p.id,
92
+ label: p.label.split(' (')[0],
93
+ }))}
94
+ />
95
+
96
+ <div className="flex items-center gap-1.5" aria-label="8-step bar sequencer">
97
+ {Array.from({ length: 8 }).map((_, idx) => {
98
+ const isBeat = synth.active && synth.currentStep === idx;
99
+ return (
100
+ <span
101
+ key={idx}
102
+ className={`h-2.5 w-2.5 rounded-full transition-all ${
103
+ isBeat
104
+ ? 'bg-[var(--zen-accent-primary)] scale-125'
105
+ : 'bg-[var(--zen-border-default)]'
106
+ }`}
107
+ />
108
+ );
109
+ })}
110
+ </div>
111
+ </div>
112
+ </div>
113
+ );
114
+ }
@@ -3,7 +3,7 @@ import {
3
3
  type ZenDensityMode,
4
4
  type ZenRadiusMode,
5
5
  type ZenThemeId,
6
- } from '../theme/ZenThemeProvider';
6
+ } from '../theme/zen-theme-core';
7
7
 
8
8
  export type CapriSubroutineId =
9
9
  | 'orion'
@@ -167,10 +167,21 @@ export function renderZenIconSvgMarkup(options: {
167
167
  readonly strokeWidth?: number;
168
168
  readonly spin?: boolean;
169
169
  }): string {
170
- const px = ZEN_ICON_SIZE_PX[options.size ?? 'md'];
171
- const color = TONE_COLOR_MAP[options.tone ?? 'inherit'];
172
- const sw = options.strokeWidth ?? 2;
173
- const inner = ZEN_ICON_SVG_INNER[options.name] ?? ZEN_ICON_SVG_INNER.sparkles;
170
+ const sizeKey = options.size ?? 'md';
171
+ const px = Object.prototype.hasOwnProperty.call(ZEN_ICON_SIZE_PX, sizeKey)
172
+ ? ZEN_ICON_SIZE_PX[sizeKey]
173
+ : ZEN_ICON_SIZE_PX.md;
174
+ const toneKey = options.tone ?? 'inherit';
175
+ const color = Object.prototype.hasOwnProperty.call(TONE_COLOR_MAP, toneKey)
176
+ ? TONE_COLOR_MAP[toneKey]
177
+ : TONE_COLOR_MAP.inherit;
178
+ const sw =
179
+ typeof options.strokeWidth === 'number' && Number.isFinite(options.strokeWidth)
180
+ ? Math.min(Math.max(options.strokeWidth, 0.5), 4)
181
+ : 2;
182
+ const inner = Object.prototype.hasOwnProperty.call(ZEN_ICON_SVG_INNER, options.name)
183
+ ? ZEN_ICON_SVG_INNER[options.name]
184
+ : ZEN_ICON_SVG_INNER.sparkles;
174
185
  const spinStyle = options.spin ? 'animation: spin 1s linear infinite;' : '';
175
186
  return `<svg xmlns="http://www.w3.org/2000/svg" width="${px}" height="${px}" viewBox="0 0 24 24" fill="none" stroke="${color}" stroke-width="${sw}" stroke-linecap="round" stroke-linejoin="round" style="display:inline-block;vertical-align:middle;flex-shrink:0;${spinStyle}">${inner}</svg>`;
176
187
  }
@@ -194,9 +205,15 @@ export function ZenIcon({
194
205
  className = '',
195
206
  ...rest
196
207
  }: ZenIconProps) {
197
- const px = ZEN_ICON_SIZE_PX[size];
198
- const strokeColor = TONE_COLOR_MAP[tone];
199
- const innerMarkup = ZEN_ICON_SVG_INNER[name] ?? ZEN_ICON_SVG_INNER.sparkles;
208
+ const px = Object.prototype.hasOwnProperty.call(ZEN_ICON_SIZE_PX, size)
209
+ ? ZEN_ICON_SIZE_PX[size]
210
+ : ZEN_ICON_SIZE_PX.md;
211
+ const strokeColor = Object.prototype.hasOwnProperty.call(TONE_COLOR_MAP, tone)
212
+ ? TONE_COLOR_MAP[tone]
213
+ : TONE_COLOR_MAP.inherit;
214
+ const innerMarkup = Object.prototype.hasOwnProperty.call(ZEN_ICON_SVG_INNER, name)
215
+ ? ZEN_ICON_SVG_INNER[name]
216
+ : ZEN_ICON_SVG_INNER.sparkles;
200
217
 
201
218
  return (
202
219
  <svg
package/src/lib/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export const ZEN_UI_VERSION = '1.1.0';
1
+ export const ZEN_UI_VERSION = '1.1.4';
2
2
  export const ZEN_PACKAGE_NAME = '@zen-ui/system';
3
3
 
4
4
  export {
@@ -6,121 +6,27 @@ import {
6
6
  useState,
7
7
  type ReactNode,
8
8
  } from 'react';
9
-
10
- export type ZenThemeId =
11
- | 'obsidian-gold'
12
- | 'blackwall-crimson'
13
- | 'neon-emerald'
14
- | 'midnight-cobalt'
15
- | 'atelier-paper'
16
- | 'nordic-snow'
17
- | 'kyoto-matcha'
18
- | 'cyber-daylight';
19
-
20
- export type ZenThemeMode = ZenThemeId | 'obsidian' | 'paper';
21
- export type ZenDensityMode = 'comfortable' | 'compact';
22
- export type ZenRadiusMode = 'sharp' | 'architectural' | 'soft';
23
-
24
- export interface ZenThemeMetadata {
25
- readonly id: ZenThemeId;
26
- readonly name: string;
27
- readonly polarity: 'dark' | 'light';
28
- readonly tagline: string;
29
- readonly canvasHex: string;
30
- readonly accentHex: string;
31
- readonly secondaryHex: string;
32
- readonly inkHex: string;
33
- }
34
-
35
- export const ZEN_THEMES: readonly ZenThemeMetadata[] = [
36
- {
37
- id: 'obsidian-gold',
38
- name: 'Obsidian Gold',
39
- polarity: 'dark',
40
- tagline: 'Warm alabaster & solar gold on deep mineral void',
41
- canvasHex: '#070709',
42
- accentHex: '#F5B841',
43
- secondaryHex: '#FB923C',
44
- inkHex: '#F6F2E9',
45
- },
46
- {
47
- id: 'blackwall-crimson',
48
- name: 'Capri // Capricorn',
49
- polarity: 'dark',
50
- tagline: 'Edgerunners S2 mixed-construct hot pink & acid neon green on void',
51
- canvasHex: '#050508',
52
- accentHex: '#FF2E93',
53
- secondaryHex: '#00FF66',
54
- inkHex: '#F4F7FA',
55
- },
56
- {
57
- id: 'neon-emerald',
58
- name: 'Bio-Emerald Terminal',
59
- polarity: 'dark',
60
- tagline: 'High-frequency financial & telemetry phosphor emerald',
61
- canvasHex: '#040807',
62
- accentHex: '#10B981',
63
- secondaryHex: '#2DD4BF',
64
- inkHex: '#ECFDF5',
65
- },
66
- {
67
- id: 'midnight-cobalt',
68
- name: 'Midnight Cobalt',
69
- polarity: 'dark',
70
- tagline: 'Cloud infrastructure & distributed systems deep slate',
71
- canvasHex: '#060911',
72
- accentHex: '#38BDF8',
73
- secondaryHex: '#818CF8',
74
- inkHex: '#F1F5F9',
75
- },
76
- {
77
- id: 'atelier-paper',
78
- name: 'Atelier Paper',
79
- polarity: 'light',
80
- tagline: 'Swiss archival cotton stock with burnished bronze ink',
81
- canvasHex: '#F6F2E9',
82
- accentHex: '#B45309',
83
- secondaryHex: '#C2410C',
84
- inkHex: '#121216',
85
- },
86
- {
87
- id: 'nordic-snow',
88
- name: 'Nordic Snow',
89
- polarity: 'light',
90
- tagline: 'Crisp daylight SaaS alabaster with royal cobalt precision',
91
- canvasHex: '#F8FAFC',
92
- accentHex: '#2563EB',
93
- secondaryHex: '#0891B2',
94
- inkHex: '#0F172A',
95
- },
96
- {
97
- id: 'kyoto-matcha',
98
- name: 'Kyoto Matcha',
99
- polarity: 'light',
100
- tagline: 'Raw botanical stone & forest ink for calm workspaces',
101
- canvasHex: '#F3F5F0',
102
- accentHex: '#15803D',
103
- secondaryHex: '#0F766E',
104
- inkHex: '#142018',
105
- },
106
- {
107
- id: 'cyber-daylight',
108
- name: 'Arasaka Daylight',
109
- polarity: 'light',
110
- tagline: 'High-contrast titanium white with razor crimson signal',
111
- canvasHex: '#F2F2F5',
112
- accentHex: '#E11D48',
113
- secondaryHex: '#0284C7',
114
- inkHex: '#09090D',
115
- },
116
- ];
117
-
118
- export interface ZenThemeTokenOverrides {
119
- readonly accentPrimary?: string;
120
- readonly bgCanvas?: string;
121
- readonly surface1?: string;
122
- readonly inkPrimary?: string;
123
- }
9
+ import {
10
+ ZEN_THEMES,
11
+ syncZenScrollbarStyles,
12
+ type ZenDensityMode,
13
+ type ZenRadiusMode,
14
+ type ZenThemeId,
15
+ type ZenThemeMetadata,
16
+ type ZenThemeMode,
17
+ type ZenThemeTokenOverrides,
18
+ } from './zen-theme-core';
19
+
20
+ export {
21
+ ZEN_THEMES,
22
+ syncZenScrollbarStyles,
23
+ type ZenDensityMode,
24
+ type ZenRadiusMode,
25
+ type ZenThemeId,
26
+ type ZenThemeMetadata,
27
+ type ZenThemeMode,
28
+ type ZenThemeTokenOverrides,
29
+ };
124
30
 
125
31
  export interface ZenThemeContextValue {
126
32
  readonly theme: ZenThemeId;
@@ -136,7 +42,16 @@ export interface ZenThemeContextValue {
136
42
  readonly setRadius: (next: ZenRadiusMode) => void;
137
43
  }
138
44
 
139
- const ZenThemeContext = createContext<ZenThemeContextValue | null>(null);
45
+ let zenThemeContextInstance: ReturnType<
46
+ typeof createContext<ZenThemeContextValue | null>
47
+ > | null = null;
48
+
49
+ function getZenThemeContext() {
50
+ if (!zenThemeContextInstance) {
51
+ zenThemeContextInstance = createContext<ZenThemeContextValue | null>(null);
52
+ }
53
+ return zenThemeContextInstance;
54
+ }
140
55
 
141
56
  export interface ZenThemeProviderProps {
142
57
  readonly defaultTheme?: ZenThemeId;
@@ -147,78 +62,6 @@ export interface ZenThemeProviderProps {
147
62
  readonly children: ReactNode;
148
63
  }
149
64
 
150
- export function syncZenScrollbarStyles(
151
- themeMeta: ZenThemeMetadata,
152
- overrides?: ZenThemeTokenOverrides
153
- ): void {
154
- if (typeof document === 'undefined') return;
155
- const accent = overrides?.accentPrimary ?? themeMeta.accentHex;
156
- const track = overrides?.bgCanvas ?? themeMeta.canvasHex;
157
- const borderSubtle =
158
- themeMeta.polarity === 'dark'
159
- ? 'rgba(255, 255, 255, 0.12)'
160
- : 'rgba(15, 23, 42, 0.14)';
161
-
162
- const styleId = 'zen-theme-scrollbar';
163
- let styleEl = document.getElementById(styleId) as HTMLStyleElement | null;
164
- if (!styleEl) {
165
- styleEl = document.createElement('style');
166
- styleEl.id = styleId;
167
- document.head.appendChild(styleEl);
168
- }
169
-
170
- styleEl.textContent = `
171
- @supports (-moz-appearance: none) {
172
- html, body, * {
173
- scrollbar-width: thin;
174
- scrollbar-color: ${accent} ${track};
175
- }
176
- }
177
- ::-webkit-scrollbar,
178
- html::-webkit-scrollbar,
179
- body::-webkit-scrollbar,
180
- *::-webkit-scrollbar {
181
- width: 10px;
182
- height: 10px;
183
- }
184
- ::-webkit-scrollbar-track,
185
- html::-webkit-scrollbar-track,
186
- body::-webkit-scrollbar-track,
187
- *::-webkit-scrollbar-track {
188
- background-color: ${track};
189
- border-left: 1px solid ${borderSubtle};
190
- }
191
- ::-webkit-scrollbar-thumb,
192
- html::-webkit-scrollbar-thumb,
193
- body::-webkit-scrollbar-thumb,
194
- *::-webkit-scrollbar-thumb {
195
- background-color: color-mix(in srgb, ${accent} 68%, ${track});
196
- border: 2px solid ${track};
197
- border-radius: 9999px;
198
- }
199
- ::-webkit-scrollbar-thumb:hover,
200
- html::-webkit-scrollbar-thumb:hover,
201
- body::-webkit-scrollbar-thumb:hover,
202
- *::-webkit-scrollbar-thumb:hover {
203
- background-color: ${accent};
204
- border: 2px solid ${track};
205
- }
206
- ::-webkit-scrollbar-thumb:active,
207
- html::-webkit-scrollbar-thumb:active,
208
- body::-webkit-scrollbar-thumb:active,
209
- *::-webkit-scrollbar-thumb:active {
210
- background-color: ${themeMeta.secondaryHex};
211
- border: 2px solid ${track};
212
- }
213
- ::-webkit-scrollbar-corner,
214
- html::-webkit-scrollbar-corner,
215
- body::-webkit-scrollbar-corner,
216
- *::-webkit-scrollbar-corner {
217
- background-color: ${track};
218
- }
219
- `.trim();
220
- }
221
-
222
65
  export function ZenThemeProvider({
223
66
  defaultTheme = 'blackwall-crimson',
224
67
  defaultDensity = 'comfortable',
@@ -312,15 +155,17 @@ export function ZenThemeProvider({
312
155
  [theme, themeMeta, density, radius, reducedMotion]
313
156
  );
314
157
 
158
+ const ThemeCtx = getZenThemeContext();
159
+
315
160
  return (
316
- <ZenThemeContext.Provider value={value}>
161
+ <ThemeCtx.Provider value={value}>
317
162
  {children}
318
- </ZenThemeContext.Provider>
163
+ </ThemeCtx.Provider>
319
164
  );
320
165
  }
321
166
 
322
167
  export function useZenTheme(): ZenThemeContextValue {
323
- const ctx = useContext(ZenThemeContext);
168
+ const ctx = useContext(getZenThemeContext());
324
169
  if (!ctx) {
325
170
  throw new Error('useZenTheme must be used within a ZenThemeProvider');
326
171
  }