@q1travel/design-system 0.30.1-next.60 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -133,6 +133,7 @@ semantic-colors:
133
133
  surface-overlay: "{colors.gray-300}"
134
134
  surface-hover: "color-mix(in srgb, var(--q1-color-text-primary) 6%, transparent)"
135
135
  surface-selected: "#1f1e38"
136
+ surface-selected-neutral: "{colors.gray-300}"
136
137
  surface-inverse: "{colors.gray-1000}"
137
138
  text-on-inverse: "#0a0a0a"
138
139
  surface-media: "#0a0a0a"
@@ -488,7 +489,7 @@ These components ship in the package. Compose from this list before writing new
488
489
  - 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.
489
490
  - Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
490
491
 
491
- **Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, MultiCombobox, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
492
+ **Option fields.** ControlOption, Checkbox, Radio, RadioGroup, Choicebox, Combobox, MultiSelect, MultiCombobox, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
492
493
 
493
494
  **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.
494
495
 
@@ -133,6 +133,7 @@ semantic-colors:
133
133
  surface-overlay: "{colors.background-100}"
134
134
  surface-hover: "color-mix(in srgb, var(--q1-color-text-primary) 4%, transparent)"
135
135
  surface-selected: "{colors.primary-100}"
136
+ surface-selected-neutral: "{colors.gray-300}"
136
137
  surface-inverse: "{colors.gray-1000}"
137
138
  text-on-inverse: "{colors.background-100}"
138
139
  surface-media: "#0a0a0a"
@@ -488,7 +489,7 @@ These components ship in the package. Compose from this list before writing new
488
489
  - 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.
489
490
  - Select is for a short, fixed list. Anything searchable, or longer than roughly seven entries, is a Combobox.
490
491
 
491
- **Option fields.** ControlOption, Checkbox, Radio, Choicebox, Combobox, MultiSelect, MultiCombobox, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
492
+ **Option fields.** ControlOption, Checkbox, Radio, RadioGroup, Choicebox, Combobox, MultiSelect, MultiCombobox, Switch, Toggle, ToggleGroup, Slider, RangeSlider.
492
493
 
493
494
  **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.
494
495
 
@@ -43,6 +43,24 @@ Decision date: 2026-08-26
43
43
 
44
44
  方向性面板(Drawer / Sheet)的阴影在组件内自持,不消费这两个 token,因此不受影响。
45
45
 
46
+ ## 2026-10-21 浅色浮层补齐贴边淡影与浅底环
47
+
48
+ `--q1-shadow-popover` 浅色主题由三层补齐为五层,自上而下:
49
+
50
+ 1. `0 0 0 1px` `--q1-color-gray-alpha-200`(黑 8%):边缘环。
51
+ 2. `0 1px 1px` 黑 2%:贴边淡影。
52
+ 3. `0 4px 8px -4px` 黑 4%:接触层。
53
+ 4. `0 16px 24px -8px` 黑 6%:环境层。
54
+ 5. `0 0 0 1px` `--q1-color-background-200`:边缘环下方的浅底环。
55
+
56
+ 暗色取值与 `--q1-shadow-modal` 均不变。
57
+
58
+ **为什么加贴边淡影**:边缘环只有 1px 且各向均匀,下缘缺少紧贴边界的过渡,接触层又偏远;`0 1px 1px` 的 2% 淡影让下缘更利落,不增加整体重量。
59
+
60
+ **为什么加浅底环**:半透明的黑 8% 边缘环会透出其下方的页面底色,在白底与灰底上呈现的环色不同。边缘环下方垫一圈 `background-200`,环色基本不随页面底色漂移,灰底上的边界更稳定。
61
+
62
+ **为什么环强度保持 8%**:该 token 与通用边框一致,是发丝线,强度低于相邻控件描边,不抢层级;边界清晰度由新增两层补足,而不是继续加深环。
63
+
46
64
  ## 下游影响面
47
65
 
48
66
  2026-08-26 对下游消费方的只读检索结果:`--q1-shadow-popover` 下游消费点 2 处,`--q1-shadow-modal` 下游消费点 0 处。
@@ -93,6 +93,7 @@ Examples:
93
93
 
94
94
  - `--q1-color-bg-canvas`
95
95
  - `--q1-color-bg-panel`
96
+ - `--q1-color-bg-selected-neutral`
96
97
  - `--q1-color-text-primary`
97
98
  - `--q1-color-border`
98
99
  - `--q1-color-action-primary`
