@q1travel/design-system 0.22.0 → 0.23.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.
@@ -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,7 +434,9 @@ 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
- **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.
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
 
@@ -445,19 +451,31 @@ These components ship in the package. Compose from this list before writing new
445
451
  - FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
446
452
  - Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
447
453
 
448
- **Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, Slider, RangeSlider.
454
+ **Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
455
+
456
+ **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.
457
+
458
+ 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.
459
+
460
+ 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.
461
+
462
+ 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.
463
+
449
464
 
450
- - Toggle represents one on/off setting and reports a requested change immediately. Checkbox stages a change that a submit applies. Choosing the wrong one misstates when the change takes effect.
465
+ - 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.
451
466
  - Switch selects among mutually exclusive views or modes.
467
+ - 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
468
  - RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
453
469
 
454
- **Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
470
+ **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
471
 
456
- **Overlays.** Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
472
+ **Overlays.** HoverCard, Popover, Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
457
473
 
474
+ - 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.
475
+ - 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
476
  - 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
477
  - 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.
478
+ - 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
479
  - Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
462
480
 
463
481
  **Data and evidence.** Table, Pagination, DocsPagination, ShowMore, Chart, Gauge, Progress, JsonView, Entity, Description, RelativeTimeCard.
@@ -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,7 +434,9 @@ 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
- **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.
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
 
@@ -445,19 +451,31 @@ These components ship in the package. Compose from this list before writing new
445
451
  - FormField owns the label, help text, and error slot. Do not assemble those three around a bare Input.
446
452
  - Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
447
453
 
448
- **Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, Slider, RangeSlider.
454
+ **Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
455
+
456
+ **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.
457
+
458
+ 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.
459
+
460
+ 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.
461
+
462
+ 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.
463
+
449
464
 
450
- - Toggle represents one on/off setting and reports a requested change immediately. Checkbox stages a change that a submit applies. Choosing the wrong one misstates when the change takes effect.
465
+ - 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.
451
466
  - Switch selects among mutually exclusive views or modes.
467
+ - 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
468
  - RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
453
469
 
454
- **Dates and ranges.** Calendar, CalendarGrid, RangeInputs, RangeField, PeriodCombobox, addDays.
470
+ **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
471
 
456
- **Overlays.** Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
472
+ **Overlays.** HoverCard, Popover, Modal, ModalInset, DestructiveActionModal, Drawer, Sheet, Menu, ContextMenu, DotsMenu, Tooltip, ContextCard, SearchDialog, CommandMenu.
457
473
 
474
+ - 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.
475
+ - 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
476
  - 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
477
  - 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.
478
+ - 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
479
  - Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
462
480
 
463
481
  **Data and evidence.** Table, Pagination, DocsPagination, ShowMore, Chart, Gauge, Progress, JsonView, Entity, Description, RelativeTimeCard.
@@ -10,6 +10,11 @@ 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
+ | 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 |
13
18
  | 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 |
14
19
  | 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
20
  | 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 |
@@ -18,7 +23,7 @@ README owns the repository inventory and generated-file boundary. This file owns
18
23
  | 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
24
  | 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
25
  | 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 |
26
+ | 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
27
  | 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
28
  | 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
29
 
@@ -28,7 +33,7 @@ README owns the repository inventory and generated-file boundary. This file owns
28
33
  - New public tokens use `--q1-*`; existing product `--ds-*` names are legacy migration inputs, not new contract names.
29
34
  - Canonical components own repeated structure. Page-local custom structures are allowed only for visual specimens with a narrow purpose.
30
35
  - Token references are the design source for production code. Current theme resolved values are clipboard output only.
31
- - The same public navigation groups remain in the product surface: `基础`, `品牌`, `组件`, and `模式`; `模式` owns compositions across components, starting with `/patterns/admin`.
36
+ - The public documentation navigation groups are `基础`, `品牌`, and `组件`. Product compositions are owned by q1travel-web.
32
37
  - If a capability is missing from a shared component, add the capability to that component before adding another page-local pattern.
33
38
 
34
39
  ## Documentation Section Ownership
@@ -94,4 +99,30 @@ does not pull focus from the activated button. Consumers own URL synchronization
94
99
 
95
100
  ## Record activity (owner approved 2026-10-06)
96
101
 
102
+ 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 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
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. `/patterns/record-activity` composes order facts, Switch, chronological activity and a pinned Composer. Existing tokens only; data, permissions, network and notifications remain caller-owned.
104
+
105
+ ## ToggleGroup selection ownership
106
+
107
+ 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.
108
+
109
+ 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.
110
+ ## Checkbox task completion circle
111
+ - 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.
112
+ - 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.
113
+ - 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.
114
+ ## Chat messages
115
+ 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.
116
+ 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).
117
+ | Web capability | Package support / caller boundary |
118
+ | --- | --- |
119
+ | message role user / assistant | Bubble direction outgoing / incoming |
120
+ | 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 |
121
+ | deliveryStatus thinking / streaming / pending | Caller maps to Bubble status pending and supplies pending text; final content/status remains controlled |
122
+ | failures and interrupted-stream reconnect | Bubble error/retry/onRetry slots; caller retains idempotent recovery and decides whether retry is permitted |
123
+ | Markdown, skill prefix, steps, toolCalls, proposals, attachments | children/actions accept existing caller-rendered nodes; parsing, tool dispatch, permissions and confirmation remain caller-owned |
124
+ | loadEarlierMessages / loadingEarlierMessages / messageCursor | before accepts caller loading button; prepend stable IDs preserves the visible message anchor |
125
+ | compact / expanded message viewport | MessageScroller className/style accepts bounded sizing; no window scrollIntoView |
126
+ | conversation switching / new conversation | conversationKey resets follow state, visible anchor and announcement |
127
+ | 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 |
128
+ 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.
@@ -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, 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.
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 28px 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.
@@ -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