@soybeanjs/shadcn-theme 0.0.11 → 0.2.0

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.en_US.md CHANGED
@@ -1,19 +1,18 @@
1
1
  # @soybeanjs/shadcn-theme
2
2
 
3
- A powerful and flexible shadcn/ui theme generator with support for dynamic CSS variable injection, preset color schemes, and light/dark mode switching.
3
+ A powerful and flexible shadcn/ui theme CSS variables generator with preset color schemes, light/dark output, and optional custom preset extension.
4
4
 
5
5
  [中文 README](README.md)
6
6
 
7
7
  ## ✨ Features
8
8
 
9
- - 🎨 **Rich Preset Themes** - Multiple base palettes, theme colors, and feedback color presets
10
- - 🌗 **Light/Dark Mode Support** - Built-in dark mode with automatic dark variant generation
11
- - 🎯 **Flexible Color Schemes** - Support for both HSL and OKLCH color formats
12
- - 🔧 **Highly Customizable** - Full control over custom theme color configurations
13
- - 📦 **Zero Runtime Dependencies** - Only depends on `@soybeanjs/colord` for color processing
14
- - 🚀 **Plug and Play** - Automatically injects CSS variables into the DOM
15
- - 🎭 **Extended Palettes** - Theme customization support for sidebars, charts, and more
16
- - 🌈 **Color Palette Generation** - Automatically generates gradient palettes (50-950) for primary colors
9
+ - 🎨 **Rich Preset Themes** - Built-in base / primary / feedback preset combinations
10
+ - 🌗 **Light/Dark Output** - Supports `.dark` / `@media (prefers-color-scheme: dark)` / custom selector
11
+ - 🎯 **Flexible Color Formats** - Supports `hsl` and `oklch` output
12
+ - 🔧 **Extensible** - Add custom colors via `preset` (including sidebar / chart fields)
13
+ - 🌈 **Palette Variables** - Generates 50-950 palette vars for primary/destructive/success/warning/info/carbon
14
+ - 📦 **Lightweight Dependency** - Only depends on `@soybeanjs/colord`
15
+ - 🧩 **Pure Generator** - Returns CSS strings only; does not automatically mutate the DOM
17
16
 
18
17
  ## 📦 Installation
19
18
 
@@ -28,97 +27,238 @@ pnpm add @soybeanjs/shadcn-theme
28
27
  ```typescript
29
28
  import { createShadcnTheme } from '@soybeanjs/shadcn-theme';
30
29
 
31
- // Use default presets (slate + indigo + classic)
32
- createShadcnTheme();
30
+ // Use default presets (gray + indigo + classic + extended)
31
+ const theme = createShadcnTheme();
32
+ const css = theme.getCss();
33
33
 
34
34
  // Custom preset combination
35
- createShadcnTheme({
36
- presets: {
37
- base: 'zinc', // Base palette: stone | zinc | neutral | gray | slate
38
- theme: 'blue', // Theme color: any Tailwind palette name
39
- feedback: 'vivid' // Feedback color style: classic | vivid | subtle | warm | cool, etc.
40
- },
41
- radius: '0.5rem', // Border radius size
42
- darkSelector: 'class', // Dark mode selector: 'class' | 'media' | custom
43
- format: 'hsl' // Color format: 'hsl' | 'oklch'
35
+ const custom = createShadcnTheme({
36
+ base: 'zinc',
37
+ primary: 'blue',
38
+ feedback: 'vivid',
39
+ sidebar: 'extended',
40
+ radius: '0.5rem',
41
+ darkSelector: 'class',
42
+ format: 'hsl'
44
43
  });
44
+
45
+ const customCss = custom.getCss();
46
+ ```
47
+
48
+ ### (Optional) Inject into the DOM
49
+
50
+ This library generates CSS strings. If you want runtime theme switching, you can inject the result yourself:
51
+
52
+ ```ts
53
+ import { createShadcnTheme } from '@soybeanjs/shadcn-theme';
54
+
55
+ const theme = createShadcnTheme({ primary: 'indigo' });
56
+
57
+ function applyTheme(cssText: string, styleId = 'SHADCN_THEME_STYLE') {
58
+ const el = document.getElementById(styleId) ?? document.createElement('style');
59
+ el.id = styleId;
60
+ el.textContent = cssText;
61
+ document.head.appendChild(el);
62
+ }
63
+
64
+ applyTheme(theme.getCss());
65
+ applyTheme(theme.getCss({ primary: 'emerald' }));
45
66
  ```
