@soybeanjs/shadcn-theme 0.0.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.
@@ -0,0 +1,370 @@
1
+ # @soybeanjs/shadcn-theme
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.
4
+
5
+ [中文 README](README.md)
6
+
7
+ ## ✨ Features
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
17
+
18
+ ## 📦 Installation
19
+
20
+ ```bash
21
+ pnpm add @soybeanjs/shadcn-theme
22
+ ```
23
+
24
+ ## 🚀 Quick Start
25
+
26
+ ### Using Preset Themes
27
+
28
+ ```typescript
29
+ import { createShadcnTheme } from '@soybeanjs/shadcn-theme';
30
+
31
+ // Use default presets (slate + indigo + classic)
32
+ createShadcnTheme();
33
+
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'
44
+ });
45
+ ```
46
+
47
+ ### Custom Theme Colors
48
+
49
+ ```typescript
50
+ createShadcnTheme({
51
+ theme: {
52
+ 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
58
+ },
59
+ dark: {
60
+ // Optional, will automatically generate dark variants if not provided
61
+ background: 'oklch(20% 0 0)',
62
+ foreground: 'oklch(100% 0 0)',
63
+ // ...
64
+ }
65
+ }
66
+ });
67
+ ```
68
+
69
+ ## 📖 API Documentation
70
+
71
+ ### `createShadcnTheme(options?: ThemeOptions)`
72
+
73
+ Main function to create and apply themes.
74
+
75
+ #### ThemeOptions
76
+
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 |
86
+
87
+ ### Preset Configuration (PresetConfig)
88
+
89
+ ```typescript
90
+ 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'
94
+ }
95
+ ```
96
+
97
+ #### Feedback Palette Key (FeedbackPaletteKey)
98
+
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 |
116
+
117
+ ### Theme Color Configuration (ThemeColors)
118
+
119
+ Supports configuration of the following color variables:
120
+
121
+ #### Base Colors
122
+ - `background` - Background color
123
+ - `foreground` - Foreground color (text)
124
+ - `card` - Card background
125
+ - `cardForeground` - Card foreground color
126
+ - `popover` - Popover background
127
+ - `popoverForeground` - Popover foreground color
128
+ - `primary` - Primary color
129
+ - `primaryForeground` - Primary foreground
130
+ - `secondary` - Secondary color
131
+ - `secondaryForeground` - Secondary foreground
132
+ - `muted` - Muted color
133
+ - `mutedForeground` - Muted foreground
134
+ - `accent` - Accent color
135
+ - `accentForeground` - Accent foreground
136
+ - `destructive` - Destructive action color
137
+ - `destructiveForeground` - Destructive foreground color
138
+ - `border` - Border color
139
+ - `input` - Input border color
140
+ - `ring` - Focus ring color
141
+
142
+ #### Extended Colors
143
+ - `success` / `successForeground` - Success state
144
+ - `warning` / `warningForeground` - Warning state
145
+ - `info` / `infoForeground` - Info state
146
+ - `carbon` / `carbonForeground` - Carbon color (additional dark scheme)
147
+
148
+ #### Sidebar Colors
149
+ - `sidebar` - Sidebar background
150
+ - `sidebarForeground` - Sidebar foreground
151
+ - `sidebarPrimary` - Sidebar primary color
152
+ - `sidebarPrimaryForeground` - Sidebar primary foreground
153
+ - `sidebarAccent` - Sidebar accent color
154
+ - `sidebarAccentForeground` - Sidebar accent foreground
155
+ - `sidebarBorder` - Sidebar border
156
+ - `sidebarRing` - Sidebar focus ring
157
+
158
+ #### Chart Colors
159
+ - `chart1` ~ `chart5` - Chart colors
160
+
161
+ ### Color Value Format (ColorValue)
162
+
163
+ Supports three color value formats:
164
+
165
+ 1. **HSL Format**
166
+ ```typescript
167
+ 'hsl(0 0% 100%)'
168
+ 'hsl(0 0% 100% / 0.5)' // with opacity
169
+ ```
170
+
171
+ 2. **OKLCH Format**
172
+ ```typescript
173
+ 'oklch(100% 0 0)'
174
+ 'oklch(100% 0 0 / 0.5)' // with opacity
175
+ ```
176
+
177
+ 3. **Tailwind Palette Reference**
178
+ ```typescript
179
+ 'slate.500'
180
+ 'blue.600'
181
+ 'red.50'
182
+ ```
183
+
184
+ ## 🎨 Usage Examples
185
+
186
+ ### Example 1: Classic Blue Theme
187
+
188
+ ```typescript
189
+ createShadcnTheme({
190
+ presets: {
191
+ base: 'slate',
192
+ theme: 'blue',
193
+ feedback: 'classic'
194
+ },
195
+ radius: '0.5rem',
196
+ darkSelector: 'class'
197
+ });
198
+ ```
199
+
200
+ ### Example 2: Modern Purple Theme
201
+
202
+ ```typescript
203
+ createShadcnTheme({
204
+ presets: {
205
+ base: 'zinc',
206
+ theme: 'violet',
207
+ feedback: 'modern'
208
+ },
209
+ radius: '0.75rem',
210
+ darkSelector: 'class',
211
+ format: 'oklch'
212
+ });
213
+ ```
214
+
215
+ ### Example 3: Custom Brand Colors
216
+
217
+ ```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
+ });
232
+ ```
233
+
234
+ ### Example 4: Media Query Dark Mode
235
+
236
+ ```typescript
237
+ createShadcnTheme({
238
+ presets: {
239
+ base: 'slate',
240
+ theme: 'indigo'
241
+ },
242
+ darkSelector: 'media' // Use system preference
243
+ });
244
+ ```
245
+
246
+ ### Example 5: Custom Dark Mode Selector
247
+
248
+ ```typescript
249
+ createShadcnTheme({
250
+ presets: {
251
+ base: 'slate',
252
+ theme: 'emerald'
253
+ },
254
+ darkSelector: '[data-theme="dark"]' // Custom selector
255
+ });
256
+ ```
257
+
258
+ ## 🎯 Generated CSS Variables
259
+
260
+ After calling `createShadcnTheme()`, a `<style>` tag containing the following variables will be automatically injected into `<head>`:
261
+
262
+ ```css
263
+ :root {
264
+ --radius: 0.625rem;
265
+ --background: 0 0% 100%;
266
+ --foreground: 222.2 84% 4.9%;
267
+ --primary: 221.2 83.2% 53.3%;
268
+ /* ... more variables */
269
+
270
+ /* Auto-generated palettes */
271
+ --primary-50: 239 84% 97%;
272
+ --primary-100: 237 84% 94%;
273
+ /* ... primary-200 to primary-950 */
274
+
275
+ /* Other color palettes */
276
+ --destructive-50: ...;
277
+ --success-50: ...;
278
+ --warning-50: ...;
279
+ --info-50: ...;
280
+ --carbon-50: ...;
281
+ }
282
+
283
+ .dark {
284
+ --background: 222.2 84% 4.9%;
285
+ --foreground: 210 40% 98%;
286
+ /* ... variables in dark mode */
287
+ }
288
+ ```
289
+
290
+ ## 💡 Advanced Usage
291
+
292
+ ### Dynamic Theme Switching
293
+
294
+ ```typescript
295
+ // Switch to light theme
296
+ createShadcnTheme({
297
+ presets: { base: 'slate', theme: 'blue' }
298
+ });
299
+
300
+ // Runtime switch to dark theme (by toggling class)
301
+ document.documentElement.classList.add('dark');
302
+
303
+ // Switch to another theme
304
+ createShadcnTheme({
305
+ presets: { base: 'zinc', theme: 'purple' }
306
+ });
307
+ ```
308
+
309
+ ### Using with Tailwind CSS
310
+
311
+ ```javascript
312
+ // tailwind.config.js
313
+ module.exports = {
314
+ darkMode: ['class'],
315
+ theme: {
316
+ extend: {
317
+ colors: {
318
+ background: 'hsl(var(--background))',
319
+ foreground: 'hsl(var(--foreground))',
320
+ primary: {
321
+ DEFAULT: 'hsl(var(--primary))',
322
+ foreground: 'hsl(var(--primary-foreground))',
323
+ 50: 'hsl(var(--primary-50))',
324
+ 100: 'hsl(var(--primary-100))',
325
+ // ... more shades
326
+ },
327
+ // ... other colors
328
+ },
329
+ borderRadius: {
330
+ lg: 'var(--radius)',
331
+ md: 'calc(var(--radius) - 2px)',
332
+ sm: 'calc(var(--radius) - 4px)',
333
+ }
334
+ }
335
+ }
336
+ }
337
+ ```
338
+
339
+ ### Using in CSS
340
+
341
+ ```css
342
+ .my-component {
343
+ background-color: hsl(var(--primary));
344
+ color: hsl(var(--primary-foreground));
345
+ border-radius: var(--radius);
346
+ border: 1px solid hsl(var(--border));
347
+ }
348
+
349
+ .my-card {
350
+ background-color: hsl(var(--card));
351
+ color: hsl(var(--card-foreground));
352
+ }
353
+ ```
354
+
355
+ ## 🔗 Related Projects
356
+
357
+ - [shadcn/ui](https://ui.shadcn.com/) - Component library based on Radix UI and Tailwind CSS
358
+ - [@soybeanjs/colord](https://github.com/soybeanjs/colord) - Color processing utility library
359
+
360
+ ## 📄 License
361
+
362
+ MIT License
363
+
364
+ ## 🤝 Contributing
365
+
366
+ Issues and Pull Requests are welcome!
367
+
368
+ ## 👨‍💻 Author
369
+
370
+ Created by [Soybean](https://github.com/soybeanjs)
package/README.md ADDED
@@ -0,0 +1,370 @@
1
+ # @soybeanjs/shadcn-theme
2
+
3
+ 一个功能强大且灵活的 shadcn/ui 主题生成器,支持动态 CSS 变量注入、预设配色方案和深浅模式切换。
4
+
5
+ [English](./README.en_US.md)
6
+
7
+ ## ✨ 特性
8
+
9
+ - 🎨 **丰富的预设主题** - 提供多种基础色板、主题色和反馈色预设
10
+ - 🌗 **深浅模式支持** - 内置深色模式支持,可自动生成深色变体
11
+ - 🎯 **灵活的配色方案** - 支持 HSL 和 OKLCH 两种颜色格式
12
+ - 🔧 **高度可定制** - 支持完全自定义主题颜色配置
13
+ - 📦 **零运行时依赖** - 仅依赖 `@soybeanjs/colord` 进行颜色处理
14
+ - 🚀 **即插即用** - 自动将 CSS 变量注入到 DOM 中
15
+ - 🎭 **扩展色板** - 支持侧边栏、图表等扩展场景的主题定制
16
+ - 🌈 **颜色调色板生成** - 自动生成主要颜色的渐变色板(50-950)
17
+
18
+ ## 📦 安装
19
+
20
+ ```bash
21
+ pnpm add @soybeanjs/shadcn-theme
22
+ ```
23
+
24
+ ## 🚀 快速开始
25
+
26
+ ### 使用预设主题
27
+
28
+ ```typescript
29
+ import { createShadcnTheme } from '@soybeanjs/shadcn-theme';
30
+
31
+ // 使用默认预设(slate + indigo + classic)
32
+ createShadcnTheme();
33
+
34
+ // 自定义预设组合
35
+ createShadcnTheme({
36
+ presets: {
37
+ base: 'zinc', // 基础色板:stone | zinc | neutral | gray | slate
38
+ theme: 'blue', // 主题色:任意 Tailwind 色板名称
39
+ feedback: 'vivid' // 反馈色风格:classic | vivid | subtle | warm | cool 等
40
+ },
41
+ radius: '0.5rem', // 圆角大小
42
+ darkSelector: 'class', // 深色模式选择器:'class' | 'media' | 自定义
43
+ format: 'hsl' // 颜色格式:'hsl' | 'oklch'
44
+ });
45
+ ```
46
+
47
+ ### 自定义主题颜色
48
+
49
+ ```typescript
50
+ createShadcnTheme({
51
+ theme: {
52
+ 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
+ // ... 更多颜色配置
58
+ },
59
+ dark: {
60
+ // 可选,如果不提供会自动生成深色变体
61
+ background: 'oklch(20% 0 0)',
62
+ foreground: 'oklch(100% 0 0)',
63
+ // ...
64
+ }
65
+ }
66
+ });
67
+ ```
68
+
69
+ ## 📖 API 文档
70
+
71
+ ### `createShadcnTheme(options?: ThemeOptions)`
72
+
73
+ 主函数,用于创建并应用主题。
74
+
75
+ #### ThemeOptions
76
+
77
+ | 参数 | 类型 | 默认值 | 描述 |
78
+ |------|------|--------|------|
79
+ | `presets` | `PresetConfig` | - | 预设配置,优先级高于 `theme` |
80
+ | `theme` | `ThemeConfig` | - | 自定义主题颜色配置 |
81
+ | `radius` | `string` | `'0.625rem'` | 全局圆角大小 |
82
+ | `styleId` | `string` | `'SHADCN_THEME_STYLES'` | 注入的 style 标签 ID |
83
+ | `styleTarget` | `'html' \| ':root'` | `':root'` | CSS 变量挂载目标 |
84
+ | `darkSelector` | `string` | `'class'` | 深色模式选择器 |
85
+ | `format` | `'hsl' \| 'oklch'` | `'hsl'` | 颜色输出格式 |
86
+
87
+ ### 预设配置(PresetConfig)
88
+
89
+ ```typescript
90
+ interface PresetConfig {
91
+ base?: 'stone' | 'zinc' | 'neutral' | 'gray' | 'slate'; // 默认: 'slate'
92
+ theme?: TailwindPaletteKey; // 任意 Tailwind 色板,默认: 'indigo'
93
+ feedback?: FeedbackPaletteKey; // 反馈色风格,默认: 'classic'
94
+ }
95
+ ```
96
+
97
+ #### 反馈色风格(FeedbackPaletteKey)
98
+
99
+ | 风格 | 描述 | 适用场景 |
100
+ |------|------|----------|
101
+ | `classic` | 经典标准 | 最常见的组合,适用于大多数场景 |
102
+ | `vivid` | 鲜艳活力 | 高饱和度,适合年轻化产品和创意应用 |
103
+ | `subtle` | 柔和优雅 | 低对比度,适合高端品牌和优雅界面 |
104
+ | `warm` | 暖色温馨 | 暖色调为主,营造友好温暖的氛围 |
105
+ | `cool` | 冷色专业 | 冷色调为主,适合科技和专业应用 |
106
+ | `nature` | 自然清新 | 自然色系,适合环保、健康类产品 |
107
+ | `modern` | 现代简约 | 现代感强,适合科技产品和 SaaS 应用 |
108
+ | `vibrant` | 活力四射 | 高能量配色,适合运动、游戏类应用 |
109
+ | `professional` | 商务专业 | 稳重大气,适合企业级应用和 B2B 产品 |
110
+ | `soft` | 梦幻柔美 | 柔和色调,适合设计工具和创意平台 |
111
+ | `bold` | 大胆醒目 | 高对比度,适合需要强烈视觉冲击的场景 |
112
+ | `calm` | 平静舒缓 | 低饱和度,适合长时间使用的应用 |
113
+ | `candy` | 糖果色彩 | 明快可爱,适合儿童产品和趣味应用 |
114
+ | `deep` | 深邃神秘 | 深色调,适合暗黑主题和神秘风格 |
115
+ | `light` | 清新淡雅 | 浅色调,适合简洁清爽的界面 |
116
+
117
+ ### 主题颜色配置(ThemeColors)
118
+
119
+ 支持配置以下颜色变量:
120
+
121
+ #### 基础颜色
122
+ - `background` - 背景色
123
+ - `foreground` - 前景色(文本)
124
+ - `card` - 卡片背景
125
+ - `cardForeground` - 卡片前景色
126
+ - `popover` - 弹出层背景
127
+ - `popoverForeground` - 弹出层前景色
128
+ - `primary` - 主色
129
+ - `primaryForeground` - 主色前景
130
+ - `secondary` - 次要色
131
+ - `secondaryForeground` - 次要色前景
132
+ - `muted` - 静音色
133
+ - `mutedForeground` - 静音色前景
134
+ - `accent` - 强调色
135
+ - `accentForeground` - 强调色前景
136
+ - `destructive` - 破坏性操作色
137
+ - `destructiveForeground` - 破坏性操作前景色
138
+ - `border` - 边框色
139
+ - `input` - 输入框边框色
140
+ - `ring` - 聚焦环颜色
141
+
142
+ #### 扩展颜色
143
+ - `success` / `successForeground` - 成功状态
144
+ - `warning` / `warningForeground` - 警告状态
145
+ - `info` / `infoForeground` - 信息状态
146
+ - `carbon` / `carbonForeground` - 碳色(额外的深色系)
147
+
148
+ #### 侧边栏颜色
149
+ - `sidebar` - 侧边栏背景
150
+ - `sidebarForeground` - 侧边栏前景
151
+ - `sidebarPrimary` - 侧边栏主色
152
+ - `sidebarPrimaryForeground` - 侧边栏主色前景
153
+ - `sidebarAccent` - 侧边栏强调色
154
+ - `sidebarAccentForeground` - 侧边栏强调色前景
155
+ - `sidebarBorder` - 侧边栏边框
156
+ - `sidebarRing` - 侧边栏聚焦环
157
+
158
+ #### 图表颜色
159
+ - `chart1` ~ `chart5` - 图表配色
160
+
161
+ ### 颜色值格式(ColorValue)
162
+
163
+ 支持三种颜色值格式:
164
+
165
+ 1. **HSL 格式**
166
+ ```typescript
167
+ 'hsl(0 0% 100%)'
168
+ 'hsl(0 0% 100% / 0.5)' // 带透明度
169
+ ```
170
+
171
+ 2. **OKLCH 格式**
172
+ ```typescript
173
+ 'oklch(100% 0 0)'
174
+ 'oklch(100% 0 0 / 0.5)' // 带透明度
175
+ ```
176
+
177
+ 3. **Tailwind 色板引用**
178
+ ```typescript
179
+ 'slate.500'
180
+ 'blue.600'
181
+ 'red.50'
182
+ ```
183
+
184
+ ## 🎨 使用示例
185
+
186
+ ### 示例 1: 经典蓝色主题
187
+
188
+ ```typescript
189
+ createShadcnTheme({
190
+ presets: {
191
+ base: 'slate',
192
+ theme: 'blue',
193
+ feedback: 'classic'
194
+ },
195
+ radius: '0.5rem',
196
+ darkSelector: 'class'
197
+ });
198
+ ```
199
+
200
+ ### 示例 2: 现代紫色主题
201
+
202
+ ```typescript
203
+ createShadcnTheme({
204
+ presets: {
205
+ base: 'zinc',
206
+ theme: 'violet',
207
+ feedback: 'modern'
208
+ },
209
+ radius: '0.75rem',
210
+ darkSelector: 'class',
211
+ format: 'oklch'
212
+ });
213
+ ```
214
+
215
+ ### 示例 3: 自定义品牌色
216
+
217
+ ```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)', // 自定义品牌紫色
224
+ primaryForeground: 'oklch(100% 0 0)',
225
+ secondary: 'oklch(95% 0.01 280)',
226
+ secondaryForeground: 'oklch(30% 0 0)',
227
+ // ... 其他颜色
228
+ }
229
+ // dark 可选,不提供会自动生成
230
+ }
231
+ });
232
+ ```
233
+
234
+ ### 示例 4: 媒体查询深色模式
235
+
236
+ ```typescript
237
+ createShadcnTheme({
238
+ presets: {
239
+ base: 'slate',
240
+ theme: 'indigo'
241
+ },
242
+ darkSelector: 'media' // 使用系统偏好
243
+ });
244
+ ```
245
+
246
+ ### 示例 5: 自定义深色模式选择器
247
+
248
+ ```typescript
249
+ createShadcnTheme({
250
+ presets: {
251
+ base: 'slate',
252
+ theme: 'emerald'
253
+ },
254
+ darkSelector: '[data-theme="dark"]' // 自定义选择器
255
+ });
256
+ ```
257
+
258
+ ## 🎯 生成的 CSS 变量
259
+
260
+ 调用 `createShadcnTheme()` 后,会自动在 `<head>` 中注入包含以下变量的 `<style>` 标签:
261
+
262
+ ```css
263
+ :root {
264
+ --radius: 0.625rem;
265
+ --background: 0 0% 100%;
266
+ --foreground: 222.2 84% 4.9%;
267
+ --primary: 221.2 83.2% 53.3%;
268
+ /* ... 更多变量 */
269
+
270
+ /* 自动生成的色板 */
271
+ --primary-50: 239 84% 97%;
272
+ --primary-100: 237 84% 94%;
273
+ /* ... primary-200 到 primary-950 */
274
+
275
+ /* 其他颜色的色板 */
276
+ --destructive-50: ...;
277
+ --success-50: ...;
278
+ --warning-50: ...;
279
+ --info-50: ...;
280
+ --carbon-50: ...;
281
+ }
282
+
283
+ .dark {
284
+ --background: 222.2 84% 4.9%;
285
+ --foreground: 210 40% 98%;
286
+ /* ... 深色模式下的变量 */
287
+ }
288
+ ```
289
+
290
+ ## 💡 高级用法
291
+
292
+ ### 动态切换主题
293
+
294
+ ```typescript
295
+ // 切换到浅色主题
296
+ createShadcnTheme({
297
+ presets: { base: 'slate', theme: 'blue' }
298
+ });
299
+
300
+ // 运行时切换到深色主题(通过切换 class)
301
+ document.documentElement.classList.add('dark');
302
+
303
+ // 切换到另一个主题
304
+ createShadcnTheme({
305
+ presets: { base: 'zinc', theme: 'purple' }
306
+ });
307
+ ```
308
+
309
+ ### 在 Tailwind CSS 中使用
310
+
311
+ ```javascript
312
+ // tailwind.config.js
313
+ module.exports = {
314
+ darkMode: ['class'],
315
+ theme: {
316
+ extend: {
317
+ colors: {
318
+ background: 'hsl(var(--background))',
319
+ foreground: 'hsl(var(--foreground))',
320
+ primary: {
321
+ DEFAULT: 'hsl(var(--primary))',
322
+ foreground: 'hsl(var(--primary-foreground))',
323
+ 50: 'hsl(var(--primary-50))',
324
+ 100: 'hsl(var(--primary-100))',
325
+ // ... 更多色阶
326
+ },
327
+ // ... 其他颜色
328
+ },
329
+ borderRadius: {
330
+ lg: 'var(--radius)',
331
+ md: 'calc(var(--radius) - 2px)',
332
+ sm: 'calc(var(--radius) - 4px)',
333
+ }
334
+ }
335
+ }
336
+ }
337
+ ```
338
+
339
+ ### 在 CSS 中使用
340
+
341
+ ```css
342
+ .my-component {
343
+ background-color: hsl(var(--primary));
344
+ color: hsl(var(--primary-foreground));
345
+ border-radius: var(--radius);
346
+ border: 1px solid hsl(var(--border));
347
+ }
348
+
349
+ .my-card {
350
+ background-color: hsl(var(--card));
351
+ color: hsl(var(--card-foreground));
352
+ }
353
+ ```
354
+
355
+ ## 🔗 相关项目
356
+
357
+ - [shadcn/ui](https://ui.shadcn.com/) - 基于 Radix UI 和 Tailwind CSS 的组件库
358
+ - [@soybeanjs/colord](https://github.com/soybeanjs/colord) - 颜色处理工具库
359
+
360
+ ## 📄 许可证
361
+
362
+ MIT License
363
+
364
+ ## 🤝 贡献
365
+
366
+ 欢迎提交 Issue 和 Pull Request!
367
+
368
+ ## 👨‍💻 作者
369
+
370
+ Created by [Soybean](https://github.com/soybeanjs)
@@ -0,0 +1,208 @@
1
+ import { TailwindPaletteKey, TailwindPaletteLevelColorKey } from "@soybeanjs/colord/palette";
2
+
3
+ //#region src/types.d.ts
4
+
5
+ /**
6
+ * the base palette key
7
+ */
8
+ type BasePaletteKey = Extract<TailwindPaletteKey, 'stone' | 'zinc' | 'neutral' | 'gray' | 'slate'>;
9
+ type FeedbackPaletteKey = 'classic' | 'vivid' | 'subtle' | 'warm' | 'cool' | 'nature' | 'modern' | 'vibrant' | 'professional' | 'soft' | 'bold' | 'calm' | 'candy' | 'deep' | 'light';
10
+ type ThemePaletteKey = TailwindPaletteKey;
11
+ /**
12
+ * the preset config
13
+ */
14
+ interface PresetConfig {
15
+ /**
16
+ * the base palette key
17
+ *
18
+ * @default 'stone'
19
+ */
20
+ base?: BasePaletteKey;
21
+ /** the theme palette key
22
+ *
23
+ * @default 'indigo'
24
+ */
25
+ theme?: ThemePaletteKey;
26
+ /**
27
+ * the feedback palette key
28
+ *
29
+ * @default 'classic'
30
+ */
31
+ feedback?: FeedbackPaletteKey;
32
+ }
33
+ interface ThemeRadius {
34
+ /**
35
+ * the border radius
36
+ *
37
+ * @default '0.625rem'
38
+ */
39
+ radius?: string;
40
+ }
41
+ type HSLColor = `hsl(${number} ${number}% ${number}%)` | `hsl(${number} ${number}% ${number}% / ${number})`;
42
+ type OKLCHColor = `oklch(${number}% ${number} ${number})` | `oklch(${number}% ${number} ${number} / ${number})`;
43
+ type ColorValue = HSLColor | OKLCHColor | TailwindPaletteLevelColorKey;
44
+ interface BaseThemeColors {
45
+ background?: ColorValue;
46
+ foreground?: ColorValue;
47
+ card?: ColorValue;
48
+ cardForeground?: ColorValue;
49
+ popover?: ColorValue;
50
+ popoverForeground?: ColorValue;
51
+ primary?: ColorValue;
52
+ primaryForeground?: ColorValue;
53
+ secondary?: ColorValue;
54
+ secondaryForeground?: ColorValue;
55
+ muted?: ColorValue;
56
+ mutedForeground?: ColorValue;
57
+ accent?: ColorValue;
58
+ accentForeground?: ColorValue;
59
+ destructive?: ColorValue;
60
+ destructiveForeground?: ColorValue;
61
+ border?: ColorValue;
62
+ input?: ColorValue;
63
+ ring?: ColorValue;
64
+ }
65
+ /**
66
+ * sidebar theme colors
67
+ */
68
+ interface SidebarThemeColors {
69
+ /**
70
+ * the sidebar background color
71
+ *
72
+ * if not set, will use the theme background color
73
+ */
74
+ sidebar?: ColorValue;
75
+ /**
76
+ * the sidebar foreground color
77
+ *
78
+ * if not set, will use the theme foreground color
79
+ */
80
+ sidebarForeground?: ColorValue;
81
+ /**
82
+ * the sidebar primary color
83
+ *
84
+ * if not set, will use the theme primary color
85
+ */
86
+ sidebarPrimary?: ColorValue;
87
+ /**
88
+ * the sidebar primary foreground color
89
+ *
90
+ * if not set, will use the theme primary foreground color
91
+ */
92
+ sidebarPrimaryForeground?: ColorValue;
93
+ /**
94
+ * the sidebar accent color
95
+ *
96
+ * if not set, will use the theme accent color
97
+ */
98
+ sidebarAccent?: ColorValue;
99
+ /**
100
+ * the sidebar accent foreground color
101
+ *
102
+ * if not set, will use the theme accent foreground color
103
+ */
104
+ sidebarAccentForeground?: ColorValue;
105
+ /**
106
+ * the sidebar border color
107
+ *
108
+ * if not set, will use the theme border color
109
+ */
110
+ sidebarBorder?: ColorValue;
111
+ /**
112
+ * the sidebar ring color
113
+ *
114
+ * if not set, will use the theme ring color
115
+ */
116
+ sidebarRing?: ColorValue;
117
+ }
118
+ /**
119
+ * chart theme colors
120
+ */
121
+ interface ChartThemeColors {
122
+ chart1?: ColorValue;
123
+ chart2?: ColorValue;
124
+ chart3?: ColorValue;
125
+ chart4?: ColorValue;
126
+ chart5?: ColorValue;
127
+ }
128
+ interface ExtendedThemeColors {
129
+ success?: ColorValue;
130
+ successForeground?: ColorValue;
131
+ warning?: ColorValue;
132
+ warningForeground?: ColorValue;
133
+ info?: ColorValue;
134
+ infoForeground?: ColorValue;
135
+ carbon?: ColorValue;
136
+ carbonForeground?: ColorValue;
137
+ }
138
+ interface ThemeColors extends BaseThemeColors, ExtendedThemeColors, SidebarThemeColors, ChartThemeColors {}
139
+ interface ShadcnTheme extends ThemeColors, ThemeRadius {}
140
+ type DarkSelector = 'class' | 'media';
141
+ /**
142
+ * theme options
143
+ */
144
+ interface ThemeOptions {
145
+ /**
146
+ * presets config
147
+ *
148
+ * this priority is higher than the theme and darkTheme
149
+ */
150
+ presets?: PresetConfig;
151
+ /**
152
+ * theme color config
153
+ */
154
+ theme?: {
155
+ /**
156
+ * light theme
157
+ */
158
+ light: Required<ThemeColors>;
159
+ /**
160
+ * dark theme
161
+ *
162
+ * if not set, will use the light theme to generate dark variant colors
163
+ */
164
+ dark?: Required<ThemeColors>;
165
+ };
166
+ /**
167
+ * the border radius
168
+ *
169
+ * @default 0.625rem
170
+ */
171
+ radius?: string;
172
+ /**
173
+ * the style id
174
+ *
175
+ * @default 'SHADCN_THEME_STYLES'
176
+ */
177
+ styleId?: string;
178
+ /**
179
+ * the style target
180
+ *
181
+ * @default ':root'
182
+ */
183
+ styleTarget?: 'html' | ':root';
184
+ /**
185
+ * dark mode selector
186
+ *
187
+ * - class: ".dark"
188
+ * - media: "@media (prefers-color-scheme: dark)"
189
+ * - custom: custom dark mode selector, e.g. ".custom-dark"
190
+ *
191
+ * @default 'class'
192
+ */
193
+ darkSelector?: DarkSelector | (string & {});
194
+ /**
195
+ * color output format
196
+ *
197
+ * - hsl: "h s l [/ alpha]", e.g. "0 0% 100% / 0.1"
198
+ * - oklch: "oklch(l c h [/ alpha])", e.g. "oklch(0 0 0 / 0.1)"
199
+ *
200
+ * @default 'hsl'
201
+ */
202
+ format?: 'hsl' | 'oklch';
203
+ }
204
+ //#endregion
205
+ //#region src/core.d.ts
206
+ declare function createShadcnTheme(options?: ThemeOptions): void;
207
+ //#endregion
208
+ export { type BasePaletteKey, type BaseThemeColors, type ChartThemeColors, type ColorValue, type ExtendedThemeColors, type FeedbackPaletteKey, type ShadcnTheme, type SidebarThemeColors, type ThemeColors, type ThemeOptions, type ThemePaletteKey, type ThemeRadius, createShadcnTheme };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{colord as e}from"@soybeanjs/colord";import{generatePalette as t,tailwindPalette as n,tailwindPaletteHsl as r}from"@soybeanjs/colord/palette";const i={radius:`--radius`,background:`--background`,foreground:`--foreground`,card:`--card`,cardForeground:`--card-foreground`,popover:`--popover`,popoverForeground:`--popover-foreground`,primary:`--primary`,primaryForeground:`--primary-foreground`,secondary:`--secondary`,secondaryForeground:`--secondary-foreground`,muted:`--muted`,mutedForeground:`--muted-foreground`,accent:`--accent`,accentForeground:`--accent-foreground`,destructive:`--destructive`,destructiveForeground:`--destructive-foreground`,border:`--border`,input:`--input`,ring:`--ring`,success:`--success`,successForeground:`--success-foreground`,warning:`--warning`,warningForeground:`--warning-foreground`,info:`--info`,infoForeground:`--info-foreground`,carbon:`--carbon`,carbonForeground:`--carbon-foreground`,sidebar:`--sidebar`,sidebarForeground:`--sidebar-foreground`,sidebarPrimary:`--sidebar-primary`,sidebarPrimaryForeground:`--sidebar-primary-foreground`,sidebarAccent:`--sidebar-accent`,sidebarAccentForeground:`--sidebar-accent-foreground`,sidebarBorder:`--sidebar-border`,sidebarRing:`--sidebar-ring`,chart1:`--chart-1`,chart2:`--chart-2`,chart3:`--chart-3`,chart4:`--chart-4`,chart5:`--chart-5`},a={class:`.dark`,media:`@media (prefers-color-scheme: dark)`},o={stone:{light:{background:`oklch(100% 0 0)`,foreground:`stone.950`,card:`oklch(100% 0 0)`,cardForeground:`stone.950`,popover:`oklch(100% 0 0)`,popoverForeground:`stone.950`,primaryForeground:`stone.50`,secondary:`stone.100`,secondaryForeground:`stone.900`,muted:`stone.100`,mutedForeground:`stone.500`,accent:`stone.100`,accentForeground:`stone.900`,destructiveForeground:`stone.50`,successForeground:`stone.50`,warningForeground:`stone.50`,infoForeground:`stone.50`,carbon:`stone.800`,carbonForeground:`stone.50`,border:`stone.200`,input:`stone.200`},dark:{background:`stone.950`,foreground:`stone.50`,card:`stone.900`,cardForeground:`stone.50`,popover:`stone.900`,popoverForeground:`stone.50`,primaryForeground:`stone.900`,secondary:`stone.800`,secondaryForeground:`stone.50`,muted:`stone.800`,mutedForeground:`stone.400`,accent:`stone.800`,accentForeground:`stone.50`,destructiveForeground:`stone.900`,successForeground:`stone.900`,warningForeground:`stone.900`,infoForeground:`stone.900`,carbon:`stone.100`,carbonForeground:`stone.900`,border:`oklch(100% 0 0 / 0.1)`,input:`oklch(100% 0 0 / 0.15)`}},zinc:{light:{background:`zinc.50`,foreground:`zinc.950`,card:`zinc.50`,cardForeground:`zinc.950`,popover:`zinc.50`,popoverForeground:`zinc.950`,primaryForeground:`zinc.50`,secondary:`zinc.100`,secondaryForeground:`zinc.900`,muted:`zinc.100`,mutedForeground:`zinc.500`,accent:`zinc.100`,accentForeground:`zinc.900`,destructiveForeground:`zinc.50`,successForeground:`zinc.50`,warningForeground:`zinc.50`,infoForeground:`zinc.50`,carbon:`zinc.800`,carbonForeground:`zinc.50`,border:`zinc.200`,input:`zinc.200`},dark:{background:`zinc.950`,foreground:`zinc.50`,card:`zinc.900`,cardForeground:`zinc.50`,popover:`zinc.900`,popoverForeground:`zinc.50`,primaryForeground:`zinc.900`,secondary:`zinc.800`,secondaryForeground:`zinc.50`,muted:`zinc.800`,mutedForeground:`zinc.400`,accent:`zinc.800`,accentForeground:`zinc.50`,destructiveForeground:`zinc.900`,successForeground:`zinc.900`,warningForeground:`zinc.900`,infoForeground:`zinc.900`,carbon:`zinc.100`,carbonForeground:`zinc.900`,border:`oklch(100% 0 0 / 0.1)`,input:`oklch(100% 0 0 / 0.15)`}},neutral:{light:{background:`neutral.50`,foreground:`neutral.950`,card:`neutral.50`,cardForeground:`neutral.950`,popover:`neutral.50`,popoverForeground:`neutral.950`,primaryForeground:`neutral.50`,secondary:`neutral.100`,secondaryForeground:`neutral.900`,muted:`neutral.100`,mutedForeground:`neutral.500`,accent:`neutral.100`,accentForeground:`neutral.900`,destructiveForeground:`neutral.50`,successForeground:`neutral.50`,warningForeground:`neutral.50`,infoForeground:`neutral.50`,carbon:`neutral.800`,carbonForeground:`neutral.50`,border:`neutral.200`,input:`neutral.200`},dark:{background:`neutral.950`,foreground:`neutral.50`,card:`neutral.900`,cardForeground:`neutral.50`,popover:`neutral.900`,popoverForeground:`neutral.50`,primaryForeground:`neutral.900`,secondary:`neutral.800`,secondaryForeground:`neutral.50`,muted:`neutral.800`,mutedForeground:`neutral.400`,accent:`neutral.800`,accentForeground:`neutral.50`,destructiveForeground:`neutral.900`,successForeground:`neutral.900`,warningForeground:`neutral.900`,infoForeground:`neutral.900`,carbon:`neutral.100`,carbonForeground:`neutral.900`,border:`oklch(100% 0 0 / 0.1)`,input:`oklch(100% 0 0 / 0.15)`}},gray:{light:{background:`oklch(100% 0 0)`,foreground:`gray.950`,card:`oklch(100% 0 0)`,cardForeground:`gray.950`,popover:`oklch(100% 0 0)`,popoverForeground:`gray.950`,primaryForeground:`gray.50`,secondary:`gray.100`,secondaryForeground:`gray.900`,muted:`gray.100`,mutedForeground:`gray.500`,accent:`gray.100`,accentForeground:`gray.900`,carbon:`gray.800`,destructiveForeground:`gray.50`,successForeground:`gray.50`,warningForeground:`gray.50`,infoForeground:`gray.50`,carbonForeground:`gray.50`,border:`gray.200`,input:`gray.200`},dark:{background:`gray.950`,foreground:`gray.50`,card:`gray.900`,cardForeground:`gray.50`,popover:`gray.900`,popoverForeground:`gray.50`,primaryForeground:`gray.900`,secondary:`gray.800`,secondaryForeground:`gray.50`,muted:`gray.800`,mutedForeground:`gray.400`,accent:`gray.800`,accentForeground:`gray.50`,carbon:`gray.100`,destructiveForeground:`gray.900`,successForeground:`gray.900`,warningForeground:`gray.900`,infoForeground:`gray.900`,carbonForeground:`gray.900`,border:`oklch(100% 0 0 / 0.1)`,input:`oklch(100% 0 0 / 0.15)`}},slate:{light:{background:`oklch(100% 0 0)`,foreground:`slate.950`,card:`oklch(100% 0 0)`,cardForeground:`slate.950`,popover:`oklch(100% 0 0)`,popoverForeground:`slate.950`,primaryForeground:`slate.50`,secondary:`slate.100`,secondaryForeground:`slate.900`,muted:`slate.100`,mutedForeground:`slate.500`,accent:`slate.100`,accentForeground:`slate.900`,destructiveForeground:`slate.50`,successForeground:`slate.50`,warningForeground:`slate.50`,infoForeground:`slate.50`,carbon:`slate.800`,carbonForeground:`slate.50`,border:`slate.200`,input:`slate.200`},dark:{background:`slate.950`,foreground:`slate.50`,card:`slate.900`,cardForeground:`slate.50`,popover:`slate.900`,popoverForeground:`slate.50`,primaryForeground:`slate.900`,secondary:`slate.800`,secondaryForeground:`slate.50`,muted:`slate.800`,mutedForeground:`slate.400`,accent:`slate.800`,accentForeground:`slate.50`,carbon:`slate.100`,destructiveForeground:`slate.900`,successForeground:`slate.900`,warningForeground:`slate.900`,infoForeground:`slate.900`,carbonForeground:`slate.900`,border:`oklch(100% 0 0 / 0.1)`,input:`oklch(100% 0 0 / 0.15)`}}},s={stone:{light:{primary:`stone.900`,ring:`stone.400`,chart1:`orange.600`,chart2:`teal.600`,chart3:`cyan.900`,chart4:`amber.400`,chart5:`amber.500`},dark:{primary:`stone.200`,ring:`stone.500`,chart1:`blue.700`,chart2:`emerald.500`,chart3:`amber.500`,chart4:`purple.500`,chart5:`rose.500`}},zinc:{light:{primary:`zinc.900`,ring:`stone.400`,chart1:`orange.600`,chart2:`teal.600`,chart3:`cyan.900`,chart4:`amber.400`,chart5:`amber.500`},dark:{primary:`zinc.200`,ring:`zinc.500`,chart1:`blue.700`,chart2:`emerald.500`,chart3:`amber.500`,chart4:`purple.500`,chart5:`rose.500`}},neutral:{light:{primary:`neutral.900`,ring:`stone.400`,chart1:`orange.600`,chart2:`teal.600`,chart3:`cyan.900`,chart4:`amber.400`,chart5:`amber.500`},dark:{primary:`neutral.200`,ring:`neutral.500`,chart1:`blue.700`,chart2:`emerald.500`,chart3:`amber.500`,chart4:`purple.500`,chart5:`rose.500`}},gray:{light:{primary:`gray.900`,ring:`stone.400`,chart1:`orange.600`,chart2:`teal.600`,chart3:`cyan.900`,chart4:`amber.400`,chart5:`amber.500`},dark:{primary:`gray.200`,ring:`gray.500`,chart1:`blue.700`,chart2:`emerald.500`,chart3:`amber.500`,chart4:`purple.500`,chart5:`rose.500`}},slate:{light:{primary:`slate.900`,ring:`stone.400`,chart1:`orange.600`,chart2:`teal.600`,chart3:`cyan.900`,chart4:`amber.400`,chart5:`amber.500`},dark:{primary:`slate.200`,ring:`slate.500`,chart1:`blue.700`,chart2:`emerald.500`,chart3:`amber.500`,chart4:`purple.500`,chart5:`rose.500`}},red:{light:{primary:`red.600`,ring:`red.400`,chart1:`red.300`,chart2:`red.500`,chart3:`red.600`,chart4:`red.700`,chart5:`red.800`},dark:{primary:`red.500`,ring:`red.900`,chart1:`red.300`,chart2:`red.500`,chart3:`red.600`,chart4:`red.700`,chart5:`red.800`}},orange:{light:{primary:`orange.600`,ring:`orange.400`,chart1:`orange.300`,chart2:`orange.500`,chart3:`orange.600`,chart4:`orange.700`,chart5:`orange.800`},dark:{primary:`orange.500`,ring:`orange.900`,chart1:`orange.300`,chart2:`orange.500`,chart3:`orange.600`,chart4:`orange.700`,chart5:`orange.800`}},amber:{light:{primary:`amber.400`,ring:`amber.400`,chart1:`amber.300`,chart2:`amber.500`,chart3:`amber.600`,chart4:`amber.700`,chart5:`amber.800`},dark:{primary:`amber.500`,ring:`amber.900`,chart1:`amber.300`,chart2:`amber.500`,chart3:`amber.600`,chart4:`amber.700`,chart5:`amber.800`}},yellow:{light:{primary:`amber.400`,ring:`amber.400`,chart1:`amber.300`,chart2:`amber.500`,chart3:`amber.600`,chart4:`amber.700`,chart5:`amber.800`},dark:{primary:`amber.500`,ring:`amber.900`,chart1:`amber.300`,chart2:`amber.500`,chart3:`amber.600`,chart4:`amber.700`,chart5:`amber.800`}},lime:{light:{primary:`lime.600`,ring:`lime.400`,chart1:`lime.300`,chart2:`lime.500`,chart3:`lime.600`,chart4:`lime.700`,chart5:`lime.800`},dark:{primary:`lime.600`,ring:`lime.900`,chart1:`lime.300`,chart2:`lime.500`,chart3:`lime.600`,chart4:`lime.700`,chart5:`lime.800`}},green:{light:{primary:`green.600`,ring:`green.400`,chart1:`green.300`,chart2:`green.500`,chart3:`green.600`,chart4:`green.700`,chart5:`green.800`},dark:{primary:`green.600`,ring:`green.900`,chart1:`green.300`,chart2:`green.500`,chart3:`green.600`,chart4:`green.700`,chart5:`green.800`}},emerald:{light:{primary:`emerald.500`,ring:`emerald.400`,chart1:`emerald.300`,chart2:`emerald.500`,chart3:`emerald.600`,chart4:`emerald.700`,chart5:`emerald.800`},dark:{primary:`emerald.600`,ring:`emerald.900`,chart1:`emerald.300`,chart2:`emerald.500`,chart3:`emerald.600`,chart4:`emerald.700`,chart5:`emerald.800`}},teal:{light:{primary:`teal.400`,ring:`teal.400`,chart1:`teal.300`,chart2:`teal.500`,chart3:`teal.600`,chart4:`teal.700`,chart5:`teal.800`},dark:{primary:`teal.600`,ring:`teal.900`,chart1:`teal.300`,chart2:`teal.500`,chart3:`teal.600`,chart4:`teal.700`,chart5:`teal.800`}},cyan:{light:{primary:`cyan.600`,ring:`cyan.400`,chart1:`cyan.300`,chart2:`cyan.500`,chart3:`cyan.600`,chart4:`cyan.700`,chart5:`cyan.800`},dark:{primary:`cyan.500`,ring:`cyan.900`,chart1:`cyan.300`,chart2:`cyan.500`,chart3:`cyan.600`,chart4:`cyan.700`,chart5:`cyan.800`}},sky:{light:{primary:`sky.600`,ring:`sky.400`,chart1:`sky.300`,chart2:`sky.500`,chart3:`sky.600`,chart4:`sky.700`,chart5:`sky.800`},dark:{primary:`sky.500`,ring:`sky.900`,chart1:`sky.300`,chart2:`sky.500`,chart3:`sky.600`,chart4:`sky.700`,chart5:`sky.800`}},blue:{light:{primary:`blue.600`,ring:`blue.400`,chart1:`blue.300`,chart2:`blue.500`,chart3:`blue.600`,chart4:`blue.700`,chart5:`blue.800`},dark:{primary:`blue.500`,ring:`blue.900`,chart1:`blue.300`,chart2:`blue.500`,chart3:`blue.600`,chart4:`blue.700`,chart5:`blue.800`}},indigo:{light:{primary:`indigo.600`,ring:`indigo.400`,chart1:`indigo.300`,chart2:`indigo.500`,chart3:`indigo.600`,chart4:`indigo.700`,chart5:`indigo.800`},dark:{primary:`indigo.500`,ring:`indigo.900`,chart1:`indigo.300`,chart2:`indigo.500`,chart3:`indigo.600`,chart4:`indigo.700`,chart5:`indigo.800`}},violet:{light:{primary:`violet.600`,ring:`violet.400`,chart1:`violet.300`,chart2:`violet.500`,chart3:`violet.600`,chart4:`violet.700`,chart5:`violet.800`},dark:{primary:`violet.500`,ring:`violet.900`,chart1:`violet.300`,chart2:`violet.500`,chart3:`violet.600`,chart4:`violet.700`,chart5:`violet.800`}},purple:{light:{primary:`purple.600`,ring:`purple.400`,chart1:`purple.300`,chart2:`purple.500`,chart3:`purple.600`,chart4:`purple.700`,chart5:`purple.800`},dark:{primary:`purple.500`,ring:`purple.900`,chart1:`purple.300`,chart2:`purple.500`,chart3:`purple.600`,chart4:`purple.700`,chart5:`purple.800`}},fuchsia:{light:{primary:`fuchsia.600`,ring:`fuchsia.400`,chart1:`fuchsia.300`,chart2:`fuchsia.500`,chart3:`fuchsia.600`,chart4:`fuchsia.700`,chart5:`fuchsia.800`},dark:{primary:`fuchsia.500`,ring:`fuchsia.900`,chart1:`fuchsia.300`,chart2:`fuchsia.500`,chart3:`fuchsia.600`,chart4:`fuchsia.700`,chart5:`fuchsia.800`}},pink:{light:{primary:`pink.600`,ring:`pink.400`,chart1:`pink.300`,chart2:`pink.500`,chart3:`pink.600`,chart4:`pink.700`,chart5:`pink.800`},dark:{primary:`pink.500`,ring:`pink.900`,chart1:`pink.300`,chart2:`pink.500`,chart3:`pink.600`,chart4:`pink.700`,chart5:`pink.800`}},rose:{light:{primary:`rose.600`,ring:`rose.400`,chart1:`rose.300`,chart2:`rose.500`,chart3:`rose.600`,chart4:`rose.700`,chart5:`rose.800`},dark:{primary:`rose.500`,ring:`rose.900`,chart1:`rose.300`,chart2:`rose.500`,chart3:`rose.600`,chart4:`rose.700`,chart5:`rose.800`}}},c={classic:{light:{destructive:`red.600`,success:`green.600`,warning:`yellow.600`,info:`blue.600`},dark:{destructive:`red.400`,success:`green.400`,warning:`yellow.400`,info:`blue.400`}},vivid:{light:{destructive:`red.500`,success:`emerald.500`,warning:`amber.500`,info:`sky.500`},dark:{destructive:`red.400`,success:`emerald.400`,warning:`amber.400`,info:`sky.400`}},subtle:{light:{destructive:`rose.500`,success:`emerald.600`,warning:`amber.600`,info:`indigo.600`},dark:{destructive:`rose.300`,success:`emerald.300`,warning:`amber.300`,info:`indigo.300`}},warm:{light:{destructive:`red.600`,success:`lime.600`,warning:`orange.600`,info:`amber.700`},dark:{destructive:`red.400`,success:`lime.400`,warning:`orange.400`,info:`amber.400`}},cool:{light:{destructive:`rose.600`,success:`teal.600`,warning:`cyan.700`,info:`blue.600`},dark:{destructive:`rose.400`,success:`teal.400`,warning:`cyan.400`,info:`blue.400`}},nature:{light:{destructive:`red.700`,success:`green.700`,warning:`lime.700`,info:`teal.700`},dark:{destructive:`red.300`,success:`green.300`,warning:`lime.300`,info:`teal.300`}},modern:{light:{destructive:`rose.600`,success:`emerald.600`,warning:`orange.600`,info:`sky.600`},dark:{destructive:`rose.400`,success:`emerald.400`,warning:`orange.400`,info:`sky.400`}},vibrant:{light:{destructive:`pink.600`,success:`lime.600`,warning:`amber.600`,info:`cyan.600`},dark:{destructive:`pink.400`,success:`lime.400`,warning:`amber.400`,info:`cyan.400`}},professional:{light:{destructive:`red.700`,success:`green.700`,warning:`amber.700`,info:`blue.700`},dark:{destructive:`red.300`,success:`green.300`,warning:`amber.300`,info:`blue.300`}},soft:{light:{destructive:`rose.500`,success:`teal.500`,warning:`yellow.500`,info:`violet.500`},dark:{destructive:`rose.300`,success:`teal.300`,warning:`yellow.300`,info:`violet.300`}},bold:{light:{destructive:`red.700`,success:`emerald.700`,warning:`orange.700`,info:`blue.700`},dark:{destructive:`red.300`,success:`emerald.300`,warning:`orange.300`,info:`blue.300`}},calm:{light:{destructive:`rose.600`,success:`teal.600`,warning:`amber.600`,info:`slate.600`},dark:{destructive:`rose.400`,success:`teal.400`,warning:`amber.400`,info:`slate.400`}},candy:{light:{destructive:`pink.500`,success:`emerald.500`,warning:`yellow.500`,info:`sky.500`},dark:{destructive:`pink.300`,success:`emerald.300`,warning:`yellow.300`,info:`sky.300`}},deep:{light:{destructive:`red.800`,success:`green.800`,warning:`amber.800`,info:`blue.800`},dark:{destructive:`red.200`,success:`green.200`,warning:`amber.200`,info:`blue.200`}},light:{light:{destructive:`red.500`,success:`green.500`,warning:`yellow.500`,info:`blue.500`},dark:{destructive:`red.400`,success:`green.400`,warning:`yellow.400`,info:`blue.400`}}};function l(e){return Object.keys(e)}function u(e,t){let n=document.getElementById(t);n&&n.remove();let r=document.createElement(`style`);r.id=t,r.textContent=e,document.head.appendChild(r)}function d(e){return!e.startsWith(`hsl(`)&&!e.startsWith(`oklch(`)}function f(e){return e.replace(/hsl\(/g,``).replace(/\)/g,``)}function p(e){let{presets:t,theme:n,radius:r=`0.625rem`,styleId:i=`SHADCN_THEME_STYLES`,styleTarget:a=`:root`,darkSelector:o=`class`,format:s=`hsl`}=e||{};u(y(t||!n?m(t):g(n),{radius:r,styleTarget:a,darkSelector:o,format:s}),i)}function m(e){let{base:t=`slate`,theme:n=`indigo`,feedback:r=`classic`}=e||{},i=o[t],a=s[n],l=c[r],u={light:{...i.light,...a.light,...l.light},dark:{...i.dark,...a.dark,...l.dark}},d=h(u);return{light:{...u.light,...d.light},dark:{...u.dark,...d.dark}}}function h(e){let{light:t,dark:n}=e;return{light:{sidebar:t.background,sidebarForeground:t.foreground,sidebarPrimary:t.primary,sidebarPrimaryForeground:t.primaryForeground,sidebarAccent:t.accent,sidebarAccentForeground:t.accentForeground,sidebarBorder:t.border,sidebarRing:t.ring},dark:{sidebar:n.background,sidebarForeground:n.foreground,sidebarPrimary:n.primary,sidebarPrimaryForeground:n.primaryForeground,sidebarAccent:n.accent,sidebarAccentForeground:n.accentForeground,sidebarBorder:n.border,sidebarRing:n.ring}}}function g(e){let{light:t,dark:n}=e,r={light:t,dark:n};if(!n){let e={};l(t).forEach(n=>{e[n]=_(t[n])}),r.dark=e}return r}function _(t){let n=e(v(t,`oklch`));return n=n.isLight()?n.darken(.3):n.lighten(.1),n.toOklchString()}function v(t,i,a=!1){let o=t;if(!d(t))i===`hsl`&&t.startsWith(`oklch(`)&&(o=e(t).toHslString()),i===`oklch`&&t.startsWith(`hsl(`)&&(o=e(t).toOklchString());else{let[e,a]=t.split(`.`);o=i===`hsl`?r[e][a]:n[e][a]}return a&&(o=f(o)),o}function y(e,t){let{light:n,dark:r}=e,{radius:o,format:s,styleTarget:c}=t,u=``,d=``;l(i).forEach(e=>{if(e===`radius`)u+=`${i[e]}: ${o};\n`;else{let t=v(n[e],s,!0),a=v(r[e],s,!0);u+=`${i[e]}: ${t};\n`,d+=`${i[e]}: ${a};\n`}});let f=b(n,s),p=`${c} {\n${u}\n${f}\n}`,m=t.darkSelector;return(m===`class`||m===`media`)&&(m=a[m]),p+=`\n\n${m} {\n${d}\n}`,p}function b(e,n){let r=[`primary`,`destructive`,`success`,`warning`,`info`,`carbon`],i=``;return r.forEach(r=>{let a=t(v(e[r],n),n===`hsl`?`hslString`:`oklchString`);l(a).forEach(e=>{let t=a[e];n===`hsl`&&(t=f(t)),i+=`${r}-${e}: ${t};\n`})}),i}export{p as createShadcnTheme};
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@soybeanjs/shadcn-theme",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "description": "A powerful and flexible shadcn/ui theme generator with support for dynamic CSS variable injection, preset color schemes, and light/dark mode switching.",
6
+ "author": {
7
+ "name": "Soybean",
8
+ "email": "soybeanjs@outlook.com",
9
+ "url": "https://github.com/soybeanjs"
10
+ },
11
+ "license": "MIT",
12
+ "homepage": "https://github.com/soybeanjs/shadcn-theme",
13
+ "repository": {
14
+ "url": "https://github.com/soybeanjs/shadcn-theme.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/soybeanjs/shadcn-theme/issues"
18
+ },
19
+ "publishConfig": {
20
+ "registry": "https://registry.npmjs.org/"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.mjs",
26
+ "require": "./dist/index.cjs"
27
+ }
28
+ },
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.mjs",
31
+ "types": "./dist/index.d.ts",
32
+ "files": [
33
+ "dist"
34
+ ],
35
+ "dependencies": {
36
+ "@soybeanjs/colord": "^0.3.4"
37
+ },
38
+ "devDependencies": {
39
+ "@soybeanjs/cli": "1.4.4",
40
+ "@soybeanjs/eslint-config": "1.7.5",
41
+ "@types/node": "25.0.9",
42
+ "eslint": "9.39.2",
43
+ "lint-staged": "16.2.7",
44
+ "simple-git-hooks": "2.13.1",
45
+ "tsdown": "0.19.0",
46
+ "tsx": "4.21.0",
47
+ "typescript": "5.9.3"
48
+ },
49
+ "simple-git-hooks": {
50
+ "commit-msg": "pnpm soy git-commit-verify",
51
+ "pre-commit": "pnpm typecheck && pnpm lint-staged"
52
+ },
53
+ "lint-staged": {
54
+ "*": "eslint --fix"
55
+ },
56
+ "scripts": {
57
+ "build": "tsdown",
58
+ "cleanup": "soy cleanup",
59
+ "commit": "soy git-commit",
60
+ "dev": "tsup --watch",
61
+ "lint": "eslint . --fix",
62
+ "publish-pkg": "pnpm publish --access public",
63
+ "release": "soy release",
64
+ "typecheck": "tsc --noEmit --skipLibCheck",
65
+ "update-pkg": "soy ncu"
66
+ }
67
+ }