@q1travel/design-system 0.3.2 → 0.5.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.
@@ -1,63 +1,40 @@
1
1
  ---
2
2
  name: product-design
3
- description: q1travel 产品设计 agent 框架。凡改动用户能看到、理解、选择或执行的界面,在动手/评审前加载本 skill 做模式判断与规则路由。Use when shaping, implementing, reviewing, copy-editing, or hardening any UI that consumes @q1travel/design-system.
3
+ description: Use when shaping, implementing, reviewing, copy-editing, or hardening any user-visible interface owned by or consuming @q1travel/design-system.
4
4
  ---
5
5
 
6
- # q1travel 产品设计(product-design)
6
+ # q1travel product design
7
7
 
8
- ## 这是什么
8
+ This skill routes interface work to the smallest relevant set of q1travel-owned contracts. It does not replace those contracts or copy external implementations.
9
9
 
10
- `@q1travel/design-system` 随包发布的产品设计判断层。它把「已被接受的设计决策」当代码管理:规范落在 `contracts/`,规则由 linter 强制,证据经 review loop 回流。它的作用是**路由到 canonical 源**,不是复制判断。
10
+ ## Authority
11
11
 
12
- - **Canonical 源恒为 `contracts/`**(随包发布):`design-engineering-principles.md`、`interface-guidelines.md`、`design.md` / `design.dark.md`、`icon-system.md`、`token-naming.md`、`implementation-map.md`。
13
- - 本 skill 的 `references/` 只是**指路条**,指向上面的 canonical 文件;正文永远读 `contracts/`,不在 references 里另立一份。
14
- - Token 恒用 `--q1-*`。product 仓里的 `--ds-*` 是 legacy,不作新决策的源命名(见 `contracts/token-naming.md`)。
12
+ - `contracts/` is canonical: design principles, interface guidelines, themes, icons, token naming, and implementation mapping.
13
+ - `references/` contains routing and workflow material only. If it conflicts with `contracts/` or an accepted decision, the canonical contract or decision wins.
14
+ - External systems may inform intent, but never supply copied markup, private classes, computed styles, or public q1travel rules.
15
+ - New tokens use `--q1-*`; product-side `--ds-*` names are legacy migration inputs.
15
16
 
16
- ## 在消费者仓库里(硬边界)
17
+ ## Choose One Mode
17
18
 
18
- 当本 skill 出现在消费 `@q1travel/design-system` 的仓库(如 q1travel-web)时:
19
+ | Mode | Trigger | Load |
20
+ |---|---|---|
21
+ | Shape | The problem is ambiguous or has meaningful alternatives | Design principles: Usefulness and Constraints; relevant interface sections |
22
+ | Implement | The requested interface behavior is settled | Relevant surface contract, theme tokens, icons, and nearby implementation/tests |
23
+ | Review | The user requests findings or sign-off | Full design principles and interface guidelines; for this repository's component review, `references/review-component.md` |
24
+ | Copy | Only user-visible wording changes | Interface guidelines: Copy; `references/copy.md` when terminology is involved |
25
+ | Harden | The task targets edge states or production readiness | Interactions, Content, Accessibility Tiers, Performance; `references/resilience.md` |
19
26
 
20
- - 它是**由 npm 版本生成的只读缓存**(gitignored、可重建)。**不要手改**;要改设计判断,改上游的包,重新发版、重新生成。
21
- - 消费者**不得**新建或维护本地设计规范(`DESIGN.md`、`docs/design/`、可编辑的本地 design skill)。设计权威只有一个:这个包。
22
- - 消费者只保留**可执行的 app 专属校验**(baseline、路由/权限矩阵、业务字段语言)——这些不是设计判断,留在本地。
27
+ Use progressive disclosure: load only the selected mode, affected surface, nearby source, and its verification seam.
23
28
 
24
- ## 何时使用
29
+ ## Surface Routing
25
30
 
26
- 改动任何用户能看到、理解、选择或执行的界面之前,先判断落在哪个模式,再按 surface 加载对应 canonical 段落。纯后端、纯数据、与界面无关的改动不需要本 skill。
31
+ - Forms and inputs: `references/surfaces-forms.md`
32
+ - Modals, drawers, menus, comboboxes, and other overlays: `references/surfaces-overlays.md`
33
+ - Tables, lists, pagination, destructive actions, icons, motion, themes, and copy: load the matching sections named in `references/surfaces.md`
34
+ - Unknown or missing coverage: record the gap through `references/coverage-gaps.md`; do not invent a new contract during unrelated work
27
35
 
28
- ## 五模式
36
+ Review and Harden require rendered local evidence. A read-only review does not authorize edits, commits, or external delivery.
29
37
 
30
- 每次先把请求归到一个模式,再加载该模式所需的 canonical 段落。
38
+ ## Consumer Boundary
31
39
 