46
67
 
47
- ### Custom Theme Colors
68
+ ### Custom Preset
69
+
70
+ Using the `preset` parameter, you can provide a complete custom color configuration that overrides the built-in base/primary/feedback/sidebar presets. When using a custom preset, you need to provide complete color definitions.
71
+
72
+ #### 1) When to use custom preset
73
+
74
+ - Use `preset` to provide a complete custom color configuration when built-in preset combinations cannot meet your design requirements.
75
+ - When using a custom preset, all base/primary/feedback/sidebar related parameters will be ignored, only the `preset` configuration is used.
76
+
77
+ #### 2) Color values and `format`
78
+
79
+ - Each color value supports: Tailwind palette reference (e.g. `slate.500`), `hsl(...)`, `oklch(...)`, or the builtin permitted color names (`inherit`, `currentColor`, `transparent`, `black`, `white`).
80
+ - `format: 'hsl'`: outputs `h s l [/ alpha]` (no outer `hsl(...)`); `oklch(...)` inputs are converted to HSL.
81
+ - `format: 'oklch'`: outputs values with the outer `oklch(...)`; `hsl(...)` inputs are converted to OKLCH.
82
+
83
+ #### Quick example: Complete custom preset
48
84
 
49
85
  ```typescript
50
- createShadcnTheme({
51
- theme: {
86
+ const theme = createShadcnTheme({
87
+ preset: {
52
88
  light: {
53
- background: 'oklch(100% 0 0)',
54
- foreground: 'oklch(20% 0 0)',
55
- primary: 'oklch(50% 0.2 250)',
56
- primaryForeground: 'oklch(100% 0 0)',
57
- // ... more color configurations
89
+ // Base colors
90
+ background: 'white',
91
+ foreground: 'slate.950',
92
+ card: 'white',
93
+ cardForeground: 'slate.950',
94
+ popover: 'white',
95
+ popoverForeground: 'slate.950',
96
+ primaryForeground: 'slate.50',
97
+ secondary: 'slate.100',
98
+ secondaryForeground: 'slate.900',
99
+ muted: 'slate.100',
100
+ mutedForeground: 'slate.500',
101
+ accent: 'slate.100',
102
+ accentForeground: 'slate.900',
103
+ destructiveForeground: 'slate.50',
104
+ successForeground: 'slate.50',
105
+ warningForeground: 'slate.50',
106
+ infoForeground: 'slate.50',
107
+ carbon: 'slate.800',
108
+ carbonForeground: 'slate.50',
109
+ border: 'slate.200',
110
+ input: 'slate.200',
111
+ // Theme colors
112
+ primary: 'blue.600',
113
+ destructive: 'red.500',
114
+ success: 'green.500',
115
+ warning: 'amber.500',
116
+ info: 'blue.500',
117
+ ring: 'blue.400',
118
+ // Chart colors
119
+ chart1: 'orange.600',
120
+ chart2: 'teal.600',
121
+ chart3: 'cyan.900',
122
+ chart4: 'amber.400',
123
+ chart5: 'amber.500',
124
+ // Sidebar colors
125
+ sidebar: 'slate.50',
126
+ sidebarForeground: 'slate.900',
127
+ sidebarPrimary: 'blue.600',
128
+ sidebarPrimaryForeground: 'slate.50',
129
+ sidebarAccent: 'slate.100',
130
+ sidebarAccentForeground: 'slate.900',
131
+ sidebarBorder: 'slate.200',
132
+ sidebarRing: 'blue.400'
58
133
  },
59
134
  dark: {
60
- // Optional, will automatically generate dark variants if not provided
61
- background: 'oklch(20% 0 0)',
62
- foreground: 'oklch(100% 0 0)',
63
- // ...
135
+ // Base colors
136
+ background: 'slate.950',
137
+ foreground: 'slate.50',
138
+ card: 'slate.900',
139
+ cardForeground: 'slate.50',
140
+ popover: 'slate.900',
141
+ popoverForeground: 'slate.50',
142
+ primaryForeground: 'slate.900',
143
+ secondary: 'slate.800',
144
+ secondaryForeground: 'slate.50',
145
+ muted: 'slate.800',
146
+ mutedForeground: 'slate.400',
147
+ accent: 'slate.800',
148
+ accentForeground: 'slate.50',
149
+ destructiveForeground: 'slate.900',
150
+ successForeground: 'slate.900',
151
+ warningForeground: 'slate.900',
152
+ infoForeground: 'slate.900',
153
+ carbon: 'slate.100',
154
+ carbonForeground: 'slate.900',
155
+ border: 'oklch(100% 0 0 / 0.1)',
156
+ input: 'oklch(100% 0 0 / 0.15)',
157
+ // Theme colors
158
+ primary: 'blue.400',
159
+ destructive: 'red.400',
160
+ success: 'green.400',
161
+ warning: 'amber.400',
162
+ info: 'blue.400',
163
+ ring: 'blue.500',
164
+ // Chart colors
165
+ chart1: 'orange.500',
166
+ chart2: 'teal.500',
167
+ chart3: 'cyan.400',
168
+ chart4: 'amber.500',
169
+ chart5: 'amber.600',
170
+ // Sidebar colors
171
+ sidebar: 'slate.950',
172
+ sidebarForeground: 'slate.50',
173
+ sidebarPrimary: 'blue.400',
174
+ sidebarPrimaryForeground: 'slate.950',
175
+ sidebarAccent: 'slate.900',
176
+ sidebarAccentForeground: 'slate.50',
177
+ sidebarBorder: 'slate.800',
178
+ sidebarRing: 'blue.500'
64
179
  }
65
180
  }
66
181
  });
182
+
183
+ const css = theme.getCss();
67
184
  ```
