@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.
- package/accordion.tsx +66 -66
- package/alert-dialog.tsx +196 -196
- package/alert.tsx +66 -66
- package/aspect-ratio.tsx +11 -11
- package/avatar.tsx +53 -53
- package/badge.tsx +46 -46
- package/breadcrumb.tsx +109 -109
- package/button-group.tsx +83 -83
- package/button.tsx +60 -60
- package/calendar.tsx +219 -219
- package/card.tsx +92 -92
- package/carousel.tsx +241 -241
- package/chart.tsx +374 -374
- package/checkbox.tsx +32 -32
- package/collapsible.tsx +33 -33
- package/command.tsx +184 -184
- package/context-menu.tsx +252 -252
- package/dialog.tsx +143 -143
- package/direction.tsx +22 -22
- package/drawer.tsx +135 -135
- package/dropdown-menu.tsx +257 -257
- package/empty.tsx +104 -104
- package/field.tsx +248 -248
- package/form.tsx +167 -167
- package/hooks/index.ts +1 -1
- package/hooks/use-mobile.ts +19 -19
- package/hover-card.tsx +44 -44
- package/icon.tsx +21 -21
- package/index.ts +59 -59
- package/input-group.tsx +170 -170
- package/input-otp.tsx +77 -77
- package/input.tsx +21 -21
- package/item.tsx +193 -193
- package/kbd.tsx +28 -28
- package/label.tsx +24 -24
- package/lib/index.ts +1 -1
- package/lib/utils.ts +6 -6
- package/menubar.tsx +276 -276
- package/native-select.tsx +62 -62
- package/navigation-menu.tsx +168 -168
- package/package.json +10 -2
- package/pagination.tsx +127 -127
- package/popover.tsx +89 -89
- package/progress.tsx +31 -31
- package/radio-group.tsx +45 -45
- package/resizable.tsx +53 -53
- package/scroll-area.tsx +58 -58
- package/select.tsx +187 -187
- package/separator.tsx +28 -28
- package/sheet.tsx +139 -139
- package/sidebar.tsx +724 -724
- package/skeleton.tsx +13 -13
- package/skill/SKILL.md +620 -599
- package/skill/customization.md +301 -263
- package/skill/rules/base-vs-radix.md +167 -167
- package/skill/rules/composition.md +240 -240
- package/skill/rules/forms.md +271 -271
- package/skill/rules/icons.md +136 -136
- package/skill/rules/styling.md +180 -180
- package/slider.tsx +63 -63
- package/sonner.tsx +40 -40
- package/spinner.tsx +16 -16
- package/styles.css +122 -0
- package/switch.tsx +35 -35
- package/table.tsx +116 -116
- package/tabs.tsx +66 -66
- package/textarea.tsx +18 -18
- package/toggle-group.tsx +83 -83
- package/toggle.tsx +47 -47
- package/tooltip.tsx +61 -61
- package/tsconfig.json +12 -12
package/skill/rules/icons.md
CHANGED
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
# 图标
|
|
2
|
-
|
|
3
|
-
本包的图标库**固定为 `lucide-react`**。所有图标从那里导入,或者通过
|
|
4
|
-
本包导出的 `Icon` 包装按名字动态渲染。
|
|
5
|
-
|
|
6
|
-
---
|
|
7
|
-
|
|
8
|
-
## `Button` 里的图标用 `data-icon` 属性
|
|
9
|
-
|
|
10
|
-
加 `data-icon="inline-start"`(前缀)或 `data-icon="inline-end"`(后缀)
|
|
11
|
-
到图标上。**图标上不要加尺寸 class**。
|
|
12
|
-
|
|
13
|
-
**Incorrect:**
|
|
14
|
-
|
|
15
|
-
```tsx
|
|
16
|
-
<Button>
|
|
17
|
-
<SearchIcon className="mr-2 size-4" />
|
|
18
|
-
Search
|
|
19
|
-
</Button>
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
**Correct:**
|
|
23
|
-
|
|
24
|
-
```tsx
|
|
25
|
-
<Button>
|
|
26
|
-
<SearchIcon data-icon="inline-start"/>
|
|
27
|
-
Search
|
|
28
|
-
</Button>
|
|
29
|
-
|
|
30
|
-
<Button>
|
|
31
|
-
Next
|
|
32
|
-
<ArrowRightIcon data-icon="inline-end"/>
|
|
33
|
-
</Button>
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## 组件内部图标不要加尺寸 class
|
|
39
|
-
|
|
40
|
-
`Button`、`DropdownMenuItem`、`Alert`、`Sidebar*` 等组件通过 CSS 自己管
|
|
41
|
-
图标尺寸。**不要**手动加 `size-4` / `w-4 h-4`。除非用户明确要求自定义
|
|
42
|
-
图标尺寸。
|
|
43
|
-
|
|
44
|
-
**Incorrect:**
|
|
45
|
-
|
|
46
|
-
```tsx
|
|
47
|
-
<Button>
|
|
48
|
-
<SearchIcon className="size-4" data-icon="inline-start" />
|
|
49
|
-
Search
|
|
50
|
-
</Button>
|
|
51
|
-
|
|
52
|
-
<DropdownMenuItem>
|
|
53
|
-
<SettingsIcon className="mr-2 size-4" />
|
|
54
|
-
Settings
|
|
55
|
-
</DropdownMenuItem>
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
**Correct:**
|
|
59
|
-
|
|
60
|
-
```tsx
|
|
61
|
-
<Button>
|
|
62
|
-
<SearchIcon data-icon="inline-start" />
|
|
63
|
-
Search
|
|
64
|
-
</Button>
|
|
65
|
-
|
|
66
|
-
<DropdownMenuItem>
|
|
67
|
-
<SettingsIcon />
|
|
68
|
-
Settings
|
|
69
|
-
</DropdownMenuItem>
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## 图标当组件对象传,不要当字符串 key
|
|
75
|
-
|
|
76
|
-
用 `icon={CheckIcon}`,不要用字符串 key 去查表。
|
|
77
|
-
|
|
78
|
-
**Incorrect:**
|
|
79
|
-
|
|
80
|
-
```tsx
|
|
81
|
-
const iconMap = {
|
|
82
|
-
check: CheckIcon,
|
|
83
|
-
alert: AlertIcon,
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
function StatusBadge({ icon }: { icon: string }) {
|
|
87
|
-
const Icon = iconMap[icon];
|
|
88
|
-
return <Icon />;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
<StatusBadge icon="check" />
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**Correct:**
|
|
95
|
-
|
|
96
|
-
```tsx
|
|
97
|
-
import { CheckIcon } from "lucide-react";
|
|
98
|
-
|
|
99
|
-
function StatusBadge({ icon: Icon }: { icon: React.ComponentType }) {
|
|
100
|
-
return <Icon />;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
<StatusBadge icon={CheckIcon} />
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
---
|
|
107
|
-
|
|
108
|
-
## 按名字动态渲染用 `Icon`
|
|
109
|
-
|
|
110
|
-
当图标名字来自数据(菜单配置、主题预设、CMS 内容)而不是代码里写死的
|
|
111
|
-
import 时,用本包导出的 `Icon`:
|
|
112
|
-
|
|
113
|
-
```tsx
|
|
114
|
-
import { Icon } from "@openconsole/shadcn";
|
|
115
|
-
|
|
116
|
-
// 数据
|
|
117
|
-
const menu = [
|
|
118
|
-
{ label: "Dashboard", icon: "LayoutDashboard", href: "/" },
|
|
119
|
-
{ label: "Settings", icon: "Settings", href: "/settings" },
|
|
120
|
-
];
|
|
121
|
-
|
|
122
|
-
// 渲染
|
|
123
|
-
{menu.map((item) => (
|
|
124
|
-
<a key={item.href} href={item.href}>
|
|
125
|
-
<Icon name={item.icon} />
|
|
126
|
-
{item.label}
|
|
127
|
-
</a>
|
|
128
|
-
))}
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
`Icon name="…"` 接受 `lucide-react` 里**任何**图标的 PascalCase 名字
|
|
132
|
-
(`LayoutDashboard`、`ChevronRight`、`Settings` 等)。找不到名字时
|
|
133
|
-
渲染为 `null`(不抛错)。
|
|
134
|
-
|
|
135
|
-
> 编译期能确定的图标用普通 import —— `<SearchIcon />` 在 tree-shaking
|
|
136
|
-
> 下比 `<Icon name="Search" />` 更省 bundle。
|
|
1
|
+
# 图标
|
|
2
|
+
|
|
3
|
+
本包的图标库**固定为 `lucide-react`**。所有图标从那里导入,或者通过
|
|
4
|
+
本包导出的 `Icon` 包装按名字动态渲染。
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## `Button` 里的图标用 `data-icon` 属性
|
|
9
|
+
|
|
10
|
+
加 `data-icon="inline-start"`(前缀)或 `data-icon="inline-end"`(后缀)
|
|
11
|
+
到图标上。**图标上不要加尺寸 class**。
|
|
12
|
+
|
|
13
|
+
**Incorrect:**
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Button>
|
|
17
|
+
<SearchIcon className="mr-2 size-4" />
|
|
18
|
+
Search
|
|
19
|
+
</Button>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Correct:**
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
<Button>
|
|
26
|
+
<SearchIcon data-icon="inline-start"/>
|
|
27
|
+
Search
|
|
28
|
+
</Button>
|
|
29
|
+
|
|
30
|
+
<Button>
|
|
31
|
+
Next
|
|
32
|
+
<ArrowRightIcon data-icon="inline-end"/>
|
|
33
|
+
</Button>
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 组件内部图标不要加尺寸 class
|
|
39
|
+
|
|
40
|
+
`Button`、`DropdownMenuItem`、`Alert`、`Sidebar*` 等组件通过 CSS 自己管
|
|
41
|
+
图标尺寸。**不要**手动加 `size-4` / `w-4 h-4`。除非用户明确要求自定义
|
|
42
|
+
图标尺寸。
|
|
43
|
+
|
|
44
|
+
**Incorrect:**
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<Button>
|
|
48
|
+
<SearchIcon className="size-4" data-icon="inline-start" />
|
|
49
|
+
Search
|
|
50
|
+
</Button>
|
|
51
|
+
|
|
52
|
+
<DropdownMenuItem>
|
|
53
|
+
<SettingsIcon className="mr-2 size-4" />
|
|
54
|
+
Settings
|
|
55
|
+
</DropdownMenuItem>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Correct:**
|
|
59
|
+
|
|
60
|
+
```tsx
|
|
61
|
+
<Button>
|
|
62
|
+
<SearchIcon data-icon="inline-start" />
|
|
63
|
+
Search
|
|
64
|
+
</Button>
|
|
65
|
+
|
|
66
|
+
<DropdownMenuItem>
|
|
67
|
+
<SettingsIcon />
|
|
68
|
+
Settings
|
|
69
|
+
</DropdownMenuItem>
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## 图标当组件对象传,不要当字符串 key
|
|
75
|
+
|
|
76
|
+
用 `icon={CheckIcon}`,不要用字符串 key 去查表。
|
|
77
|
+
|
|
78
|
+
**Incorrect:**
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
const iconMap = {
|
|
82
|
+
check: CheckIcon,
|
|
83
|
+
alert: AlertIcon,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
function StatusBadge({ icon }: { icon: string }) {
|
|
87
|
+
const Icon = iconMap[icon];
|
|
88
|
+
return <Icon />;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
<StatusBadge icon="check" />
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Correct:**
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
import { CheckIcon } from "lucide-react";
|
|
98
|
+
|
|
99
|
+
function StatusBadge({ icon: Icon }: { icon: React.ComponentType }) {
|
|
100
|
+
return <Icon />;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
<StatusBadge icon={CheckIcon} />
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## 按名字动态渲染用 `Icon`
|
|
109
|
+
|
|
110
|
+
当图标名字来自数据(菜单配置、主题预设、CMS 内容)而不是代码里写死的
|
|
111
|
+
import 时,用本包导出的 `Icon`:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
import { Icon } from "@openconsole/shadcn";
|
|
115
|
+
|
|
116
|
+
// 数据
|
|
117
|
+
const menu = [
|
|
118
|
+
{ label: "Dashboard", icon: "LayoutDashboard", href: "/" },
|
|
119
|
+
{ label: "Settings", icon: "Settings", href: "/settings" },
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
// 渲染
|
|
123
|
+
{menu.map((item) => (
|
|
124
|
+
<a key={item.href} href={item.href}>
|
|
125
|
+
<Icon name={item.icon} />
|
|
126
|
+
{item.label}
|
|
127
|
+
</a>
|
|
128
|
+
))}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
`Icon name="…"` 接受 `lucide-react` 里**任何**图标的 PascalCase 名字
|
|
132
|
+
(`LayoutDashboard`、`ChevronRight`、`Settings` 等)。找不到名字时
|
|
133
|
+
渲染为 `null`(不抛错)。
|
|
134
|
+
|
|
135
|
+
> 编译期能确定的图标用普通 import —— `<SearchIcon />` 在 tree-shaking
|
|
136
|
+
> 下比 `<Icon name="Search" />` 更省 bundle。
|
package/skill/rules/styling.md
CHANGED
|
@@ -1,180 +1,180 @@
|
|
|
1
|
-
# 样式 & 定制
|
|
2
|
-
|
|
3
|
-
主题 / CSS 变量 / 自定义色见 [customization.md](../customization.md)。
|
|
4
|
-
这份文件全是 Incorrect / Correct 对照,方便快速识别和修复违例。
|
|
5
|
-
|
|
6
|
-
## 目录
|
|
7
|
-
|
|
8
|
-
- 语义色
|
|
9
|
-
- 状态色不要裸值
|
|
10
|
-
- 内置 variant 优先
|
|
11
|
-
- `className` 只管布局
|
|
12
|
-
- 不要 `space-x-*` / `space-y-*`
|
|
13
|
-
- 宽高相等用 `size-*`
|
|
14
|
-
- `truncate` 简写
|
|
15
|
-
- 不要手写 `dark:` 颜色覆盖
|
|
16
|
-
- `cn()` 用于条件 class
|
|
17
|
-
- overlay 不要手写 z-index
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## 语义色
|
|
22
|
-
|
|
23
|
-
**Incorrect:**
|
|
24
|
-
|
|
25
|
-
```tsx
|
|
26
|
-
<div className="bg-blue-500 text-white">
|
|
27
|
-
<p className="text-gray-600">Secondary text</p>
|
|
28
|
-
</div>
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
**Correct:**
|
|
32
|
-
|
|
33
|
-
```tsx
|
|
34
|
-
<div className="bg-primary text-primary-foreground">
|
|
35
|
-
<p className="text-muted-foreground">Secondary text</p>
|
|
36
|
-
</div>
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
---
|
|
40
|
-
|
|
41
|
-
## 状态色不要裸值
|
|
42
|
-
|
|
43
|
-
正面、负面、状态指示要么用 `Badge` variant,要么用语义 token
|
|
44
|
-
(`text-destructive` 等),要么定义自定义 CSS 变量 —— **不要**裸用
|
|
45
|
-
Tailwind 调色板。
|
|
46
|
-
|
|
47
|
-
**Incorrect:**
|
|
48
|
-
|
|
49
|
-
```tsx
|
|
50
|
-
<span className="text-emerald-600">+20.1%</span>
|
|
51
|
-
<span className="text-green-500">Active</span>
|
|
52
|
-
<span className="text-red-600">-3.2%</span>
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
**Correct:**
|
|
56
|
-
|
|
57
|
-
```tsx
|
|
58
|
-
<Badge variant="secondary">+20.1%</Badge>
|
|
59
|
-
<Badge>Active</Badge>
|
|
60
|
-
<span className="text-destructive">-3.2%</span>
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
需要 success / positive 色但没有对应语义 token? 用 `Badge` variant,
|
|
64
|
-
或者按 [customization.md —— 新增自定义色](../customization.md#新增自定义色)
|
|
65
|
-
加一个 `--success` 变量。
|
|
66
|
-
|
|
67
|
-
---
|
|
68
|
-
|
|
69
|
-
## 内置 variant 优先
|
|
70
|
-
|
|
71
|
-
**Incorrect:**
|
|
72
|
-
|
|
73
|
-
```tsx
|
|
74
|
-
<Button className="border border-input bg-transparent hover:bg-accent">
|
|
75
|
-
Click me
|
|
76
|
-
</Button>
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
**Correct:**
|
|
80
|
-
|
|
81
|
-
```tsx
|
|
82
|
-
<Button variant="outline">Click me</Button>
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
`Button` variant: `default` / `secondary` / `outline` / `ghost` /
|
|
86
|
-
`destructive` / `link`。`Badge` variant: `default` / `secondary` /
|
|
87
|
-
`destructive` / `outline`。其它原语自行查 source。
|
|
88
|
-
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
## `className` 只管布局
|
|
92
|
-
|
|
93
|
-
`className` 用来加布局类(`max-w-md`、`mx-auto`、`mt-4`),**不要**
|
|
94
|
-
覆盖组件颜色或排版。改色用语义 token、内置 variant、或 CSS 变量。
|
|
95
|
-
|
|
96
|
-
**Incorrect:**
|
|
97
|
-
|
|
98
|
-
```tsx
|
|
99
|
-
<Card className="bg-blue-100 text-blue-900 font-bold">
|
|
100
|
-
<CardContent>Dashboard</CardContent>
|
|
101
|
-
</Card>
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
**Correct:**
|
|
105
|
-
|
|
106
|
-
```tsx
|
|
107
|
-
<Card className="max-w-md mx-auto">
|
|
108
|
-
<CardContent>Dashboard</CardContent>
|
|
109
|
-
</Card>
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
定制顺序:
|
|
113
|
-
1. **内置 variant** —— `variant="outline"`、`variant="destructive"`…
|
|
114
|
-
2. **语义 token** —— `bg-primary`、`text-muted-foreground`。
|
|
115
|
-
3. **CSS 变量** —— 加到全局 CSS(见
|
|
116
|
-
[customization.md](../customization.md))。
|
|
117
|
-
|
|
118
|
-
---
|
|
119
|
-
|
|
120
|
-
## 不要 `space-x-*` / `space-y-*`
|
|
121
|
-
|
|
122
|
-
改用 `gap-*`。`space-y-4` → `flex flex-col gap-4`。`space-x-2` → `flex gap-2`。
|
|
123
|
-
|
|
124
|
-
```tsx
|
|
125
|
-
<div className="flex flex-col gap-4">
|
|
126
|
-
<Input />
|
|
127
|
-
<Input />
|
|
128
|
-
<Button>Submit</Button>
|
|
129
|
-
</div>
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
---
|
|
133
|
-
|
|
134
|
-
## 宽高相等用 `size-*`
|
|
135
|
-
|
|
136
|
-
`size-10` 不是 `w-10 h-10`。适用于图标、头像、骨架屏、按钮等。
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## `truncate` 简写
|
|
141
|
-
|
|
142
|
-
`truncate` 不是 `overflow-hidden text-ellipsis whitespace-nowrap`。
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
## 不要手写 `dark:` 颜色覆盖
|
|
147
|
-
|
|
148
|
-
语义 token 通过 CSS 变量自动处理亮 / 暗 —— `bg-background text-foreground`
|
|
149
|
-
不是 `bg-white dark:bg-gray-950`。
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
## `cn()` 用于条件 class
|
|
154
|
-
|
|
155
|
-
从 `@openconsole/shadcn` 导入 `cn()` 来拼条件或合并的 class,**不要**在
|
|
156
|
-
`className` 字符串里手写模板字面量的三元。
|
|
157
|
-
|
|
158
|
-
**Incorrect:**
|
|
159
|
-
|
|
160
|
-
```tsx
|
|
161
|
-
<div className={`flex items-center ${isActive ? "bg-primary text-primary-foreground" : "bg-muted"}`}>
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**Correct:**
|
|
165
|
-
|
|
166
|
-
```tsx
|
|
167
|
-
import { cn } from "@openconsole/shadcn";
|
|
168
|
-
|
|
169
|
-
<div className={cn("flex items-center", isActive ? "bg-primary text-primary-foreground" : "bg-muted")}>
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
> 注意导入路径是 `@openconsole/shadcn`,不是 `@/lib/utils`。
|
|
173
|
-
|
|
174
|
-
---
|
|
175
|
-
|
|
176
|
-
## overlay 不要手写 z-index
|
|
177
|
-
|
|
178
|
-
`Dialog`、`Sheet`、`Drawer`、`AlertDialog`、`DropdownMenu`、`Popover`、
|
|
179
|
-
`Tooltip`、`HoverCard` **自己管堆叠**。绝对不要加 `z-50` 或 `z-[999]` ——
|
|
180
|
-
这样会破坏它们跟 `Toaster` 之间的堆叠顺序。
|
|
1
|
+
# 样式 & 定制
|
|
2
|
+
|
|
3
|
+
主题 / CSS 变量 / 自定义色见 [customization.md](../customization.md)。
|
|
4
|
+
这份文件全是 Incorrect / Correct 对照,方便快速识别和修复违例。
|
|
5
|
+
|
|
6
|
+
## 目录
|
|
7
|
+
|
|
8
|
+
- 语义色
|
|
9
|
+
- 状态色不要裸值
|
|
10
|
+
- 内置 variant 优先
|
|
11
|
+
- `className` 只管布局
|
|
12
|
+
- 不要 `space-x-*` / `space-y-*`
|
|
13
|
+
- 宽高相等用 `size-*`
|
|
14
|
+
- `truncate` 简写
|
|
15
|
+
- 不要手写 `dark:` 颜色覆盖
|
|
16
|
+
- `cn()` 用于条件 class
|
|
17
|
+
- overlay 不要手写 z-index
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 语义色
|
|
22
|
+
|
|
23
|
+
**Incorrect:**
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
<div className="bg-blue-500 text-white">
|
|
27
|
+
<p className="text-gray-600">Secondary text</p>
|
|
28
|
+
</div>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**Correct:**
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
<div className="bg-primary text-primary-foreground">
|
|
35
|
+
<p className="text-muted-foreground">Secondary text</p>
|
|
36
|
+
</div>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 状态色不要裸值
|
|
42
|
+
|
|
43
|
+
正面、负面、状态指示要么用 `Badge` variant,要么用语义 token
|
|
44
|
+
(`text-destructive` 等),要么定义自定义 CSS 变量 —— **不要**裸用
|
|
45
|
+
Tailwind 调色板。
|
|
46
|
+
|
|
47
|
+
**Incorrect:**
|
|
48
|
+
|
|
49
|
+
```tsx
|
|
50
|
+
<span className="text-emerald-600">+20.1%</span>
|
|
51
|
+
<span className="text-green-500">Active</span>
|
|
52
|
+
<span className="text-red-600">-3.2%</span>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Correct:**
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Badge variant="secondary">+20.1%</Badge>
|
|
59
|
+
<Badge>Active</Badge>
|
|
60
|
+
<span className="text-destructive">-3.2%</span>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
需要 success / positive 色但没有对应语义 token? 用 `Badge` variant,
|
|
64
|
+
或者按 [customization.md —— 新增自定义色](../customization.md#新增自定义色)
|
|
65
|
+
加一个 `--success` 变量。
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 内置 variant 优先
|
|
70
|
+
|
|
71
|
+
**Incorrect:**
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
<Button className="border border-input bg-transparent hover:bg-accent">
|
|
75
|
+
Click me
|
|
76
|
+
</Button>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Correct:**
|
|
80
|
+
|
|
81
|
+
```tsx
|
|
82
|
+
<Button variant="outline">Click me</Button>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`Button` variant: `default` / `secondary` / `outline` / `ghost` /
|
|
86
|
+
`destructive` / `link`。`Badge` variant: `default` / `secondary` /
|
|
87
|
+
`destructive` / `outline`。其它原语自行查 source。
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## `className` 只管布局
|
|
92
|
+
|
|
93
|
+
`className` 用来加布局类(`max-w-md`、`mx-auto`、`mt-4`),**不要**
|
|
94
|
+
覆盖组件颜色或排版。改色用语义 token、内置 variant、或 CSS 变量。
|
|
95
|
+
|
|
96
|
+
**Incorrect:**
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Card className="bg-blue-100 text-blue-900 font-bold">
|
|
100
|
+
<CardContent>Dashboard</CardContent>
|
|
101
|
+
</Card>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Correct:**
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<Card className="max-w-md mx-auto">
|
|
108
|
+
<CardContent>Dashboard</CardContent>
|
|
109
|
+
</Card>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
定制顺序:
|
|
113
|
+
1. **内置 variant** —— `variant="outline"`、`variant="destructive"`…
|
|
114
|
+
2. **语义 token** —— `bg-primary`、`text-muted-foreground`。
|
|
115
|
+
3. **CSS 变量** —— 加到全局 CSS(见
|
|
116
|
+
[customization.md](../customization.md))。
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 不要 `space-x-*` / `space-y-*`
|
|
121
|
+
|
|
122
|
+
改用 `gap-*`。`space-y-4` → `flex flex-col gap-4`。`space-x-2` → `flex gap-2`。
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
<div className="flex flex-col gap-4">
|
|
126
|
+
<Input />
|
|
127
|
+
<Input />
|
|
128
|
+
<Button>Submit</Button>
|
|
129
|
+
</div>
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## 宽高相等用 `size-*`
|
|
135
|
+
|
|
136
|
+
`size-10` 不是 `w-10 h-10`。适用于图标、头像、骨架屏、按钮等。
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## `truncate` 简写
|
|
141
|
+
|
|
142
|
+
`truncate` 不是 `overflow-hidden text-ellipsis whitespace-nowrap`。
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## 不要手写 `dark:` 颜色覆盖
|
|
147
|
+
|
|
148
|
+
语义 token 通过 CSS 变量自动处理亮 / 暗 —— `bg-background text-foreground`
|
|
149
|
+
不是 `bg-white dark:bg-gray-950`。
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## `cn()` 用于条件 class
|
|
154
|
+
|
|
155
|
+
从 `@openconsole/shadcn` 导入 `cn()` 来拼条件或合并的 class,**不要**在
|
|
156
|
+
`className` 字符串里手写模板字面量的三元。
|
|
157
|
+
|
|
158
|
+
**Incorrect:**
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
<div className={`flex items-center ${isActive ? "bg-primary text-primary-foreground" : "bg-muted"}`}>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
**Correct:**
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { cn } from "@openconsole/shadcn";
|
|
168
|
+
|
|
169
|
+
<div className={cn("flex items-center", isActive ? "bg-primary text-primary-foreground" : "bg-muted")}>
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
> 注意导入路径是 `@openconsole/shadcn`,不是 `@/lib/utils`。
|
|
173
|
+
|
|
174
|
+
---
|
|
175
|
+
|
|
176
|
+
## overlay 不要手写 z-index
|
|
177
|
+
|
|
178
|
+
`Dialog`、`Sheet`、`Drawer`、`AlertDialog`、`DropdownMenu`、`Popover`、
|
|
179
|
+
`Tooltip`、`HoverCard` **自己管堆叠**。绝对不要加 `z-50` 或 `z-[999]` ——
|
|
180
|
+
这样会破坏它们跟 `Toaster` 之间的堆叠顺序。
|