32
- | 模式 | 何时 | 加载(canonical) | 产出 |
33
- |---|---|---|---|
34
- | **Shape** 立题 | 需求模糊/多解,先框问题、比方案 | `design-engineering-principles.md`(Usefulness / Constraints);`interface-guidelines.md` 相关章 | 问题陈述 + 方案取舍,不写实现 |
35
- | **Implement** 落地 | 明确范围内端到端做 | 按 surface 路由(见下)+ `design.md` token + `icon-system.md` | 改动 + 自测 |
36
- | **Review** 评审 | 审代码 + 审**渲染后**的界面 | 全量 `interface-guidelines.md` + `design-engineering-principles.md`;评审本仓组件对齐 Geist 时加载 `references/review-geist.md` | 差异单(见 `references/rules.md`) |
37
- | **Copy** 文案 | 只改用户可见文字 | `interface-guidelines.md` §Copy | 文案 diff,不动结构 |
38
- | **Harden** 收口 | 打磨 + 边界态 | `interface-guidelines.md`(Interactions / Content / Accessibility Tiers / Performance);`references/resilience.md` | 每个可达态齐备 |
39
-
40
- Review 与 Harden 必须核**渲染后的界面**,不能只看代码(见 `design-engineering-principles.md` §Whole Experience)。
41
-
42
- ## 按 surface 路由
43
-
44
- Implement / Harden 时,按改动落在哪种 surface 加载对应段落:
45
-
46
- | Surface | 加载 |
47
- |---|---|
48
- | 表单 / 输入 | `interface-guidelines.md` §Forms;`references/surfaces.md` |
49
- | 弹层(modal / drawer / menu / combobox…) | `interface-guidelines.md` §Interactions + §Accessibility Tiers(Tier A APG 键盘模型) |
50
- | 表格 / 列表 / 分页 | §Layout + §Performance(长列表虚拟化)+ §Content(tabular numbers) |
51
- | 破坏性动作 | §Interactions(确认 / 撤销);`references/rules.md`(命名 scope + 后果) |
52
- | 空 / 加载 / 错误 / 无权限态 | `references/resilience.md`(每个可达态都要设计) |
53
- | 图标 | `icon-system.md`;§Content(icon-only 必须有可访问名) |
54
- | 动效 | §Animation(honor reduced-motion;禁 `transition: all`) |
55
- | 主题 / 颜色 | §Theming;`design.md` / `design.dark.md` token;`token-naming.md` |
56
- | 文案 | §Copy;`references/glossary.md` |
57
-
58
- ## 红线
59
-
60
- - 在消费者仓库手改本缓存、或另立本地设计文档 = 违规。改判断走上游包。
61
- - `references/` 里重抄 `contracts/` 正文 = 违规。references 只指路。(review-geist.md 是收编的流程文档,属例外,其权威边界见该文件头部。)
62
- - 用 `--ds-*` 作新决策的源命名 = 违规。新代码 `--q1-*`。
63
- - 声称"看着对"就算过 = 未验证。Review / Harden 必须核渲染后的界面。
40
+ In a repository that consumes `@q1travel/design-system`, this skill is generated, read-only package material. Change design authority in this source repository, publish through the approved release flow, then regenerate the consumer cache. Do not create a competing local design authority.
@@ -4,6 +4,7 @@
4
4
 
5
5
  - **稳定 ID 规则层部分完成**:`q1-design lint` 已实现 `Q1-DS-001`(`--ds-*`)/`Q1-DS-401`(`transition: all`)/`Q1-DS-501`(裸颜色字面量)三条纯机械规则;其余 `Q1-DS-*` 规则(含带判断成分的 deterministic 规则与全部 judgment 规则)尚未落地(见 `references/rules.md`)。
6
6
  - **per-surface 细粒度契约未展开**:`implementation-map.md` 的组件契约还没按 modal / drawer / table 等逐一展开组合约束(见 `references/surfaces.md`)。
7
+ - **Tier A pattern applicability is ambiguous**:`interface-guidelines.md` 当前把 composite widgets 与 open / close、focus return 的共享措辞放在一起。现有 canonical 在变更前仍然有效;若要按 matching APG pattern 区分适用性,必须通过单独的 canonical contract 变更澄清,reference 不得先行改写。
7
8
  - **术语表未成形**:统一 glossary 待消费者黑话清理产出回流(见 `references/glossary.md`)。
8
9
  - **exemplars 为空**:还没有从已合并 PR 收录的范例(见 `../exemplars/`)。
9
10
  - **review loop 未接线**:消费者证据 → 包决策的回流路径(复用轻量 register + `gh`)尚未搭。
@@ -0,0 +1,32 @@
1
+ # Review mode: q1travel component review
2
+
3
+ Use this workflow when reviewing this repository's component implementation or documentation. Load `../SKILL.md` first so the mode and canonical contract routing remain intact.
4
+
5
+ ## Authority And Scope
6
+
7
+ - Design judgment comes from `contracts/`.
8
+ - `review/decision-log/0002-external-references-are-inspiration-not-source.md` is the accepted external-reference boundary.
9
+ - An external reference system may provide optional structural intent when the user explicitly requests that comparison. It is not an implementation source, a required live verification target, or a source of q1travel contract language.
10
+ - Establish the exact component and whether the request is read-only before starting. Review does not authorize componentization, fixes, commits, or delivery.
11
+
12
+ ## Workflow
13
+
14
+ 1. Load the full design principles and interface guidelines, then the affected theme, icon, token, or implementation-map contract.
15
+ 2. Inspect the target component, its documentation page, exports, nearby tests, and the relevant browser assertions.
16
+ 3. Check the rendered local component in light and dark themes. Cover sparse, dense, loading, error, focus, disabled, and reduced-motion states that the component can reach.
17
+ 4. Probe every element that appears interactive. Verify semantics, accessible names, keyboard behavior, focus, state announcement, disabled behavior, cleanup, and provider independence where applicable.
18
+ 5. Compare the implementation with the matching local component family before proposing a new page-local pattern.
19
+ 6. Run the verification command routed by `AGENTS.md`. Keep tool limitations separate from product findings.
20
+ 7. Report findings by severity with contract, current evidence, impact, and the smallest corrective direction. If there are no findings, state the remaining evidence limits.
21
+
22
+ ## Evidence Pitfalls
23
+
24
+ - Geometry alone does not prove visible overflow; use hit testing when overflow matters.
25
+ - Hidden automation tabs can freeze animations; inspect animation state rather than inferring from a static frame.
26
+ - Screenshot compression can mimic texture; confirm visual effects through computed styles.
27
+ - A detached subcomponent can render while silently losing parent-scoped CSS variables; assert resolved colors as well as geometry.
28
+ - Synthetic clicks may lack user activation, so clipboard success requires an appropriate seam rather than a forced browser assertion.
29
+
30
+ ## Completion Boundary
31
+
32
+ For a read-only review, stop after the findings. If the user separately authorizes fixes, return to Implement or Harden mode, make the minimal change, run fresh checks, and review the resulting diff before any separately authorized commit.
@@ -1,12 +1,7 @@
1
- # surfaces: overlays(弹层:modal / drawer / menu / combobox…)
1
+ # Surface route: overlays
2
2
 
