@q1travel/design-system 0.20.0 → 0.22.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/.agents/skills/product-design/references/glossary.md +28 -0
- package/contracts/design.dark.md +57 -25
- package/contracts/design.md +59 -27
- package/contracts/icon-system.md +1 -1
- package/contracts/implementation-map.md +60 -2
- package/contracts/interface-guidelines.md +38 -0
- package/contracts/overlay-shadow-migration.md +12 -0
- package/contracts/token-naming.md +13 -3
- package/dist-pkg/design-system.d.ts +177 -40
- package/dist-pkg/design-system.js +5165 -4647
- package/dist-pkg/style.css +674 -447
- package/dist-pkg/tokens.css +71 -35
- package/package.json +3 -3
|
@@ -3,3 +3,31 @@
|
|
|
3
3
|
用户可见文字与契约里的名词以此为准,跨页 / 表格 / 示例保持一致(`contracts/interface-guidelines.md` §Copy 要求 "consistent nouns")。
|
|
4
4
|
|
|
5
5
|
**Coverage gap**:统一术语表尚未成形。消费者(如 q1travel-web)正在做「内部黑话清理」,清理产出的中性术语应回流到这里,成为 canonical 词表。在此之前,遇到术语一致性问题记入 `references/coverage-gaps.md`。
|
|
6
|
+
|
|
7
|
+
## 组件文档页
|
|
8
|
+
|
|
9
|
+
组件文档页(`contracts/interface-guidelines.md` §Component Documentation Pages)的共享词表。同一概念全库只用左列一个名字;右列的说法不再使用。
|
|
10
|
+
|
|
11
|
+
### 小节名
|
|
12
|
+
|
|
13
|
+
| 概念 | 小节名 | 不再使用 |
|
|
14
|
+
|---|---|---|
|
|
15
|
+
| 外观的几种分支 | 变体 | 样式、类型、风格 |
|
|
16
|
+
| 大小档位 | 尺寸 | 大小、规格、小号、大号 |
|
|
17
|
+
| 不可用 | 禁用 | 不可用、禁止 |
|
|
18
|
+
| 校验失败或出错 | 错误 | 失败态、异常、报错态 |
|
|
19
|
+
| 等待中 | 加载中 | 加载态、载入 |
|
|
20
|
+
| 状态由外部持有 | 受控用法 | 受控、状态管理 |
|
|
21
|
+
| 相关组件 | 另请参阅 | 相关组件、延伸阅读 |
|
|
22
|
+
|
|
23
|
+
### 描述用词
|
|
24
|
+
|
|
25
|
+
| 用 | 不用 | 说明 |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| 显示 | 呈现、承载 | 描述组件在界面上出现什么 |
|
|
28
|
+
| 读屏读出的内容 | 可访问名称 | 面向读者的说法;API 表里可保留 `aria-label` 等属性名 |
|
|
29
|
+
| 调用 `onChange` | 回调、输出 | 描述里不写;放到 API 表 |
|
|
30
|
+
|
|
31
|
+
## 中英混排
|
|
32
|
+
|
|
33
|
+
以 `contracts/interface-guidelines.md` §Copy 为准:代码标识符用行内 `code` 或 `DocToken`;通用英文译为中文;品牌、产品与组件名以及 Token 保留英文正文。中文与相邻拉丁字母、数字留半角空格,中文句子使用全角逗号、句号、冒号、分号;代码、网址、界面原文引述与源码示例除外。语义判断通过文案审阅完成。
|
package/contracts/design.dark.md
CHANGED
|
@@ -8,6 +8,8 @@ source-policy:
|
|
|
8
8
|
implementation: "Translate references into q1travel tokens and canonical components before production use."
|
|
9
9
|
forbidden: "Reference markup, class names, copied computed styles, and screenshot-only dimensions."
|
|
10
10
|
colors:
|
|
11
|
+
gray-alpha-100: "rgba(255, 255, 255, 0.08)"
|
|
12
|
+
gray-alpha-200: "rgba(255, 255, 255, 0.12)"
|
|
11
13
|
primary: "#533afd"
|
|
12
14
|
primary-hover: "#4631d6"
|
|
13
15
|
primary-press: "#342899"
|
|
@@ -146,8 +148,8 @@ semantic-colors:
|
|
|
146
148
|
overlay-scrim: "rgba(2, 6, 23, 0.44)"
|
|
147
149
|
selection-background: "{colors.text-primary}"
|
|
148
150
|
selection-foreground: "{colors.background-100}"
|
|
149
|
-
focus-ring: "
|
|
150
|
-
focus: "
|
|
151
|
+
focus-ring: "#ffffff"
|
|
152
|
+
focus: "#ffffff"
|
|
151
153
|
status-success: "{colors.green-900}"
|
|
152
154
|
status-success-text: "{colors.green-900}"
|
|
153
155
|
status-success-surface: "{colors.green-100}"
|
|
@@ -169,62 +171,86 @@ typography:
|
|
|
169
171
|
fontFamily: Noto Sans SC
|
|
170
172
|
fontSize: 72px
|
|
171
173
|
fontWeight: 500
|
|
172
|
-
lineHeight:
|
|
173
|
-
letterSpacing:
|
|
174
|
+
lineHeight: 88px
|
|
175
|
+
letterSpacing: 0
|
|
174
176
|
heading-64:
|
|
175
177
|
fontFamily: Noto Sans SC
|
|
176
178
|
fontSize: 64px
|
|
177
179
|
fontWeight: 500
|
|
178
|
-
lineHeight:
|
|
179
|
-
letterSpacing:
|
|
180
|
+
lineHeight: 80px
|
|
181
|
+
letterSpacing: 0
|
|
180
182
|
heading-56:
|
|
181
183
|
fontFamily: Noto Sans SC
|
|
182
184
|
fontSize: 56px
|
|
183
185
|
fontWeight: 500
|
|
184
|
-
lineHeight:
|
|
185
|
-
letterSpacing:
|
|
186
|
+
lineHeight: 72px
|
|
187
|
+
letterSpacing: 0
|
|
186
188
|
heading-48:
|
|
187
189
|
fontFamily: Noto Sans SC
|
|
188
190
|
fontSize: 48px
|
|
189
191
|
fontWeight: 500
|
|
192
|
+
lineHeight: 64px
|
|
193
|
+
letterSpacing: 0
|
|
194
|
+
heading-40-strong:
|
|
195
|
+
fontFamily: Noto Sans SC
|
|
196
|
+
fontSize: 40px
|
|
197
|
+
fontWeight: 600
|
|
190
198
|
lineHeight: 52px
|
|
191
|
-
letterSpacing:
|
|
199
|
+
letterSpacing: 0
|
|
192
200
|
heading-40:
|
|
193
201
|
fontFamily: Noto Sans SC
|
|
194
202
|
fontSize: 40px
|
|
195
203
|
fontWeight: 500
|
|
204
|
+
lineHeight: 52px
|
|
205
|
+
letterSpacing: 0
|
|
206
|
+
heading-32-strong:
|
|
207
|
+
fontFamily: Noto Sans SC
|
|
208
|
+
fontSize: 32px
|
|
209
|
+
fontWeight: 600
|
|
196
210
|
lineHeight: 44px
|
|
197
|
-
letterSpacing:
|
|
211
|
+
letterSpacing: 0
|
|
198
212
|
heading-32:
|
|
199
213
|
fontFamily: Noto Sans SC
|
|
200
214
|
fontSize: 32px
|
|
201
215
|
fontWeight: 500
|
|
202
|
-
lineHeight:
|
|
203
|
-
letterSpacing:
|
|
216
|
+
lineHeight: 44px
|
|
217
|
+
letterSpacing: 0
|
|
218
|
+
heading-24-strong:
|
|
219
|
+
fontFamily: Noto Sans SC
|
|
220
|
+
fontSize: 24px
|
|
221
|
+
fontWeight: 600
|
|
222
|
+
lineHeight: 32px
|
|
223
|
+
letterSpacing: 0
|
|
204
224
|
heading-24:
|
|
205
225
|
fontFamily: Noto Sans SC
|
|
206
226
|
fontSize: 24px
|
|
207
227
|
fontWeight: 500
|
|
208
228
|
lineHeight: 32px
|
|
209
|
-
letterSpacing:
|
|
229
|
+
letterSpacing: 0
|
|
230
|
+
heading-20-strong:
|
|
231
|
+
fontFamily: Noto Sans SC
|
|
232
|
+
fontSize: 20px
|
|
233
|
+
fontWeight: 600
|
|
234
|
+
lineHeight: 28px
|
|
235
|
+
letterSpacing: 0
|
|
210
236
|
heading-20:
|
|
211
237
|
fontFamily: Noto Sans SC
|
|
212
238
|
fontSize: 20px
|
|
213
239
|
fontWeight: 500
|
|
214
240
|
lineHeight: 28px
|
|
215
|
-
letterSpacing:
|
|
241
|
+
letterSpacing: 0
|
|
216
242
|
heading-16:
|
|
217
243
|
fontFamily: Noto Sans SC
|
|
218
244
|
fontSize: 16px
|
|
219
245
|
fontWeight: 500
|
|
220
246
|
lineHeight: 24px
|
|
221
|
-
letterSpacing:
|
|
247
|
+
letterSpacing: 0
|
|
222
248
|
heading-14:
|
|
223
249
|
fontFamily: Noto Sans SC
|
|
224
250
|
fontSize: 14px
|
|
225
251
|
fontWeight: 500
|
|
226
252
|
lineHeight: 20px
|
|
227
|
-
letterSpacing:
|
|
253
|
+
letterSpacing: 0
|
|
228
254
|
label-14:
|
|
229
255
|
fontFamily: Noto Sans SC
|
|
230
256
|
fontSize: 14px
|
|
@@ -241,13 +267,13 @@ typography:
|
|
|
241
267
|
fontFamily: Noto Sans SC
|
|
242
268
|
fontSize: 16px
|
|
243
269
|
fontWeight: 400
|
|
244
|
-
lineHeight:
|
|
270
|
+
lineHeight: 28px
|
|
245
271
|
letterSpacing: 0
|
|
246
272
|
copy-14:
|
|
247
273
|
fontFamily: Noto Sans SC
|
|
248
274
|
fontSize: 14px
|
|
249
275
|
fontWeight: 400
|
|
250
|
-
lineHeight:
|
|
276
|
+
lineHeight: 24px
|
|
251
277
|
letterSpacing: 0
|
|
252
278
|
code-13:
|
|
253
279
|
fontFamily: Geist Mono
|
|
@@ -261,12 +287,14 @@ spacing:
|
|
|
261
287
|
3: 12px
|
|
262
288
|
4: 16px
|
|
263
289
|
6: 24px
|
|
290
|
+
7: 28px
|
|
264
291
|
8: 32px
|
|
265
292
|
10: 40px
|
|
266
293
|
16: 64px
|
|
267
294
|
24: 96px
|
|
268
295
|
base: 4px
|
|
269
296
|
sizing:
|
|
297
|
+
touch-target-min: 44px
|
|
270
298
|
control-default: 40px
|
|
271
299
|
rounded:
|
|
272
300
|
xs: 4px
|
|
@@ -307,9 +335,9 @@ components:
|
|
|
307
335
|
height: 40px
|
|
308
336
|
button-secondary:
|
|
309
337
|
backgroundColor: "{semantic-colors.surface-panel}"
|
|
310
|
-
hoverBackgroundColor: "{semantic-colors.surface-
|
|
338
|
+
hoverBackgroundColor: "{semantic-colors.surface-hover}"
|
|
311
339
|
textColor: "{semantic-colors.text-primary}"
|
|
312
|
-
borderColor: "{semantic-colors.border
|
|
340
|
+
borderColor: "{semantic-colors.border}"
|
|
313
341
|
typography: "{typography.label-14}"
|
|
314
342
|
rounded: "{rounded.sm}"
|
|
315
343
|
height: 40px
|
|
@@ -317,7 +345,7 @@ components:
|
|
|
317
345
|
backgroundColor: "{semantic-colors.surface-panel}"
|
|
318
346
|
textColor: "{semantic-colors.text-primary}"
|
|
319
347
|
placeholderColor: "{semantic-colors.text-muted}"
|
|
320
|
-
borderColor: "{semantic-colors.border
|
|
348
|
+
borderColor: "{semantic-colors.border}"
|
|
321
349
|
focusColor: "{semantic-colors.focus-ring}"
|
|
322
350
|
typography: "{typography.label-14}"
|
|
323
351
|
rounded: "{rounded.sm}"
|
|
@@ -402,14 +430,18 @@ These components ship in the package. Compose from this list before writing new
|
|
|
402
430
|
|
|
403
431
|
`q1-design ls` prints this list from the installed package, and `q1-design doc <name>` prints one component's public API. Use those when the package is installed; use this section when it is not.
|
|
404
432
|
|
|
405
|
-
**
|
|
433
|
+
**Record activity.** Timeline, TimelineItem, Marker, Message, Mention, Composer, Attachment. Timeline uses chronological ol/li with tone dots or Avatar; Marker keeps system events quiet and dates centered. Message supports pending/failed and caller actions; Mention renders caller-owned inline member references using action-text and the selected surface. Composer is a textarea combobox with listbox mentions, Enter sends and Shift+Enter inserts a newline; IME composition never sends or picks. Attachment reuses Progress and named removal. Network, permissions and notifications remain caller-owned.
|
|
434
|
+
|
|
435
|
+
**Actions.** Button, ButtonLink, IconButton, SplitButton, LoadMoreButton, ClearButton, CopyButton, FilterChip, Link, Toolbar.
|
|
406
436
|
|
|
407
437
|
- Button carries the action. ButtonLink navigates but reads as a button, for a page-level call to action. Link reads as text, inside a sentence. Substituting one for another makes the page read wrong.
|
|
408
438
|
- SplitButton is a primary action with an adjacent menu whose first entry mirrors it. Do not use it to hide unrelated actions behind a chevron.
|
|
439
|
+
- FilterChip opens a consumer-owned filter picker; undefined means unset, other values mean set. Its 28px pill uses a dashed border-strong outline with CirclePlus when unset, and a solid outline with label, border-token divider, action-text value and ChevronDown when set. The optional leading CircleX clear action is a separate named native button before the picker, with its own Tab stop and shared focus ring.
|
|
409
440
|
- IconButton requires an accessible name. An icon-only control without one is not deliverable.
|
|
410
441
|
|
|
411
|
-
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput,
|
|
442
|
+
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, InputOTP, Textarea, Select, Fieldset, ErrorMessage.
|
|
412
443
|
|
|
444
|
+
- InputOTP collects a fixed six-digit code in one joined group by default; `separator` explicitly enables 3+3 groups. VerificationCodeInput and VerificationCodeInputProps remain deprecated aliases for one minor version; migrate to InputOTP and InputOTPProps before their removal in the next minor version. The old `/components/verification-code-input` route redirects to `/components/input-otp`.
|
|
413
445
|
- FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
|
|
414
446
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
415
447
|
|
|
@@ -419,7 +451,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
419
451
|
- Switch selects among mutually exclusive views or modes.
|
|
420
452
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
421
453
|
|
|
422
|
-
**Dates and ranges.** CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
|
|
454
|
+
**Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
|
|
423
455
|
|
|
424
456
|
**Overlays.** Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
|
|
425
457
|
|
|
@@ -435,7 +467,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
435
467
|
- Gauge is one ratio as a ring. Progress is advancement along a task, and takes segment stops.
|
|
436
468
|
- RelativeTimeCard reveals the absolute time behind a relative one. Prefer it to printing both.
|
|
437
469
|
|
|
438
|
-
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, ErrorMessage, EmptyState, Badge, Pill, StatusDot, ToastProvider, useToast, Feedback, Spinner, LoadingDots, Skeleton.
|
|
470
|
+
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, ErrorMessage, EmptyState, Badge, Pill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton.
|
|
439
471
|
|
|
440
472
|
- Banner announces at page scope. Note is an inline aside. ErrorMessage sits against one field. ErrorCard replaces a region that failed to load. Picking the widest one available overstates the problem.
|
|
441
473
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
package/contracts/design.md
CHANGED
|
@@ -8,6 +8,8 @@ source-policy:
|
|
|
8
8
|
implementation: "Translate references into q1travel tokens and canonical components before production use."
|
|
9
9
|
forbidden: "Reference markup, class names, copied computed styles, and screenshot-only dimensions."
|
|
10
10
|
colors:
|
|
11
|
+
gray-alpha-100: "rgba(0, 0, 0, 0.06)"
|
|
12
|
+
gray-alpha-200: "rgba(0, 0, 0, 0.08)"
|
|
11
13
|
primary: "#533afd"
|
|
12
14
|
primary-hover: "#4434d4"
|
|
13
15
|
primary-press: "#2e2b8c"
|
|
@@ -134,7 +136,7 @@ semantic-colors:
|
|
|
134
136
|
text-on-inverse: "{colors.background-100}"
|
|
135
137
|
surface-media: "#0a0a0a"
|
|
136
138
|
text-on-media: "#ffffff"
|
|
137
|
-
border: "{colors.gray-
|
|
139
|
+
border: "{colors.gray-alpha-200}"
|
|
138
140
|
border-strong: "{colors.gray-500}"
|
|
139
141
|
border-active: "{colors.primary-700}"
|
|
140
142
|
border-subtle: "rgba(203, 213, 225, 0.82)"
|
|
@@ -146,8 +148,8 @@ semantic-colors:
|
|
|
146
148
|
overlay-scrim: "rgba(2, 6, 23, 0.44)"
|
|
147
149
|
selection-background: "{colors.text-primary}"
|
|
148
150
|
selection-foreground: "{colors.background-100}"
|
|
149
|
-
focus-ring: "
|
|
150
|
-
focus: "
|
|
151
|
+
focus-ring: "#000000"
|
|
152
|
+
focus: "#000000"
|
|
151
153
|
status-success: "{colors.green-700}"
|
|
152
154
|
status-success-text: "{colors.green-900}"
|
|
153
155
|
status-success-surface: "{colors.green-100}"
|
|
@@ -169,62 +171,86 @@ typography:
|
|
|
169
171
|
fontFamily: Noto Sans SC
|
|
170
172
|
fontSize: 72px
|
|
171
173
|
fontWeight: 500
|
|
172
|
-
lineHeight:
|
|
173
|
-
letterSpacing:
|
|
174
|
+
lineHeight: 88px
|
|
175
|
+
letterSpacing: 0
|
|
174
176
|
heading-64:
|
|
175
177
|
fontFamily: Noto Sans SC
|
|
176
178
|
fontSize: 64px
|
|
177
179
|
fontWeight: 500
|
|
178
|
-
lineHeight:
|
|
179
|
-
letterSpacing:
|
|
180
|
+
lineHeight: 80px
|
|
181
|
+
letterSpacing: 0
|
|
180
182
|
heading-56:
|
|
181
183
|
fontFamily: Noto Sans SC
|
|
182
184
|
fontSize: 56px
|
|
183
185
|
fontWeight: 500
|
|
184
|
-
lineHeight:
|
|
185
|
-
letterSpacing:
|
|
186
|
+
lineHeight: 72px
|
|
187
|
+
letterSpacing: 0
|
|
186
188
|
heading-48:
|
|
187
189
|
fontFamily: Noto Sans SC
|
|
188
190
|
fontSize: 48px
|
|
189
191
|
fontWeight: 500
|
|
192
|
+
lineHeight: 64px
|
|
193
|
+
letterSpacing: 0
|
|
194
|
+
heading-40-strong:
|
|
195
|
+
fontFamily: Noto Sans SC
|
|
196
|
+
fontSize: 40px
|
|
197
|
+
fontWeight: 600
|
|
190
198
|
lineHeight: 52px
|
|
191
|
-
letterSpacing:
|
|
199
|
+
letterSpacing: 0
|
|
192
200
|
heading-40:
|
|
193
201
|
fontFamily: Noto Sans SC
|
|
194
202
|
fontSize: 40px
|
|
195
203
|
fontWeight: 500
|
|
204
|
+
lineHeight: 52px
|
|
205
|
+
letterSpacing: 0
|
|
206
|
+
heading-32-strong:
|
|
207
|
+
fontFamily: Noto Sans SC
|
|
208
|
+
fontSize: 32px
|
|
209
|
+
fontWeight: 600
|
|
196
210
|
lineHeight: 44px
|
|
197
|
-
letterSpacing:
|
|
211
|
+
letterSpacing: 0
|
|
198
212
|
heading-32:
|
|
199
213
|
fontFamily: Noto Sans SC
|
|
200
214
|
fontSize: 32px
|
|
201
215
|
fontWeight: 500
|
|
202
|
-
lineHeight:
|
|
203
|
-
letterSpacing:
|
|
216
|
+
lineHeight: 44px
|
|
217
|
+
letterSpacing: 0
|
|
218
|
+
heading-24-strong:
|
|
219
|
+
fontFamily: Noto Sans SC
|
|
220
|
+
fontSize: 24px
|
|
221
|
+
fontWeight: 600
|
|
222
|
+
lineHeight: 32px
|
|
223
|
+
letterSpacing: 0
|
|
204
224
|
heading-24:
|
|
205
225
|
fontFamily: Noto Sans SC
|
|
206
226
|
fontSize: 24px
|
|
207
227
|
fontWeight: 500
|
|
208
228
|
lineHeight: 32px
|
|
209
|
-
letterSpacing:
|
|
229
|
+
letterSpacing: 0
|
|
230
|
+
heading-20-strong:
|
|
231
|
+
fontFamily: Noto Sans SC
|
|
232
|
+
fontSize: 20px
|
|
233
|
+
fontWeight: 600
|
|
234
|
+
lineHeight: 28px
|
|
235
|
+
letterSpacing: 0
|
|
210
236
|
heading-20:
|
|
211
237
|
fontFamily: Noto Sans SC
|
|
212
238
|
fontSize: 20px
|
|
213
239
|
fontWeight: 500
|
|
214
240
|
lineHeight: 28px
|
|
215
|
-
letterSpacing:
|
|
241
|
+
letterSpacing: 0
|
|
216
242
|
heading-16:
|
|
217
243
|
fontFamily: Noto Sans SC
|
|
218
244
|
fontSize: 16px
|
|
219
245
|
fontWeight: 500
|
|
220
246
|
lineHeight: 24px
|
|
221
|
-
letterSpacing:
|
|
247
|
+
letterSpacing: 0
|
|
222
248
|
heading-14:
|
|
223
249
|
fontFamily: Noto Sans SC
|
|
224
250
|
fontSize: 14px
|
|
225
251
|
fontWeight: 500
|
|
226
252
|
lineHeight: 20px
|
|
227
|
-
letterSpacing:
|
|
253
|
+
letterSpacing: 0
|
|
228
254
|
label-14:
|
|
229
255
|
fontFamily: Noto Sans SC
|
|
230
256
|
fontSize: 14px
|
|
@@ -241,13 +267,13 @@ typography:
|
|
|
241
267
|
fontFamily: Noto Sans SC
|
|
242
268
|
fontSize: 16px
|
|
243
269
|
fontWeight: 400
|
|
244
|
-
lineHeight:
|
|
270
|
+
lineHeight: 28px
|
|
245
271
|
letterSpacing: 0
|
|
246
272
|
copy-14:
|
|
247
273
|
fontFamily: Noto Sans SC
|
|
248
274
|
fontSize: 14px
|
|
249
275
|
fontWeight: 400
|
|
250
|
-
lineHeight:
|
|
276
|
+
lineHeight: 24px
|
|
251
277
|
letterSpacing: 0
|
|
252
278
|
code-13:
|
|
253
279
|
fontFamily: Geist Mono
|
|
@@ -261,12 +287,14 @@ spacing:
|
|
|
261
287
|
3: 12px
|
|
262
288
|
4: 16px
|
|
263
289
|
6: 24px
|
|
290
|
+
7: 28px
|
|
264
291
|
8: 32px
|
|
265
292
|
10: 40px
|
|
266
293
|
16: 64px
|
|
267
294
|
24: 96px
|
|
268
295
|
base: 4px
|
|
269
296
|
sizing:
|
|
297
|
+
touch-target-min: 44px
|
|
270
298
|
control-default: 40px
|
|
271
299
|
rounded:
|
|
272
300
|
xs: 4px
|
|
@@ -307,9 +335,9 @@ components:
|
|
|
307
335
|
height: 40px
|
|
308
336
|
button-secondary:
|
|
309
337
|
backgroundColor: "{semantic-colors.surface-panel}"
|
|
310
|
-
hoverBackgroundColor: "{semantic-colors.surface-
|
|
338
|
+
hoverBackgroundColor: "{semantic-colors.surface-hover}"
|
|
311
339
|
textColor: "{semantic-colors.text-primary}"
|
|
312
|
-
borderColor: "{semantic-colors.border
|
|
340
|
+
borderColor: "{semantic-colors.border}"
|
|
313
341
|
typography: "{typography.label-14}"
|
|
314
342
|
rounded: "{rounded.sm}"
|
|
315
343
|
height: 40px
|
|
@@ -317,7 +345,7 @@ components:
|
|
|
317
345
|
backgroundColor: "{semantic-colors.surface-panel}"
|
|
318
346
|
textColor: "{semantic-colors.text-primary}"
|
|
319
347
|
placeholderColor: "{semantic-colors.text-muted}"
|
|
320
|
-
borderColor: "{semantic-colors.border
|
|
348
|
+
borderColor: "{semantic-colors.border}"
|
|
321
349
|
focusColor: "{semantic-colors.focus-ring}"
|
|
322
350
|
typography: "{typography.label-14}"
|
|
323
351
|
rounded: "{rounded.sm}"
|
|
@@ -402,14 +430,18 @@ These components ship in the package. Compose from this list before writing new
|
|
|
402
430
|
|
|
403
431
|
`q1-design ls` prints this list from the installed package, and `q1-design doc <name>` prints one component's public API. Use those when the package is installed; use this section when it is not.
|
|
404
432
|
|
|
405
|
-
**
|
|
433
|
+
**Record activity.** Timeline, TimelineItem, Marker, Message, Mention, Composer, Attachment. Timeline uses chronological ol/li with tone dots or Avatar; Marker keeps system events quiet and dates centered. Message supports pending/failed and caller actions; Mention renders caller-owned inline member references using action-text and the selected surface. Composer is a textarea combobox with listbox mentions, Enter sends and Shift+Enter inserts a newline; IME composition never sends or picks. Attachment reuses Progress and named removal. Network, permissions and notifications remain caller-owned.
|
|
434
|
+
|
|
435
|
+
**Actions.** Button, ButtonLink, IconButton, SplitButton, LoadMoreButton, ClearButton, CopyButton, FilterChip, Link, Toolbar.
|
|
406
436
|
|
|
407
437
|
- Button carries the action. ButtonLink navigates but reads as a button, for a page-level call to action. Link reads as text, inside a sentence. Substituting one for another makes the page read wrong.
|
|
408
438
|
- SplitButton is a primary action with an adjacent menu whose first entry mirrors it. Do not use it to hide unrelated actions behind a chevron.
|
|
439
|
+
- FilterChip opens a consumer-owned filter picker; undefined means unset, other values mean set. Its 28px pill uses a dashed border-strong outline with CirclePlus when unset, and a solid outline with label, border-token divider, action-text value and ChevronDown when set. The optional leading CircleX clear action is a separate named native button before the picker, with its own Tab stop and shared focus ring.
|
|
409
440
|
- IconButton requires an accessible name. An icon-only control without one is not deliverable.
|
|
410
441
|
|
|
411
|
-
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput,
|
|
442
|
+
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, InputOTP, Textarea, Select, Fieldset, ErrorMessage.
|
|
412
443
|
|
|
444
|
+
- InputOTP collects a fixed six-digit code in one joined group by default; `separator` explicitly enables 3+3 groups. VerificationCodeInput and VerificationCodeInputProps remain deprecated aliases for one minor version; migrate to InputOTP and InputOTPProps before their removal in the next minor version. The old `/components/verification-code-input` route redirects to `/components/input-otp`.
|
|
413
445
|
- FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
|
|
414
446
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
415
447
|
|
|
@@ -419,7 +451,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
419
451
|
- Switch selects among mutually exclusive views or modes.
|
|
420
452
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
421
453
|
|
|
422
|
-
**Dates and ranges.** CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
|
|
454
|
+
**Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
|
|
423
455
|
|
|
424
456
|
**Overlays.** Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
|
|
425
457
|
|
|
@@ -435,7 +467,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
435
467
|
- Gauge is one ratio as a ring. Progress is advancement along a task, and takes segment stops.
|
|
436
468
|
- RelativeTimeCard reveals the absolute time behind a relative one. Prefer it to printing both.
|
|
437
469
|
|
|
438
|
-
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, ErrorMessage, EmptyState, Badge, Pill, StatusDot, ToastProvider, useToast, Feedback, Spinner, LoadingDots, Skeleton.
|
|
470
|
+
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, ErrorMessage, EmptyState, Badge, Pill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton.
|
|
439
471
|
|
|
440
472
|
- Banner announces at page scope. Note is an inline aside. ErrorMessage sits against one field. ErrorCard replaces a region that failed to load. Picking the widest one available overstates the problem.
|
|
441
473
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
|
@@ -445,7 +477,7 @@ These components ship in the package. Compose from this list before writing new
|
|
|
445
477
|
**Structure and navigation.** Card, AuthCard, Sidebar, SidebarDrawer, Navigation, NavigationSection, NavigationItem, NavigationGroup, ContextSwitcher, Breadcrumb, CollapsibleBreadcrumb, Tabs, Collapse, CollapseGroup, Separator, Scroller, ScrollerWithButtons, FileTree.
|
|
446
478
|
|
|
447
479
|
- Card is a surface you have earned, not a default wrapper. Reach for spacing and alignment first.
|
|
448
|
-
- Tabs switch between peer views of the same subject. They are not a way to shorten a page.
|
|
480
|
+
- Tabs switch between peer views of the same subject. They are not a way to shorten a page. Optional `value` / `onValueChange` let the consumer own selection and URL synchronization; `defaultValue` seeds uncontrolled selection. Invalid or disabled values render the first enabled item without notifying. See `implementation-map.md` for selection ownership.
|
|
449
481
|
- ContextSwitcher is the two-level selection model; the consumer owns the state behind it.
|
|
450
482
|
|
|
451
483
|
**Identity and media.** Avatar, AvatarGroup, Book, Browser, BrowserDots, BrowserControls, BrowserAddress, BrowserTabs, BrowserPlaceholder, Phone, Video, ThemeSwitcher, ProductLockup, Q1travelSymbol.
|
package/contracts/icon-system.md
CHANGED
|
@@ -75,4 +75,4 @@ q1travel 的界面图标**目标是**一套自建的线性图标系统:几何
|
|
|
75
75
|
- **颜色**:不为图标单独设色,依赖 `currentColor` 跟随文本。
|
|
76
76
|
- **间距**:图标与文本之间保留一致间距,并与文本基线对齐。
|
|
77
77
|
- **一致性**:同一控件组内不混用不同描边或尺寸。
|
|
78
|
-
- **光学位移**:16px 线性图标贴文字标签时,图标框几何居中不等于看起来居中。位移统一消费 `--q1-optical-icon-shift-cjk-label`,通过独立的 `translate` 属性施加,不改 `transform`,因此不会冲掉 chevron 的旋转。取值按字体栈校准:包默认 `--q1-font-sans` 为 `0px`;拉丁首选字体决定行盒、中文回退 PingFang SC 的字体栈(web 当前字体栈)为 `-1px`,由消费方在自己的根上覆盖。纯英文标签会因此偏高约 1–2px,这是 x-height 的物理位置,不再按脚本细分。已接入:Navigation 分组图标、分组 chevron、链接项图标;ContextSwitcher 单行条目图标与动作图标。Button 前后缀默认不位移,消费方在已知全是中文的场景把 `--q1-button-affix-optical-y` 设为该 token
|
|
78
|
+
- **光学位移**:16px 线性图标贴文字标签时,图标框几何居中不等于看起来居中。位移统一消费 `--q1-optical-icon-shift-cjk-label`,通过独立的 `translate` 属性施加,不改 `transform`,因此不会冲掉 chevron 的旋转。取值按字体栈校准:包默认 `--q1-font-sans` 为 `0px`;拉丁首选字体决定行盒、中文回退 PingFang SC 的字体栈(web 当前字体栈)为 `-1px`,由消费方在自己的根上覆盖。纯英文标签会因此偏高约 1–2px,这是 x-height 的物理位置,不再按脚本细分。已接入:Navigation 分组图标、分组 chevron、链接项图标;ContextSwitcher 单行条目图标与动作图标。Button 前后缀默认不位移,消费方在已知全是中文的场景把 `--q1-button-affix-optical-y` 设为该 token。组件内部另有两处具名、整数像素的局部位移,只作用于本组件:`--q1-product-lockup-optical-y`(品牌符号与竖线对齐产品名,`1px`)与 `--q1-note-icon-optical-y`(Note 图标对齐第一行正文,默认 `3px`、`large` `2px`)。数值由 `check:browser` 的光学 fixture 锁定,见 `contracts/token-naming.md` 的 Optical Tokens。
|
|
@@ -9,6 +9,8 @@ README owns the repository inventory and generated-file boundary. This file owns
|
|
|
9
9
|
| Reference intent | q1travel semantic | Canonical surface | Token source | State | Responsive | Accessibility |
|
|
10
10
|
| --- | --- | --- | --- | --- | --- | --- |
|
|
11
11
|
| Bounded documentation frame | Public design-system shell | `AppShell`, `Sidebar`, `Topbar` | `--q1-color-bg-canvas`, `--q1-color-border`, spacing tokens | Route changes reset document scroll | Frame width and sidebar collapse through layout CSS | Landmarks, keyboard search, focus-visible ring |
|
|
12
|
+
| Aligned date-range field | Date-range trigger shares the FieldSize scale | `Calendar`, `RangeField` (`fieldSize`; `size` remains grid-only) | `--q1-control-height-default`, `--q1-space-6`, `--q1-space-8`, `--q1-space-2`, `--q1-touch-target-min` | Draft, apply, clear and popover keyboard behavior preserved; clear/caret centered | Default md40 matches Select/Input; coarse44 matches Select | Named sibling buttons, APG date-picker dialog and focus return |
|
|
13
|
+
| Compact dashboard filters | Consumer-owned filter picker | `FilterChip` | `--q1-space-7` (28px), border-strong, text-secondary, action-text, shared focus | Unset dashed CirclePlus; set solid leading CircleX clear, label, divider, value and ChevronDown | Ellipsis bounds long values; shared TouchTarget preserves coarse 44px targets | Sibling native buttons, clear before picker in Tab order, focus returns to picker |
|
|
12
14
|
| Compact search and command menu | Fast documentation navigation | `CommandMenu`, `Topbar` search trigger | Surface, border, text, focus tokens | Open, active option, escape close | Dialog width clamps on small screens | `role="dialog"`, `aria-modal`, keyboard navigation |
|
|
13
15
|
| Foundation page rhythm | Scannable reference pages | `PageHeader`, `SpecSection`, `SpecTable` | Heading, copy, border, spacing tokens | None beyond links and focus | Tables scroll horizontally when needed | Semantic headings and tables |
|
|
14
16
|
| Introduction tile grid | Entry points into public groups | `IntroTileGrid`, `IntroTile` | Surface, border, text, spacing tokens | Hover, focus | Two columns on desktop, one column on small screens | Tile is a single link with hidden decorative visual |
|
|
@@ -26,14 +28,70 @@ README owns the repository inventory and generated-file boundary. This file owns
|
|
|
26
28
|
- New public tokens use `--q1-*`; existing product `--ds-*` names are legacy migration inputs, not new contract names.
|
|
27
29
|
- Canonical components own repeated structure. Page-local custom structures are allowed only for visual specimens with a narrow purpose.
|
|
28
30
|
- Token references are the design source for production code. Current theme resolved values are clipboard output only.
|
|
29
|
-
- The same public navigation groups remain in the product surface: `基础`, `品牌`, and
|
|
31
|
+
- The same public navigation groups remain in the product surface: `基础`, `品牌`, `组件`, and `模式`; `模式` owns compositions across components, starting with `/patterns/admin`.
|
|
30
32
|
- If a capability is missing from a shared component, add the capability to that component before adding another page-local pattern.
|
|
31
33
|
|
|
32
34
|
## Documentation Section Ownership
|
|
33
35
|
|
|
34
36
|
Two component-page sections are q1travel supersets with no external counterpart, so their scope is defined here rather than inferred page by page.
|
|
35
37
|
|
|
36
|
-
- **设计 Token** — every Tier B native-semantic control page (Button, Input, Textarea, Select, Checkbox, Radio, Toggle) MUST carry this section. Those controls take their whole surface from semantic tokens, so the role/token/usage table is the only place a consumer can see which tokens to override. Tier A and Tier C pages MUST NOT add it by default: their appearance comes mostly from structure, and a token table there restates the global palette without telling the reader anything component-specific. **Switch and Tabs are the two recorded exceptions** — these Tier A components are likewise entirely token-driven; they keep their tables, and they do not license other Tier A pages to add one.
|
|
38
|
+
- **设计 Token** — every Tier B native-semantic control page (Button, Input, Textarea, Select, Checkbox, Radio, Toggle, FilterChip) MUST carry this section. Those controls take their whole surface from semantic tokens, so the role/token/usage table is the only place a consumer can see which tokens to override. Tier A and Tier C pages MUST NOT add it by default: their appearance comes mostly from structure, and a token table there restates the global palette without telling the reader anything component-specific. **Switch and Tabs are the two recorded exceptions** — these Tier A components are likewise entirely token-driven; they keep their tables, and they do not license other Tier A pages to add one.
|
|
37
39
|
- **API 与行为矩阵** — reserved for composite host components that the consumer assembles while the host owns the state (ContextSwitcher, Navigation, Sidebar). The table exists to state who owns what; a component that owns its own state does not need it.
|
|
38
40
|
|
|
39
41
|
Rows in these tables reference semantic tokens (`--q1-color-*`, `--q1-shadow-*`, `--q1-radius-*`), not component-private custom properties. Component-private properties driven by props (for example an avatar diameter) are implementation detail and MUST NOT be documented as a consumer-facing token contract.
|
|
42
|
+
|
|
43
|
+
## Embedded documentation examples
|
|
44
|
+
|
|
45
|
+
`DemoBlock` owns one preview and its `ShowCode` footer. `SpecSection` pairs adjacent native
|
|
46
|
+
`.specimen` / `ShowCode` children, including multiple pairs; other previews use explicit
|
|
47
|
+
`DemoBlock` boundaries. Explanation tables and standalone code retain their own structure.
|
|
48
|
+
`ShowCode` opts into `Collapse headingLevel={null}` and `CodeBlock variant="flush"`.
|
|
49
|
+
Collapse keeps its h3 default, keyboard disclosure semantics, focus restoration, and inert
|
|
50
|
+
collapsed panel. A null heading level uses a plain wrapper for an ancillary disclosure;
|
|
51
|
+
it does not enter the document heading outline. CodeBlock keeps its standalone default
|
|
52
|
+
header and outer frame; flush removes that frame and floats copy only when there is no
|
|
53
|
+
filename or language switch. No existing consumer needs an API migration.
|
|
54
|
+
|
|
55
|
+
Light documentation canvas uses background-200, previews use background-100, and the
|
|
56
|
+
code footer uses bg-subtle. The public light border now uses gray-alpha-200 (black 8%);
|
|
57
|
+
dark borders keep their established value. Consumers with border screenshots should
|
|
58
|
+
refresh them; token names and existing overrides remain valid. Gray-alpha-100/200
|
|
59
|
+
provide theme-relative list-state overlays. Strong heading-40/32/24/20 token roles are
|
|
60
|
+
opt-in 600 weights; existing heading roles keep 500. Space-7 adds the 28px spacing step.
|
|
61
|
+
|
|
62
|
+
## Component visual subtraction
|
|
63
|
+
|
|
64
|
+
Destructive Action Modal keeps its typed confirmation gate, error recovery, busy lock,
|
|
65
|
+
and Escape/Cancel dismissal. Optional irreversible copy lives inside the scrollable
|
|
66
|
+
body on status-danger-surface/text tokens; disabled confirmation uses bg-subtle and
|
|
67
|
+
text-muted. There is no duplicate header close control.
|
|
68
|
+
|
|
69
|
+
Command Menu cycles Tab between its search input and sole Esc keycap close button;
|
|
70
|
+
commands use text without leading arrows and space-8 + space-1 row height. Multi Select
|
|
71
|
+
options use background-only focus feedback; its bulk text action is compact and neutral,
|
|
72
|
+
with no divider. Coarse-pointer targets retain the existing 44px minimum; the compact
|
|
73
|
+
Command Menu close button uses the shared TouchTarget hit region.
|
|
74
|
+
|
|
75
|
+
Context Switcher identifies the current context with a neutral check and regular-weight
|
|
76
|
+
labels, without selected fill or a stronger selected press layer. File Tree starts with
|
|
77
|
+
no selection and uses only subtle hover fill, retaining tree semantics and visible
|
|
78
|
+
keyboard focus. Folder icons are neutral. Secondary text Button hover changes its
|
|
79
|
+
background while keeping its border static. Public props and token values are unchanged.
|
|
80
|
+
|
|
81
|
+
## Tabs selection ownership
|
|
82
|
+
|
|
83
|
+
Owner approved 2026-10-06: `Tabs` accepts optional `value?: string`,
|
|
84
|
+
`defaultValue?: string`, and `onValueChange?: (id: string) => void`. Existing
|
|
85
|
+
consumers need no migration: without these props, selection starts at the first
|
|
86
|
+
enabled item and preserves automatic activation, roving tabindex, and availability
|
|
87
|
+
fallback. `defaultValue` seeds uncontrolled selection once. With `value` defined,
|
|
88
|
+
render derives selection from that value; a missing or disabled ID renders the first
|
|
89
|
+
enabled item without emitting a callback. All-disabled lists select nothing.
|
|
90
|
+
Click, ArrowLeft/ArrowRight, Home, and End notify activation; controlled selection
|
|
91
|
+
changes only when the parent updates `value`. Keyboard navigation follows the
|
|
92
|
+
focused enabled tab while the parent update is pending, and a later value update
|
|
93
|
+
does not pull focus from the activated button. Consumers own URL synchronization.
|
|
94
|
+
|
|
95
|
+
## Record activity (owner approved 2026-10-06)
|
|
96
|
+
|
|
97
|
+
Timeline / TimelineItem (ol/li, 8px token dot or Avatar), Marker (quiet actor/time, date separator), Message (default/pending/failed, caller actions) / Mention (inline action-text on selected surface), Composer (Tier A textarea combobox/listbox, IME guard, Enter send / Shift+Enter newline), Attachment (Progress, retry/remove) are public package components. `/patterns/record-activity` composes order facts, Switch, chronological activity and a pinned Composer. Existing tokens only; data, permissions, network and notifications remain caller-owned.
|
|
@@ -24,6 +24,9 @@ Adding a clause therefore means answering two questions before the wording: who
|
|
|
24
24
|
- [both] MUST use native links for navigation and native buttons for actions.
|
|
25
25
|
- [both] MUST keep visual and hit targets aligned; controls smaller than 24px need a larger interactive area.
|
|
26
26
|
- [both] MUST keep mobile control targets at least 44px where touch is expected.
|
|
27
|
+
- [both] MUST use `--q1-touch-target-min` (44px) for the shared centered invisible child-span hit region of Button (all sizes, including compact consumer overrides and icon-only), CopyButton, Menu/DotsMenu triggers, and Description info/hint buttons only under `(pointer: coarse), (any-pointer: coarse)`. Preserve every existing coarse-pointer visual-size rule. Only axes still below the minimum receive extra hit area; existing 44px targets do not expand outside their visual frame. The region covers the complete border box: canonical Button/CopyButton/Menu/DotsMenu borders are 1px, while Description borders are none. Consumers overriding a control border width must also set its private `--touch-target-border-width` to the actual per-side width (including zero for `border: none`); asymmetric borders need their own hit-region sizing. Focus rings stay on the visual control; child hit-region events activate that native control without replacing its pseudo-elements or overflow rules.
|
|
28
|
+
- [both] MUST leave enough space for expanded targets: dense neighbors must not overlap each other's visual frames. Consumers must reserve the expansion on each side and avoid clipping ancestors; if this is impossible, regroup controls or use larger visual controls. The shared region cannot infer neighbor gaps or escape control/ancestor clipping; clipped compact controls must use a visual size of at least 44px.
|
|
29
|
+
- [both] SHOULD deliberately depart from Geist's measured 24–32px controls without coarse-pointer enlargement: Apple HIG's 44pt guidance and WCAG 2.5.5 motivate q1travel's 44×44 CSS-pixel mobile usability target; pt and CSS pixels are different units.
|
|
27
30
|
- [both] NEVER disable browser zoom.
|
|
28
31
|
- [both] MUST avoid dead zones: if a region looks clickable, the clickable element must cover it.
|
|
29
32
|
- [both] MUST show a loading indicator on in-flight buttons while keeping the original label.
|
|
@@ -183,6 +186,12 @@ Theming owns tokens and `color-scheme`; the interaction-contrast rule lives in D
|
|
|
183
186
|
|
|
184
187
|
- [both] MUST be public, neutral, and specific.
|
|
185
188
|
- [both] MUST use consistent nouns across pages, navigation, tables, and examples.
|
|
189
|
+
- [both] SHOULD render code identifiers (classes, CSS variables, tokens, props, values, filenames, commands, and code values) with the existing inline `code` or `DocToken` treatment.
|
|
190
|
+
- [both] SHOULD translate generic English terms into Chinese and keep brand, product, and component names, and the term "Token", in plain text.
|
|
191
|
+
- [both] MUST separate Chinese characters from adjacent Latin letters or digits with a half-width space in authored prose; code contents, URLs, literal UI quotations, and code samples are excluded.
|
|
192
|
+
- [both] MUST use full-width `,。:;` immediately after Chinese characters in authored Chinese sentences; code contents, URLs, literal UI quotations, and code samples are excluded.
|
|
193
|
+
- [both] SHOULD use full-width `,。:;` throughout Chinese sentences, including after Latin words or inline code; identifying sentence language across mixed JSX nodes remains an editorial review item.
|
|
194
|
+
- [both] SHOULD follow common Chinese technical-writing practice for mixed-language wording; Microsoft's Chinese localization guidance is a reference example, not a proprietary q1travel rule. Semantic choices and text whose rendered role cannot be reliably inferred remain editorial review items rather than machine-enforced requirements.
|
|
186
195
|
- [both] SHOULD prefer short action-oriented labels.
|
|
187
196
|
- [both] MUST use active voice.
|
|
188
197
|
- [both] MUST use unambiguous action labels (「保存 API Key」, not 「继续」).
|
|
@@ -190,6 +199,35 @@ Theming owns tokens and `color-scheme`; the interaction-contrast rule lives in D
|
|
|
190
199
|
- [both] SHOULD default to positive, problem-solving language, including errors.
|
|
191
200
|
- [both] SHOULD use numerals for counts and keep number/unit formatting consistent.
|
|
192
201
|
- [product] MUST keep currency decimal places consistent within a context (0 or 2, never mixed).
|
|
202
|
+
- [both] MUST address the reader as 「您」, never 「你」.
|
|
203
|
+
- [both] MUST NOT end a button or link label with punctuation (「删除项目」, not 「删除项目?」).
|
|
204
|
+
- [both] MUST keep a Toast title to one short line of at most 15 Chinese characters, excluding interpolated values; details belong in the description or in a Banner.
|
|
205
|
+
- [both] SHOULD write button and link labels as a verb plus a noun (「保存草稿」「添加成员」) and name the action or destination; common single actions (「完成」「关闭」「取消」「添加」「删除」) may stand alone.
|
|
206
|
+
- [both] SHOULD compose an empty state from a title, an explanatory description, and an action. Use 「没有…」 for a no-data title (「没有成员」), without 「还」 or trailing punctuation; a first-use title may guide the reader with 「添加您的第一个…」 (「添加您的第一个产品」), also without trailing punctuation. Write the description as one sentence ending in 「。」 that explains when content will appear (「成员加入后将显示在此处。」), and name the action explicitly (「添加成员」, not 「开始使用」).
|
|
207
|
+
- [both] SHOULD offer 「清除筛选」 instead of a create action when filters hide existing records, and show a load error with a retry instead of an empty state when data failed to load.
|
|
208
|
+
- [both] SHOULD use a compact, centered empty state for an empty section, with a regular-weight title, secondary description, and a dashed token border; omit actions when no immediate action is available.
|
|
209
|
+
- [both] SHOULD resolve data states in the order loading → error → empty → content.
|
|
210
|
+
- [both] SHOULD give every flow a way forward and a way back (「取消」「返回」), and confirm costly or hard-to-reverse actions before running them.
|
|
211
|
+
|
|
212
|
+
These copy clauses follow the actual Stripe Chinese Dashboard for reader address and empty-state copy: address the reader as 「您」, omit trailing punctuation from empty-state titles, and explain when content will appear in the description.
|
|
213
|
+
|
|
214
|
+
### Component Naming
|
|
215
|
+
|
|
216
|
+
- [repo] SHOULD use the Geist component name when Geist has the component; otherwise use the industry name, preferring shadcn/ui, then Radix. Invent a name only when neither has one. Name what the component is, not how it is built.
|
|
217
|
+
- [repo] MUST switch public names directly to the new export without deprecated aliases or old documentation route redirects; record the removal as a breaking change in CHANGELOG.
|
|
218
|
+
|
|
219
|
+
### Component Documentation Pages
|
|
220
|
+
|
|
221
|
+
Component pages follow one skeleton so a reader who has used one page can find the same thing on every other. The shape follows the Stripe Apps component reference: one sentence says what the component is for, the fixed sections sit in a fixed order, and only the feature sections in the middle vary by component. Shared terms live in `.agents/skills/product-design/references/glossary.md`.
|
|
222
|
+
|
|
223
|
+
- [repo] MUST open each component page with one sentence that says what a reader uses the component for. It names no props, no implementation, and no 「输入…输出…」 pairing.
|
|
224
|
+
- [repo] MUST keep the fixed sections in this order: examples first, then feature sections, then 「API」, then 「使用原则」, then 「可访问性」 where the page has one, then 「另请参阅」 as the last section.
|
|
225
|
+
- [repo] MUST end each component page with 「另请参阅」, linking the components a reader is likely to confuse with this one or use beside it.
|
|
226
|
+
- [repo] MUST name a section that covers a shared concept with the glossary's section name (for example 「尺寸」, never 「小号」 or 「大小」).
|
|
227
|
+
- [repo] SHOULD name a component-specific section after the feature it covers, usually the prop it documents (「图标」, 「品牌名」), not after a concept or as a sentence; the explanation goes in the section description.
|
|
228
|
+
- [repo] SHOULD follow the opening sentence with one sentence that separates the component from its nearest neighbour (「和 Button 不同,Badge 只显示、不能点。」).
|
|
229
|
+
|
|
230
|
+
`scripts/check-shell.mjs` enforces the MUST clauses above on every component page.
|
|
193
231
|
|
|
194
232
|
## Agent Integration
|
|
195
233
|
|
|
@@ -4,6 +4,18 @@ Status: accepted
|
|
|
4
4
|
|
|
5
5
|
Decision date: 2026-08-26
|
|
6
6
|
|
|
7
|
+
## 2026-10-05 所有者批准的直接替换
|
|
8
|
+
|
|
9
|
+
本批跳过过渡流程,直接在 Token 层替换,不保留旧阴影或兼容档。以下取值取代后文的历史取值及方向性面板例外;后文保留为历史证据,不代表当前下游版本。
|
|
10
|
+
|
|
11
|
+
- 浅色 popover:1px gray-alpha-200 发丝边 + `0 4px 8px -4px` 黑 4% + `0 16px 24px -8px` 黑 6%。
|
|
12
|
+
- 暗色 popover:1px 白 alpha 发丝边 + 同几何黑 12% / 18%。
|
|
13
|
+
- 浅色 modal:同发丝边 + `0 8px 24px -12px` 黑 12% + `0 32px 72px -12px` 黑 6%。
|
|
14
|
+
- 暗色 modal:同发丝边 + 同几何黑 24% / 18%。
|
|
15
|
+
- Menu、Combobox、Calendar、Multi Select、Context Menu / Card / Switcher、Relative Time Card 删除额外实体边框,由 popover token 统一提供发丝边;Toast 使用同一 token。
|
|
16
|
+
- Drawer / Sheet 使用 modal token,删除组件自持重阴影及重复边框。
|
|
17
|
+
- 名称与消费方式保留;只改本仓,不声明下游已接入。升级后仍需明暗视觉复核;回滚单位仍为完整 token 声明。
|
|
18
|
+
|
|
7
19
|
关联:q1travel-design-system#140(B2-1)、q1travel-design-system#138(界面准则 · 设计章节)
|
|
8
20
|
|
|
9
21
|
这份合同冻结 `--q1-shadow-popover` 与 `--q1-shadow-modal` 由单层改为分层的取值、下游影响面与回滚单位。它**不修改 q1travel-web**,不创建下游迁移票,也不声称消费方已完成接入。
|