@snowcone-app/ui 0.1.43 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +18 -4
  3. package/dist/index.cjs +5 -2
  4. package/dist/index.cjs.map +1 -1
  5. package/dist/index.js +5 -2
  6. package/dist/index.js.map +1 -1
  7. package/package.json +9 -5
  8. package/src/components/CanvasIsolationBoundary.tsx +202 -0
  9. package/src/components/LoadingOverlayPrism.tsx +251 -0
  10. package/src/composed/AddToCart.tsx +229 -0
  11. package/src/composed/ArtAlignment.tsx +703 -0
  12. package/src/composed/ArtSelector.tsx +290 -0
  13. package/src/composed/ArtworkCustomizer.tsx +212 -0
  14. package/src/composed/CanvasEditor.tsx +79 -0
  15. package/src/composed/ColorPicker.tsx +111 -0
  16. package/src/composed/CurrentSelectionDisplay.tsx +86 -0
  17. package/src/composed/HeroProductImage.tsx +1079 -0
  18. package/src/composed/Lightbox.index.ts +2 -0
  19. package/src/composed/Lightbox.tsx +230 -0
  20. package/src/composed/PlacementClipShapeSelector.tsx +88 -0
  21. package/src/composed/PlacementTabs.tsx +179 -0
  22. package/src/composed/ProductCard.tsx +298 -0
  23. package/src/composed/ProductGallery.tsx +54 -0
  24. package/src/composed/ProductImage.tsx +129 -0
  25. package/src/composed/ProductList.tsx +147 -0
  26. package/src/composed/ProductOptions.tsx +305 -0
  27. package/src/composed/RealtimeMockup.tsx +121 -0
  28. package/src/composed/TileCount.tsx +348 -0
  29. package/src/composed/carousels/HeroCarousel.tsx +240 -0
  30. package/src/composed/carousels/MobileProductCarousel.tsx +1002 -0
  31. package/src/composed/carousels/index.ts +11 -0
  32. package/src/composed/carousels/types.ts +58 -0
  33. package/src/composed/grids/MasonryGrid.tsx +238 -0
  34. package/src/composed/grids/index.ts +9 -0
  35. package/src/composed/search/CurrentRefinements.tsx +80 -0
  36. package/src/composed/search/Filters.tsx +49 -0
  37. package/src/composed/search/FiltersButton.tsx +57 -0
  38. package/src/composed/search/FiltersDrawer.tsx +375 -0
  39. package/src/composed/search/ProductGrid.tsx +118 -0
  40. package/src/composed/search/ProductHit.tsx +56 -0
  41. package/src/composed/search/SearchBox.tsx +109 -0
  42. package/src/composed/search/SearchProvider.tsx +136 -0
  43. package/src/composed/search/facetConfig.ts +16 -0
  44. package/src/composed/search/index.ts +22 -0
  45. package/src/composed/search/meilisearchAdapter.ts +20 -0
  46. package/src/composed/search/types.ts +22 -0
  47. package/src/composed/zoom/EnhancedImageViewer.tsx +505 -0
  48. package/src/composed/zoom/ResponsiveZoom.tsx +134 -0
  49. package/src/composed/zoom/ZoomOverlay.tsx +194 -0
  50. package/src/composed/zoom/index.ts +12 -0
  51. package/src/composed/zoom/types.ts +12 -0
  52. package/src/design-system/ColorPalette.tsx +126 -0
  53. package/src/design-system/ColorSwatch.tsx +49 -0
  54. package/src/design-system/DesignSystemPage.tsx +130 -0
  55. package/src/design-system/ThemeSwitcher.tsx +181 -0
  56. package/src/design-system/TypographyScale.tsx +106 -0
  57. package/src/design-system/index.ts +5 -0
  58. package/src/ecommerce/stories/HeroProductImage.stories.tsx +66 -0
  59. package/src/ecommerce/stories/PDPHeroGallery.stories.tsx +105 -0
  60. package/src/ecommerce/stories/PDPInfoPanel.stories.tsx +472 -0
  61. package/src/ecommerce/stories/PDPLayout.stories.tsx +365 -0
  62. package/src/hooks/useBrand.ts +41 -0
  63. package/src/hooks/useCanvasContext.ts +127 -0
  64. package/src/hooks/useDeviceDetection.ts +64 -0
  65. package/src/hooks/useFocusTrap.ts +70 -0
  66. package/src/hooks/useImagePreloader.ts +268 -0
  67. package/src/hooks/useImageTransition.ts +608 -0
  68. package/src/hooks/usePlacementsProcessor.ts +74 -0
  69. package/src/hooks/useProductGallery.ts +193 -0
  70. package/src/hooks/useProductPage.ts +467 -0
  71. package/src/hooks/useRenderGuard.ts +96 -0
  72. package/src/hooks/useScrollDirection.ts +196 -0
  73. package/src/hooks/viewport/index.ts +25 -0
  74. package/src/hooks/viewport/useContainerWidth.ts +59 -0
  75. package/src/hooks/viewport/useMediaQuery.ts +52 -0
  76. package/src/hooks/viewport/useResponsiveImageCap.ts +149 -0
  77. package/src/hooks/viewport/useViewportDimensions.ts +135 -0
  78. package/src/hooks/viewport/useWideMonitorMode.ts +150 -0
  79. package/src/hooks/visibility/index.ts +15 -0
  80. package/src/hooks/visibility/observerPool.ts +150 -0
  81. package/src/index.ts +240 -0
  82. package/src/layouts/hero-zoom/HeroShrinkLayout.tsx +209 -0
  83. package/src/layouts/hero-zoom/HeroZoomLayout.tsx +351 -0
  84. package/src/layouts/hero-zoom/index.ts +30 -0
  85. package/src/layouts/hero-zoom/stories/HeroZoomLayout.stories.tsx +350 -0
  86. package/src/layouts/hero-zoom/types.ts +113 -0
  87. package/src/layouts/hero-zoom/useHeroZoomScales.ts +156 -0
  88. package/src/layouts/index.ts +9 -0
  89. package/src/layouts/pdp/EdgeBlurBox.tsx +210 -0
  90. package/src/layouts/pdp/ImageBlurExtension.tsx +215 -0
  91. package/src/layouts/pdp/ImageEdgeBlur.tsx +215 -0
  92. package/src/layouts/pdp/PDPLayout.tsx +246 -0
  93. package/src/layouts/pdp/SimpleImageBlur.tsx +140 -0
  94. package/src/layouts/pdp/index.ts +40 -0
  95. package/src/lib/env.ts +15 -0
  96. package/src/lib/locale.ts +167 -0
  97. package/src/lib/router.tsx +46 -0
  98. package/src/lib/utils.ts +6 -0
  99. package/src/lightbox/README.md +77 -0
  100. package/src/next/index.tsx +26 -0
  101. package/src/patterns/MockupPriorityProvider.tsx +1014 -0
  102. package/src/patterns/Product.tsx +850 -0
  103. package/src/patterns/ProductPageProvider.tsx +224 -0
  104. package/src/patterns/RealtimeProvider.tsx +1162 -0
  105. package/src/patterns/ShopProvider.tsx +603 -0
  106. package/src/personalization/PersonalizationBridge.tsx +235 -0
  107. package/src/personalization/PersonalizationContext.ts +29 -0
  108. package/src/personalization/PersonalizationInputs.tsx +110 -0
  109. package/src/personalization/PersonalizationProvider.tsx +407 -0
  110. package/src/personalization/canvas-stub.d.ts +22 -0
  111. package/src/personalization/index.ts +43 -0
  112. package/src/personalization/types.ts +48 -0
  113. package/src/personalization/usePersonalization.ts +32 -0
  114. package/src/personalization/usePersonalizationShimmer.ts +159 -0
  115. package/src/personalization/utils.ts +59 -0
  116. package/src/primitives/BrandLogo.tsx +65 -0
  117. package/src/primitives/BrandName.tsx +51 -0
  118. package/src/primitives/Button.tsx +123 -0
  119. package/src/primitives/ColorSwatch.tsx +221 -0
  120. package/src/primitives/DragHintAnimation.tsx +190 -0
  121. package/src/primitives/EdgeSwipeGuards.tsx +60 -0
  122. package/src/primitives/FloatingActionGroup.tsx +176 -0
  123. package/src/primitives/ProductPrice.tsx +171 -0
  124. package/src/primitives/ProgressiveBlur.tsx +295 -0
  125. package/src/primitives/ThemeToggle.tsx +125 -0
  126. package/src/primitives/__tests__/story-coverage.test.ts +98 -0
  127. package/src/primitives/accordion.tsx +280 -0
  128. package/src/primitives/badge.tsx +137 -0
  129. package/src/primitives/card.tsx +61 -0
  130. package/src/primitives/checkbox.tsx +56 -0
  131. package/src/primitives/collapsible.tsx +51 -0
  132. package/src/primitives/drawer.tsx +828 -0
  133. package/src/primitives/dropdown-menu.tsx +197 -0
  134. package/src/primitives/fieldset.tsx +73 -0
  135. package/src/primitives/index.ts +138 -0
  136. package/src/primitives/input.tsx +91 -0
  137. package/src/primitives/kbd.tsx +130 -0
  138. package/src/primitives/label.tsx +20 -0
  139. package/src/primitives/link.tsx +182 -0
  140. package/src/primitives/popover.tsx +80 -0
  141. package/src/primitives/radio-group.tsx +79 -0
  142. package/src/primitives/scroll-fade.tsx +159 -0
  143. package/src/primitives/select.tsx +170 -0
  144. package/src/primitives/separator.tsx +25 -0
  145. package/src/primitives/slider.tsx +221 -0
  146. package/src/primitives/spinner.tsx +72 -0
  147. package/src/primitives/stories/Accordion.stories.tsx +121 -0
  148. package/src/primitives/stories/Badge.stories.tsx +221 -0
  149. package/src/primitives/stories/Button.stories.tsx +185 -0
  150. package/src/primitives/stories/Card.stories.tsx +171 -0
  151. package/src/primitives/stories/Checkbox.stories.tsx +214 -0
  152. package/src/primitives/stories/Collapsible.stories.tsx +230 -0
  153. package/src/primitives/stories/Drawer.stories.tsx +378 -0
  154. package/src/primitives/stories/DropdownMenu.stories.tsx +182 -0
  155. package/src/primitives/stories/Fieldset.stories.tsx +212 -0
  156. package/src/primitives/stories/Input.stories.tsx +172 -0
  157. package/src/primitives/stories/Kbd.stories.tsx +183 -0
  158. package/src/primitives/stories/Label.stories.tsx +98 -0
  159. package/src/primitives/stories/Link.stories.tsx +260 -0
  160. package/src/primitives/stories/Popover.stories.tsx +178 -0
  161. package/src/primitives/stories/RadioGroup.stories.tsx +205 -0
  162. package/src/primitives/stories/Select.stories.tsx +222 -0
  163. package/src/primitives/stories/Separator.stories.tsx +134 -0
  164. package/src/primitives/stories/Slider.stories.tsx +203 -0
  165. package/src/primitives/stories/Spinner.stories.tsx +142 -0
  166. package/src/primitives/stories/Surface.stories.tsx +257 -0
  167. package/src/primitives/stories/Switch.stories.tsx +131 -0
  168. package/src/primitives/stories/Tabs.stories.tsx +275 -0
  169. package/src/primitives/stories/TextField.stories.tsx +139 -0
  170. package/src/primitives/stories/Textarea.stories.tsx +148 -0
  171. package/src/primitives/stories/Tooltip.stories.tsx +119 -0
  172. package/src/primitives/surface.tsx +86 -0
  173. package/src/primitives/switch.tsx +35 -0
  174. package/src/primitives/tabs.tsx +206 -0
  175. package/src/primitives/text-field.tsx +84 -0
  176. package/src/primitives/textarea.tsx +50 -0
  177. package/src/primitives/tooltip.tsx +58 -0
  178. package/src/services/CanvasExportService.ts +518 -0
  179. package/src/styles/base.css +380 -0
  180. package/src/styles/defaults.css +280 -0
  181. package/src/styles/globals.css +1242 -0
  182. package/src/styles/index.css +17 -0
  183. package/src/styles/ne-themes.css +4740 -0
  184. package/src/styles/tailwind.css +11 -0
  185. package/src/styles/tokens.css +117 -0
  186. package/src/styles/utilities.css +188 -0
  187. package/src/themes/apply-theme.ts +449 -0
  188. package/src/themes/getThemeStyles.ts +454 -0
  189. package/src/themes/index.ts +48 -0
  190. package/src/themes/oklch-theme.ts +283 -0
  191. package/src/themes/presets.ts +989 -0
  192. package/src/themes/types.ts +386 -0
  193. package/src/themes/useTheme.tsx +450 -0
  194. package/src/utils/dev-warnings.ts +161 -0
  195. package/src/utils/devWarnings.ts +153 -0
  196. package/dist/styles.css +0 -1