68
185
 
186
+ #### Notes
187
+
188
+ - When providing the `preset` parameter, the `base`, `primary`, `feedback`, and `sidebar` parameters will be ignored.
189
+ - The preset must include complete color definitions for both `light` and `dark` modes.
190
+ - It is recommended to start from the structure of built-in presets and modify according to your design needs.
191
+
69
192
  ## 📖 API Documentation
70
193
 
71
194
  ### `createShadcnTheme(options?: ThemeOptions)`
72
195
 
73
- Main function to create and apply themes.
196
+ Main function to create a theme CSS generator.
197
+
198
+ Return value:
199
+
200
+ ```ts
201
+ const theme = createShadcnTheme();
202
+
203
+ theme.getCss(config?: PresetConfig, radius?: string): string
204
+ theme.getColorCss(config: PresetConfig): string
205
+ theme.getRadiusCss(radius?: string): string
206
+ ```
74
207
 
75
208
  #### ThemeOptions
76
209
 
77
- | Parameter | Type | Default | Description |
78
- |------|------|--------|------|
79
- | `presets` | `PresetConfig` | - | Preset configuration, takes priority over `theme` |
80
- | `theme` | `ThemeConfig` | - | Custom theme color configuration |
81
- | `radius` | `string` | `'0.625rem'` | Global border radius size |
82
- | `styleId` | `string` | `'SHADCN_THEME_STYLES'` | ID of the injected style tag |
83
- | `styleTarget` | `'html' \| ':root'` | `':root'` | CSS variable mount target |
84
- | `darkSelector` | `string` | `'class'` | Dark mode selector |
85
- | `format` | `'hsl' \| 'oklch'` | `'hsl'` | Color output format |
210
+ | Parameter | Type | Default | Description |
211
+ | -------------- | ------------------------------ | ------------ | --------------------------------------------------------------------------------------- |
212
+ | `base` | `BuiltinBasePresetKey` | `'neutral'` | Base preset key |
213
+ | `primary` | `BuiltinPrimaryPresetKey` | `'indigo'` | Primary preset key |
214
+ | `feedback` | `BuiltinFeedbackPresetKey` | `'classic'` | Feedback preset key |
215
+ | `sidebar` | `'extended'` | `'extended'` | Sidebar mode; `extended` derives from base/primary |
216
+ | `preset` | `ThemeColorPresetItem` | - | Complete custom color preset (when provided, base/primary/feedback/sidebar are ignored) |
217
+ | `radius` | `string` | `'0.625rem'` | Global border radius |
218
+ | `styleTarget` | `'html' \| ':root'` | `':root'` | CSS variables mount selector |
219
+ | `darkSelector` | `'class' \| 'media' \| string` | `'class'` | Dark mode selector (custom string supported) |
220
+ | `format` | `'hsl' \| 'oklch'` | `'hsl'` | Output color format |
86
221
 
