@q1travel/design-system 0.22.0 → 0.24.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/contracts/design.dark.md +33 -8
- package/contracts/design.md +33 -8
- package/contracts/implementation-map.md +44 -4
- package/contracts/interface-guidelines.md +3 -3
- package/contracts/token-naming.md +4 -0
- package/dist-pkg/design-system.d.ts +273 -39
- package/dist-pkg/design-system.js +6290 -5539
- package/dist-pkg/style.css +485 -20
- package/dist-pkg/tokens.css +8 -0
- package/package.json +2 -2
package/contracts/design.dark.md
CHANGED
|
@@ -119,6 +119,7 @@ colors:
|
|
|
119
119
|
pink-900: "#ff4d8d"
|
|
120
120
|
pink-1000: "#ffe9f4"
|
|
121
121
|
semantic-colors:
|
|
122
|
+
accent-graphic: "{colors.primary-900}"
|
|
122
123
|
action-primary: "{colors.primary}"
|
|
123
124
|
action-primary-hover: "{colors.primary-hover}"
|
|
124
125
|
action-primary-press: "{colors.primary-press}"
|
|
@@ -137,6 +138,7 @@ semantic-colors:
|
|
|
137
138
|
surface-media: "#0a0a0a"
|
|
138
139
|
text-on-media: "#ffffff"
|
|
139
140
|
border: "{colors.gray-400}"
|
|
141
|
+
border-control: "{colors.gray-600}"
|
|
140
142
|
border-strong: "{colors.gray-400}"
|
|
141
143
|
border-active: "{colors.primary-900}"
|
|
142
144
|
border-subtle: "#2a2a2a"
|
|
@@ -158,6 +160,8 @@ semantic-colors:
|
|
|
158
160
|
status-warning-text: "#fac053"
|
|
159
161
|
status-warning-surface: "{colors.amber-100}"
|
|
160
162
|
status-warning-border: "#754b10"
|
|
163
|
+
border-warning: "#754b10"
|
|
164
|
+
bg-warning-subtle: "{colors.amber-100}"
|
|
161
165
|
status-danger: "{colors.red-900}"
|
|
162
166
|
status-danger-text: "#f97770"
|
|
163
167
|
status-danger-surface: "{colors.red-100}"
|
|
@@ -430,34 +434,51 @@ These components ship in the package. Compose from this list before writing new
|
|
|
430
434
|
|
|
431
435
|
`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.
|
|
432
436
|
|
|
433
|
-
**
|
|
437
|
+
**Chat messages.** Bubble (incoming/outgoing, optional avatar/name/time, controlled pending/error and retry/actions slots) and MessageScroller (stable keyed items, near-bottom following, streaming growth, prepend anchor preservation, conversationKey reset, named native jump button and separate polite new-message announcements) are public components. They reuse existing tokens and Button; Message and Scroller APIs remain unchanged. Tier B uses native keyboard scrolling and buttons; callers own constrained height, message data, streaming, Markdown, tool cards, history cursors, permissions and network recovery.
|
|
438
|
+
|
|
439
|
+
**Record activity.** Timeline, TimelineItem, TimelineCollapsedGroup, Marker, Message, Mention, Composer, Attachment. Timeline uses chronological ol/li with tone dots or Avatar; TimelineCollapsedGroup uses a native disclosure for consecutive system events; 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
440
|
|
|
435
441
|
**Actions.** Button, ButtonLink, IconButton, SplitButton, LoadMoreButton, ClearButton, CopyButton, FilterChip, Link, Toolbar.
|
|
436
442
|
|
|
437
443
|
- 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.
|
|
438
444
|
- 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
|
|
445
|
+
- FilterChip opens a consumer-owned filter picker; undefined means unset, other values mean set. Its default sm 32px 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.
|
|
446
|
+
- FilterOptionPanel is searchable single-select content inside Popover, usually anchored by FilterChip. It owns initial search focus, named combobox/listbox, active-descendant arrow navigation with disabled skipping, loading/error/retry/empty/no-match states, and label/keywords includes filtering. Callers own selection, closing, business ranking and async queries through controlled query/onQueryChange and an injected filter; Escape dismissal and trigger focus return stay with Popover. It does not provide multiselect or an application footer.
|
|
440
447
|
- IconButton requires an accessible name. An icon-only control without one is not deliverable.
|
|
441
448
|
|
|
442
449
|
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, InputOTP, Textarea, Select, Fieldset, ErrorMessage.
|
|
443
450
|
|
|
444
451
|
- 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`.
|
|
445
452
|
- FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
|
|
453
|
+
- `labelHidden` defaults to false. For a single inline field already named by context, it visually hides the real associated label through `q1-sr-only` without reserving a label row or gap; description, visible error, and `aria-describedby` remain unchanged. A label is still required; bare mode rejects `labelHidden`. Input’s existing string `hideLabel` still names the password hide action.
|
|
454
|
+
- FormField positions its visually hidden label and empty live status within its own containing block so an overflowing inline row cannot enlarge the page scroll width.
|
|
446
455
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
447
456
|
|
|
448
|
-
**Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, Slider, RangeSlider.
|
|
457
|
+
**Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
|
|
458
|
+
|
|
459
|
+
**Checkbox task completion circle.** `Checkbox` adds `shape?: 'square' | 'circle'` (default square). Circle-only `tone?: 'neutral' | 'info' | 'warning' | 'danger'` defaults to neutral; `size?: 'sm' | 'md'` defaults to sm. 圆形只用于任务与清单的完成场景;表单多选一律用方形。 Native checkbox, ref, bare accessible names and Space activation remain intact; Enter does not toggle. Circle rejects indeterminate with a development warning. `readOnly` remains focusable, exposes aria-disabled and prevents toggles, hover previews and press feedback; completed readonly circles keep a desaturated fill and tick.
|
|
460
|
+
|
|
461
|
+
Circle geometry is private: sm derives 18 from space-4 + space-1 / 2, stroke 1.5 from space-1-5 / 4; md derives 24 from space-6, stroke 2 from space-1 / 2. No public diameter/stroke/dwell tokens are added. Fine hit areas use the native label plus a transparent target of at least space-6 (24); coarse targets use the shared child-span `q1-touch-target` with `--touch-target-border-width: 0px`. Align the circle with the first title line, not the multiline row; priority requires visible text with aria-describedby or an explicit accessible name.
|
|
462
|
+
|
|
463
|
+
Uncompleted circles use tone stroke + subtle surface (neutral transparent); completed fill follows tone, neutral uses text-secondary, and the tick uses text-on-inverse. Hover preview is opacity 0.5, keyboard focus shows only the focus ring, press scale is 0.9. Tick uses duration-fast/ease-state, fill duration-base/ease-out, press duration-press/ease-out. Reduced motion removes scale and tick fades, retaining only color transitions. The component owns only the circle; the collaboration task center owner chose completion in place with muted row text. Row gray/strike/remove and undo remain consumer-owned.
|
|
449
464
|
|
|
450
|
-
-
|
|
451
|
-
|
|
465
|
+
Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.329/5.329, info 4.618/4.618, warning 5.569/5.569, danger 4.046/4.046; dark neutral 5.793/5.382, info 7.416/6.890, warning 10.086/9.371, danger 6.020/5.593. Neutral border-strong failed (light 1.656/1.656; dark 1.483/1.378), so the circle uses existing text-muted (ratios above). All colored status tokens pass unchanged; no stronger token was added.
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
- Toggle represents one on/off setting and reports a requested change immediately. Square Checkbox stages a form change that a submit applies; circle Checkbox marks task completion through the consumer’s change handler. Choosing the wrong one misstates when the change takes effect.
|
|
469
|
+
- Switch selects among mutually exclusive views or modes; its outer sm / md / lg heights are 32 / 40 / 48px on fine pointers, defaulting to md.
|
|
470
|
+
- ToggleGroup uses pressed buttons for single or multiple choices; arrow keys move focus without selecting. Icon-only items require an accessible name. See `implementation-map.md` for selection ownership.
|
|
452
471
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
453
472
|
|
|
454
|
-
**Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
|
|
473
|
+
**Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays. The Calendar family defaults to range mode; `mode="single"` accepts `Date | null`. Explicit `withTime` edits local 24-hour time and displays `YYYY-MM-DD HH:mm`. Selection and time edits remain drafts until Apply; Escape/outside cancel, Clear commits null (or an empty range). `fieldSize` controls the trigger; `size` controls the grid.
|
|
455
474
|
|
|
456
|
-
**Overlays.** Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
|
|
475
|
+
**Overlays.** HoverCard, Popover, Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
|
|
457
476
|
|
|
477
|
+
- HoverCard presents supplemental noninteractive content on fine-pointer hover or keyboard focus after openDelay (default 0ms); closeDelay defaults to 90ms and entering the card cancels closing. Escape dismisses without moving focus into the card; aria-describedby connects the named native trigger to a nonmodal tooltip. Coarse/touch hover is ignored, trigger activation is preserved, and reduced-motion uses the shared Popover styles and anchored overlay foundation.
|
|
478
|
+
- Popover is a named anchored dialog for interactive content; it supports controlled/default open, four-side collision positioning plus controlled anchorRef placement, menu/listbox role focus, width matching, Escape focus return and outside pointer-down dismissal. Keyboard opening moves focus into content; pointer opening retains trigger focus and Tab enters the panel. It is nonmodal by default; opt-in modal locks the background and traps focus. ensurePopoverRoot exposes the same shared Foundation host for consumer-owned sibling portals; dismissExcludeRefs and nested panels preserve parent dismissal boundaries.
|
|
458
479
|
- Modal blocks and demands an answer. Use it for confirmation, not for a long form; a long form belongs in a Drawer or its own route.
|
|
459
480
|
- DestructiveActionModal is the required shape for irreversible actions. Do not rebuild that confirmation from a plain Modal.
|
|
460
|
-
- Sheet slides in from an edge with a focus trap; Drawer is the wider panel form. Menu is anchored and flips on collision; ContextMenu and DotsMenu share its keyboard engine.
|
|
481
|
+
- Sheet slides in from an edge with a focus trap; opt-in `mobileFullscreen` fills narrow viewports with safe-area padding and no outer inset or radius, while default floating spacing remains unchanged; Drawer is the wider panel form. Menu is anchored and flips on collision; ContextMenu and DotsMenu share its keyboard engine.
|
|
461
482
|
- Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
|
|
462
483
|
|
|
463
484
|
**Data and evidence.** Table, Pagination, DocsPagination, ShowMore, Chart, Gauge, Progress, JsonView, Entity, Description, RelativeTimeCard.
|
|
@@ -601,3 +622,7 @@ Before delivery, confirm each of these. They are observable; none of them is a m
|
|
|
601
622
|
In this repository, `npm run check` is the full gate; a partial run is not a pass. In consuming repositories, run their design and token gates before reporting completion.
|
|
602
623
|
|
|
603
624
|
公开 Foundation 集成:`getOverlayCoordinator` 协调 package 与宿主 overlay 的栈序、inert 与滚动锁;契约见 [Overlay Foundation](./overlay-foundation.md#11-o3-公开-host-协调扩展2026-09-08)。
|
|
625
|
+
|
|
626
|
+
### 单行控件尺寸
|
|
627
|
+
|
|
628
|
+
显式 sm / md / lg 的单行控件与 Button 对齐为 32 / 40 / 48px;使用现有 `--q1-space-8`、`--q1-control-height-default` 与 `--q1-space-2`。SearchInput 与 Menu 文字触发器默认 md,FilterChip 默认 sm32,Switch 外框 sm / md / lg 同高 32 / 40 / 48px,默认 md。Calendar 的 size 只控制网格,触发器使用 fieldSize。空或缺省 label 不创建可见标签行,消费方须提供可访问名称。粗指针最小 44px 命中规则保持现有契约。
|
package/contracts/design.md
CHANGED
|
@@ -119,6 +119,7 @@ colors:
|
|
|
119
119
|
pink-900: "#c41562"
|
|
120
120
|
pink-1000: "#460523"
|
|
121
121
|
semantic-colors:
|
|
122
|
+
accent-graphic: "{colors.primary-700}"
|
|
122
123
|
action-primary: "{colors.primary}"
|
|
123
124
|
action-primary-hover: "{colors.primary-hover}"
|
|
124
125
|
action-primary-press: "{colors.primary-press}"
|
|
@@ -137,6 +138,7 @@ semantic-colors:
|
|
|
137
138
|
surface-media: "#0a0a0a"
|
|
138
139
|
text-on-media: "#ffffff"
|
|
139
140
|
border: "{colors.gray-alpha-200}"
|
|
141
|
+
border-control: "{semantic-colors.border}"
|
|
140
142
|
border-strong: "{colors.gray-500}"
|
|
141
143
|
border-active: "{colors.primary-700}"
|
|
142
144
|
border-subtle: "rgba(203, 213, 225, 0.82)"
|
|
@@ -158,6 +160,8 @@ semantic-colors:
|
|
|
158
160
|
status-warning-text: "{colors.amber-900}"
|
|
159
161
|
status-warning-surface: "{colors.amber-100}"
|
|
160
162
|
status-warning-border: "#edc47d"
|
|
163
|
+
border-warning: "#edc47d"
|
|
164
|
+
bg-warning-subtle: "{colors.amber-100}"
|
|
161
165
|
status-danger: "{colors.red-700}"
|
|
162
166
|
status-danger-text: "#c62828"
|
|
163
167
|
status-danger-surface: "{colors.red-100}"
|
|
@@ -430,34 +434,51 @@ These components ship in the package. Compose from this list before writing new
|
|
|
430
434
|
|
|
431
435
|
`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.
|
|
432
436
|
|
|
433
|
-
**
|
|
437
|
+
**Chat messages.** Bubble (incoming/outgoing, optional avatar/name/time, controlled pending/error and retry/actions slots) and MessageScroller (stable keyed items, near-bottom following, streaming growth, prepend anchor preservation, conversationKey reset, named native jump button and separate polite new-message announcements) are public components. They reuse existing tokens and Button; Message and Scroller APIs remain unchanged. Tier B uses native keyboard scrolling and buttons; callers own constrained height, message data, streaming, Markdown, tool cards, history cursors, permissions and network recovery.
|
|
438
|
+
|
|
439
|
+
**Record activity.** Timeline, TimelineItem, TimelineCollapsedGroup, Marker, Message, Mention, Composer, Attachment. Timeline uses chronological ol/li with tone dots or Avatar; TimelineCollapsedGroup uses a native disclosure for consecutive system events; 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. Composer also accepts `onMentionQuery(query, {signal})` with 200ms debounce, abort/stale-result rejection, loading/empty/error states and keyboard retry; async lookup takes precedence over static members. Optional `placeholder` and `hint` preserve the input label; member instructions appear only with mention support. Han text may directly precede @, while Unicode word/email interiors do not trigger. Selection inserts text and sets the caret synchronously. Attachment reuses Progress and named removal. Network, permissions and notifications remain caller-owned.
|
|
434
440
|
|
|
435
441
|
**Actions.** Button, ButtonLink, IconButton, SplitButton, LoadMoreButton, ClearButton, CopyButton, FilterChip, Link, Toolbar.
|
|
436
442
|
|
|
437
443
|
- 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.
|
|
438
444
|
- 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
|
|
445
|
+
- FilterChip opens a consumer-owned filter picker; undefined means unset, other values mean set. Its default sm 32px 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.
|
|
446
|
+
- FilterOptionPanel is searchable single-select content inside Popover, usually anchored by FilterChip. It owns initial search focus, named combobox/listbox, active-descendant arrow navigation with disabled skipping, loading/error/retry/empty/no-match states, and label/keywords includes filtering. Callers own selection, closing, business ranking and async queries through controlled query/onQueryChange and an injected filter; Escape dismissal and trigger focus return stay with Popover. It does not provide multiselect or an application footer.
|
|
440
447
|
- IconButton requires an accessible name. An icon-only control without one is not deliverable.
|
|
441
448
|
|
|
442
449
|
**Text fields.** FormField, Label, Input, ClearableInput, SearchInput, InputOTP, Textarea, Select, Fieldset, ErrorMessage.
|
|
443
450
|
|
|
444
451
|
- 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`.
|
|
445
452
|
- FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
|
|
453
|
+
- `labelHidden` defaults to false. For a single inline field already named by context, it visually hides the real associated label through `q1-sr-only` without reserving a label row or gap; description, visible error, and `aria-describedby` remain unchanged. A label is still required; bare mode rejects `labelHidden`. Input’s existing string `hideLabel` still names the password hide action.
|
|
454
|
+
- FormField positions its visually hidden label and empty live status within its own containing block so an overflowing inline row cannot enlarge the page scroll width.
|
|
446
455
|
- Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
|
|
447
456
|
|
|
448
|
-
**Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, Slider, RangeSlider.
|
|
457
|
+
**Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
|
|
458
|
+
|
|
459
|
+
**Checkbox task completion circle.** `Checkbox` adds `shape?: 'square' | 'circle'` (default square). Circle-only `tone?: 'neutral' | 'info' | 'warning' | 'danger'` defaults to neutral; `size?: 'sm' | 'md'` defaults to sm. 圆形只用于任务与清单的完成场景;表单多选一律用方形。 Native checkbox, ref, bare accessible names and Space activation remain intact; Enter does not toggle. Circle rejects indeterminate with a development warning. `readOnly` remains focusable, exposes aria-disabled and prevents toggles, hover previews and press feedback; completed readonly circles keep a desaturated fill and tick.
|
|
460
|
+
|
|
461
|
+
Circle geometry is private: sm derives 18 from space-4 + space-1 / 2, stroke 1.5 from space-1-5 / 4; md derives 24 from space-6, stroke 2 from space-1 / 2. No public diameter/stroke/dwell tokens are added. Fine hit areas use the native label plus a transparent target of at least space-6 (24); coarse targets use the shared child-span `q1-touch-target` with `--touch-target-border-width: 0px`. Align the circle with the first title line, not the multiline row; priority requires visible text with aria-describedby or an explicit accessible name.
|
|
462
|
+
|
|
463
|
+
Uncompleted circles use tone stroke + subtle surface (neutral transparent); completed fill follows tone, neutral uses text-secondary, and the tick uses text-on-inverse. Hover preview is opacity 0.5, keyboard focus shows only the focus ring, press scale is 0.9. Tick uses duration-fast/ease-state, fill duration-base/ease-out, press duration-press/ease-out. Reduced motion removes scale and tick fades, retaining only color transitions. The component owns only the circle; the collaboration task center owner chose completion in place with muted row text. Row gray/strike/remove and undo remain consumer-owned.
|
|
449
464
|
|
|
450
|
-
-
|
|
451
|
-
|
|
465
|
+
Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.329/5.329, info 4.618/4.618, warning 5.569/5.569, danger 4.046/4.046; dark neutral 5.793/5.382, info 7.416/6.890, warning 10.086/9.371, danger 6.020/5.593. Neutral border-strong failed (light 1.656/1.656; dark 1.483/1.378), so the circle uses existing text-muted (ratios above). All colored status tokens pass unchanged; no stronger token was added.
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
- Toggle represents one on/off setting and reports a requested change immediately. Square Checkbox stages a form change that a submit applies; circle Checkbox marks task completion through the consumer’s change handler. Choosing the wrong one misstates when the change takes effect.
|
|
469
|
+
- Switch selects among mutually exclusive views or modes; its outer sm / md / lg heights are 32 / 40 / 48px on fine pointers, defaulting to md.
|
|
470
|
+
- ToggleGroup uses pressed buttons for single or multiple choices; arrow keys move focus without selecting. Icon-only items require an accessible name. See `implementation-map.md` for selection ownership.
|
|
452
471
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
453
472
|
|
|
454
|
-
**Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
|
|
473
|
+
**Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays. The Calendar family defaults to range mode; `mode="single"` accepts `Date | null`. Explicit `withTime` edits local 24-hour time and displays `YYYY-MM-DD HH:mm`. Selection and time edits remain drafts until Apply; Escape/outside cancel, Clear commits null (or an empty range). `fieldSize` controls the trigger; `size` controls the grid.
|
|
455
474
|
|
|
456
|
-
**Overlays.** Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
|
|
475
|
+
**Overlays.** HoverCard, Popover, Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
|
|
457
476
|
|
|
477
|
+
- HoverCard presents supplemental noninteractive content on fine-pointer hover or keyboard focus after openDelay (default 0ms); closeDelay defaults to 90ms and entering the card cancels closing. Escape dismisses without moving focus into the card; aria-describedby connects the named native trigger to a nonmodal tooltip. Coarse/touch hover is ignored, trigger activation is preserved, and reduced-motion uses the shared Popover styles and anchored overlay foundation.
|
|
478
|
+
- Popover is a named anchored dialog for interactive content; it supports controlled/default open, four-side collision positioning plus controlled anchorRef placement, menu/listbox role focus, width matching, Escape focus return and outside pointer-down dismissal. Keyboard opening moves focus into content; pointer opening retains trigger focus and Tab enters the panel. It is nonmodal by default; opt-in modal locks the background and traps focus. ensurePopoverRoot exposes the same shared Foundation host for consumer-owned sibling portals; dismissExcludeRefs and nested panels preserve parent dismissal boundaries.
|
|
458
479
|
- Modal blocks and demands an answer. Use it for confirmation, not for a long form; a long form belongs in a Drawer or its own route.
|
|
459
480
|
- DestructiveActionModal is the required shape for irreversible actions. Do not rebuild that confirmation from a plain Modal.
|
|
460
|
-
- Sheet slides in from an edge with a focus trap; Drawer is the wider panel form. Menu is anchored and flips on collision; ContextMenu and DotsMenu share its keyboard engine.
|
|
481
|
+
- Sheet slides in from an edge with a focus trap; opt-in `mobileFullscreen` fills narrow viewports with safe-area padding and no outer inset or radius, while default floating spacing remains unchanged; Drawer is the wider panel form. Menu is anchored and flips on collision; ContextMenu and DotsMenu share its keyboard engine. DotsMenu accepts contextual `label` (default 更多操作) for trigger and menu, plus `{type:'separator'}` entries using the shared Menu separator; roving focus and typeahead skip separators and disabled items.
|
|
461
482
|
- Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
|
|
462
483
|
|
|
463
484
|
**Data and evidence.** Table, Pagination, DocsPagination, ShowMore, Chart, Gauge, Progress, JsonView, Entity, Description, RelativeTimeCard.
|
|
@@ -601,3 +622,7 @@ Before delivery, confirm each of these. They are observable; none of them is a m
|
|
|
601
622
|
In this repository, `npm run check` is the full gate; a partial run is not a pass. In consuming repositories, run their design and token gates before reporting completion.
|
|
602
623
|
|
|
603
624
|
公开 Foundation 集成:`getOverlayCoordinator` 协调 package 与宿主 overlay 的栈序、inert 与滚动锁;契约见 [Overlay Foundation](./overlay-foundation.md#11-o3-公开-host-协调扩展2026-09-08)。
|
|
625
|
+
|
|
626
|
+
### 单行控件尺寸
|
|
627
|
+
|
|
628
|
+
显式 sm / md / lg 的单行控件与 Button 对齐为 32 / 40 / 48px;使用现有 `--q1-space-8`、`--q1-control-height-default` 与 `--q1-space-2`。SearchInput 与 Menu 文字触发器默认 md,FilterChip 默认 sm32,Switch 外框 sm / md / lg 同高 32 / 40 / 48px,默认 md。Calendar 的 size 只控制网格,触发器使用 fieldSize。空或缺省 label 不创建可见标签行,消费方须提供可访问名称。粗指针最小 44px 命中规则保持现有契约。
|
|
@@ -10,7 +10,13 @@ README owns the repository inventory and generated-file boundary. This file owns
|
|
|
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
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
|
-
|
|
|
13
|
+
| Anchored interactive content | Consumer-owned inline dialog | `Popover`, `ensurePopoverRoot`, `FilterChip`, shared overlay foundation | Panel background, popover shadow, radius, spacing and motion tokens | Controlled/default open or external anchorRef; menu/listbox role focus; nested/excluded targets, focusout and anchor visibility dismissal; retained exit animation | Four-side collision plus auto/fixed placement; preferred-height flip, viewport clamp, matchWidth and ancestor scroll; fixed shared portal | Named dialog, expanded/controls wiring, focus return; opt-in modal trap |
|
|
14
|
+
| Single date and optional time | Calendar family discriminated single mode | `Calendar`, `CalendarGrid`, `RangeInputs`, `RangeField` | Existing calendar/control tokens | Apply commits Date or null; Escape/outside discard draft; explicit withTime uses local 24h HH:mm | fieldSize remains separate from grid size | Shared APG grid/dialog and named time inputs |
|
|
15
|
+
| Mobile full-viewport details | Opt-in Sheet mobileFullscreen | `Sheet` | Existing spacing/radius/surface tokens and safe-area env | Default floating inset and overlay lifecycle preserved | Narrow viewport opt-in removes inset/radius and fills viewport | Existing focus trap, Escape and return focus |
|
|
16
|
+
| Hoverable supplemental information | Read-only member or role preview | `HoverCard`, shared anchored overlay foundation | Panel background, popover shadow, radius, spacing and motion tokens | Fine-pointer hover or keyboard focus; configurable 0ms open / 90ms close; card entry cancels close; Escape dismissal; trigger click preserved | Shared collision positioning and portal; coarse/touch hover ignored | Named native trigger, aria-describedby tooltip, no focus transfer or trap, reduced motion |
|
|
17
|
+
| Anchored interactive content | Consumer-owned inline dialog | `Popover`, `ensurePopoverRoot`, `FilterChip`, shared overlay foundation | Panel background, popover shadow, radius, spacing and motion tokens | Controlled/default open or external anchorRef (FilterChip ref forwards to picker); menu/listbox role focus; nested/excluded targets, focusout and anchor visibility dismissal; retained exit animation | Four-side collision plus auto/fixed placement; preferred-height flip, viewport clamp, matchWidth and ancestor scroll; fixed shared portal | Named dialog, expanded/controls wiring, focus return; opt-in modal trap |
|
|
18
|
+
| Compact dashboard filters | Consumer-owned filter picker | `FilterChip` | `--q1-space-8` (default sm32), 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 |
|
|
19
|
+
| Searchable filter choices | Consumer-owned single-select content | `FilterOptionPanel`, `Popover`, `FilterChip` | Existing control, surface, text, radius, spacing and focus tokens | Label/keywords includes or injected filter; controlled query; loading/error/retry/empty/no-match; select callback | Viewport-bound width and scroll list; coarse option height | Named combobox/listbox, initial search focus, aria-activedescendant, disabled skipping; Popover Escape returns to anchor |
|
|
14
20
|
| 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 |
|
|
15
21
|
| 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 |
|
|
16
22
|
| 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 |
|
|
@@ -18,7 +24,7 @@ README owns the repository inventory and generated-file boundary. This file owns
|
|
|
18
24
|
| Icon matrix preview | Icon component density sample | `showcaseIconEntries` rendered with Lucide icons | `currentColor`, text-secondary token | None | Grid clamps inside tile | Icons are decorative in the tile, named on the Icon component page |
|
|
19
25
|
| Component preview tile | Control density sample | Custom preview composition using component token mappings | Surface, border, action, focus, radius tokens | Button and segment visual states | Flex wrap preserves compact layout | Decorative in intro, real controls documented on component pages |
|
|
20
26
|
| Color rods preview | Color scale summary | Custom preview composition using color scale aliases | `--q1-color-scale-*` aliases | None | Rods distribute across available width | Semantic labels identify the represented scale |
|
|
21
|
-
| Color page reference and semantic tokens | Token reference and theme value copy | `ColorSwatch`, `SemanticColorListItem`, and `SemanticColorDemo` inside `ColorsPage` | `--q1-color-*`, semantic color tokens, and semantic action tokens | Left click copies token, context menu copies resolved value. The focus shadow token shows as an applied focus ring, not a flat color swatch | Ten-step grid and semantic rows adapt to available width | Button labels, semantic rows, live copy feedback |
|
|
27
|
+
| Color page reference and semantic tokens | Token reference and theme value copy | `ColorSwatch`, `SemanticColorListItem`, and `SemanticColorDemo` inside `ColorsPage` | `--q1-color-*`, semantic color tokens (including `--q1-color-border-warning` and `--q1-color-bg-warning-subtle`), and semantic action tokens | Left click copies token, context menu copies resolved value. The focus shadow token shows as an applied focus ring, not a flat color swatch | Ten-step grid and semantic rows adapt to available width | Button labels, semantic rows, live copy feedback |
|
|
22
28
|
| Optical alignment | Aligned-looking media, icons, labels, and column edges | `ContextSwitcher` two-line rows, `Navigation` icons and chevron, `Table` edge columns and actions column, `Badge` label wrapper, `Button` affix | Optical tokens: `--q1-optical-icon-shift-cjk-label`, `--q1-optical-media-first-line-offset`, `--q1-surface-inset-inline`, `--q1-table-edge-padding-inline`, `--q1-button-hang-inline`, `--q1-badge-optical-shift`, `--q1-button-affix-optical-y` | Shifts use standalone `translate`; rotations and layout stay intact | Integer-pixel values only; locked by the `check:browser` optical fixture | Wrapper and class changes add no accessible names or roles |
|
|
23
29
|
| Page footer navigation | Sequential documentation reading | `PageNavigation` | Surface, border, text, spacing tokens | Hover, focus, feedback buttons | Three-column footer collapses with content | Links are named and feedback controls are buttons |
|
|
24
30
|
|
|
@@ -28,7 +34,7 @@ README owns the repository inventory and generated-file boundary. This file owns
|
|
|
28
34
|
- New public tokens use `--q1-*`; existing product `--ds-*` names are legacy migration inputs, not new contract names.
|
|
29
35
|
- Canonical components own repeated structure. Page-local custom structures are allowed only for visual specimens with a narrow purpose.
|
|
30
36
|
- Token references are the design source for production code. Current theme resolved values are clipboard output only.
|
|
31
|
-
- The
|
|
37
|
+
- The public documentation navigation groups are `基础`, `品牌`, and `组件`. Product compositions are owned by q1travel-web.
|
|
32
38
|
- If a capability is missing from a shared component, add the capability to that component before adding another page-local pattern.
|
|
33
39
|
|
|
34
40
|
## Documentation Section Ownership
|
|
@@ -70,7 +76,7 @@ Command Menu cycles Tab between its search input and sole Esc keycap close butto
|
|
|
70
76
|
commands use text without leading arrows and space-8 + space-1 row height. Multi Select
|
|
71
77
|
options use background-only focus feedback; its bulk text action is compact and neutral,
|
|
72
78
|
with no divider. Coarse-pointer targets retain the existing 44px minimum; the compact
|
|
73
|
-
Command Menu close button uses the shared TouchTarget hit region.
|
|
79
|
+
Command Menu close button uses the shared TouchTarget hit region. DotsMenu accepts contextual `label` (default 更多操作) for trigger and menu, plus `{type:'separator'}` entries using the shared Menu separator; roving focus and typeahead skip separators and disabled items.
|
|
74
80
|
|
|
75
81
|
Context Switcher identifies the current context with a neutral check and regular-weight
|
|
76
82
|
labels, without selected fill or a stronger selected press layer. File Tree starts with
|
|
@@ -94,4 +100,38 @@ does not pull focus from the activated button. Consumers own URL synchronization
|
|
|
94
100
|
|
|
95
101
|
## Record activity (owner approved 2026-10-06)
|
|
96
102
|
|
|
103
|
+
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. TimelineCollapsedGroup uses a native disclosure button with aria-expanded and Enter/Space; callers group runs of consecutive system events (docs example uses three or more). Marker unread uses centered action text; caller places and scrolls to the boundary. Message acknowledgements and its caller-owned actions slot display receipt state. Composer also accepts `onMentionQuery(query, {signal})` with 200ms debounce, abort/stale-result rejection, loading/empty/error states and keyboard retry; async lookup takes precedence over static members. Optional `placeholder` and `hint` preserve the input label; member instructions appear only with mention support. Han text may directly precede @, while Unicode word/email interiors do not trigger. Selection inserts text and sets the caret synchronously. Composer commands share the mention combobox/listbox keyboard model and IME guard; known slash commands invoke onCommand instead of onSubmit. Attachment paste/drop passes File[] to onAttach; legacy clicks remain argument-free. Callers own the file picker and object URLs and revoke previews on removal or unmount. Existing tokens only; data, permissions, network and notifications remain caller-owned.
|
|
97
104
|
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.
|
|
105
|
+
|
|
106
|
+
## ToggleGroup selection ownership
|
|
107
|
+
|
|
108
|
+
Owner approved 2026-10-07: public `ToggleGroup` uses the shadcn/Radix name (Geist has no corresponding component). Its `items` API exposes `value`, `label`, optional `icon`, `disabled`, and `ariaLabel`; icon-only items require an icon and `ariaLabel`. `type="single"` accepts string `value` / `defaultValue` and `onValueChange`; `allowEmpty` defaults to false. `type="multiple"` accepts string arrays. Controlled values remain caller-owned; uncontrolled single selection falls back to the first enabled item when its value is absent or unknown and empty selection is disallowed. Disabling an existing selected item retains its pressed state and moves only the tabstop. No existing Switch consumer is migrated.
|
|
109
|
+
|
|
110
|
+
Native buttons expose `aria-pressed` within `role="group"`. Roving tabindex and Arrow / Home / End move focus without selecting; Enter / Space activate through native clicks. Disabled items are skipped. Button-aligned sm32 / md40 / lg48 sizes use existing tokens, quiet selected background and primary text without a selected ring, shared focus shadow, and the shared coarse-pointer 44px TouchTarget with reserved neighbor space. The Tier A keyboard contract lives on `/components/toggle-group`; Switch retains radio semantics and Tabs retains panel semantics.
|
|
111
|
+
## Checkbox task completion circle
|
|
112
|
+
- Native base: `src/components/ui/ControlOption.tsx`; circle-only readonly/warning module: `src/components/ui/CircleCheckbox.tsx`; independent style: `src/styles/checkbox-circle.css`, imported at the top of global.css and included by build-style.mjs. Square defaults remain unchanged.
|
|
113
|
+
- Documentation: `src/pages/CheckboxPage.tsx`, `CheckboxCircleDemo.tsx`, `src/styles/checkbox-circle-specimen.css`; circle completion is restricted to tasks/checklists and row completion treatment stays consumer-owned.
|
|
114
|
+
- Verification: `scripts/check-checkbox-circle-contract.test.mjs`, `scripts/fixtures/checkbox-circle-api-consumer.tsx`, `scripts/lib/checkbox-circle-contrast.mjs`, `scripts/lib/check-checkbox-circle-browser.mjs`. Package gate covers API/default/native markup/readonly semantics/token existence and both-theme contrast; browser gate covers native toggles, focus without preview, readonly, tone fill, reduced motion and coarse hit targets.
|
|
115
|
+
## Chat messages
|
|
116
|
+
Bubble is separate from Message: chat has optional identity and direction, while Message retains its required author, comment attachments and receipt API unchanged. MessageScroller is separate from Scroller: keyed prepend anchors and message following need ownership of the viewport; the general Scroller API remains unchanged. The shadcn chat reference supplies conversation intent only; quiet Geist surfaces map to existing q1travel panel/selected backgrounds, borders, spacing, radii and Button. No reference CSS, motion background or private tokens are shipped.
|
|
117
|
+
The consumer mapping was checked against q1travel-web origin/main: `src/components/AssistantDrawer.jsx`, `src/components/AssistantDrawer.css`, `src/components/copilot/copilot.css`, and `src/components/assistant/useAssistantConversation.js` (local ref, no downstream edits).
|
|
118
|
+
| Web capability | Package support / caller boundary |
|
|
119
|
+
| --- | --- |
|
|
120
|
+
| message role user / assistant | Bubble direction outgoing / incoming |
|
|
121
|
+
| message content and same-ID thinking/streaming updates | Bubble children ReactNode; MessageScroller items `{ id: message.id, content: renderedBubble }`; ResizeObserver follows content growth only while following |
|
|
122
|
+
| deliveryStatus thinking / streaming / pending | Caller maps to Bubble status pending and supplies pending text; final content/status remains controlled |
|
|
123
|
+
| failures and interrupted-stream reconnect | Bubble error/retry/onRetry slots; caller retains idempotent recovery and decides whether retry is permitted |
|
|
124
|
+
| Markdown, skill prefix, steps, toolCalls, proposals, attachments | children/actions accept existing caller-rendered nodes; parsing, tool dispatch, permissions and confirmation remain caller-owned |
|
|
125
|
+
| loadEarlierMessages / loadingEarlierMessages / messageCursor | before accepts caller loading button; prepend stable IDs preserves the visible message anchor |
|
|
126
|
+
| compact / expanded message viewport | MessageScroller className/style accepts bounded sizing; no window scrollIntoView |
|
|
127
|
+
| conversation switching / new conversation | conversationKey resets follow state, visible anchor and announcement |
|
|
128
|
+
| scroll-to-latest and streaming narration | named jumpLabel button, labelled region, separate polite announcement; caller can pass completed-turn text or empty announcement to silence |
|
|
129
|
+
Keyboard: native viewport Arrow/PageUp/PageDown/Home/End; Tab reaches the viewport and buttons; Enter/Space activates retry or jump. Jump moves focus to the viewport before its button disappears. No custom composite widget keyboard model is introduced.
|
|
130
|
+
|
|
131
|
+
## Hidden field labels
|
|
132
|
+
|
|
133
|
+
Owner approved 2026-10-08: additive `labelHidden?: boolean` (default false) keeps a real associated label through `q1-sr-only` while removing its visual row and gap. Input, Textarea, Select, InputOTP and Checkbox/Radio (including circular Checkbox) expose the shell option; bare mode rejects it. SearchInput and Combobox compositions use `FormField labelHidden`, preserving their existing APIs. Calendar/RangeField does not consume FieldShellProps and retains its existing date-trigger behavior. Errors and descriptions remain visible and connected through `aria-describedby`. Input's existing string `hideLabel` remains the password hide-action copy.
|
|
134
|
+
|
|
135
|
+
`FormField` establishes a relative containing block for hidden labels and empty status regions. Package consumer fixtures enforce label ownership, SSR contracts cover names/messages, and `scripts/lib/check-label-hidden-browser.mjs` checks native labels, visible errors, inline Button alignment and a deterministic 390px scrollable max-content row without page overflow.
|
|
136
|
+
## Control density scale
|
|
137
|
+
Owner approved 2026-10-08: explicitly sized single-line Button, ButtonLink, Input, SearchInput, Select, RangeField trigger, Menu text trigger and FilterChip share sm32 / md40 / lg48 on fine pointers. SearchInput defaults to md and retains native numeric size compatibility; FilterChip defaults to sm32. Menu text triggers default to md40 after the rendered fine-pointer audit confirmed the prior omitted-size trigger measured 38–39px; icon triggers retain their prior behavior. Calendar size remains grid-only; fieldSize controls its trigger. Switch outer sm / md / lg heights align to 32 / 40 / 48px, defaulting to md; coarse-pointer 44px rules remain unchanged. FormField and Select omit empty or absent visible labels; callers supply aria-label, aria-labelledby or a native external label. Select reuses the development-only bare-field accessible-name warning.
|
|
@@ -24,7 +24,7 @@ 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,
|
|
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, Description info/hint buttons, and the independent FilterChip picker/clear 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. FilterChip keeps its default sm32 visual height and existing gaps/margins; its picker and clear hit regions must not overlap. Menu item rows use a real `min-height: var(--q1-touch-target-min)` under the same coarse-pointer query so neighboring rows remain independent. Focus rings stay on the visual control; child hit-region events activate that native control without replacing its pseudo-elements or overflow rules.
|
|
28
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
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.
|
|
30
30
|
- [both] NEVER disable browser zoom.
|
|
@@ -209,7 +209,7 @@ Theming owns tokens and `color-scheme`; the interaction-contrast rule lives in D
|
|
|
209
209
|
- [both] SHOULD resolve data states in the order loading → error → empty → content.
|
|
210
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
211
|
|
|
212
|
-
These copy clauses
|
|
212
|
+
These copy clauses keep reader address and empty states consistent with a formal business console: address the reader as 「您」, omit trailing punctuation from empty-state titles, and explain when content will appear in the description.
|
|
213
213
|
|
|
214
214
|
### Component Naming
|
|
215
215
|
|
|
@@ -218,7 +218,7 @@ These copy clauses follow the actual Stripe Chinese Dashboard for reader address
|
|
|
218
218
|
|
|
219
219
|
### Component Documentation Pages
|
|
220
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
|
|
221
|
+
Component pages follow one skeleton so a reader who has used one page can find the same thing on every other. The shape keeps every page predictable: 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
222
|
|
|
223
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
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.
|
|
@@ -102,12 +102,16 @@ Examples:
|
|
|
102
102
|
|
|
103
103
|
`--q1-color-status-danger` is the bright red for fills and strokes only; use `--q1-color-status-danger-text` for text and weak notices without an underline. `--q1-color-status-danger-surface` and `--q1-color-status-danger-border` keep the background and stroke roles separate. `--q1-color-text-soft`, `--q1-color-border-subtle`, and `--q1-color-overlay-scrim` cover tertiary text, quiet separators, and overlay scrims.
|
|
104
104
|
|
|
105
|
+
`--q1-color-border-warning` and `--q1-color-bg-warning-subtle` name the warning border and subtle background by role. They are equivalent to the existing `--q1-color-status-warning-border` and `--q1-color-status-warning-surface`; the background uses amber-100 in both themes, matching the success/danger subtle-surface mapping. Existing status names remain valid. Use `--q1-color-status-warning-text` on the subtle background for AA normal text. The border is a quiet decorative outline, not a standalone control boundary or the sole warning cue; pair it with warning text or an icon.
|
|
106
|
+
|
|
105
107
|
Owner-approved exception (2026-10-05): ErrorMessage, field errors, and ErrorCard titles use `--q1-color-red-900`; other status text retains its existing semantic text token.
|
|
106
108
|
|
|
107
109
|
`--q1-color-focus` is neutral black in light mode and white in dark mode. `--q1-shadow-focus` and `--q1-shadow-focus-input` use a gapless 1px ring at 34% plus a 4px halo at 16%. Buttons and inputs consume this recipe; input focus preserves the resting border, without an active-border swap or inset stroke. `--q1-input-border-focus` remains an opt-in override and defaults to the resting input border. Invalid inputs retain their danger border; `--q1-shadow-focus-input-danger` and `--q1-shadow-focus-danger` use the same 1px / 4px geometry with danger color at 34% / 16%. Text links retain the 3px neutral focus underline. Forced colors and high contrast retain visible system outlines.
|
|
108
110
|
|
|
109
111
|
Migration: `--q1-color-bg-panel` remains `#ffffff` in light mode and changes from `#0a0a0a` to `#1a1a1a` in dark mode. The dark canvas, raised surface, and overlay now use `#121212`, `#222222`, and `#2b2b2b`. Downstream overrides that manually raised panel or overlay backgrounds should be reviewed. The 2026-10-05 owner-approved focus change replaces the brand ring and inset input stroke with the neutral 1px / 4px recipe and preserves resting input borders. Consumers with custom focus visuals should update their screenshots and forced-colors fallback. The unused `--q1-shadow-card` name is removed before release.
|
|
110
112
|
|
|
113
|
+
Owner-approved contrast roles (2026-10-07): `--q1-color-border-control` is the resting form-control boundary (light aliases `border`, dark uses `gray-600`); controls with a strong light border retain that value and opt into this role only in dark mode. Decorative borders retain `border` / `border-strong`. `--q1-color-accent-graphic` is for brand-colored non-text graphics (light `primary-700`, dark `primary-900`), independent of button action colors. Slider dark unfilled tracks use `gray-500`.
|
|
114
|
+
|
|
111
115
|
`--q1-touch-target-min` (`44px`) is the theme-independent semantic minimum for coarse-pointer hit regions, stored as `sizing.touch-target-min` in both theme contracts. It changes the invisible target, not the visual size.
|
|
112
116
|
|
|
113
117
|
### Component Tokens
|