@@ -0,0 +1,283 @@
1
+ /**
2
+ * ============================================================================
3
+ * OKLCH THEME SYSTEM - Quick Reference
4
+ * ============================================================================
5
+ *
6
+ * This module generates all theme colors from a hue + chroma definition.
7
+ * Colors are computed algorithmically for consistent contrast and readability.
8
+ *
9
+ * ┌─────────────────────────────────────────────────────────────────────────┐
10
+ * │ SEMANTIC VARIABLES │
11
+ * ├─────────────────────────────────────────────────────────────────────────┤
12
+ * │ │
13
+ * │ SURFACES (neutral backgrounds) │
14
+ * │ ────────────────────────────── │
15
+ * │ --color-background Page background (darkest in dark mode) │
16
+ * │ --color-surface Cards, panels, modals │
17
+ * │ --color-surface-raised Popovers, dropdowns (on top of surface) │
18
+ * │ --color-muted Muted areas, switch/slider tracks │
19
+ * │ │
20
+ * │ FIELDS (form inputs - context-aware) │
21
+ * │ ──────────────────────────────────── │
22
+ * │ --color-field Field on page/transparent bg │
23
+ * │ --color-field-on-surface Field inside Surface/Card (auto-detected) │
24
+ * │ │
25
+ * │ TEXT (always neutral - no color tint) │
26
+ * │ ───────────────────────────────────── │
27
+ * │ --color-foreground Primary text │
28
+ * │ --color-muted-foreground Secondary/muted text │
29
+ * │ │
30
+ * │ ACCENTS (uses theme hue + chroma) │
31
+ * │ ──────────────────────────────── │
32
+ * │ --color-primary Brand accent color │
33
+ * │ --color-secondary Secondary accent │
34
+ * │ --color-border Dividers and borders │
35
+ * │ │
36
+ * └─────────────────────────────────────────────────────────────────────────┘
37
+ *
38
+ * USAGE IN COMPONENTS:
39
+ * - Use `bg-surface` for Card/Surface backgrounds
40
+ * - Use `bg-muted` for hover states, tracks, muted areas
41
+ * - Use `bg-[var(--color-field-on-surface)]` for fields inside surfaces
42
+ * - Use `useSurface()` hook to detect surface context automatically
43
+ *
44
+ * DO NOT USE: content1, content2, content3, content4 (legacy, removed)
45
+ */
46
+
47
+ import type { OklchThemeConfig } from './types';
48
+
49
+ // =============================================================================
50
+ // SEMANTIC LIGHTNESS VALUES
51
+ // =============================================================================
52
+
53
+ /**
54
+ * Lightness values with semantic names.
55
+ * Each value describes WHAT it's for, not an abstract "level".
56
+ */
57
+ export const LIGHTNESS = {
58
+ dark: {
59
+ // Page & Surfaces
60
+ background: 0.12, // Page background (darkest)
61
+ surface: 0.16, // Cards, panels (~#18181b)
62
+ surfaceRaised: 0.25, // Popovers, dropdowns, muted backgrounds
63
+ surfaceHover: 0.25, // Hover state for surfaces
64
+
65
+ // Form Fields
66
+ field: 0.16, // Field bg on transparent/page (same as surface)
67
+ fieldOnSurface: 0.20, // Field bg inside Surface/Card (~#252528)
68
+
69
+ // Text
70
+ foreground: 0.96, // Primary text (near white)
71
+ foregroundMuted: 0.65, // Secondary text
72
+
73
+ // Accents
74
+ primary: 0.70, // Primary brand color
75
+ primaryHover: 0.75, // Primary hover state
76
+ secondary: 0.68, // Secondary accent
77
+
78
+ // Borders
79
+ border: 0.25, // Dividers, borders
80
+ },
81
+ light: {
82
+ // Page & Surfaces
83
+ background: 0.97, // Page background (near white)
84
+ surface: 1.0, // Cards, panels (pure white)
85
+ surfaceRaised: 0.94, // Popovers, dropdowns, muted backgrounds
86
+ surfaceHover: 0.94, // Hover state
87
+
88
+ // Form Fields
89
+ field: 1.0, // Field bg on transparent/page (white)
90
+ fieldOnSurface: 0.96, // Field bg inside Surface/Card (subtle gray)
91
+
92
+ // Text
93
+ foreground: 0.15, // Primary text (near black)
94
+ foregroundMuted: 0.45, // Secondary text
95
+
96
+ // Accents
97
+ primary: 0.55, // Primary brand color
98
+ primaryHover: 0.50, // Primary hover (darker)
99
+ secondary: 0.45, // Secondary accent
100
+
101
+ // Borders
102
+ border: 0.85, // Dividers, borders
103
+ },
104
+ };
105
+
106
+ /**
107
+ * Chroma values - how much color tint to add
108
+ *
109
+ * DESIGN PRINCIPLE: Text is always neutral, theme color is for accents only.
110
+ * - Text (foreground, heading, muted) = 0 chroma (pure grayscale)
111
+ * - Backgrounds/surfaces = 0 chroma (neutral)
112
+ * - Accents (primary, icons, links) = full theme chroma
113
+ */
114
+ export const CHROMA = {
115
+ neutral: 0, // For all non-accent colors (text, surfaces, borders)
116
+ };
117
+
118
+ // =============================================================================
119
+ // COLOR GENERATION FUNCTIONS
120
+ // =============================================================================
121
+
122
+ /**
123
+ * Generate an OKLCH color string
124
+ */
125
+ export function oklch(lightness: number, chroma: number, hue: number): string {
126
+ const l = Math.max(0, Math.min(1, lightness));
127
+ const c = Math.max(0, Math.min(0.4, chroma));
128
+ const h = ((hue % 360) + 360) % 360;
129
+ return `oklch(${(l * 100).toFixed(1)}% ${c.toFixed(4)} ${h.toFixed(1)})`;
130
+ }
131
+
132
+ /**
133
+ * Generate all CSS color variables for a theme
134
+ *
135
+ * Variable naming convention:
136
+ * - `--color-{name}` is the canonical form
137
+ * - `--{name}` is an alias for convenience
138
+ */
139
+ export function generateThemeVariables(config: OklchThemeConfig): Record<string, string> {
140
+ const { hue, isDark, chroma: primaryChroma = 0.18, primaryLightness } = config;
141
+ const L = isDark ? LIGHTNESS.dark : LIGHTNESS.light;
142
+
143
+ // Allow per-theme override of primary lightness
144
+ const effectivePrimaryL = primaryLightness ?? L.primary;
145
+
146
+ // Calculate primary foreground based on primary lightness
147
+ const primaryFgLightness = effectivePrimaryL > 0.6 ? 0.12 : 0.96;
148
+
149
+ const vars: Record<string, string> = {
150
+ // =========================================================================
151
+ // BACKGROUND - Page level
152
+ // =========================================================================
153
+ '--color-background': oklch(L.background, CHROMA.neutral, hue),
154
+ '--background': oklch(L.background, CHROMA.neutral, hue),
155
+
156
+ // =========================================================================
157
+ // SURFACE - Cards, panels, modals
158
+ // =========================================================================
159
+ '--color-surface': oklch(L.surface, CHROMA.neutral, hue),
160
+ '--color-card': oklch(L.surface, CHROMA.neutral, hue),
161
+ '--surface': oklch(L.surface, CHROMA.neutral, hue),
162
+
163
+ // Raised surface (popovers, dropdowns on top of surface)
164
+ '--color-surface-raised': oklch(L.surfaceRaised, CHROMA.neutral, hue),
165
+ '--color-popover': oklch(L.surfaceRaised, CHROMA.neutral, hue),
166
+
167
+ // =========================================================================
168
+ // FIELDS - Form inputs
169
+ // =========================================================================
170
+ // Field on transparent/page background
171
+ '--color-field': oklch(L.field, CHROMA.neutral, hue),
172
+
173
+ // Field inside Surface/Card (needs contrast with surface)
174
+ '--color-field-on-surface': oklch(L.fieldOnSurface, CHROMA.neutral, hue),
175
+
176
+ // =========================================================================
177
+ // FOREGROUND - Text colors
178
+ // =========================================================================
179
+ '--color-foreground': oklch(L.foreground, CHROMA.neutral, hue),
180
+ '--foreground': oklch(L.foreground, CHROMA.neutral, hue),
181
+ '--color-heading': oklch(L.foreground, CHROMA.neutral, hue),
182
+ '--color-card-foreground': oklch(L.foreground, CHROMA.neutral, hue),
183
+ '--color-popover-foreground': oklch(L.foreground, CHROMA.neutral, hue),
184
+
185
+ // Muted text
186
+ '--color-muted-foreground': oklch(L.foregroundMuted, CHROMA.neutral, hue),
187
+ '--muted-foreground': oklch(L.foregroundMuted, CHROMA.neutral, hue),
188
+
189
+ // =========================================================================
190
+ // PRIMARY - Brand accent
191
+ // =========================================================================
192
+ '--color-primary': oklch(effectivePrimaryL, primaryChroma, hue),
193
+ '--primary': oklch(effectivePrimaryL, primaryChroma, hue),
194
+ '--color-accent': oklch(effectivePrimaryL, primaryChroma, hue),
195
+ '--accent': oklch(effectivePrimaryL, primaryChroma, hue),
196
+ '--color-ring': oklch(effectivePrimaryL, primaryChroma, hue),
197
+ '--focus': oklch(effectivePrimaryL, primaryChroma, hue),
198
+
199
+ // Primary foreground (text on primary bg)
200
+ '--color-primary-foreground': oklch(primaryFgLightness, CHROMA.neutral, hue),
201
+ '--primary-foreground': oklch(primaryFgLightness, CHROMA.neutral, hue),
202
+ '--color-accent-foreground': oklch(primaryFgLightness, CHROMA.neutral, hue),
203
+
204
+ // =========================================================================
205
+ // SECONDARY - Secondary accent
206
+ // =========================================================================
207
+ '--color-secondary': oklch(L.secondary, primaryChroma * 0.8, hue),
208
+ '--secondary': oklch(L.secondary, primaryChroma * 0.8, hue),
209
+ '--color-secondary-foreground': oklch(L.foreground, CHROMA.neutral, hue),
210
+ '--secondary-foreground': oklch(L.foreground, CHROMA.neutral, hue),
211
+
212
+ // =========================================================================
213
+ // BORDER - Dividers, borders
214
+ // =========================================================================
215
+ '--color-border': oklch(L.border, CHROMA.neutral, hue),
216
+ '--border': oklch(L.border, CHROMA.neutral, hue),
217
+ '--color-divider': oklch(L.border, CHROMA.neutral, hue),
218
+ '--divider': oklch(L.border, CHROMA.neutral, hue),
219
+
220
+ // =========================================================================
221
+ // MUTED - Muted backgrounds
222
+ // =========================================================================
223
+ '--color-muted': oklch(L.surfaceRaised, CHROMA.neutral, hue),
224
+ '--muted': oklch(L.surfaceRaised, CHROMA.neutral, hue),
225
+
226
+ // =========================================================================
227
+ // ICON - Icon colors (uses primary with reduced chroma)
228
+ // =========================================================================
229
+ '--color-icon': oklch(isDark ? 0.75 : 0.55, primaryChroma * 0.7, hue),
230
+
231
+ // =========================================================================
232
+ // ACCENT TEXT - For overlays and labels
233
+ // =========================================================================
234
+ '--color-accent-text-overlay': oklch(isDark ? 0.75 : 0.45, primaryChroma * 0.5, hue),
235
+ '--color-accent-text-page': oklch(isDark ? 0.60 : 0.50, primaryChroma * 0.5, hue),
236
+ };
237
+
238
+ return vars;
239
+ }
240
+
241
+ /**
242
+ * Apply OKLCH theme to DOM
243
+ */
244
+ export function applyOklchTheme(
245
+ config: OklchThemeConfig,
246
+ root: HTMLElement = document.documentElement
247
+ ): void {
248
+ const vars = generateThemeVariables(config);
249
+
250
+ // Apply all CSS variables
251
+ for (const [key, value] of Object.entries(vars)) {
252
+ root.style.setProperty(key, value);
253
+ }
254
+
255
+ // Apply dark mode class and color scheme
256
+ if (config.isDark) {
257
+ root.classList.add('dark');
258
+ root.classList.remove('light');
259
+ root.style.colorScheme = 'dark';
260
+ } else {
261
+ root.classList.remove('dark');
262
+ root.classList.add('light');
263
+ root.style.colorScheme = 'light';
264
+ }
265
+
266
+ // Set data-theme attribute for CSS selectors
267
+ const themeId = `ne-${config.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
268
+ root.setAttribute('data-theme', themeId);
269
+ }
270
+
271
+ /**
272
+ * Get theme styles as an object (for SSR/React style prop)
273
+ */
274
+ export function getOklchThemeStyles(config: OklchThemeConfig): Record<string, string> {
275
+ const vars = generateThemeVariables(config);
276
+
277
+ return {
278
+ ...vars,
279
+ colorScheme: config.isDark ? 'dark' : 'light',
280
+ backgroundColor: vars['--background'],
281
+ color: vars['--foreground'],
282
+ };
283
+ }