87
222
  ### Preset Configuration (PresetConfig)
88
223
 
89
224
  ```typescript
90
225
  interface PresetConfig {
91
- base?: 'stone' | 'zinc' | 'neutral' | 'gray' | 'slate'; // Default: 'slate'
92
- theme?: TailwindPaletteKey; // Any Tailwind palette, default: 'indigo'
93
- feedback?: FeedbackPaletteKey; // Feedback color style, default: 'classic'
226
+ base?: BuiltinBasePresetKey | 'custom';
227
+ primary?: BuiltinPrimaryPresetKey | 'custom';
228
+ feedback?: BuiltinFeedbackPresetKey | 'custom';
229
+ sidebar?: 'extended' | 'custom';
230
+ preset?: ThemeColorPresetItem;
94
231
  }
95
232
  ```
96
233
 
234
+ When using the `preset` parameter, other configuration parameters (base/primary/feedback/sidebar) will be ignored.
235
+
97
236
  #### Feedback Palette Key (FeedbackPaletteKey)
98
237
 
99
- | Style | Description | Use Cases |
100
- |------|------|----------|
101
- | `classic` | Classic Standard | Most common combination, suitable for most scenarios |
102
- | `vivid` | Vivid & Energetic | High saturation, suitable for youth-oriented products and creative applications |
103
- | `subtle` | Soft & Elegant | Low contrast, suitable for premium brands and elegant interfaces |
104
- | `warm` | Warm & Welcoming | Warm color tones, creates a friendly and warm atmosphere |
105
- | `cool` | Cool & Professional | Cool color tones, suitable for tech and professional applications |
106
- | `nature` | Natural & Fresh | Natural colors, suitable for eco-friendly and health products |
107
- | `modern` | Modern & Minimalist | Strong modern feel, suitable for tech products and SaaS applications |
108
- | `vibrant` | Vibrant & Dynamic | High-energy colors, suitable for sports and gaming applications |
109
- | `professional` | Business Professional | Stable and dignified, suitable for enterprise applications and B2B products |
110
- | `soft` | Dreamy & Soft | Soft tones, suitable for design tools and creative platforms |
111
- | `bold` | Bold & Eye-catching | High contrast, suitable for scenarios requiring strong visual impact |
112
- | `calm` | Calm & Soothing | Low saturation, suitable for long-term use applications |
113
- | `candy` | Candy Colors | Bright and cute, suitable for children's products and fun applications |
114
- | `deep` | Deep & Mysterious | Deep tones, suitable for dark themes and mysterious styles |
115
- | `light` | Fresh & Light | Light tones, suitable for clean and refreshing interfaces |
238
+ | Style | Description | Use Cases |
239
+ | -------------- | --------------------- | ------------------------------------------------------------------------------- |
240
+ | `classic` | Classic Standard | Most common combination, suitable for most scenarios |
241
+ | `vivid` | Vivid & Energetic | High saturation, suitable for youth-oriented products and creative applications |
242
+ | `subtle` | Soft & Elegant | Low contrast, suitable for premium brands and elegant interfaces |
243
+ | `warm` | Warm & Welcoming | Warm color tones, creates a friendly and warm atmosphere |
244
+ | `cool` | Cool & Professional | Cool color tones, suitable for tech and professional applications |
245
+ | `nature` | Natural & Fresh | Natural colors, suitable for eco-friendly and health products |
246
+ | `modern` | Modern & Minimalist | Strong modern feel, suitable for tech products and SaaS applications |
247
+ | `vibrant` | Vibrant & Dynamic | High-energy colors, suitable for sports and gaming applications |
248
+ | `professional` | Business Professional | Stable and dignified, suitable for enterprise applications and B2B products |
249
+ | `soft` | Dreamy & Soft | Soft tones, suitable for design tools and creative platforms |
250
+ | `bold` | Bold & Eye-catching | High contrast, suitable for scenarios requiring strong visual impact |
251
+ | `calm` | Calm & Soothing | Low saturation, suitable for long-term use applications |
252
+ | `candy` | Candy Colors | Bright and cute, suitable for children's products and fun applications |
253
+ | `deep` | Deep & Mysterious | Deep tones, suitable for dark themes and mysterious styles |
254
+ | `light` | Fresh & Light | Light tones, suitable for clean and refreshing interfaces |
116
255
 
