@q1travel/design-system 0.30.0 → 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.
- 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 +50 -3
- package/dist-pkg/design-system.js +6046 -5927
- package/dist-pkg/style.css +273 -21
- package/dist-pkg/tokens.css +5 -1
- package/package.json +4 -4
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;
|
|
@@ -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 = {
|