@@ -104,6 +105,8 @@ Examples:
104
105
 
105
106
  `--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
107
 
108
+ `--q1-color-bg-selected-neutral` is the opaque neutral current-item background, separate from the branded `bg-selected`, quiet `bg-subtle`, and transient `bg-hover`. It references existing `gray-300` in both light and dark mode. Default, secondary, and ghost Button selected states consume it while retaining border and `aria-pressed` cues and using a heavier font weight than unselected controls; primary selected retains its primary press color, and warning/danger selected retains `bg-subtle`. The neutral selected background has measured DeltaE00 >= 3 against resting canvas, panel, and subtle surfaces in both themes; no separation from transient hover is required. Consumers may adopt the new role without renaming existing tokens; revert the Button background references and remove this additive role to roll back.
109
+
107
110
  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.
108
111
 
109
112
  `--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.
@@ -209,6 +212,16 @@ ship with values, because the pre-mixed colors exist nowhere else; `Table` still
209
212
  a fallback (`bg-canvas`, `bg-subtle`, `bg-selected`) so a stylesheet without them keeps an opaque
210
213
  row. A table on a surface other than the canvas overrides all four on the table or its container.
211
214
 
215
+ `Table` also exposes `--q1-table-header-bg` for every header cell, including sticky headers
216
+ and sticky-column intersections. Without an override, ordinary headers retain their transparent
217
+ background; sticky headers retain `var(--q1-table-row-bg)` (then
218
+ `var(--q1-color-bg-canvas)` when row tokens are absent). Like the input customization slots,
219
+ it is intentionally not defined at the root, preserving both existing defaults and table-local
220
+ row background overrides. Set it on the table or its container to `var(--q1-color-bg-subtle)`
221
+ for a subtle header; every header then uses that same color, while body row and selection
222
+ backgrounds remain independent. Existing tables need no migration. Remove the override to
223
+ restore the corresponding default.
224
+
212
225
  Every component token MUST declare a fallback to the semantic token it replaces, so a
213
226
  consumer that defines none of them sees no change. A component token without a fallback
214
227
  turns an opt-in customisation surface into a required one: the first consumer to upgrade
@@ -1,10 +1,12 @@
1
1
  import { AnchorHTMLAttributes } from 'react';
2
2
  import { AriaRole } from 'react';
3
3
  import { ButtonHTMLAttributes } from 'react';
4
+ import { ChangeEvent } from 'react';
4
5
  import { ComponentProps } from 'react';
5
6
  import { ComponentPropsWithoutRef } from 'react';
6
7
  import { ComponentPropsWithRef } from 'react';
7
8
  import { CSSProperties } from 'react';
9
+ import { FieldsetHTMLAttributes } from 'react';
8
10
  import { FocusEventHandler } from 'react';
9
11
  import { ForwardRefExoticComponent } from 'react';
10
12
  import { HTMLAttributes } from 'react';
@@ -505,7 +507,7 @@ export declare type ClearableInputProps = {
505
507
  ref?: Ref<HTMLInputElement>;
506
508
  } & Omit<InputHTMLAttributes<HTMLInputElement>, 'aria-label' | 'defaultValue' | 'id' | 'ref' | 'type' | 'value'>;
507
509
 