3
- **Canonical**:`contracts/interface-guidelines.md` §Interactions + §Accessibility Tiers(Tier A)。
3
+ **Canonical:** `contracts/interface-guidelines.md`, Interactions and Accessibility Tiers.
4
4
 
5
- 要点(正文见 canonical):
5
+ Use for modals, drawers, menus, comboboxes, and other surfaces that open above existing content. Load the exact component pattern and nearby browser assertions after the canonical sections.
6
6
 
7
- - **Tier A**:必须实现对应 APG 模式的完整键盘模型 —— roving tabindex;方向 / `Home` / `End` / `PageUp` / `PageDown`;`Enter` / `Space` 激活;`Esc` 关闭并把焦点还给触发器。
8
- - 打开 / 关闭都要管理焦点;暴露必需 ARIA(`role`、`aria-expanded` / `aria-selected` / `aria-controls`、状态变化处 `aria-live`)。
9
- - 键位以「按键 | 行为」表记入页面可访问性节,并由 `check-browser.mjs` 的 runtime 键盘断言锁住。
10
- - 浮层在视口边缘做碰撞翻转。
11
-
12
- **参考实现**:Calendar / Tabs(见 §Accessibility Tiers)。只指路,不复制正文。
7
+ This file does not restate keyboard or focus requirements. See `coverage-gaps.md` before applying shared Tier A wording outside overlays.
@@ -1,14 +1,13 @@
1
- # surfaces(指路)
1
+ # Surface routing
2
2
 
3
- 按 surface 类型把 `contracts/interface-guidelines.md` 的章节与组件契约对应起来。
3
+ Use `contracts/interface-guidelines.md` and `contracts/implementation-map.md` as canonical sources. Load only the route matching the affected surface:
4
4
 
5
- **Canonical**:`contracts/interface-guidelines.md` + `contracts/implementation-map.md`(组件 → 契约映射)。
5
+ - Forms and inputs: `surfaces-forms.md`
6
+ - Modals, drawers, menus, comboboxes, and other overlays: `surfaces-overlays.md`
7
+ - Tabs, sliders, and trees: Accessibility Tiers; check the known applicability issue in `coverage-gaps.md`
8
+ - Tables, lists, and pagination: Layout, Performance, and Content
9
+ - Destructive actions: Interactions
10
+ - Empty, loading, error, and permission states: `resilience.md`
11
+ - Icons, motion, themes, and copy: the matching named contract section
6
12
 
7
- Surface → 章节的路由表见 `SKILL.md`「按 surface 路由」。
8
-
9
- **已展开的 per-surface**(命名约定 `surfaces-<surface>.md`):
10
-
11
- - `surfaces-forms.md` —— 表单 / 输入(Tier B)
12
- - `surfaces-overlays.md` —— 弹层 modal / drawer / menu / combobox(Tier A)
13
-
14
- **Coverage gap**:其余 surface(table / list、破坏性动作等)的细粒度契约尚未从 `implementation-map.md` 完全展开,见 `references/coverage-gaps.md`。
13
+ If no route covers the requested behavior, record the missing coverage in `coverage-gaps.md` instead of inventing a contract during unrelated work.
package/README.md CHANGED
@@ -8,11 +8,11 @@ The site is written as a public product standard. Avoid internal-only or narrow
8
8
 
9
9
  ## Sources
10
10
 
11
- - Source contracts: `contracts/design.md`, `contracts/design.dark.md`, `contracts/token-naming.md`, `contracts/interface-guidelines.md`, `contracts/design-engineering-principles.md`, and `contracts/implementation-map.md`.
11
+ - Source contracts: `contracts/design.md`, `contracts/design.dark.md`, `contracts/token-naming.md`, `contracts/button-api-compatibility.md`, `contracts/interface-guidelines.md`, `contracts/design-engineering-principles.md`, and `contracts/implementation-map.md`.
12
12
  - Generated mirrors: `public/design.md` and `public/design.dark.md`.
13
13
  - Runtime tokens: `src/styles/tokens.css`.
14
14
  - Runtime UI: `src/components` owns shared structure; `src/pages` composes documentation examples.
15
- - Scripts: `scripts/sync-design-contracts.mjs` publishes contract mirrors; `scripts/check-shell.mjs` verifies source and contract boundaries; `scripts/check-browser.mjs` verifies built UI behavior. `npm run check` runs the build, shell, and browser gates.
15
+ - Scripts: `scripts/sync-design-contracts.mjs` publishes contract mirrors; `scripts/check-shell.mjs` verifies source and contract boundaries; `scripts/check-browser.mjs` verifies built UI behavior; `scripts/check-package-api.mjs` verifies the built package declarations and runtime API. Run both `npm run check` and `npm run build:pkg` for Button API changes.
16
16
 
17
17
  ## v1 scope
18
18
 
