@openconsole/shadcn 0.0.0 → 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.
Files changed (71) hide show
  1. package/accordion.tsx +66 -66
  2. package/alert-dialog.tsx +196 -196
  3. package/alert.tsx +66 -66
  4. package/aspect-ratio.tsx +11 -11
  5. package/avatar.tsx +53 -53
  6. package/badge.tsx +46 -46
  7. package/breadcrumb.tsx +109 -109
  8. package/button-group.tsx +83 -83
  9. package/button.tsx +60 -60
  10. package/calendar.tsx +219 -219
  11. package/card.tsx +92 -92
  12. package/carousel.tsx +241 -241
  13. package/chart.tsx +374 -374
  14. package/checkbox.tsx +32 -32
  15. package/collapsible.tsx +33 -33
  16. package/command.tsx +184 -184
  17. package/context-menu.tsx +252 -252
  18. package/dialog.tsx +143 -143
  19. package/direction.tsx +22 -22
  20. package/drawer.tsx +135 -135
  21. package/dropdown-menu.tsx +257 -257
  22. package/empty.tsx +104 -104
  23. package/field.tsx +248 -248
  24. package/form.tsx +167 -167
  25. package/hooks/index.ts +1 -1
  26. package/hooks/use-mobile.ts +19 -19
  27. package/hover-card.tsx +44 -44
  28. package/icon.tsx +21 -21
  29. package/index.ts +59 -59
  30. package/input-group.tsx +170 -170
  31. package/input-otp.tsx +77 -77
  32. package/input.tsx +21 -21
  33. package/item.tsx +193 -193
  34. package/kbd.tsx +28 -28
  35. package/label.tsx +24 -24
  36. package/lib/index.ts +1 -1
  37. package/lib/utils.ts +6 -6
  38. package/menubar.tsx +276 -276
  39. package/native-select.tsx +62 -62
  40. package/navigation-menu.tsx +168 -168
  41. package/package.json +10 -2
  42. package/pagination.tsx +127 -127
  43. package/popover.tsx +89 -89
  44. package/progress.tsx +31 -31
  45. package/radio-group.tsx +45 -45
  46. package/resizable.tsx +53 -53
  47. package/scroll-area.tsx +58 -58
  48. package/select.tsx +187 -187
  49. package/separator.tsx +28 -28
  50. package/sheet.tsx +139 -139
  51. package/sidebar.tsx +724 -724
  52. package/skeleton.tsx +13 -13
  53. package/skill/SKILL.md +620 -599
  54. package/skill/customization.md +301 -263
  55. package/skill/rules/base-vs-radix.md +167 -167
  56. package/skill/rules/composition.md +240 -240
  57. package/skill/rules/forms.md +271 -271
  58. package/skill/rules/icons.md +136 -136
  59. package/skill/rules/styling.md +180 -180
  60. package/slider.tsx +63 -63
  61. package/sonner.tsx +40 -40
  62. package/spinner.tsx +16 -16
  63. package/styles.css +122 -0
  64. package/switch.tsx +35 -35
  65. package/table.tsx +116 -116
  66. package/tabs.tsx +66 -66
  67. package/textarea.tsx +18 -18
  68. package/toggle-group.tsx +83 -83
  69. package/toggle.tsx +47 -47
  70. package/tooltip.tsx +61 -61
  71. package/tsconfig.json +12 -12