508
- export declare function ClearButton({ className, label, onClick, ...buttonProps }: {
510
+ export declare function ClearButton({ className, label, onClick, onMouseDown, ...buttonProps }: {
509
511
  className: string;
510
512
  label: string;
511
513
  onClick: () => void;
@@ -1532,6 +1534,8 @@ export declare function Modal(props: ModalProps): ReactNode;
1532
1534
  declare type ModalBaseProps = {
1533
1535
  open: boolean;
1534
1536
  title: string;
1537
+ /** 标题下方的一行导语;读屏打开对话框时随标题一并读出。不传则不渲染。 */
1538
+ description?: ReactNode;
1535
1539
  children: ReactNode;
1536
1540
  footer: ReactNode;
1537
1541
  initialFocusRef?: RefObject<HTMLElement | null>;
@@ -1903,7 +1907,7 @@ export declare type ProductLockupProps = {
1903
1907
  export declare type ProductLockupSize = 'sm' | 'md';
1904
1908
 
1905
1909
  export declare function Progress({ value, max, tone, slim, stops, label, }: {
1906
- value: number;
1910
+ value: number | null;
1907
1911
  max?: number;
1908
1912
  tone?: 'neutral' | ThresholdTone;
1909
1913
  slim?: boolean;
@@ -1940,6 +1944,40 @@ export declare function Q1travelSymbol(props: SVGProps<SVGSVGElement>): JSX.Elem
1940
1944
 
1941
1945
  export declare function Radio({ ...props }: RadioProps): JSX.Element;
1942
1946
 
1947
+ export declare function RadioGroup({ label, labelHidden, options, name, value, defaultValue, onChange, disabled, required, description, error, form, id, className, 'aria-describedby': ariaDescribedBy, ...fieldsetProps }: RadioGroupProps): JSX.Element;
1948
+
1949
+ export declare type RadioGroupOption = {
1950
+ value: string;
1951
+ label: string;
1952
+ description?: string;
1953
+ disabled?: boolean;
1954
+ };
1955
+
1956
+ export declare type RadioGroupProps = Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, 'onChange' | 'defaultValue' | 'children' | 'name' | 'form'> & {
1957
+ /** 组名,渲染为 legend;与 Label 同一视觉。 */
1958
+ label: string;
1959
+ /** 视觉隐藏组名,读屏仍读出(用于上下文已给出标题的场景)。 */
1960
+ labelHidden?: boolean;
1961
+ options: RadioGroupOption[];
1962
+ /** 原生 radio 的 name;缺省自动生成,同表单内提交时显式传入。 */
1963
+ name?: string;
1964
+ /** 受控值;与 defaultValue 二选一。 */
1965
+ value?: string;
1966
+ defaultValue?: string;
1967
+ /** 选中项变化时回调,参数为新选中项的 value。 */
1968
+ onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void;
1969
+ /** 整组禁用,原生 fieldset 级联禁用所有选项。 */
1970
+ disabled?: boolean;
1971
+ /** 必选:组名后显示必填标记,选项带原生 required。 */
1972
+ required?: boolean;
1973
+ /** 组名下方的补充说明。 */
1974
+ description?: string;
1975
+ /** 组级错误,显示在最后一个选项之后。 */
1976
+ error?: string;
1977
+ /** 表单归属,透传给每个选项。 */
1978
+ form?: string;
1979
+ };
1980
+
1943
1981
  /** Radio 的完整 props。 */
1944
1982
  export declare type RadioProps = ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & FieldLabelVisibilityProps & ControlOptionNativeInputProps;
1945
1983
 
@@ -2049,12 +2087,18 @@ export declare function ScrollerWithButtons({ axis, contentName, label, children
2049
2087
  children: ReactNode;
2050
2088
  }): JSX.Element;
2051
2089
 
2052
- export declare function SearchDialog({ open, onOpenChange, query, onQueryChange, sections, onSelect, renderTrigger, label, placeholder, emptyTitle, clearQueryLabel, }: SearchDialogProps): JSX.Element;
2090
+ export declare function SearchDialog({ open, onOpenChange, query, onQueryChange, sections, onSelect, renderTrigger, label, placeholder, emptyTitle, clearQueryLabel, footer, }: SearchDialogProps): JSX.Element;
2053
2091
 
2054
2092
  export declare type SearchDialogItem = {
2055
2093
  id: string;
2056
2094
  label: string;
2057
2095
  description?: string;
2096
+ /** 装饰性前置图标;不参与选项名称。 */
2097
+ icon?: ReactNode;
2098
+ /** 非交互式尾注。 */
2099
+ suffix?: ReactNode;
2100
+ /** 仅展示快捷键;实际绑定由调用方负责。 */
2101
+ keybind?: string;
2058
2102
  disabled?: boolean;
2059
2103
  /**
2060
2104
  * 标记当前项(例如站内搜索里的当前页面):选项输出 aria-current 并显示勾选标记,
@@ -2075,12 +2119,15 @@ export declare type SearchDialogProps = {
2075
2119
  placeholder?: string;
2076
2120
  emptyTitle?: string;
2077
2121
  clearQueryLabel?: string;
2122
+ footer?: ReactNode;
2078
2123
  };
2079
2124
 
2080
2125
  export declare type SearchDialogSection = {
2081
2126
  id: string;
2082
2127
  heading?: string;
2083
2128
  items: SearchDialogItem[];
2129
+ /** 选项集合外的加载、错误或重试内容;播报语义由调用方提供。 */
2130
+ status?: ReactNode;
2084
2131
  };
2085
2132
 
2086
2133
  export declare type SearchDialogTriggerProps = {