117
256
  ### Theme Color Configuration (ThemeColors)
118
257
 
119
258
  Supports configuration of the following color variables:
120
259
 
121
260
  #### Base Colors
261
+
122
262
  - `background` - Background color
123
263
  - `foreground` - Foreground color (text)
124
264
  - `card` - Card background
@@ -140,12 +280,14 @@ Supports configuration of the following color variables:
140
280
  - `ring` - Focus ring color
141
281
 
142
282
  #### Extended Colors
283
+
143
284
  - `success` / `successForeground` - Success state
144
285
  - `warning` / `warningForeground` - Warning state
145
286
  - `info` / `infoForeground` - Info state
146
287
  - `carbon` / `carbonForeground` - Carbon color (additional dark scheme)
147
288
 
148
289
  #### Sidebar Colors
290
+
149
291
  - `sidebar` - Sidebar background
150
292
  - `sidebarForeground` - Sidebar foreground
151
293
  - `sidebarPrimary` - Sidebar primary color
@@ -156,6 +298,7 @@ Supports configuration of the following color variables:
156
298
  - `sidebarRing` - Sidebar focus ring
157
299
 
158
300
  #### Chart Colors
301
+
159
302
  - `chart1` ~ `chart5` - Chart colors
160
303
 
161
304
  ### Color Value Format (ColorValue)
@@ -163,22 +306,25 @@ Supports configuration of the following color variables:
163
306
  Supports three color value formats:
164
307
 
165
308
  1. **HSL Format**
309
+
166
310
  ```typescript
167
- 'hsl(0 0% 100%)'
168
- 'hsl(0 0% 100% / 0.5)' // with opacity
311
+ 'hsl(0 0% 100%)';
312
+ 'hsl(0 0% 100% / 0.5)'; // with opacity
169
313
  ```
170
314
 
171
315
  2. **OKLCH Format**
316
+
172
317
  ```typescript
173
- 'oklch(100% 0 0)'
174
- 'oklch(100% 0 0 / 0.5)' // with opacity
318
+ 'oklch(100% 0 0)';
319
+ 'oklch(100% 0 0 / 0.5)'; // with opacity
175
320
  ```
176
321
 
177
322
  3. **Tailwind Palette Reference**
323
+
178
324
  ```typescript
179
- 'slate.500'
180
- 'blue.600'
181
- 'red.50'
325
+ 'slate.500';
326
+ 'blue.600';
327
+ 'red.50';
182
328
  ```
183
329
 
184
330
  ## 🎨 Usage Examples
@@ -186,78 +332,75 @@ Supports three color value formats:
186
332
  ### Example 1: Classic Blue Theme
187
333
 
188
334
  ```typescript
189
- createShadcnTheme({
190
- presets: {
191
- base: 'slate',
192
- theme: 'blue',
193
- feedback: 'classic'
194
- },
335
+ const theme = createShadcnTheme({
336
+ base: 'slate',
337
+ primary: 'blue',
338
+ feedback: 'classic',
195
339
  radius: '0.5rem',
196
340
  darkSelector: 'class'
197
341
  });
342
+
343
+ const css = theme.getCss();
198
344
  ```
199
345
 
200
346
  ### Example 2: Modern Purple Theme
201
347
 
