@q1travel/design-system 0.30.1-next.60 → 0.30.2
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 +2 -1
- package/contracts/design.md +2 -1
- package/contracts/overlay-shadow-migration.md +18 -0
- package/contracts/token-naming.md +13 -0
- package/dist-pkg/design-system.d.ts +61 -5
- package/dist-pkg/design-system.js +6995 -6681
- package/dist-pkg/style.css +295 -22
- package/dist-pkg/tokens.css +5 -1
- package/package.json +3 -3
package/contracts/design.dark.md
CHANGED
|
@@ -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
|
|
package/contracts/design.md
CHANGED
|
@@ -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;
|
|
@@ -578,13 +580,14 @@ export declare function CollapsibleBreadcrumb({ items, label }: {
|
|
|
578
580
|
label: string;
|
|
579
581
|
}): JSX.Element;
|
|
580
582
|
|
|
581
|
-
export declare function Combobox({ options, value, onChange, onSearch, isLoading, placeholder, size, disabled, readOnly, invalid, errorId, error, emptyText, clearable, id, className, name, inputMode, spellCheck, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ref, ...rest }: ComboboxProps): JSX.Element;
|
|
583
|
+
export declare function Combobox({ options, searchPlacement, searchPlaceholder, footer, value, onChange, onSearch, isLoading, placeholder, size, disabled, readOnly, invalid, errorId, error, emptyText, clearable, id, className, name, inputMode, spellCheck, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ref, ...rest }: ComboboxProps): JSX.Element;
|
|
582
584
|
|
|
583
585
|
export declare type ComboboxOption = {
|
|
584
586
|
/** 禁止空字符串(与「未选」的受控空值歧义,adapter 层保证)。 */
|
|
585
587
|
value: string;
|
|
586
588
|
/** 显示与过滤的唯一来源。 */
|
|
587
589
|
label: string;
|
|
590
|
+
description?: string;
|
|
588
591
|
disabled?: boolean;
|
|
589
592
|
/** 组件透而不读,仅随 onChange 第二参原样带回。 */
|
|
590
593
|
meta?: unknown;
|
|
@@ -597,6 +600,12 @@ export declare type ComboboxOption = {
|
|
|
597
600
|
export declare type ComboboxProps = {
|
|
598
601
|
/** 唯一 shape,后端字段在 adapter 层转换。 */
|
|
599
602
|
options: ComboboxOption[];
|
|
603
|
+
/** 默认在触发器输入;panel 使用按钮触发并将搜索放在弹层。 */
|
|
604
|
+
searchPlacement?: 'input' | 'panel';
|
|
605
|
+
/** 面板搜索的独立占位文案。 */
|
|
606
|
+
searchPlaceholder?: string;
|
|
607
|
+
/** 列表之外的操作区,进入正常 Tab 顺序。 */
|
|
608
|
+
footer?: ReactNode;
|
|
600
609
|
/** 受控选中值;'' = 未选。 */
|
|
601
610
|
value: string;
|
|
602
611
|
/** 选中传 (option.value, option);clearable 清除传 ('', undefined)。 */
|
|
@@ -1532,6 +1541,8 @@ export declare function Modal(props: ModalProps): ReactNode;
|
|
|
1532
1541
|
declare type ModalBaseProps = {
|
|
1533
1542
|
open: boolean;
|
|
1534
1543
|
title: string;
|
|
1544
|
+
/** 标题下方的一行导语;读屏打开对话框时随标题一并读出。不传则不渲染。 */
|
|
1545
|
+
description?: ReactNode;
|
|
1535
1546
|
children: ReactNode;
|
|
1536
1547
|
footer: ReactNode;
|
|
1537
1548
|
initialFocusRef?: RefObject<HTMLElement | null>;
|
|
@@ -1569,7 +1580,7 @@ declare type ModalLegacyDismissalProps = {
|
|
|
1569
1580
|
export declare type ModalProps = ModalBaseProps & (ModalLegacyDismissalProps | ModalEscapeActionProps);
|
|
1570
1581
|
|
|
1571
1582
|
/** 标签触发器与弹层搜索组合;选项变更保持展开,选中值完全受控。 */
|
|
1572
|
-
export declare function MultiCombobox({ options, value, onChange, onOpenChange, selectedOptions, placeholder, searchPlaceholder, onSearch, searchValue, isLoading, loadError, onRetry, emptyText, disabled, readOnly, invalid, errorId, error, clearable, id, name, className, ref, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby }: MultiComboboxProps): JSX.Element;
|
|
1583
|
+
export declare function MultiCombobox({ options, value, onChange, onOpenChange, selectedOptions, placeholder, searchPlaceholder, onSearch, searchValue, isLoading, loadError, onRetry, emptyText, footer, required, disabled, readOnly, invalid, errorId, error, clearable, id, name, className, ref, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, 'aria-describedby': ariaDescribedby }: MultiComboboxProps): JSX.Element;
|
|
1573
1584
|
|
|
1574
1585
|
export declare type MultiComboboxChangeDetails = {
|
|
1575
1586
|
action: 'select' | 'remove' | 'clear';
|
|
@@ -1603,6 +1614,8 @@ export declare type MultiComboboxProps = {
|
|
|
1603
1614
|
loadError?: ReactNode;
|
|
1604
1615
|
onRetry?: () => void;
|
|
1605
1616
|
emptyText?: ReactNode;
|
|
1617
|
+
footer?: ReactNode;
|
|
1618
|
+
required?: boolean;
|
|
1606
1619
|
disabled?: boolean;
|
|
1607
1620
|
readOnly?: boolean;
|
|
1608
1621
|
invalid?: boolean;
|
|
@@ -1903,7 +1916,7 @@ export declare type ProductLockupProps = {
|
|
|
1903
1916
|
export declare type ProductLockupSize = 'sm' | 'md';
|
|
1904
1917
|
|
|
1905
1918
|
export declare function Progress({ value, max, tone, slim, stops, label, }: {
|
|
1906
|
-
value: number;
|
|
1919
|
+
value: number | null;
|
|
1907
1920
|
max?: number;
|
|
1908
1921
|
tone?: 'neutral' | ThresholdTone;
|
|
1909
1922
|
slim?: boolean;
|
|
@@ -1940,6 +1953,40 @@ export declare function Q1travelSymbol(props: SVGProps<SVGSVGElement>): JSX.Elem
|
|
|
1940
1953
|
|
|
1941
1954
|
export declare function Radio({ ...props }: RadioProps): JSX.Element;
|
|
1942
1955
|
|
|
1956
|
+
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;
|
|
1957
|
+
|
|
1958
|
+
export declare type RadioGroupOption = {
|
|
1959
|
+
value: string;
|
|
1960
|
+
label: string;
|
|
1961
|
+
description?: string;
|
|
1962
|
+
disabled?: boolean;
|
|
1963
|
+
};
|
|
1964
|
+
|
|
1965
|
+
export declare type RadioGroupProps = Omit<FieldsetHTMLAttributes<HTMLFieldSetElement>, 'onChange' | 'defaultValue' | 'children' | 'name' | 'form'> & {
|
|
1966
|
+
/** 组名,渲染为 legend;与 Label 同一视觉。 */
|
|
1967
|
+
label: string;
|
|
1968
|
+
/** 视觉隐藏组名,读屏仍读出(用于上下文已给出标题的场景)。 */
|
|
1969
|
+
labelHidden?: boolean;
|
|
1970
|
+
options: RadioGroupOption[];
|
|
1971
|
+
/** 原生 radio 的 name;缺省自动生成,同表单内提交时显式传入。 */
|
|
1972
|
+
name?: string;
|
|
1973
|
+
/** 受控值;与 defaultValue 二选一。 */
|
|
1974
|
+
value?: string;
|
|
1975
|
+
defaultValue?: string;
|
|
1976
|
+
/** 选中项变化时回调,参数为新选中项的 value。 */
|
|
1977
|
+
onChange?: (value: string, event: ChangeEvent<HTMLInputElement>) => void;
|
|
1978
|
+
/** 整组禁用,原生 fieldset 级联禁用所有选项。 */
|
|
1979
|
+
disabled?: boolean;
|
|
1980
|
+
/** 必选:组名后显示必填标记,选项带原生 required。 */
|
|
1981
|
+
required?: boolean;
|
|
1982
|
+
/** 组名下方的补充说明。 */
|
|
1983
|
+
description?: string;
|
|
1984
|
+
/** 组级错误,显示在最后一个选项之后。 */
|
|
1985
|
+
error?: string;
|
|
1986
|
+
/** 表单归属,透传给每个选项。 */
|
|
1987
|
+
form?: string;
|
|
1988
|
+
};
|
|
1989
|
+
|
|
1943
1990
|
/** Radio 的完整 props。 */
|
|
1944
1991
|
export declare type RadioProps = ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & FieldLabelVisibilityProps & ControlOptionNativeInputProps;
|
|
1945
1992
|
|
|
@@ -2049,12 +2096,18 @@ export declare function ScrollerWithButtons({ axis, contentName, label, children
|
|
|
2049
2096
|
children: ReactNode;
|
|
2050
2097
|
}): JSX.Element;
|
|
2051
2098
|
|
|
2052
|
-
export declare function SearchDialog({ open, onOpenChange, query, onQueryChange, sections, onSelect, renderTrigger, label, placeholder, emptyTitle, clearQueryLabel, }: SearchDialogProps): JSX.Element;
|
|
2099
|
+
export declare function SearchDialog({ open, onOpenChange, query, onQueryChange, sections, onSelect, renderTrigger, label, placeholder, emptyTitle, clearQueryLabel, footer, }: SearchDialogProps): JSX.Element;
|
|
2053
2100
|
|
|
2054
2101
|
export declare type SearchDialogItem = {
|
|
2055
2102
|
id: string;
|
|
2056
2103
|
label: string;
|
|
2057
2104
|
description?: string;
|
|
2105
|
+
/** 装饰性前置图标;不参与选项名称。 */
|
|
2106
|
+
icon?: ReactNode;
|
|
2107
|
+
/** 非交互式尾注。 */
|
|
2108
|
+
suffix?: ReactNode;
|
|
2109
|
+
/** 仅展示快捷键;实际绑定由调用方负责。 */
|
|
2110
|
+
keybind?: string;
|
|
2058
2111
|
disabled?: boolean;
|
|
2059
2112
|
/**
|
|
2060
2113
|
* 标记当前项(例如站内搜索里的当前页面):选项输出 aria-current 并显示勾选标记,
|
|
@@ -2075,12 +2128,15 @@ export declare type SearchDialogProps = {
|
|
|
2075
2128
|
placeholder?: string;
|
|
2076
2129
|
emptyTitle?: string;
|
|
2077
2130
|
clearQueryLabel?: string;
|
|
2131
|
+
footer?: ReactNode;
|
|
2078
2132
|
};
|
|
2079
2133
|
|
|
2080
2134
|
export declare type SearchDialogSection = {
|
|
2081
2135
|
id: string;
|
|
2082
2136
|
heading?: string;
|
|
2083
2137
|
items: SearchDialogItem[];
|
|
2138
|
+
/** 选项集合外的加载、错误或重试内容;播报语义由调用方提供。 */
|
|
2139
|
+
status?: ReactNode;
|
|
2084
2140
|
};
|
|
2085
2141
|
|
|
2086
2142
|
export declare type SearchDialogTriggerProps = {
|