@q1travel/design-system 0.28.0 → 0.29.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.
@@ -28,6 +28,8 @@ Button and IconButton accept native button props, ARIA attributes, `data-*` attr
28
28
 
29
29
  Package-owned semantics are applied after native props. `type`, `className`, `disabled`, busy/loading `aria-disabled`/`aria-busy`/`tabIndex`, the resolved accessible name, `ref`, and the busy/loading click guards therefore keep their documented behavior. Outside busy/loading, a consumer-provided `tabIndex` or `aria-disabled` remains intact. The canonical native `aria-keyshortcuts` value wins over the compatibility `ariaKeyshortcuts` field by nullish precedence.
30
30
 
31
+ Button preserves an explicitly supplied native `aria-busy` when `busy` is false, including during `loading`; `loading` alone still does not add it. This lets consumers announce an external task immediately while controlling visual loading separately. `busy=true` always sets `aria-busy="true"`, even when a consumer passes false. ButtonLink keeps its existing package-owned busy semantics.
32
+
31
33
  ## Loading and Busy States
32
34
 
33
35
  `loading` and `busy` are intentionally different public states:
@@ -105,7 +105,7 @@ Timeline / TimelineItem (ol/li, 8px token dot or Avatar), Marker (quiet actor/ti
105
105
 
106
106
  ## ToggleGroup selection ownership
107
107
 
108
- Accepted 2026-10-07: public `ToggleGroup` uses the shadcn/Radix name (no corresponding industry-common 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
+ Accepted 2026-10-07: public `ToggleGroup` names a group of pressable controls with single or multiple selection. 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
109
 
110
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
111
  ## Checkbox task completion circle
@@ -113,7 +113,7 @@ Native buttons expose `aria-pressed` within `role="group"`. Roving tabindex and
113
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
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
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 chat surfaces map to existing q1travel panel/selected backgrounds, borders, spacing, radii and Button. No reference CSS, motion background or private tokens are shipped.
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. Quiet chat surfaces use existing q1travel panel/selected backgrounds, borders, spacing, radii and Button. No reference CSS, motion background or private tokens are shipped.
117
117
  The consumer mapping was checked against downstream chat-style implementations (read-only, no downstream edits).
118
118
  | Web capability | Package support / caller boundary |
119
119
  | --- | --- |
@@ -26,7 +26,7 @@ Adding a clause therefore means answering two questions before the wording: who
26
26
  - [both] MUST keep mobile control targets at least 44px where touch is expected.
27
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
- - [both] SHOULD NOT ship dense measured 24–32px controls to coarse pointers without 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.
29
+ - [both] SHOULD NOT ship dense measured 24–32px controls to coarse pointers without enlargement: q1travel uses a 44×44 CSS-pixel mobile usability target, informed by WCAG 2.5.5.
30
30
  - [both] NEVER disable browser zoom.
31
31
  - [both] MUST avoid dead zones: if a region looks clickable, the clickable element must cover it.
32
32
  - [both] MUST show a loading indicator on in-flight buttons while keeping the original label.
@@ -191,7 +191,7 @@ Theming owns tokens and `color-scheme`; the interaction-contrast rule lives in D
191
191
  - [both] MUST separate Chinese characters from adjacent Latin letters or digits with a half-width space in authored prose; code contents, URLs, literal UI quotations, and code samples are excluded.
192
192
  - [both] MUST use full-width `,。:;` immediately after Chinese characters in authored Chinese sentences; code contents, URLs, literal UI quotations, and code samples are excluded.
193
193
  - [both] SHOULD use full-width `,。:;` throughout Chinese sentences, including after Latin words or inline code; identifying sentence language across mixed JSX nodes remains an editorial review item.
194
- - [both] SHOULD follow common Chinese technical-writing practice for mixed-language wording; Microsoft's Chinese localization guidance is a reference example, not a proprietary q1travel rule. Semantic choices and text whose rendered role cannot be reliably inferred remain editorial review items rather than machine-enforced requirements.
194
+ - [both] SHOULD follow common Chinese technical-writing practice for mixed-language wording. Semantic choices and text whose rendered role cannot be reliably inferred remain editorial review items rather than machine-enforced requirements.
195
195
  - [both] SHOULD prefer short action-oriented labels.
196
196
  - [both] MUST use active voice.
197
197
  - [both] MUST use unambiguous action labels (「保存 API Key」, not 「继续」).
@@ -213,7 +213,7 @@ These copy clauses keep reader address and empty states consistent with a formal
213
213
 
214
214
  ### Component Naming
215
215
 
216
- - [repo] SHOULD use the industry-common component name when one exists, preferring shadcn/ui, then Radix. Invent a name only when neither has one. Name what the component is, not how it is built.
216
+ - [repo] SHOULD use the industry-common component name when one exists. Invent a name only when no established name fits. Name what the component is, not how it is built.
217
217
  - [repo] MUST switch public names directly to the new export without deprecated aliases or old documentation route redirects; record the removal as a breaking change in CHANGELOG.
218
218
 
219
219
  ### Component Documentation Pages
@@ -1578,7 +1578,7 @@ export declare type MultiSelectProps = {
1578
1578
  preserveSelectionOrder?: boolean;
1579
1579
  };
1580
1580
 
1581
- export declare function Navigation({ className, orientation, ...props }: NavigationProps): JSX.Element;
1581
+ export declare function Navigation({ className, orientation, variant, ...props }: NavigationProps): JSX.Element;
1582
1582
 
1583
1583
  export declare function NavigationGroup({ label, icon, expanded, defaultExpanded, onExpandedChange, children, }: NavigationGroupProps): JSX.Element;
1584
1584
 
@@ -1617,6 +1617,8 @@ export declare type NavigationItemProps = NavigationItemAsLink | NavigationItemA
1617
1617
  export declare type NavigationProps = HTMLAttributes<HTMLElement> & {
1618
1618
  'aria-label': string;
1619
1619
  orientation?: 'vertical' | 'horizontal';
1620
+ /** Line styling applies only to horizontal navigation; vertical navigation ignores it. */
1621
+ variant?: 'default' | 'line';
1620
1622
  };
1621
1623
 
1622
1624
  export declare function NavigationSection({ label, action, children, className, ...props }: NavigationSectionProps): JSX.Element;
@@ -2536,10 +2538,12 @@ export declare type ToolbarProps = {
2536
2538
  className?: string;
2537
2539
  };
2538
2540
 
2539
- export declare function Tooltip({ content, placement, delay, triggerLabel, children, align, tip, center, }: {
2541
+ export declare function Tooltip({ content, placement, delay, disabled, triggerLabel, children, align, tip, center, }: {
2540
2542
  content: ReactNode;
2541
2543
  placement?: TooltipPlacement;
2542
2544
  delay?: number;
2545
+ /** 禁用提示而保留触发器;关闭已显示或待显示的提示,不影响按钮自身的可操作性。 */
2546
+ disabled?: boolean;
2543
2547
  /** 内部默认触发器的文案;传 children 时不使用。 */
2544
2548
  triggerLabel?: string;
2545
2549
  /** 自定义触发器:包裹任意单个元素/组件(Button、原生 a/span 等)代替内部默认按钮。 */