@q1travel/design-system 0.17.0 → 0.17.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/README.md CHANGED
@@ -1,20 +1,93 @@
1
1
  # q1travel Design System
2
2
 
3
- q1travel 产品家族的设计权威上游仓,三件事集于一处:`@q1travel/design-system` npm 包、设计契约(`contracts/`)与公开文档站。下游各仓只读消费已发布的包,并用包内 `q1-design` CLI 同步、校验契约缓存。推荐公开域名:`design.q1lx.com`。
3
+ [![npm version](https://img.shields.io/npm/v/@q1travel/design-system.svg)](https://www.npmjs.com/package/@q1travel/design-system)
4
4
 
5
- **技术栈**:React 19(peerDependency)· TypeScript · Vite 7(文档站 / 库双构建管线)· lucide-react · npm
5
+ `@q1travel/design-system` 提供一套 React 组件、设计 token 与设计契约,用于构建一致、可访问、支持明暗主题的产品界面。
6
6
 
7
- 文档站以公开产品标准的口径撰写,信息架构为 `基础`、`品牌`、`组件` 三组。
7
+ ## 文档
8
8
 
9
- ## Sources
9
+ 完整的组件指南、设计基础与使用示例见 [design.q1lx.com](https://design.q1lx.com)。
10
10
 
11
- - Source contracts(设计契约源,canonical):`contracts/design.md`、`contracts/design.dark.md`、`contracts/token-naming.md`、`contracts/icon-system.md`、`contracts/button-api-compatibility.md`、`contracts/interface-guidelines.md`、`contracts/design-engineering-principles.md`、`contracts/implementation-map.md`、`contracts/overlay-foundation.md`,以及迁移契约 `contracts/context-contrast-migration.md`、`contracts/status-chip-badge-migration.md`、`contracts/pagination-migration.md`、`contracts/table-migration.md`、`contracts/overlay-shadow-migration.md`。独立 HTML 文档的有界 class 词汇表源为 `contracts/design.css`。
12
- - Generated mirrors(生成镜像,勿手改):`public/design.md`、`public/design.dark.md` 由 `scripts/sync-design-contracts.mjs` 从契约源逐字节复制;`public/design.css` 由 `scripts/build-design-css.mjs` 把两份契约 frontmatter 的取值注入 `contracts/design.css` 的类词汇后生成,因而自足、可被独立 HTML 页面直接加载。
13
- - Runtime tokens:`src/styles/tokens.css`(canonical `--q1-*`,仅 `:root` 与 `:root[data-theme='dark']` 两个扁平 selector)。
14
- - 发布台账:`contracts/releases/`(仅存于仓库,不进 npm 包)。
15
- - npm 发布与下游自动更新:版本 tag 触发 GitHub Actions OIDC 免令牌发布,详见[发布操作说明](docs/npm-release.md)。
11
+ ## 环境要求
12
+
13
+ - React 19 与 React DOM 19(peerDependency)
14
+ - 支持 ES Module 的构建工具(如 Vite)
15
+ - 使用 `q1-design` CLI 需要 Node.js 22+
16
+
17
+ ## 安装
18
+
19
+ ```bash
20
+ npm install @q1travel/design-system
21
+ ```
22
+
23
+ ## 使用
24
+
25
+ 在应用入口引入样式表,然后按需导入组件:
26
+
27
+ ```tsx
28
+ import '@q1travel/design-system/style.css';
29
+ import { Button } from '@q1travel/design-system';
30
+
31
+ export function SaveAction() {
32
+ return <Button variant="primary">保存</Button>;
33
+ }
34
+ ```
35
+
36
+ `style.css` 已包含全部设计 token 与组件样式。如果只需要设计 token(例如为自有组件取色、取间距),可以单独引入:
37
+
38
+ ```ts
39
+ import '@q1travel/design-system/tokens.css';
40
+ ```
41
+
42
+ 所有 token 以 `--q1-*` 命名,例如 `var(--q1-color-gray-1000)`。
43
+
44
+ ### 主题
45
+
46
+ 组件默认使用浅色主题。在根元素上设置 `data-theme="dark"` 即可切换为深色主题:
47
+
48
+ ```html
49
+ <html data-theme="dark">
50
+ ```
16
51
 
17
- ## Getting Started
52
+ ### 反馈提示
53
+
54
+ 使用 `useToast` 前,需要在应用根部包裹 `ToastProvider`:
55
+
56
+ ```tsx
57
+ import { ToastProvider } from '@q1travel/design-system';
58
+
59
+ root.render(
60
+ <ToastProvider>
61
+ <App />
62
+ </ToastProvider>,
63
+ );
64
+ ```
65
+
66
+ ## CLI
67
+
68
+ 包内附带 `q1-design` 命令行工具,用于查询组件 API 并检查样式用法:
69
+
70
+ | 命令 | 说明 |
71
+ | --- | --- |
72
+ | `npx q1-design ls` | 列出包内提供的组件与 hook |
73
+ | `npx q1-design doc <组件名>` | 打印组件的公共 API |
74
+ | `npx q1-design lint [路径...]` | 按 `Q1-DS-*` 规则检查样式用法,默认扫描 `src` |
75
+ | `npx q1-design lint --diff <base-ref>` | 只检查相对 `base-ref` 有改动的文件 |
76
+ | `npx q1-design overrides [路径...]` | 统计对组件外观的 `className` 覆写 |
77
+ | `npx q1-design sync` | 将设计契约与 AI 编码指南同步为项目内的只读缓存 |
78
+ | `npx q1-design verify` | 校验缓存未被修改且与已安装版本一致 |
79
+
80
+ ## 设计契约与 AI 编码指南
81
+
82
+ 包内随附设计契约(`contracts/*.md`)与 product-design 编码指南(`.agents/`),可供 AI 编码助手读取,使生成的界面遵循同一套 token 与组件规范。契约文件通过 `@q1travel/design-system/contracts/*` 路径导出,也可以用 `npx q1-design sync` 同步到项目中。
83
+
84
+ ## 版本
85
+
86
+ 历史版本见 npm 的 [Versions](https://www.npmjs.com/package/@q1travel/design-system?activeTab=versions) 页。
87
+
88
+ ## 参与开发
89
+
90
+ 以下内容面向本仓库的贡献者。
18
91
 
19
92
  前置要求:Node.js 22+(CI 基线 22)与 npm(依赖锁定在 `package-lock.json`);运行 `check:browser` 需要本机 Chrome(可用 `CHROME_PATH` 指定)。`check:browser` 在本地与 CI 都默认使用 `--headless=new`,不探测 TTY 或 `DISPLAY`;只有显式设置 `Q1_DESIGN_BROWSER_HEADLESS=false` 才切换到 headed 模式。
20
93
 
@@ -23,7 +96,17 @@ npm install
23
96
  npm run dev
24
97
  ```
25
98
 
26
- ## Available Scripts
99
+ **技术栈**:React 19 · TypeScript · Vite 7(文档站 / 库双构建管线)· lucide-react · npm
100
+
101
+ ### Sources
102
+
103
+ - Source contracts(设计契约源,canonical):`contracts/design.md`、`contracts/design.dark.md`、`contracts/token-naming.md`、`contracts/icon-system.md`、`contracts/button-api-compatibility.md`、`contracts/interface-guidelines.md`、`contracts/design-engineering-principles.md`、`contracts/implementation-map.md`、`contracts/overlay-foundation.md`,以及迁移契约 `contracts/context-contrast-migration.md`、`contracts/status-chip-badge-migration.md`、`contracts/pagination-migration.md`、`contracts/table-migration.md`、`contracts/overlay-shadow-migration.md`。独立 HTML 文档的有界 class 词汇表源为 `contracts/design.css`。
104
+ - Generated mirrors(生成镜像,勿手改):`public/design.md`、`public/design.dark.md` 由 `scripts/sync-design-contracts.mjs` 从契约源逐字节复制;`public/design.css` 由 `scripts/build-design-css.mjs` 把两份契约 frontmatter 的取值注入 `contracts/design.css` 的类词汇后生成,因而自足、可被独立 HTML 页面直接加载。
105
+ - Runtime tokens:`src/styles/tokens.css`(canonical `--q1-*`,仅 `:root` 与 `:root[data-theme='dark']` 两个扁平 selector)。
106
+ - 发布记录:`contracts/releases/`(仅存于仓库,不进 npm 包)。
107
+ - npm 发布:版本 tag 触发 GitHub Actions OIDC 免令牌发布,详见 `docs/npm-release.md`。
108
+
109
+ ### Available Scripts
27
110
 
28
111
  | 命令 | 用途 |
29
112
  | --- | --- |
@@ -34,15 +117,15 @@ npm run dev
34
117
  | `npm run check:shell` | 结构与源码契约断言 + token 包检查 + 发布溯源检查 |
35
118
  | `npm run check:browser` | 基于 CDP 的浏览器行为契约(含 StrictMode 前置轮) |
36
119
  | `npm run check:package-api` | 已构建包的声明与运行时 API + 组件契约 `node --test` 套件 |
37
- | `npm run check:release-provenance` | 发布台账 vs `CHANGELOG.md`、origin tag 与 npm registry |
38
- | `npm run check:release-sync` | 发布后 registry 完整性与四仓 Renovate 通知脚本测试 |
120
+ | `npm run check:release-provenance` | 发布记录 vs `CHANGELOG.md`、origin tag 与 npm registry |
121
+ | `npm run check:release-sync` | 发布后 registry 完整性校验与依赖更新通知脚本测试 |
39
122
  | `npm run release:verify -- <tarball>` | 只校验 workflow tarball 与 npm registry 完整性 |
40
- | `npm run release:sync -- <tarball>` | 校验 registry 后通知四仓 Renovate;不具备发布能力 |
123
+ | `npm run release:sync -- <tarball>` | 校验 registry 后发送依赖更新通知;不具备发布能力 |
41
124
  | `npm run build:pkg` | 库构建(产物 `dist-pkg/`)+ CSS 拼装 + 包 API 检查(`prepack` 同款) |
42
125
 
43
126
  本仓没有独立的 test / lint script——规范由 `scripts/` 下的契约门禁脚本守护。
44
127
 
45
- ## Project Structure
128
+ ### Project Structure
46
129
 
47
130
  ```
48
131
  src/components/ui/ npm 包组件源(入口 index.ts,统一公共导出面)
@@ -50,20 +133,19 @@ src/components/ docs/、layout/、brand/ 文档站结构组件
50
133
  src/pages/ 文档站页面(组件示例编排)
51
134
  src/styles/ tokens.css、foundations.css、components.css 等分桶样式
52
135
  src/browser-fixtures/ check:browser 的行为契约 fixture
53
- contracts/ 设计契约源 + releases/ 发布台账
136
+ contracts/ 设计契约源 + releases/ 发布记录
54
137
  public/ 契约镜像与品牌/字体/图标静态资产
55
138
  scripts/ 契约门禁与构建脚本(check-shell / check-browser / …)
56
- bin/ q1-design CLI(消费仓 ls / doc / sync / verify / lint / eval)
139
+ bin/ q1-design CLI(ls / doc / sync / verify / lint / eval / overrides)
57
140
  tooling/ 规则清单(Q1-DS-*)与 eval fixture
58
141
  .agents/ 随包分发的 product-design skill
59
- docs/ review/ 架构骨架、agent 规范、历史决策记录
142
+ docs/ review/ 架构说明、agent 规范、历史决策记录
60
143
  ```
61
144
 
62
- ## Documentation & AI Guidelines
145
+ ### Documentation & AI Guidelines
63
146
 
64
- - **AI Agent 必须遵循根目录 `AGENTS.md`**(Claude Code 与 Codex 共用的 canonical 入口:命令速查、红线约束、工作闭环与知识索引);`CLAUDE.md` 仅为其薄包装。
65
- - `docs/architecture.md` — 双构建管线、契约生成与下游消费模型、文档站托管与公开契约的缓存策略(含 Cloudflare Cache Rule 与 Purge 运维备忘)、发布溯源链路。
147
+ - AI 编码助手遵循根目录 `AGENTS.md`(命令速查、约束、工作闭环与知识索引);`CLAUDE.md` 仅为其薄包装。
148
+ - `docs/architecture.md` — 双构建管线、契约生成与消费模型、文档站托管与缓存策略、发布溯源链路。
66
149
  - `docs/adr/template.md` — 新 ADR 从此模板开始;早期已接受决策存于 `review/decision-log/`。
67
- - `docs/token-coverage-gaps.md` — 下游有真实消费需求但本仓未导出的 `--q1-*` 能力缺口登记(待决清单,非契约)。
150
+ - `docs/token-coverage-gaps.md` — 尚未导出的 `--q1-*` 能力缺口登记(待决清单,非契约)。
68
151
  - `docs/research/design-md-eval/` — design.md 有/无 对照评测:固定场景、确定性检查、修正队列(研究记录,非契约)。
69
- - 下游消费方式:安装 `@q1travel/design-system` 后 import 组件与 `style.css` / `tokens.css`;`npx q1-design sync` 把包内 skill 与契约物化为只读缓存,`q1-design verify` 校验缓存未被改动,`q1-design lint` 执行 `Q1-DS-*` 机械规则;`q1-design ls` 列出随包组件,`q1-design doc <组件名>` 打印其公共 API(两者现推自随包的 `.d.ts`,不另存清单);`q1-design overrides` 统计并分类消费方对组件外观的覆写。
@@ -128,15 +128,22 @@ semantic-colors:
128
128
  text-on-media: "#ffffff"
129
129
  border: "{colors.gray-200}"
130
130
  border-strong: "{colors.gray-400}"
131
+ border-subtle: "#1f1f1f"
131
132
  text-primary: "{colors.text-primary}"
132
133
  text-secondary: "{colors.text-secondary}"
133
134
  text-muted: "{colors.text-muted}"
135
+ text-soft: "#9a9a9c"
136
+ overlay-scrim: "rgba(2, 6, 23, 0.44)"
134
137
  selection-background: "{colors.text-primary}"
135
138
  selection-foreground: "{colors.background-100}"
136
139
  focus-ring: "{colors.primary}"
140
+ focus: "{colors.primary-700}"
137
141
  status-success: "{colors.green-900}"
138
142
  status-warning: "{colors.amber-700}"
139
143
  status-danger: "{colors.red-900}"
144
+ status-danger-text: "#ef5350"
145
+ status-danger-surface: "{colors.red-100}"
146
+ status-danger-border: "#ef5350"
140
147
  typography:
141
148
  heading-72:
142
149
  fontFamily: Noto Sans SC
@@ -128,15 +128,22 @@ semantic-colors:
128
128
  text-on-media: "#ffffff"
129
129
  border: "{colors.gray-400}"
130
130
  border-strong: "{colors.gray-500}"
131
+ border-subtle: "rgba(203, 213, 225, 0.82)"
131
132
  text-primary: "{colors.text-primary}"
132
133
  text-secondary: "{colors.text-secondary}"
133
134
  text-muted: "{colors.text-muted}"
135
+ text-soft: "#6e6e73"
136
+ overlay-scrim: "rgba(2, 6, 23, 0.44)"
134
137
  selection-background: "{colors.text-primary}"
135
138
  selection-foreground: "{colors.background-100}"
136
139
  focus-ring: "{colors.primary}"
140
+ focus: "{colors.primary-700}"
137
141
  status-success: "{colors.green-700}"
138
142
  status-warning: "{colors.amber-900}"
139
143
  status-danger: "{colors.red-700}"
144
+ status-danger-text: "#d32f2f"
145
+ status-danger-surface: "{colors.red-100}"
146
+ status-danger-border: "#d32f2f"
140
147
  typography:
141
148
  heading-72:
142
149
  fontFamily: Noto Sans SC
@@ -27,7 +27,7 @@ Decision date: 2026-08-26
27
27
 
28
28
  **为什么环境层要降不透明度**:新增的接触层已经补上了元件根部的密度。若环境层沿用原不透明度,两层叠加后整体偏重,浮层看起来像是"脏"了而不是"浮起来"。popover 16%→14%、modal 32%→28% 是为抵消这部分叠加。
29
29
 
30
- `--q1-shadow-focus` 不在本次范围内:焦点环是独立语义,不参与光照模型,保持单层。
30
+ `--q1-shadow-focus` 不在本次浮层阴影迁移范围内:焦点环是独立语义,不参与光照模型。其后续双层对比环见 `contracts/token-naming.md`。
31
31
 
32
32
  方向性面板(Drawer / Sheet)的阴影在组件内自持,不消费这两个 token,因此不受影响。
33
33
 
@@ -78,5 +78,5 @@ q1travel-web 当前停留在 0.7.3,与本仓 0.9.0 之间还隔着其他变更
78
78
 
79
79
  - 不修改 q1travel-web,不代下游提交。
80
80
  - 不新增第三档阴影,不为 Drawer / Sheet 引入共享阴影 token。
81
- - 不改动 `--q1-shadow-focus`。
81
+ - 本次浮层阴影迁移不改动 `--q1-shadow-focus`;后续焦点环调整独立进行。
82
82
  - 不在本合同内处理 `contracts/interface-guidelines.md` Design 章节的其余条款(嵌套圆角见 #141,色相一致见 #142)。
@@ -95,7 +95,11 @@ requires each capability.
95
95
  receives the sort-change event, and owns data ordering or server requests.
96
96
  - A sortable heading is a native button inside the heading cell. The heading
97
97
  exposes `aria-sort` with the applicable `none`, `ascending`, or `descending`
98
- value. The sort icon is decorative and is not the only state cue.
98
+ value for the current state. Its button name describes the direction after
99
+ activation. A column may provide `nextSortDirection` when the consumer's
100
+ sorting policy differs from the default ascending-first toggle; that same
101
+ direction is sent to `onSortChange`. The sort icon is decorative and is not
102
+ the only state cue.
99
103
  - Table does not silently sort data, fetch a new page, mutate URL/query state,
100
104
  or fabricate a total while sorting.
101
105
 
@@ -94,7 +94,9 @@ Examples:
94
94
  - `--q1-shadow-focus`
95
95
  - `--q1-shadow-focus-danger`
96
96
 
97
- `--q1-shadow-focus-danger` is the ring the field family already draws on an invalid control, now named: one step wider and one step lighter than `--q1-shadow-focus`, because red at the same opacity swallows the 1px red border it sits on. Both rings resolve their color through a semantic token, so light and dark need no separate declaration.
97
+ `--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.
98
+
99
+ `--q1-color-focus` resolves to `--q1-color-primary-700`. `--q1-shadow-focus` draws a 1px canvas contrast ring and a 3px focus color ring, keeping keyboard focus visible against a purple control or dark surface. `--q1-shadow-focus-danger` remains the field family's invalid-control ring and does not change with this focus-ring migration.
98
100
 
99
101
  ### Component Tokens
100
102
 
@@ -1781,6 +1781,8 @@ export declare type TableColumn<T> = {
1781
1781
  render: (row: T, rowIndex: number) => ReactNode;
1782
1782
  sortable?: boolean;
1783
1783
  sortLabel?: string;
1784
+ /** 下次点击此列时的排序方向;未提供时沿用默认的升序优先切换。 */
1785
+ nextSortDirection?: TableSortDirection;
1784
1786
  /**
1785
1787
  * 横向对齐,默认 `start`。数值列用 `end`——个位对齐个位,一眼可比大小与位数;
1786
1788
  * 左对齐的数字列要逐行数位数才知道谁大。表头随单元格一起对齐,否则列会读成两栏。
@@ -3447,8 +3447,11 @@ function Is({
3447
3447
  }, []);
3448
3448
  Z(() => {
3449
3449
  const R = D.current !== r;
3450
- D.current = r, k((j) => R ? r.trim() ? x[0]?.key : void 0 : x.some((G) => G.key === j) ? j : r.trim() ? x[0]?.key : void 0);
3451
- }, [x, r]), Z(() => {
3450
+ D.current = r, k((j) => {
3451
+ if (e)
3452
+ return R ? r.trim() ? x[0]?.key : void 0 : x.some((G) => G.key === j) ? j : r.trim() ? x[0]?.key : void 0;
3453
+ });
3454
+ }, [x, e, r]), Z(() => {
3452
3455
  if (!e || !I) return;
3453
3456
  p.current?.querySelector('[data-search-dialog-active="true"]')?.scrollIntoView({ block: "nearest" });
3454
3457
  }, [e, I]), Z(() => {
@@ -8106,10 +8109,10 @@ function bc({
8106
8109
  !E || Wn(A.target, A.currentTarget) || A.key !== "Enter" && A.key !== " " || (A.preventDefault(), E.onActivate(T.row, T.rowIndex));
8107
8110
  }
8108
8111
  function j(T) {
8109
- return f === T && _ === "asc" ? "desc" : "asc";
8112
+ return T.nextSortDirection ?? (f === T.key && _ === "asc" ? "desc" : "asc");
8110
8113
  }
8111
8114
  function G(T) {
8112
- N && N(T, j(T));
8115
+ N && N(T.key, j(T));
8113
8116
  }
8114
8117
  function ee(T) {
8115
8118
  if (T.sortable)
@@ -8134,14 +8137,14 @@ function bc({
8134
8137
  }
8135
8138
  ) }) }) : null,
8136
8139
  t.map((T) => {
8137
- const A = ee(T), Y = T.sortable && f === T.key && _ ? _ === "asc" ? "升序" : "降序" : null, J = j(T.key) === "asc" ? "升序" : "降序", te = Y ? /* @__PURE__ */ n("span", { className: "table__sort-state", children: Y }) : null, B = T.sortLabel ?? (typeof T.header == "string" ? T.header : T.key), H = T.sortable ? /* @__PURE__ */ w(
8140
+ const A = ee(T), Y = T.sortable && f === T.key && _ ? _ === "asc" ? "升序" : "降序" : null, J = j(T) === "asc" ? "升序" : "降序", te = Y ? /* @__PURE__ */ n("span", { className: "table__sort-state", children: Y }) : null, B = T.sortLabel ?? (typeof T.header == "string" ? T.header : T.key), H = T.sortable ? /* @__PURE__ */ w(
8138
8141
  "button",
8139
8142
  {
8140
8143
  type: "button",
8141
8144
  className: "table__sort",
8142
- "aria-label": `按${B}${J}排序`,
8145
+ "aria-label": `点击后将按${B}${J}排序`,
8143
8146
  disabled: !N,
8144
- onClick: N ? () => G(T.key) : void 0,
8147
+ onClick: N ? () => G(T) : void 0,
8145
8148
  children: [
8146
8149
  T.header,
8147
8150
  te
@@ -122,15 +122,23 @@
122
122
  --q1-color-bg-media: #0a0a0a;
123
123
  --q1-color-text-on-media: #ffffff;
124
124
  --q1-color-border: var(--q1-color-gray-400);
125
+ --q1-color-border-subtle: rgba(203, 213, 225, 0.82);
125
126
  --q1-color-border-strong: var(--q1-color-gray-500);
126
127
  --q1-color-text-primary: var(--q1-color-gray-1000);
127
128
  --q1-color-text-secondary: var(--q1-color-gray-900);
128
129
  --q1-color-text-muted: #6b6b6b;
130
+ --q1-color-text-soft: #6e6e73;
131
+ --q1-color-overlay-scrim: rgba(2, 6, 23, 0.44);
132
+ --q1-color-focus: var(--q1-color-primary-700);
129
133
  --q1-color-selection-bg: var(--q1-color-text-primary);
130
134
  --q1-color-selection-fg: var(--q1-color-bg-canvas);
131
135
  --q1-color-status-success: var(--q1-color-green-700);
132
136
  --q1-color-status-warning: var(--q1-color-amber-900);
133
137
  --q1-color-status-danger: var(--q1-color-red-700);
138
+ /* status-danger 是亮红,只用于填充和描边;文字或无下划线弱提示使用 danger-text。 */
139
+ --q1-color-status-danger-text: #d32f2f;
140
+ --q1-color-status-danger-surface: var(--q1-color-red-100);
141
+ --q1-color-status-danger-border: #d32f2f;
134
142
  --q1-color-status-info: var(--q1-color-blue-700);
135
143
  --q1-font-sans: "Noto Sans SC", "PingFang SC", "Microsoft YaHei UI", sans-serif;
136
144
  --q1-font-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
@@ -232,7 +240,7 @@
232
240
  --q1-radius-5: 16px;
233
241
  /* 胶囊:半径大于任何可能的半高即可,具体数值不参与计算。 */
234
242
  --q1-radius-full: 9999px;
235
- --q1-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--q1-color-primary-700) 22%, transparent);
243
+ --q1-shadow-focus: 0 0 0 1px var(--q1-color-bg-canvas), 0 0 0 3px var(--q1-color-focus);
236
244
  /* 危险态环:字段家族错误态与危险动作共用。比默认焦点环宽一档、淡一档——红色本身已经足够响,
237
245
  同样浓度会盖过 1px 红描边。取值沿用字段错误态的既有标定,status-danger 明暗各有取值。 */
238
246
  --q1-shadow-focus-danger: 0 0 0 4px color-mix(in srgb, var(--q1-color-status-danger) 16%, transparent);
@@ -374,15 +382,22 @@
374
382
  --q1-color-bg-media: #0a0a0a;
375
383
  --q1-color-text-on-media: #ffffff;
376
384
  --q1-color-border: var(--q1-color-gray-200);
385
+ --q1-color-border-subtle: #1f1f1f;
377
386
  /* 降档:控件描边不再泛白。 */
378
387
  --q1-color-border-strong: var(--q1-color-gray-400);
379
388
  --q1-color-border-overlay: color-mix(in srgb, #ffffff 14%, transparent);
380
389
  --q1-color-text-primary: var(--q1-color-gray-1000);
381
390
  --q1-color-text-secondary: var(--q1-color-gray-900);
382
391
  --q1-color-text-muted: var(--q1-color-gray-700);
392
+ --q1-color-text-soft: #9a9a9c;
393
+ --q1-color-overlay-scrim: rgba(2, 6, 23, 0.44);
394
+ --q1-color-focus: var(--q1-color-primary-700);
383
395
  --q1-color-status-success: var(--q1-color-green-900);
384
396
  --q1-color-status-warning: var(--q1-color-amber-700);
385
397
  --q1-color-status-danger: var(--q1-color-red-900);
398
+ --q1-color-status-danger-text: #ef5350;
399
+ --q1-color-status-danger-surface: var(--q1-color-red-100);
400
+ --q1-color-status-danger-border: #ef5350;
386
401
  --q1-color-status-info: var(--q1-color-blue-900);
387
402
  /* Override only the press state: the dark Primary ramp runs left-dark to
388
403
  right-light, so Primary-900 is a light tint. Point action-press at a darker
@@ -6399,7 +6414,7 @@ a.q1-card:focus-visible {
6399
6414
  }
6400
6415
  .collapse__trigger:focus-visible {
6401
6416
  outline: none;
6402
- box-shadow: inset var(--q1-shadow-focus);
6417
+ box-shadow: inset 0 0 0 1px var(--q1-color-bg-canvas), inset 0 0 0 3px var(--q1-color-focus);
6403
6418
  }
6404
6419
  .collapse__title {
6405
6420
  font-size: 14px;
@@ -118,15 +118,22 @@
118
118
  --q1-color-bg-media: #0a0a0a;
119
119
  --q1-color-text-on-media: #ffffff;
120
120
  --q1-color-border: var(--q1-color-gray-400);
121
+ --q1-color-border-subtle: rgba(203, 213, 225, 0.82);
121
122
  --q1-color-border-strong: var(--q1-color-gray-500);
122
123
  --q1-color-text-primary: var(--q1-color-gray-1000);
123
124
  --q1-color-text-secondary: var(--q1-color-gray-900);
124
125
  --q1-color-text-muted: #6b6b6b;
126
+ --q1-color-text-soft: #6e6e73;
127
+ --q1-color-overlay-scrim: rgba(2, 6, 23, 0.44);
128
+ --q1-color-focus: var(--q1-color-primary-700);
125
129
  --q1-color-selection-bg: var(--q1-color-text-primary);
126
130
  --q1-color-selection-fg: var(--q1-color-bg-canvas);
127
131
  --q1-color-status-success: var(--q1-color-green-700);
128
132
  --q1-color-status-warning: var(--q1-color-amber-900);
129
133
  --q1-color-status-danger: var(--q1-color-red-700);
134
+ --q1-color-status-danger-text: #d32f2f;
135
+ --q1-color-status-danger-surface: var(--q1-color-red-100);
136
+ --q1-color-status-danger-border: #d32f2f;
130
137
  --q1-color-status-info: var(--q1-color-blue-700);
131
138
  --q1-font-sans: "Noto Sans SC", "PingFang SC", "Microsoft YaHei UI", sans-serif;
132
139
  --q1-font-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
@@ -220,7 +227,7 @@
220
227
  --q1-radius-4: 12px;
221
228
  --q1-radius-5: 16px;
222
229
  --q1-radius-full: 9999px;
223
- --q1-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--q1-color-primary-700) 22%, transparent);
230
+ --q1-shadow-focus: 0 0 0 1px var(--q1-color-bg-canvas), 0 0 0 3px var(--q1-color-focus);
224
231
  --q1-shadow-focus-danger: 0 0 0 4px color-mix(in srgb, var(--q1-color-status-danger) 16%, transparent);
225
232
  --q1-shadow-popover: 0 1px 3px color-mix(in srgb, var(--q1-color-shadow) 10%, transparent),
226
233
  0 8px 28px color-mix(in srgb, var(--q1-color-shadow) 14%, transparent);
@@ -358,15 +365,22 @@
358
365
  --q1-color-bg-media: #0a0a0a;
359
366
  --q1-color-text-on-media: #ffffff;
360
367
  --q1-color-border: var(--q1-color-gray-200);
368
+ --q1-color-border-subtle: #1f1f1f;
361
369
  --q1-color-border-strong: var(--q1-color-gray-400);
362
370
  --q1-color-text-primary: var(--q1-color-gray-1000);
363
371
  --q1-color-text-secondary: var(--q1-color-gray-900);
364
372
  --q1-color-text-muted: var(--q1-color-gray-700);
373
+ --q1-color-text-soft: #9a9a9c;
374
+ --q1-color-overlay-scrim: rgba(2, 6, 23, 0.44);
375
+ --q1-color-focus: var(--q1-color-primary-700);
365
376
  --q1-color-selection-bg: var(--q1-color-text-primary);
366
377
  --q1-color-selection-fg: var(--q1-color-bg-canvas);
367
378
  --q1-color-status-success: var(--q1-color-green-900);
368
379
  --q1-color-status-warning: var(--q1-color-amber-700);
369
380
  --q1-color-status-danger: var(--q1-color-red-900);
381
+ --q1-color-status-danger-text: #ef5350;
382
+ --q1-color-status-danger-surface: var(--q1-color-red-100);
383
+ --q1-color-status-danger-border: #ef5350;
370
384
  --q1-color-status-info: var(--q1-color-blue-900);
371
385
  --q1-font-sans: "Noto Sans SC", "PingFang SC", "Microsoft YaHei UI", sans-serif;
372
386
  --q1-font-mono: "Geist Mono", ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;
@@ -460,7 +474,7 @@
460
474
  --q1-radius-4: 12px;
461
475
  --q1-radius-5: 16px;
462
476
  --q1-radius-full: 9999px;
463
- --q1-shadow-focus: 0 0 0 3px color-mix(in srgb, var(--q1-color-primary-700) 22%, transparent);
477
+ --q1-shadow-focus: 0 0 0 1px var(--q1-color-bg-canvas), 0 0 0 3px var(--q1-color-focus);
464
478
  --q1-shadow-focus-danger: 0 0 0 4px color-mix(in srgb, var(--q1-color-status-danger) 16%, transparent);
465
479
  --q1-shadow-popover: 0 1px 3px color-mix(in srgb, var(--q1-color-shadow) 10%, transparent),
466
480
  0 8px 28px color-mix(in srgb, var(--q1-color-shadow) 14%, transparent);
package/package.json CHANGED
@@ -1,6 +1,16 @@
1
1
  {
2
2
  "name": "@q1travel/design-system",
3
- "version": "0.17.0",
3
+ "version": "0.17.2",
4
+ "description": "React components, design tokens, and interface contracts for q1travel products.",
5
+ "keywords": [
6
+ "react",
7
+ "design-system",
8
+ "components",
9
+ "design-tokens",
10
+ "theme"
11
+ ],
12
+ "homepage": "https://design.q1lx.com",
13
+ "license": "UNLICENSED",
4
14
  "private": false,
5
15
  "type": "module",
6
16
  "repository": {