@@ -0,0 +1,63 @@
1
+ # Button API Compatibility Contract
2
+
3
+ Status: accepted
4
+
5
+ Decision date: 2026-07-20
6
+
7
+ Published baseline: `@q1travel/design-system` `0.4.0`
8
+
9
+ This contract records the unreleased post-`0.4.0` Button API migration tracked by issue #59. It keeps existing consumers working while the public API moves to the accepted names from the Button design decision.
10
+
11
+ ## Canonical API
12
+
13
+ | Surface | Canonical API | Deprecated compatibility API |
14
+ | --- | --- | --- |
15
+ | Button and ButtonLink icon-only layout | `svgOnly` | `iconOnly` |
16
+ | Button, ButtonLink, and IconButton accessible name | `aria-label` | `ariaLabel` |
17
+ | ButtonLink button-only field | none | `type` is accepted but ignored |
18
+
19
+ New recommended code examples and product migrations use only the canonical API.
20
+
21
+ When a canonical prop and its deprecated alias are both defined, the canonical prop wins. Resolution uses nullish precedence, so `svgOnly={false}` still overrides `iconOnly={true}`, and a defined `aria-label` still overrides `ariaLabel`.
22
+
23
+ IconButton requires either `aria-label` or the deprecated `ariaLabel` at the TypeScript boundary. Its development warning evaluates the resolved accessible name.
24
+
25
+ ## Component-Owned Semantics
26
+
27
+ - `prefix` and `suffix` are decorative slots. Button and ButtonLink apply `aria-hidden="true"` to the slot boundary; callers do not repeat that attribute on the supplied icon.
28
+ - `ariaKeyshortcuts` is rendered as `aria-keyshortcuts` by both Button and ButtonLink. The visible `shortcut` badge remains decorative and must correspond to a real registered shortcut.
29
+ - ButtonLink renders an anchor. Its inherited button-only `type` field remains a deprecated compatibility no-op and is never forwarded to the anchor.
30
+
31
+ ## Known Consumer Scan
32
+
33
+ Scan date: 2026-07-20
34
+
35
+ The scan covers the current local checkouts named below; it is not evidence about un-fetched branches or third-party packages.
36
+
37
+ - `q1travel-design-system`: recommended code examples in this source revision use `svgOnly` and `aria-label`. Deprecated names remain only where compatibility behavior is exercised or declared.
38
+ - `q1travel-web`: the package is used for its stylesheet and Badge export. Button and IconButton remain local components, so this change does not migrate them.
39
+ - `q1travel-chrome-ozon-price-index`: package Button consumers use text-button props and do not use `iconOnly`, `ariaLabel`, or `svgOnly`.
40
+ - Unknown third-party npm consumers cannot be enumerated from q1travel repositories, so a repository-local zero-result scan is not sufficient evidence for alias removal.
41
+
42
+ ## Verification Boundary
43
+
44
+ - `npm run check` verifies the documentation build, source contracts, and real-browser behavior.
45
+ - `npm run build:pkg` builds the package and verifies emitted declarations, consumer type compatibility, and package runtime behavior.
46
+ - Both gates are required for this migration. Neither gate publishes a package.
47
+
48
+ ## Removal Conditions
49
+
50
+ The deprecated `iconOnly`, `ariaLabel`, and ButtonLink `type` compatibility fields may be removed only when all of the following are true:
51
+
52
+ 1. At least one package release containing this migration has been published.
53
+ 2. A fresh scan of q1travel-owned consumers finds no deprecated usage outside explicit compatibility tests.
54
+ 3. A separate removal issue records known consumers, migration notes, and the intended breaking release.
55
+ 4. The breaking change receives explicit approval.
56
+
57
+ Until then, the aliases remain tested public compatibility contracts.
58
+
59
+ ## Non-Goals
60
+
61
+ - This contract does not migrate `q1travel-web`.
62
+ - This contract does not choose a package version, publish a package, or remove an alias.
63
+ - This contract does not change Button size, loading, variant, route, or styling contracts beyond the decorative slot boundary described above.
@@ -71,6 +71,24 @@ Every component is classified into one accessibility tier; the tier sets its bar
71
71
  - **Tier B — native-semantic controls** (button, input, textarea, checkbox, radio, switch, select, links): MUST rely on native elements for keyboard and role, MUST carry an associated label or accessible name, and MUST show a visible `:focus-visible` state through the shared focus token. No custom key handling is expected.
72
72
  - **Tier C — display / decorative** (avatar, badge, banner, book, browser, icon, card, skeleton, …): MUST hide purely decorative visuals from assistive technology (`aria-hidden`, non-focusable) and MUST give every icon-only control an accessible name. No keyboard model is required.
73
73
 