202
348
  ```typescript
203
- createShadcnTheme({
204
- presets: {
205
- base: 'zinc',
206
- theme: 'violet',
207
- feedback: 'modern'
208
- },
349
+ const theme = createShadcnTheme({
350
+ base: 'zinc',
351
+ primary: 'violet',
352
+ feedback: 'modern',
209
353
  radius: '0.75rem',
210
354
  darkSelector: 'class',
211
355
  format: 'oklch'
212
356
  });
357
+
358
+ const css = theme.getCss();
213
359
  ```
214
360
 
215
- ### Example 3: Custom Brand Colors
361
+ ### Example 3: Override Per Generation
216
362
 
217
363
  ```typescript
218
- createShadcnTheme({
219
- theme: {
220
- light: {
221
- background: 'oklch(100% 0 0)',
222
- foreground: 'oklch(20% 0 0)',
223
- primary: 'oklch(50% 0.25 280)', // Custom brand purple
224
- primaryForeground: 'oklch(100% 0 0)',
225
- secondary: 'oklch(95% 0.01 280)',
226
- secondaryForeground: 'oklch(30% 0 0)',
227
- // ... other colors
228
- }
229
- // dark is optional, will be auto-generated if not provided
230
- }
231
- });
364
+ const theme = createShadcnTheme({ base: 'slate', primary: 'indigo', feedback: 'classic' });
365
+
366
+ const css1 = theme.getCss();
367
+ const css2 = theme.getCss({ primary: 'emerald', feedback: 'vivid' });
232
368
  ```
233
369
 
234
370
  ### Example 4: Media Query Dark Mode
235
371
 
236
372
  ```typescript
237
- createShadcnTheme({
238
- presets: {
239
- base: 'slate',
240
- theme: 'indigo'
241
- },
373
+ const theme = createShadcnTheme({
374
+ base: 'slate',
375
+ primary: 'indigo',
242
376
  darkSelector: 'media' // Use system preference
243
377
  });
378
+
379
+ const css = theme.getCss();
244
380
  ```
245
381
 
246
382
  ### Example 5: Custom Dark Mode Selector
247
383
 
248
384
  ```typescript
249
- createShadcnTheme({
250
- presets: {
251
- base: 'slate',
252
- theme: 'emerald'
253
- },
385
+ const theme = createShadcnTheme({
386
+ base: 'slate',
387
+ primary: 'emerald',
254
388
  darkSelector: '[data-theme="dark"]' // Custom selector
255
389
  });
390
+
391
+ const css = theme.getCss();
256
392
  ```
257
393
 
258
394
  ## 🎯 Generated CSS Variables
259
395
 
260
- After calling `createShadcnTheme()`, a `<style>` tag containing the following variables will be automatically injected into `<head>`:
396
+ `getCss()` returns a CSS string containing variables like the following.
397
+
398
+ When `format: 'hsl'`, variable values are `h s l [/ alpha]` (without the outer `hsl(...)` wrapper):
399
+
400
+ Notes:
401
+
402
+ - When `format: 'hsl'` and the color key is `border`, `input`, or `sidebarBorder`, if the value includes opacity (e.g. `hsl(... / 0.1)` or `oklch(... / 0.1)`), the library also emits alpha variables: `--border-alpha`, `--input-alpha`, `--sidebar-border-alpha`. Meanwhile `--border` / `--input` / `--sidebar-border` keep only the `h s l` part (without the `/ alpha`).
403
+ - It generates 11 palette variables (50-950) for `primary`, `destructive`, `success`, `warning`, `info`, and `carbon`: `50/100/200/300/400/500/600/700/800/900/950`.
261
404
 
262
405
  ```css
263
406
  :root {
@@ -265,9 +408,13 @@ After calling `createShadcnTheme()`, a `<style>` tag containing the following va
265
408
  --background: 0 0% 100%;
266
409
  --foreground: 222.2 84% 4.9%;
267
410
  --primary: 221.2 83.2% 53.3%;
411
+
412
+ /* In hsl mode: border/input/sidebarBorder also output alpha vars */
413
+ --border: 214.3 31.8% 91.4%;
414
+ --border-alpha: 0.1;
268
415
  /* ... more variables */
269
416
 
270
- /* Auto-generated palettes */
417
+ /* Auto-generated palettes (11 levels: 50-950) */
271
418
  --primary-50: 239 84% 97%;
272
419
  --primary-100: 237 84% 94%;
273
420
  /* ... primary-200 to primary-950 */
@@ -287,23 +434,36 @@ After calling `createShadcnTheme()`, a `<style>` tag containing the following va
287
434
  }
288
435
  ```