@@ -1,263 +1,301 @@
1
- # 主题化 & 定制
2
-
3
- 组件引用语义化的 CSS 变量。改变量就等于改所有组件。
4
-
5
- ## 目录
6
-
7
- - 工作原理(CSS 变量 → Tailwind 工具类 → 组件)
8
- - 颜色变量与 OKLCH 格式
9
- - 暗色模式配置
10
- - 切主题(编辑全局 CSS 或粘 CSS)
11
- - 新增自定义色(Tailwind v4
12
- - 圆角 `--radius`
13
- - view-transition 变量(圆形展开切主题)
14
- - 定制组件的边界(只能从外面来)
15
-
16
- ---
17
-
18
- ## 工作原理
19
-
20
- 1. CSS 变量定义在 `:root`(亮)和 `.dark`(暗)。
21
- 2. Tailwind v4 把它们映射成工具类: `bg-primary`、`text-muted-foreground` 等。
22
- 3. 组件用这些工具类 —— **改一个变量,所有引用它的组件全跟着变**。
23
-
24
- ---
25
-
26
- ## 颜色变量
27
-
28
- 每种颜色都遵循 `name` / `name-foreground` 约定。基础变量给背景用,
29
- `-foreground` 给该背景上的文字 / 图标用。
30
-
31
- | 变量 | 用途 |
32
- |---|---|
33
- | `--background` / `--foreground` | 页面背景与默认文字 |
34
- | `--card` / `--card-foreground` | 卡片表面 |
35
- | `--primary` / `--primary-foreground` | 主按钮与主操作 |
36
- | `--secondary` / `--secondary-foreground` | 次要操作 |
37
- | `--muted` / `--muted-foreground` | 弱化 / 禁用状态 |
38
- | `--accent` / `--accent-foreground` | 悬浮与点缀 |
39
- | `--destructive` / `--destructive-foreground` | 错误与破坏性操作 |
40
- | `--border` | 默认边框 |
41
- | `--input` | 表单输入边框 |
42
- | `--ring` | 焦点环 |
43
- | `--chart-1` 到 `--chart-5` | 图表 / 可视化 |
44
- | `--sidebar-*` | 侧边栏专用色 |
45
- | `--surface` / `--surface-foreground` | 次级表面 |
46
-
47
- 颜色用 **OKLCH**: `--primary: oklch(0.205 0 0)`。三个值依次是 lightness
48
- (0–1)、chroma(0 表示灰)、hue(0–360)。
49
-
50
- ---
51
-
52
- ## 暗色模式
53
-
54
- 通过根元素上的 `.dark` 类切换。用 `next-themes` 的 `ThemeProvider`
55
- 包根:
56
-
57
- ```tsx
58
- "use client";
59
- import { ThemeProvider } from "next-themes";
60
-
61
- // app/layout.tsx 或类似根布局
62
- <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
63
- {children}
64
- </ThemeProvider>
65
- ```
66
-
67
- 主题切换按钮自己写一个,调用 `useTheme()`:
68
-
69
- ```tsx
70
- "use client";
71
- import { Moon, Sun } from "lucide-react";
72
- import { useTheme } from "next-themes";
73
- import { Button } from "@openconsole/shadcn";
74
-
75
- export function ThemeToggle() {
76
- // 用 resolvedTheme,处于 System 模式时也能正确翻转。
77
- const { resolvedTheme, setTheme } = useTheme();
78
- return (
79
- <Button
80
- variant="outline"
81
- size="icon"
82
- onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
83
- >
84
- <Sun className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
85
- <Moon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
86
- <span className="sr-only">切换主题</span>
87
- </Button>
88
- );
89
- }
90
- ```
91
-
92
- ---
93
-
94
- ## 切主题
95
-
96
- ### 直接编辑全局 CSS
97
-
98
- 如果要永久换主题色,找到 app 的全局 CSS 文件(一般是
99
- `app/globals.css`)直接改 `:root` / `.dark` 块。修改后所有组件自动
100
- 跟着变。
101
-
102
- ### CSS 主题
103
-
104
- <https://ui.shadcn.com/themes> 或 <https://tweakcn.com> 复制出来
105
- 的 `:root { … } .dark { … }` 直接粘到全局 CSS 文件覆盖现有的同名
106
- 变量即可。
107
-
108
- ---
109
-
110
- ## 新增自定义色
111
-
112
- 不要新建 CSS 文件 —— 加到 app 的全局 CSS 文件里。
113
-
114
- ```css
115
- /* 1. 在全局 CSS 文件里定义。 */
116
- :root {
117
- --warning: oklch(0.84 0.16 84);
118
- --warning-foreground: oklch(0.28 0.07 46);
119
- }
120
- .dark {
121
- --warning: oklch(0.41 0.11 46);
122
- --warning-foreground: oklch(0.99 0.02 95);
123
- }
124
-
125
- /* 2. Tailwind v4 的 @theme inline 注册成工具类。 */
126
- @theme inline {
127
- --color-warning: var(--warning);
128
- --color-warning-foreground: var(--warning-foreground);
129
- }
130
- ```
131
-
132
- ```tsx
133
- // 3. 在组件里用。
134
- <div className="bg-warning text-warning-foreground">Warning</div>
135
- ```
136
-
137
- ---
138
-
139
- ## 圆角
140
-
141
- `--radius` 全局控制圆角。组件从它派生:
142
-
143
- | 工具类 | 等价值 |
144
- |---|---|
145
- | `rounded-lg` | `var(--radius)` |
146
- | `rounded-md` | `calc(var(--radius) - 2px)` |
147
- | `rounded-sm` | `calc(var(--radius) - 4px)` |
148
-
149
- 要在运行时调圆角,直接 `document.documentElement.style.setProperty("--radius", "0.75rem")`。
150
-
151
- ---
152
-
153
- ## View-transition 变量(圆形展开切主题)
154
-
155
- 切主题时想要从点击位置圆形展开的过渡动画,用 View Transitions API +
156
- CSS 变量做。给全局 CSS 加:
157
-
158
- ```css
159
- @supports (view-transition-name: root) {
160
- ::view-transition-new(root) {
161
- clip-path: circle(0% at var(--vt-origin-x, 50%) var(--vt-origin-y, 50%));
162
- animation: vt-circle-in 350ms ease-out forwards;
163
- }
164
- @keyframes vt-circle-in {
165
- to { clip-path: circle(150% at var(--vt-origin-x, 50%) var(--vt-origin-y, 50%)); }
166
- }
167
- }
168
- ```
169
-
170
- 切换按钮里在点击时设置原点变量并启动 transition:
171
-
172
- ```tsx
173
- "use client";
174
- import { useTheme } from "next-themes";
175
- import { Button } from "@openconsole/shadcn";
176
-
177
- type TransitionDocument = Document & {
178
- startViewTransition?: (cb: () => void) => { finished: Promise<void> };
179
- };
180
-
181
- export function ThemeToggle() {
182
- const { resolvedTheme, setTheme } = useTheme();
183
-
184
- const onToggle = (e: React.MouseEvent) => {
185
- const root = document.documentElement;
186
- root.style.setProperty("--vt-origin-x", `${(e.clientX / window.innerWidth) * 100}%`);
187
- root.style.setProperty("--vt-origin-y", `${(e.clientY / window.innerHeight) * 100}%`);
188
- const next = resolvedTheme === "dark" ? "light" : "dark";
189
- const doc = document as TransitionDocument;
190
- if (doc.startViewTransition) {
191
- doc.startViewTransition(() => setTheme(next));
192
- } else {
193
- setTheme(next);
194
- }
195
- };
196
-
197
- return <Button variant="outline" size="icon" onClick={onToggle}>…</Button>;
198
- }
199
- ```
200
-
201
- > 变量名用 `--vt-origin-x` / `--vt-origin-y` 而不是 `--x` / `--y` ——
202
- > 太通用的名字容易跟其他库的临时变量冲突。
203
-
204
- ---
205
-
206
- ## 定制组件的边界
207
-
208
- `@openconsole/shadcn` 是只读消费包 —— 你**不能**改组件源码加 variant、
209
- 不能 fork 文件、不能 patch。能做的就是从外面调整:
210
-
211
- ### 1. 内置 variant(优先)
212
-
213
- ```tsx
214
- <Button variant="outline" size="sm">Click</Button>
215
- ```
216
-
217
- `Button` variant: `default` / `secondary` / `outline` / `ghost` /
218
- `destructive` / `link`。`Badge` variant: `default` / `secondary` /
219
- `destructive` / `outline`。其它原语的 variant hover 上去看 IDE 提示。
220
-
221
- ### 2. `className` 加 Tailwind 类(仅布局)
222
-
223
- ```tsx
224
- <Card className="mx-auto max-w-md">…</Card>
225
- ```
226
-
227
- **不要**用 `className` 覆盖颜色或排版 —— 改主题变量。
228
-
229
- ### 3. CSS 变量(颜色 / 圆角 / 字体)
230
-
231
- 见上面 [新增自定义色](#新增自定义色) 和 [圆角](#圆角)。
232
-
233
- ### 4. wrapper 组件(应用层抽象)
234
-
235
- 应用里要 `ConfirmDialog`、`PageHeader`、`Toolbar` 这种复合形态,在自己
236
- 的应用代码里拼:
237
-
238
- ```tsx
239
- // 在你的应用代码里
240
- import {
241
- AlertDialog, AlertDialogTrigger, AlertDialogContent,
242
- AlertDialogHeader, AlertDialogTitle, AlertDialogDescription,
243
- AlertDialogFooter, AlertDialogCancel, AlertDialogAction,
244
- } from "@openconsole/shadcn";
245
-
246
- export function ConfirmDialog({ title, description, onConfirm, children }) {
247
- return (
248
- <AlertDialog>
249
- <AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
250
- <AlertDialogContent>
251
- <AlertDialogHeader>
252
- <AlertDialogTitle>{title}</AlertDialogTitle>
253
- <AlertDialogDescription>{description}</AlertDialogDescription>
254
- </AlertDialogHeader>
255
- <AlertDialogFooter>
256
- <AlertDialogCancel>Cancel</AlertDialogCancel>
257
- <AlertDialogAction onClick={onConfirm}>Confirm</AlertDialogAction>
258
- </AlertDialogFooter>
259
- </AlertDialogContent>
260
- </AlertDialog>
261
- );
262
- }
263
- ```
1
+ # 主题化 & 定制
2
+
3
+ 组件引用语义化的 CSS 变量。改变量就等于改所有组件。
4
+
5
+ ## 目录
6
+
7
+ - 接入(一次 @import 搞定)
8
+ - 工作原理(CSS 变量 → Tailwind 工具类 → 组件)
9
+ - 颜色变量与 OKLCH 格式
10
+ - 暗色模式配置
11
+ - 切主题(覆盖默认 token 或粘 CSS
12
+ - 新增自定义色(Tailwind v4)
13
+ - 圆角 `--radius`
14
+ - view-transition 变量(圆形展开切主题)
15
+ - 定制组件的边界(只能从外面来)
16
+
17
+ ---
18
+
19
+ ## 接入
20
+
21
+ app 的全局 CSS(一般是 `app/globals.css`)里 @import 本包的 styles.css:
22
+
23
+ ```css
24
+ @import "tailwindcss";
25
+ @import "@openconsole/shadcn/styles.css";
26
+ @import "@openconsole/atoms/styles.css"; /* 用到 atoms 时 */
27
+ ```
28
+
29
+ `@openconsole/shadcn/styles.css` 自带:
30
+
31
+ - `@source` 指令注册自己的源码(Tailwind 自动扫到所有组件用到的工具类)
32
+ - 完整的 `:root` / `.dark` 主题 token 默认值
33
+ - `@theme inline` 映射(让 token 变成 `bg-primary` / `text-muted-foreground` 等工具类)
34
+ - `@custom-variant dark`
35
+ - `tw-animate-css` 动画工具
36
+ - base reset
37
+
38
+ **消费方不需要重复定义 token、不需要手写 `@source`**。要覆盖 token
39
+ `@import` 之后重新声明同名变量即可(见下面 [切主题](#切主题))。
40
+
41
+ ---
42
+
43
+ ## 工作原理
44
+
45
+ 1. CSS 变量定义在 `:root`(亮)和 `.dark`(暗)—— 默认值在
46
+ `@openconsole/shadcn/styles.css` 里。
47
+ 2. Tailwind v4 把它们映射成工具类: `bg-primary`、`text-muted-foreground` 等。
48
+ 3. 组件用这些工具类 —— **改一个变量,所有引用它的组件全跟着变**。
49
+
50
+ ---
51
+
52
+ ## 颜色变量
53
+
54
+ 每种颜色都遵循 `name` / `name-foreground` 约定。基础变量给背景用,
55
+ `-foreground` 给该背景上的文字 / 图标用。
56
+
57
+ | 变量 | 用途 |
58
+ |---|---|
59
+ | `--background` / `--foreground` | 页面背景与默认文字 |
60
+ | `--card` / `--card-foreground` | 卡片表面 |
61
+ | `--primary` / `--primary-foreground` | 主按钮与主操作 |
62
+ | `--secondary` / `--secondary-foreground` | 次要操作 |
63
+ | `--muted` / `--muted-foreground` | 弱化 / 禁用状态 |
64
+ | `--accent` / `--accent-foreground` | 悬浮与点缀 |
65
+ | `--destructive` / `--destructive-foreground` | 错误与破坏性操作 |
66
+ | `--border` | 默认边框 |
67
+ | `--input` | 表单输入边框 |
68
+ | `--ring` | 焦点环 |
69
+ | `--chart-1` 到 `--chart-5` | 图表 / 可视化 |
70
+ | `--sidebar-*` | 侧边栏专用色 |
71
+ | `--surface` / `--surface-foreground` | 次级表面 |
72
+
73
+ 颜色用 **OKLCH**: `--primary: oklch(0.205 0 0)`。三个值依次是 lightness
74
+ (0–1)、chroma(0 表示灰)、hue(0–360)。
75
+
76
+ ---
77
+
78
+ ## 暗色模式
79
+
80
+ 通过根元素上的 `.dark` 类切换。用 `next-themes` 的 `ThemeProvider`
81
+ 包根:
82
+
83
+ ```tsx
84
+ "use client";
85
+ import { ThemeProvider } from "next-themes";
86
+
87
+ // app/layout.tsx 或类似根布局
88
+ <ThemeProvider attribute="class" defaultTheme="system" enableSystem>
89
+ {children}
90
+ </ThemeProvider>
91
+ ```
92
+
93
+ 主题切换按钮自己写一个,调用 `useTheme()`:
94
+
95
+ ```tsx
96
+ "use client";
97
+ import { Moon, Sun } from "lucide-react";
98
+ import { useTheme } from "next-themes";
99
+ import { Button } from "@openconsole/shadcn";
100
+
101
+ export function ThemeToggle() {
102
+ // resolvedTheme,处于 System 模式时也能正确翻转。
103
+ const { resolvedTheme, setTheme } = useTheme();
104
+ return (
105
+ <Button
106
+ variant="outline"
107
+ size="icon"
108
+ onClick={() => setTheme(resolvedTheme === "dark" ? "light" : "dark")}
109
+ >
110
+ <Sun className="rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
111
+ <Moon className="absolute rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
112
+ <span className="sr-only">切换主题</span>
113
+ </Button>
114
+ );
115
+ }
116
+ ```
117
+
118
+ ---
119
+
120
+ ## 切主题
121
+
122
+ ### 覆盖默认 token
123
+
124
+ 在 app 的全局 CSS 里 `@import "@openconsole/shadcn/styles.css"` **之后**
125
+ 重新声明同名变量即可。只列要改的, 没列的保持 shadcn 默认值:
126
+
127
+ ```css
128
+ @import "tailwindcss";
129
+ @import "@openconsole/shadcn/styles.css";
130
+
131
+ :root {
132
+ --primary: oklch(0.6 0.18 250); /* 覆盖默认黑色为蓝 */
133
+ --radius: 0.5rem; /* 覆盖默认圆角 */
134
+ }
135
+ .dark {
136
+ --primary: oklch(0.7 0.18 250);
137
+ }
138
+ ```
139
+
140
+ ### 粘 CSS 主题
141
+
142
+ 从 <https://ui.shadcn.com/themes> 或 <https://tweakcn.com> 复制出来
143
+ `:root { } .dark { … }`, 同样粘在 `@import` 之后即可。CSS 后定义
144
+ 的规则覆盖前面的, 所以默认 token 会被整套替换。
145
+
146
+ ---
147
+
148
+ ## 新增自定义色
149
+
150
+ 加到 app 的全局 CSS 里(同样在 `@import` 之后, 不需要新建 CSS 文件)。
151
+
152
+ ```css
153
+ /* 1. 在全局 CSS 文件里定义。 */
154
+ :root {
155
+ --warning: oklch(0.84 0.16 84);
156
+ --warning-foreground: oklch(0.28 0.07 46);
157
+ }
158
+ .dark {
159
+ --warning: oklch(0.41 0.11 46);
160
+ --warning-foreground: oklch(0.99 0.02 95);
161
+ }
162
+
163
+ /* 2. 用 Tailwind v4 的 @theme inline 注册成工具类。 */
164
+ @theme inline {
165
+ --color-warning: var(--warning);
166
+ --color-warning-foreground: var(--warning-foreground);
167
+ }
168
+ ```
169
+
170
+ ```tsx
171
+ // 3. 在组件里用。
172
+ <div className="bg-warning text-warning-foreground">Warning</div>
173
+ ```
174
+
175
+ ---
176
+
177
+ ## 圆角
178
+
179
+ `--radius` 全局控制圆角。组件从它派生:
180
+
181
+ | 工具类 | 等价值 |
182
+ |---|---|
183
+ | `rounded-lg` | `var(--radius)` |
184
+ | `rounded-md` | `calc(var(--radius) - 2px)` |
185
+ | `rounded-sm` | `calc(var(--radius) - 4px)` |
186
+
187
+ 要在运行时调圆角,直接 `document.documentElement.style.setProperty("--radius", "0.75rem")`。
188
+
189
+ ---
190
+
191
+ ## View-transition 变量(圆形展开切主题)
192
+
193
+ 切主题时想要从点击位置圆形展开的过渡动画,用 View Transitions API +
194
+ CSS 变量做。给全局 CSS 加:
195
+
196
+ ```css
197
+ @supports (view-transition-name: root) {
198
+ ::view-transition-new(root) {
199
+ clip-path: circle(0% at var(--vt-origin-x, 50%) var(--vt-origin-y, 50%));
200
+ animation: vt-circle-in 350ms ease-out forwards;
201
+ }
202
+ @keyframes vt-circle-in {
203
+ to { clip-path: circle(150% at var(--vt-origin-x, 50%) var(--vt-origin-y, 50%)); }
204
+ }
205
+ }
206
+ ```
207
+
208
+ 切换按钮里在点击时设置原点变量并启动 transition:
209
+
210
+ ```tsx
211
+ "use client";
212
+ import { useTheme } from "next-themes";
213
+ import { Button } from "@openconsole/shadcn";
214
+
215
+ type TransitionDocument = Document & {
216
+ startViewTransition?: (cb: () => void) => { finished: Promise<void> };
217
+ };
218
+
219
+ export function ThemeToggle() {
220
+ const { resolvedTheme, setTheme } = useTheme();
221
+
222
+ const onToggle = (e: React.MouseEvent) => {
223
+ const root = document.documentElement;
224
+ root.style.setProperty("--vt-origin-x", `${(e.clientX / window.innerWidth) * 100}%`);
225
+ root.style.setProperty("--vt-origin-y", `${(e.clientY / window.innerHeight) * 100}%`);
226
+ const next = resolvedTheme === "dark" ? "light" : "dark";
227
+ const doc = document as TransitionDocument;
228
+ if (doc.startViewTransition) {
229
+ doc.startViewTransition(() => setTheme(next));
230
+ } else {
231
+ setTheme(next);
232
+ }
233
+ };
234
+
235
+ return <Button variant="outline" size="icon" onClick={onToggle}>…</Button>;
236
+ }
237
+ ```
238
+
239
+ > 变量名用 `--vt-origin-x` / `--vt-origin-y` 而不是 `--x` / `--y` ——
240
+ > 太通用的名字容易跟其他库的临时变量冲突。
241
+
242
+ ---
243
+
244
+ ## 定制组件的边界
245
+
246
+ `@openconsole/shadcn` 是只读消费包 —— 你**不能**改组件源码加 variant、
247
+ 不能 fork 文件、不能 patch。能做的就是从外面调整:
248
+
249
+ ### 1. 内置 variant(优先)
250
+
251
+ ```tsx
252
+ <Button variant="outline" size="sm">Click</Button>
253
+ ```
254
+
255
+ `Button` variant: `default` / `secondary` / `outline` / `ghost` /
256
+ `destructive` / `link`。`Badge` variant: `default` / `secondary` /
257
+ `destructive` / `outline`。其它原语的 variant hover 上去看 IDE 提示。
258
+
259
+ ### 2. `className` 加 Tailwind 类(仅布局)
260
+
261
+ ```tsx
262
+ <Card className="mx-auto max-w-md">…</Card>
263
+ ```
264
+
265
+ **不要**用 `className` 覆盖颜色或排版 —— 改主题变量。
266
+
267
+ ### 3. CSS 变量(颜色 / 圆角 / 字体)
268
+
269
+ 见上面 [新增自定义色](#新增自定义色) 和 [圆角](#圆角)。
270
+
271
+ ### 4. wrapper 组件(应用层抽象)
272
+
273
+ 应用里要 `ConfirmDialog`、`PageHeader`、`Toolbar` 这种复合形态,在自己
274
+ 的应用代码里拼:
275
+
276
+ ```tsx
277
+ // 在你的应用代码里
278
+ import {
279
+ AlertDialog, AlertDialogTrigger, AlertDialogContent,
280
+ AlertDialogHeader, AlertDialogTitle, AlertDialogDescription,
281
+ AlertDialogFooter, AlertDialogCancel, AlertDialogAction,
282
+ } from "@openconsole/shadcn";
283
+
284
+ export function ConfirmDialog({ title, description, onConfirm, children }) {
285
+ return (
286
+ <AlertDialog>
287
+ <AlertDialogTrigger asChild>{children}</AlertDialogTrigger>
288
+ <AlertDialogContent>
289
+ <AlertDialogHeader>
290
+ <AlertDialogTitle>{title}</AlertDialogTitle>
291
+ <AlertDialogDescription>{description}</AlertDialogDescription>
292
+ </AlertDialogHeader>
293
+ <AlertDialogFooter>
294
+ <AlertDialogCancel>Cancel</AlertDialogCancel>
295
+ <AlertDialogAction onClick={onConfirm}>Confirm</AlertDialogAction>
296
+ </AlertDialogFooter>
297
+ </AlertDialogContent>
298
+ </AlertDialog>
299
+ );
300
+ }
301
+ ```