74
+ ### Editable Combobox With Listbox Popup Variant
75
+
76
+ WAI-ARIA and WCAG define normative requirements ([WAI-ARIA 1.2 Combobox](https://www.w3.org/TR/wai-aria/#combobox), [WCAG 2.2](https://www.w3.org/TR/WCAG22/)). They govern role, state, and property conformance, keyboard operation and exit paths, focus order, and name, role, and value; they do not prescribe whether `ArrowUp` or `ArrowDown` wraps at listbox endpoints. APG is informative guidance ([APG Introduction](https://www.w3.org/WAI/ARIA/apg/about/introduction/#apg-is--not-normative)). The [abstract Combobox Pattern](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/#keyboard_interaction) lists returning to the combobox or not moving at the endpoints, while the official editable examples are illustrative, not prescriptive, and implement wrapping: [list autocomplete](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-list/#kbd_label), [no autocomplete](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-none/#kbd_label), and [inline autocomplete](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/examples/combobox-autocomplete-both/#kbd_label). The abstract Pattern guidance is not a normative prohibition against wrapping, and this variant does not claim to be a normative WAI-ARIA or WCAG requirement or a verbatim copy of any one example. It does not relax Tier A requirements for other components.
77
+
78
+ q1travel combines the Arrow-key wrapping used by those official editable examples with the abstract Pattern's optional `Home` / `End` option-navigation outcome: while the popup is open, `Home` / `End` move to the first / last enabled option. By contrast, the editable examples instead use `Home` / `End` to move the text-input cursor to its start / end. This combined behavior is a q1travel project choice.
79
+
80
+ | Key | Behavior |
81
+ | --- | --- |
82
+ | `ArrowDown` | When closed, open the popup. When open, move to the next enabled option and skip disabled options; after the last enabled option, wrap to the first enabled option. If no enabled option exists, preserve the current active-option state. |
83
+ | `ArrowUp` | When open, move to the previous enabled option and skip disabled options; before the first enabled option, wrap to the last enabled option. If no enabled option exists, preserve the current active-option state. When closed, preserve the editable input's native editing behavior. |
84
+ | `Home` / `End` | When open, move to the first / last enabled option. If no enabled option exists, preserve the current active-option state. When closed, preserve the editable input's native editing behavior. |
85
+ | `Enter` | When closed, open the popup. When open, select the active enabled option; do nothing when no enabled option is active. |
86
+ | `Space` / printable characters | Edit the query. Do not treat printable input as a popup-only command. |
87
+ | `Escape` | When open, close the popup without selecting and keep DOM focus in the input. When closed, a clearable Combobox may clear its selected value. |
88
+ | `Tab` | When a focusable empty-state action is present, forward Tab leaves input/listbox navigation, moves focus to that action, and keeps the popup open. Otherwise, Tab and Shift+Tab close the popup and continue the native focus order. |
89
+
90
+ During input/listbox option navigation while the popup is open, DOM focus remains in the input and `aria-activedescendant` identifies the active option. The focusable empty-state action is the documented exception: forward Tab moves DOM focus into that action while keeping the popup open. Wrapping changes only the directional-key endpoint behavior; the existing selection, announcement, dismissal, and focus model remains unchanged.
91
+
74
92
  Calendar (`src/pages/CalendarPage.tsx`) and Tabs (`src/pages/TabsPage.tsx`) are the Tier A reference implementations to copy.
75
93
 
76
94
  ## Animation
@@ -55,14 +55,16 @@ export declare type AvatarProps = {
55
55
 
56
56
  declare type Axis = 'vertical' | 'horizontal' | 'free';
57
57
 
58
- export declare function Badge({ tone, subtle, inverted, size, icon, count, title, className, children, ...rest }: BadgeProps): JSX.Element;
58
+ export declare function Badge({ tone, subtle, quiet, inverted, size, icon, count, title, className, children, ...rest }: BadgeProps): JSX.Element;
59
59
 
60
60
  export declare type BadgeProps = {
61
61
  /** 语义色调,颜色即语义:灰=中性、蓝=信息、绿=成功、琥珀=警告、红=危险。 */
62
62
  tone?: BadgeTone;
63
63
  /** 密集表面上的低对比形态。 */
64
64
  subtle?: boolean;
65
- /** 反转高对比形态;设置后忽略 tone/subtle。 */
65
+ /** 低强调元数据形态(视觉≈subtle:淡染底、无描边),不带 tone 语义色,用于 SKU/条码/待绑定包裹等纯展示性元数据;设置后忽略 tone/subtle。 */
66
+ quiet?: boolean;
67
+ /** 反转高对比形态;设置后忽略 tone/subtle/quiet。 */
66
68
  inverted?: boolean;
67
69
  /** 尺寸档 20/24/32。 */
68
70
  size?: BadgeSize;
@@ -183,31 +185,67 @@ export declare type BrowserVariant = 'light' | 'dark';
183
185
 
184
186
  export declare function Button(props: ButtonProps): JSX.Element;
185
187
 
186
- declare type ButtonProps = {
188
+ declare type ButtonBaseProps = {
187
189
  variant?: ButtonVariant;
188
190
  size?: ButtonSize;
191
+ svgOnly?: boolean;
192
+ /**
193
+ * @deprecated Deprecated in the unreleased post-0.4.0 migration on 2026-07-20.
194
+ * Use svgOnly. Migration is tracked by #59; removal conditions are in contracts/button-api-compatibility.md.
195
+ */
189
196
  iconOnly?: boolean;
190
197
  /** 圆形——仅用于 icon-only 按钮。与 rounded 互斥(同传时 circle 优先)。 */
191
198
  circle?: boolean;
192
199
  /** 药丸形(full radius)+ 柔和抬起阴影,营销页/落地页文字 CTA 用。与 circle 互斥。 */
193
200
  rounded?: boolean;
201
+ /** 载入态:禁点 + 保留可聚焦 + spinner 的 role=status 播报;不改变控件尺寸。 */
194
202
  loading?: boolean;
203
+ /** loading 时 role=status 播报的文案,默认"加载中…";仅供屏幕阅读器,不影响可见文案。 */
204
+ loadingLabel?: string;
195
205
  disabled?: boolean;
196
- href?: string;
197
206
  type?: 'button' | 'submit' | 'reset';
198
207
  shortcut?: string;
199
208
  className?: string;
200
209
  children?: ReactNode;
201
- onClick?: MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>;
210
+ /** 文字前置图标槽(对齐外部参考 prefix);14–16px、currentColor,装饰性(自带 aria-hidden)。loading 时被 spinner 接管。 */
211
+ prefix?: ReactNode;
212
+ /** 文字后置图标槽(对齐外部参考 suffix),同上。 */
213
+ suffix?: ReactNode;
214
+ 'aria-label'?: string;
215
+ /**
216
+ * @deprecated Deprecated in the unreleased post-0.4.0 migration on 2026-07-20.
217
+ * Use aria-label. Migration is tracked by #59; removal conditions are in contracts/button-api-compatibility.md.
218
+ */
202
219
  ariaLabel?: string;
203
220
  ariaKeyshortcuts?: string;
204
221
  /** 供 Tooltip 等外部浮层关联描述文本、绑定展开/收起时机用;不是 Button 自身的常规用法。 */
205
222
  'aria-describedby'?: string;
206
- onFocus?: FocusEventHandler<HTMLButtonElement | HTMLAnchorElement>;
207
- onBlur?: FocusEventHandler<HTMLButtonElement | HTMLAnchorElement>;
208
- onKeyDown?: KeyboardEventHandler<HTMLButtonElement | HTMLAnchorElement>;
209
- /** 渲染为 <button> 或 <a>(取决于是否传 href),供 focus 管理、测量、浮层定位等场景使用。 */
210
- ref?: Ref<HTMLButtonElement | HTMLAnchorElement>;
223
+ };
224
+
225
+ export declare function ButtonLink(props: ButtonLinkProps): JSX.Element;
226
+
227
+ export declare type ButtonLinkProps = Omit<ButtonBaseProps, 'type'> & {
228
+ /**
229
+ * @deprecated Deprecated in the unreleased post-0.4.0 migration on 2026-07-20.
230
+ * ButtonLink renders an anchor and ignores this compatibility field.
231
+ * Migration is tracked by #59; removal conditions are in contracts/button-api-compatibility.md.
232
+ */
233
+ type?: ButtonBaseProps['type'];
234
+ href: string;
235
+ onClick?: MouseEventHandler<HTMLAnchorElement>;
236
+ onFocus?: FocusEventHandler<HTMLAnchorElement>;
237
+ onBlur?: FocusEventHandler<HTMLAnchorElement>;
238
+ onKeyDown?: KeyboardEventHandler<HTMLAnchorElement>;
239
+ ref?: Ref<HTMLAnchorElement>;
240
+ };
241
+
242
+ declare type ButtonProps = ButtonBaseProps & {
243
+ onClick?: MouseEventHandler<HTMLButtonElement>;
244
+ onFocus?: FocusEventHandler<HTMLButtonElement>;
245
+ onBlur?: FocusEventHandler<HTMLButtonElement>;
246
+ onKeyDown?: KeyboardEventHandler<HTMLButtonElement>;
247
+ /** 供 focus 管理、测量、浮层定位等场景使用。 */
248
+ ref?: Ref<HTMLButtonElement>;
211
249
  };
212
250
 
213
251
  export declare type ButtonSize = 'sm' | 'md' | 'lg';
@@ -262,7 +300,7 @@ declare type CardOwnProps = {
262
300
 
263
301
  export declare type CardProps = CardAsLink | CardAsContainer;
264
302
 
265
- export declare function Checkbox(props: ControlOptionOwnProps & CheckboxOnlyProps & NativeInputProps): JSX.Element;
303
+ export declare function Checkbox({ ...props }: ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & CheckboxOnlyProps & NativeInputProps): JSX.Element;
266
304
 
267
305
  declare type CheckboxOnlyProps = {
268
306
  /** 半选:视觉态而非第三个值,由父级逻辑驱动(如"部分子项已选")。AT 读作 mixed。 */
@@ -365,10 +403,16 @@ export declare function CollapsibleBreadcrumb({ items, label }: {
365
403
  label: string;
366
404
  }): JSX.Element;
367
405
 
368
- export declare function Combobox({ options, placeholder, emptyLabel, size, disabled, error, clearable, inputId, }: ComboboxProps): JSX.Element;
406
+ export declare function Combobox({ options, value, onChange, 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;
369
407
 
370
408
  export declare type ComboboxOption = {
409
+ /** 禁止空字符串(与「未选」的受控空值歧义,adapter 层保证)。 */
371
410
  value: string;
411
+ /** 显示与过滤的唯一来源。 */
412
+ label: string;
413
+ disabled?: boolean;
414
+ /** 组件透而不读,仅随 onChange 第二参原样带回。 */
415
+ meta?: unknown;
372
416
  /** 选项文本前的图标/装饰(如区域旗帜、状态点)。 */
373
417
  prefix?: ReactNode;
374
418
  /** 选项文本后的图标/装饰(如快捷键、外链标记)。 */
@@ -376,16 +420,41 @@ export declare type ComboboxOption = {
376
420
  };
377
421
 
378
422
  declare type ComboboxProps = {
423
+ /** 唯一 shape,后端字段在 adapter 层转换。 */
379
424
  options: ComboboxOption[];
380
- placeholder: string;
381
- /** 空状态模板的名词(「没有匹配『查询』的{emptyLabel}」)。 */
382
- emptyLabel?: string;
425
+ /** 受控选中值;'' = 未选。 */
426
+ value: string;
427
+ /** 选中传 (option.value, option);clearable 清除传 ('', undefined)。 */
428
+ onChange?: (value: string, option?: ComboboxOption) => void;
429
+ /** 同时作 listbox 的 aria-label。 */
430
+ placeholder?: string;
431
+ /** 家族刻度 sm32/md40/lg48(与 Input/Select/Button 同一体系)。 */
383
432
  size?: ComboboxSize;
384
433
  disabled?: boolean;
434
+ /** 值只读:不可输入、不展开列表。 */
435
+ readOnly?: boolean;
436
+ /** 业务错误模型(主):置 aria-invalid 与错误描边,文案由外部渲染(配 errorId)。 */
437
+ invalid?: boolean;
438
+ /** 外部错误文案元素的 id → aria-describedby;组件不渲染任何文案(外部拥有)。 */
439
+ errorId?: string;
440
+ /** 家族便捷(辅):组件自渲染错误文案并隐含 invalid;与 invalid/errorId 可共存。 */
385
441
  error?: string;
442
+ /** 空态条目;默认业务中立平文案,可传 ReactNode(如带链接的引导)。 */
443
+ emptyText?: ReactNode;
444
+ /** 清除按钮 + Esc 二段清除。 */
386
445
  clearable?: boolean;
387
- /** 供外部 <label htmlFor> 关联;组件自身无 label 属性。 */
388
- inputId?: string;
446
+ /** 施于内部 input,供外部 <label htmlFor> 关联。 */
447
+ id?: string;
448
+ className?: string;
449
+ name?: string;
450
+ inputMode?: InputHTMLAttributes<HTMLInputElement>['inputMode'];
451
+ spellCheck?: boolean;
452
+ 'aria-label'?: string;
453
+ 'aria-labelledby'?: string;
454
+ /** 指向内部 role=combobox 的真实 input(非 wrapper),与内部回焦 ref 合并。 */
455
+ ref?: Ref<HTMLInputElement>;
456
+ } & {
457
+ [dataAttr: `data-${string}`]: string | number | boolean | undefined;
389
458
  };
390
459
 
391
460
  export declare type ComboboxSize = 'sm' | 'md' | 'lg';
@@ -452,15 +521,15 @@ export declare type ContextMenuEntry = {
452
521
  type: 'separator';
453
522
  };
454
523
 
455
- export declare function ControlOption({ id, label, description, error, indeterminate, type, ...rest }: ControlOptionOwnProps & CheckboxOnlyProps & {
524
+ export declare function ControlOption({ id, label, description, error, indeterminate, ref, type, ...rest }: ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & CheckboxOnlyProps & {
456
525
  type: 'checkbox' | 'radio';
457
526
  } & NativeInputProps): JSX.Element;
458
527
 
459
528
  declare type ControlOptionOwnProps = {
460
- id: string;
461
- label: string;
462
- description?: string;
463
- error?: string;
529
+ /** 缺省走 useId;显式传入时行为同现状(specimen/测试可寻址)。 */
530
+ id?: string;
531
+ /** 指向原生 input(ref-as-prop,React 19),供表头全选聚焦等场景使用;与内部 indeterminate ref 合并。 */
532
+ ref?: Ref<HTMLInputElement>;
464
533
  };
465
534
 
466
535
  export declare function CopyButton({ value, label, successMessage, size, className, }: {
@@ -516,13 +585,14 @@ export declare function Drawer({ open, onClose, title, children, footer, height,
516
585
  height?: number;
517
586
  }): JSX.Element | null;
518
587
 
519
- export declare function EmptyState({ icon, title, description, actions, live, }: {
520
- icon: ReactNode;
588
+ export declare function EmptyState({ icon, title, description, actions, headingLevel, }: {
589
+ icon?: ReactNode;
521
590
  title: string;
522
- description: string;
591
+ description?: string;
523
592
  /** CTA 区:传家族 <Button> 与次要链接;最多一个主 CTA。 */
524
593
  actions?: ReactNode;
525
- live?: boolean;
594
+ /** 标题层级:缺省渲染 p(不进标题树);作为页面区块时传 2-6 渲染对应 h 标签。 */
595
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
526
596
  }): JSX.Element;
527
597
 
528
598
  export declare function Entity({ leading, title, subtitle, trailing, }: {
@@ -558,6 +628,12 @@ export declare function Feedback({ label, prompt, placeholder, inline, topics, }
558
628
  topics?: string[];
559
629
  }): JSX.Element;
560
630
 
631
+ declare type FieldBareProps = {
632
+ label?: never;
633
+ description?: never;
634
+ error?: never;
635
+ };
636
+
561
637
  export declare function Fieldset({ title, subtitle, tone, footerStatus, footerAction, disabled, children, }: {
562
638
  title?: string;
563
639
  subtitle: string;
@@ -570,6 +646,12 @@ export declare function Fieldset({ title, subtitle, tone, footerStatus, footerAc
570
646
 
571
647
  export declare type FieldsetTone = 'danger' | 'warning';
572
648
 
649
+ declare type FieldShellProps = {
650
+ label: string;
651
+ description?: string;
652
+ error?: string;
653
+ };
654
+
573
655
  export declare type FieldSize = 'xs' | 'sm' | 'md' | 'lg';
574
656
 
575
657
  export declare function FileTree({ nodes, label, defaultExpanded, }: {
@@ -607,11 +689,51 @@ declare type GaugeSize = 'xs' | 'sm' | 'md' | 'lg';
607
689
 
608
690
  declare type GaugeToneDirection = 'quota' | 'health';
609
691
 
610
- export declare function Input({ id, label, description, error, prefix, suffix, prefixStyling, suffixStyling, rounded, wide, size, className, ...rest }: {
611
- id: string;
612
- label: string;
613
- description?: string;
614
- error?: string;
692
+ export declare function IconButton({ 'aria-label': canonicalAriaLabel, ariaLabel: legacyAriaLabel, ...rest }: IconButtonProps): JSX.Element;
693
+
694
+ declare type IconButtonAccessibleName = {
695
+ 'aria-label': string;
696
+ /**
697
+ * @deprecated Deprecated in the unreleased post-0.4.0 migration on 2026-07-20.
698
+ * Use aria-label. Migration is tracked by #59; removal conditions are in contracts/button-api-compatibility.md.
699
+ */
700
+ ariaLabel?: string;
701
+ } | {
702
+ 'aria-label'?: never;
703
+ /**
704
+ * @deprecated Deprecated in the unreleased post-0.4.0 migration on 2026-07-20.
705
+ * Use aria-label. Migration is tracked by #59; removal conditions are in contracts/button-api-compatibility.md.
706
+ */
707
+ ariaLabel: string;
708
+ };
709
+
710
+ export declare type IconButtonProps = IconButtonAccessibleName & {
711
+ variant?: ButtonVariant;
712
+ size?: ButtonSize;
713
+ circle?: boolean;
714
+ loading?: boolean;
715
+ loadingLabel?: string;
716
+ disabled?: boolean;
717
+ type?: 'button' | 'submit' | 'reset';
718
+ className?: string;
719
+ children?: ReactNode;
720
+ onClick?: MouseEventHandler<HTMLButtonElement>;
721
+ 'aria-describedby'?: string;
722
+ onFocus?: FocusEventHandler<HTMLButtonElement>;
723
+ onBlur?: FocusEventHandler<HTMLButtonElement>;
724
+ onKeyDown?: KeyboardEventHandler<HTMLButtonElement>;
725
+ ref?: Ref<HTMLButtonElement>;
726
+ };
727
+
728
+ export declare function Input({ id, label, description, error, invalid, errorId, ref, prefix, suffix, prefixStyling, suffixStyling, rounded, wide, size, className, ...rest }: {
729
+ /** 可选:未传时内部 useId 兜底(对齐 Combobox);显式传入时行为同 0.3.0。 */
730
+ id?: string;
731
+ /** 置 aria-invalid 的业务校验态,不渲染文案;与壳模式 error 并轨(invalid || Boolean(error))。 */
732
+ invalid?: boolean;
733
+ /** 业务侧错误文案元素的 id,拼入 aria-describedby;文案由业务层渲染。 */
734
+ errorId?: string;
735
+ /** 指向原生 input(ref-as-prop),供校验失败聚焦、测量等场景。 */
736
+ ref?: Ref<HTMLInputElement>;
615
737
  prefix?: ReactNode;
616
738
  suffix?: ReactNode;
617
739
  /** 置 false 去掉前缀的灰块底色与分隔线,变成纯文本/裸图标。 */
@@ -622,7 +744,7 @@ export declare function Input({ id, label, description, error, prefix, suffix, p
622
744
  rounded?: boolean;
623
745
  wide?: boolean;
624
746
  size?: FieldSize;
625
- } & Omit<InputHTMLAttributes<HTMLInputElement>, 'id' | 'size' | 'prefix'>): JSX.Element;
747
+ } & (FieldShellProps | FieldBareProps) & Omit<InputHTMLAttributes<HTMLInputElement>, 'id' | 'size' | 'prefix'>): JSX.Element;
626
748
 
627
749
  export declare function Kbd({ meta, shift, alt, ctrl, small, children, }: {
628
750
  meta?: boolean;
@@ -784,7 +906,7 @@ export declare function ProjectBanner({ variant, message, cta }: {
784
906
  cta: string;
785
907
  }): JSX.Element;
786
908
 
787
- export declare function Radio(props: ControlOptionOwnProps & NativeInputProps): JSX.Element;
909
+ export declare function Radio({ ...props }: ControlOptionOwnProps & (FieldShellProps | FieldBareProps) & NativeInputProps): JSX.Element;
788
910
 
789
911
  export declare function RangeField({ layout, trigger, size, minValue, maxValue, pinnedTimezone, today, value, onChange, }: {
790
912
  layout?: PopoverLayout;
@@ -999,14 +1121,18 @@ export declare function Tabs({ tabs, ariaLabel, variant, }: {
999
1121
  variant?: 'default' | 'secondary';
1000
1122
  }): JSX.Element | null;
1001
1123
 
1002
- export declare function Textarea({ id, label, description, error, wide, size, className, ...rest }: {
1003
- id: string;
1004
- label: string;
1005
- description?: string;
1006
- error?: string;
1124
+ export declare function Textarea({ id, label, description, error, invalid, errorId, ref, wide, size, className, maxLength, onInput, ...rest }: {
1125
+ /** 缺省走 useId;显式传入时行为同现状(specimen/测试可寻址)。 */
1126
+ id?: string;
1127
+ /** 业务校验模型(主):置 aria-invalid;文案由业务层经 errorId 关联,组件不渲染。 */
1128
+ invalid?: boolean;
1129
+ /** 外部错误文案元素的 id,拼入 aria-describedby;文案由业务层渲染。 */
1130
+ errorId?: string;
1131
+ /** 指向原生 textarea(ref-as-prop,React 19),供校验失败聚焦、测量等场景使用。 */
1132
+ ref?: Ref<HTMLTextAreaElement>;
1007
1133
  wide?: boolean;
1008
1134
  size?: FieldSize;
1009
- } & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'id'>): JSX.Element;
1135
+ } & (FieldShellProps | FieldBareProps) & Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'id'>): JSX.Element;
1010
1136
 
1011
1137
  export declare function TextWithCopyButton({ text, copyValue, successMessage, size, }: {
1012
1138
  text: string;
@@ -1043,8 +1169,13 @@ export declare function ToastProvider({ children }: {
1043
1169
 
1044
1170
  export declare type ToastTone = 'default' | 'success' | 'warning' | 'error';
1045
1171
 
1046
- export declare function Toggle({ defaultOn, disabled, size, onColor, label, description, labelSide, ariaLabel, }: {
1172
+ export declare function Toggle({ checked, defaultOn, onChange, disabled, size, onColor, label, description, labelSide, ariaLabel, }: {
1173
+ /** 受控值;提供后内部状态失效,显示值以外部回写的 checked 为准。 */
1174
+ checked?: boolean;
1175
+ /** 非受控模式的初始值;与 checked 同传时以 checked 为准。 */
1047
1176
  defaultOn?: boolean;
1177
+ /** 切换回调,参数为目标布尔值;受控与非受控模式都会触发。 */
1178
+ onChange?: (checked: boolean) => void;
1048
1179
  disabled?: boolean;
1049
1180
  size?: ToggleSize;
1050
1181
  onColor?: string;