289
436
 
437
+ When `format: 'oklch'`, variable values include the outer `oklch(...)` wrapper:
438
+
439
+ ```css
440
+ :root {
441
+ --background: oklch(100% 0 0);
442
+ --foreground: oklch(20% 0 0);
443
+ --border: oklch(100% 0 0 / 0.1);
444
+ }
445
+ ```
446
+
290
447
  ## 💡 Advanced Usage
291
448
 
292
449
  ### Dynamic Theme Switching
293
450
 
294
451
  ```typescript
295
- // Switch to light theme
296
- createShadcnTheme({
297
- presets: { base: 'slate', theme: 'blue' }
298
- });
452
+ const theme = createShadcnTheme();
453
+
454
+ function apply(cssText: string) {
455
+ const id = 'SHADCN_THEME_STYLE';
456
+ const el = document.getElementById(id) ?? document.createElement('style');
457
+ el.id = id;
458
+ el.textContent = cssText;
459
+ document.head.appendChild(el);
460
+ }
299
461
 
300
- // Runtime switch to dark theme (by toggling class)
301
- document.documentElement.classList.add('dark');
462
+ apply(theme.getCss());
463
+ apply(theme.getCss({ base: 'zinc', primary: 'purple' }));
302
464
 
303
- // Switch to another theme
304
- createShadcnTheme({
305
- presets: { base: 'zinc', theme: 'purple' }
306
- });
465
+ // Dark mode switching is controlled by your darkSelector (e.g. default is adding .dark)
466
+ document.documentElement.classList.add('dark');
307
467
  ```
308
468
 
309
469
  ### Using with Tailwind CSS
@@ -321,19 +481,42 @@ module.exports = {
321
481
  DEFAULT: 'hsl(var(--primary))',
322
482
  foreground: 'hsl(var(--primary-foreground))',
323
483
  50: 'hsl(var(--primary-50))',
324
- 100: 'hsl(var(--primary-100))',
484
+ 100: 'hsl(var(--primary-100))'
325
485
  // ... more shades
326
- },
486
+ }
327
487
  // ... other colors
328
488
  },
329
489
  borderRadius: {
330
490
  lg: 'var(--radius)',
331
491
  md: 'calc(var(--radius) - 2px)',
332
- sm: 'calc(var(--radius) - 4px)',
492
+ sm: 'calc(var(--radius) - 4px)'
333
493
  }
334
494
  }
335
495
  }
336
- }
496
+ };
497
+ ```
498
+
499
+ When you use `format: 'hsl'`, opacity must be handled separately, especially for `border` / `input` / `sidebarBorder`:
500
+
501
+ - These variables output `h s l` (without `/ alpha`). If opacity is present, the library also emits `--border-alpha` / `--input-alpha` / `--sidebar-border-alpha`.
502
+ - In Tailwind, you can compose them using the slash syntax:
503
+
504
+ ```js
505
+ // Use the generated alpha value
506
+ border: 'hsl(var(--border) / var(--border-alpha))';
507
+ ```
508
+
509
+ If you want Tailwind opacity modifiers to work (e.g. `border-border/50`), use the `<alpha-value>` placeholder (in this case you typically don't use `--border-alpha`):
510
+
511
+ ```js
512
+ // Let Tailwind inject opacity
513
+ border: 'hsl(var(--border) / <alpha-value>)';
514
+ ```
515
+
516
+ If you use `format: 'oklch'`, since the variable value already contains `oklch(...)`, use `var(--xxx)` directly in Tailwind (no extra `oklch(...)` wrapper needed):
517
+
518
+ ```js
519
+ background: 'var(--background)';
337
520
  ```
338
521
 
339
522
  ### Using in CSS