@q1travel/design-system 0.31.2 → 0.31.4
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/button-api-compatibility.md +1 -1
- package/contracts/design.dark.md +7 -1
- package/contracts/design.md +10 -2
- package/contracts/implementation-map.md +10 -2
- package/contracts/interface-guidelines.md +14 -2
- package/dist-pkg/design-system.d.ts +17 -6
- package/dist-pkg/design-system.js +7886 -7708
- package/dist-pkg/style.css +344 -37
- package/package.json +6 -4
|
@@ -46,7 +46,7 @@ Under `prefers-reduced-motion: reduce`, no attention animation runs and the stat
|
|
|
46
46
|
|
|
47
47
|
| State | Button | ButtonLink | Label and status |
|
|
48
48
|
| --- | --- | --- | --- |
|
|
49
|
-
| `loading` | Keeps the existing `aria-disabled="true"` and `tabIndex=0` focusable contract; does not add `aria-busy` or native `disabled` unless `disabled` was also passed. | Removes `href`, keeps `aria-disabled="true"` and `tabIndex=0` so the loading state remains discoverable. |
|
|
49
|
+
| `loading` | Keeps the existing `aria-disabled="true"` and `tabIndex=0` focusable contract; does not add `aria-busy` or native `disabled` unless `disabled` was also passed. | Removes `href`, keeps `aria-disabled="true"` and `tabIndex=0` so the loading state remains discoverable. | Preserves the original children and affix layout as visually hidden size placeholders, retains the accessible name, and centers an overlaid spinner announcing `loadingLabel` through the existing internal `role="status"`. Width, height, and position remain unchanged. |
|
|
50
50
|
| `busy` | Uses native `disabled`, sets `aria-busy="true"`, and blocks clicks. | Removes `href`, sets `aria-disabled="true"`, `aria-busy="true"`, `tabIndex=-1`, and blocks clicks. | Shows one spinner. A non-empty `busyLabel` replaces the visible label without replacing the stable accessible name; a separate polite `role="status"` announces `busyLabel`, then `loadingLabel`, then `正在处理…`. |
|
|
51
51
|
| `busy + loading` | Busy wins; one spinner and the busy interaction contract apply. | Busy wins; the link is not navigable or focusable. | `busyLabel` wins for visible/status text; `loadingLabel` is only a fallback when busyLabel is empty. |
|
|
52
52
|
|
package/contracts/design.dark.md
CHANGED
|
@@ -413,6 +413,11 @@ components:
|
|
|
413
413
|
---
|
|
414
414
|
# q1travel Design System
|
|
415
415
|
|
|
416
|
+
只读值用 text-primary、禁用值用 text-muted、只读占位用 text-secondary(亮暗均 ≥4.5:1),disabled 优先。禁用文字在真实背景对比至少 3:1 且弱于可用态;禁用图标保留既有颜色。
|
|
417
|
+
|
|
418
|
+
Combobox、MultiSelect、Cascader 可用态仅在精细指针悬停时加深描边(亮 gray-800、暗 gray-700),底色不变;聚焦、只读、禁用与 invalid 不响应悬停。状态修复覆盖全部尺寸,尺寸与字级统一仅限 md。
|
|
419
|
+
Combobox 的 `showSelectedPrefix?: boolean` 默认 false;开启时只在选中项有 prefix 且未输入编辑时显示装饰前置内容(aria-hidden),无 prefix 不留空位。选中前置槽 sm/md/lg 分别使用 space-5/space-6/space-7(20/24/28),编辑态搜索槽等宽,图标保持原尺寸并居中。Combobox 选中值、MultiSelect 触发摘要与 Cascader 单选值单行省略,实际截断时悬停显示全文;输入聚焦编辑使用原生滚动,Cascader 多选标签不变。
|
|
420
|
+
|
|
416
421
|
## Overview
|
|
417
422
|
|
|
418
423
|
This is the dark-theme source contract. It keeps token names aligned with the light contract and changes values only where the theme requires a different surface, text, border, or status value.
|
|
@@ -504,6 +509,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
504
509
|
|
|
505
510
|
|
|
506
511
|
- Toggle represents one on/off setting and reports a requested change immediately. Square Checkbox stages a form change that a submit applies; circle Checkbox marks task completion through the consumer’s change handler. Choosing the wrong one misstates when the change takes effect.
|
|
512
|
+
- Toggle 默认关闭轨道在亮暗主题均用 `gray-700` 填充、`gray-800` 悬停填充;暗色无边框,圆点保持 `action-on-primary`。三尺寸在面板与页面底上的非禁用轨道边界/父底及圆点边界/轨道均至少 3:1;悬停轨道/父底不弱于常态且颜色不同。开启保留主操作色,禁用保留整体 0.5 透明度;按压轨道保持对应悬停色,圆点增加既有 `--q1-shadow-popover` 阴影;轨道与圆点均无额外缩放或位移,释放恢复。
|
|
507
513
|
- Switch selects among mutually exclusive views or modes; its outer sm / md / lg heights are 32 / 40 / 48px on fine pointers, defaulting to md. Optional `variant="cards"` provides equal-width wrapping cards with a left-aligned label-12 label above caller-owned `meta` (number or ReactNode) in regular label-14 tabular figures, space-2 padding/gap and content-driven equal sm/md/lg height; cards alone use a two-pixel border-active selected border and inset line without a tint, a matching border-active label and the complete shared keyboard focus ring; disabled selected cards keep a neutral two-pixel border-strong outline with muted text, so the selection stays distinguishable. An auto-fit grid keeps any item count and its last row equally wide, with a readable 10rem minimum; the four-item example uses 全部 / 需要处理 / 更新 / 已归档 with numeric counts 128 / 12 / 8 / 32; the default `segmented` markup, dimensions and keyboard/radio semantics remain unchanged.
|
|
508
514
|
- ToggleGroup uses pressed buttons for single or multiple choices; arrow keys move focus without selecting. Icon-only items require an accessible name. See `implementation-map.md` for selection ownership.
|
|
509
515
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
@@ -541,7 +547,7 @@ CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); succ
|
|
|
541
547
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
542
548
|
|
|
543
549
|
- Banner accepts a string `title` and paragraph-inline `children` (text and inline Link, no block elements). `action: BannerAction` is either `{ label, href }` or `{ label, onClick, loading?, disabled? }`, mutually exclusive. The exported `BannerAction` derives button events and states from ButtonProps. Buttons use `type="button"`, size sm, rounded, primary for announcements and secondary for tone strips, with layout class `banner__button-action`. Loading retains focus with aria-disabled and blocks activation; disabled takes priority and leaves the Tab order. Loading adds no aria-busy; Banner adds no role or live region. Progress ignores either action. Existing links and calls omitting new capabilities retain their complete markup; no existing consumer migration is required. Remove button calls and BannerAction imports before reverting this addition. Banner announces at page scope. Note is an inline aside. ErrorMessage sits against one field. ErrorCard replaces a region that failed to load. Picking the widest one available overstates the problem.
|
|
544
|
-
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data: default `layout="center"` keeps the full empty view; `layout="inline"` is a small illustration or icon and regular
|
|
550
|
+
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data: default `layout="center"` keeps the full empty view; `layout="inline"` is a small illustration or icon and regular primary-text title row inside a section whose heading and actions belong to the host, without description, actions, or headingLevel. Inline accepts `illustration?: ReactNode`, using `--q1-space-10` (40) for its width and preserving the artwork aspect ratio; illustration takes priority over icon with a development warning when both are supplied. Without illustration, icon-only and title-only calls use the same zero horizontal padding and primary title color. Center limits the decorative illustration slot to `--q1-space-16` (64), with no extra bottom margin: the existing `--q1-space-2` container gap gives 8 between illustration and title. Standalone artwork remains 96 wide; inline built-in scenes use simplified geometry with at most two narrative details, clear strokes, left alignment and vertical centering with the title. Standard inline usage pairs the 40-wide illustration with a visible title on its right in the same row (for example, 「还没有登录记录」). Use titleHidden only in the few contexts where the host already supplies a visible title. All inline variants use zero horizontal padding; illustration, icon and title-only content align with the host section body content edge. Hosts without padding supply their own inset. Inline titles use text-primary at the unchanged copy-14 size. Center accepts a decorative `illustration?: ReactNode` in place of icon; when neither illustration nor icon is supplied, it renders `EmptyListIllustration variant="neutral"` inside the decorative `empty-state__illustration` slot. Explicit icons retain their slot with a transparent background in both themes. Both layouts accept `titleHidden?: boolean`: true adds `empty-state--title-hidden` to the existing root and `q1-sr-only` to the required title, preserving its p or h2–h6 tag, text and accessibility semantics. Center keeps illustration/icon, optional description and actions; inline keeps its small illustration or icon. The hidden title takes no layout space; remaining siblings keep the existing gap and margins, and an illustration/icon alone has no trailing margin. Omitted or false preserves the existing DOM, classes and styles exactly. Built-in `EmptyListIllustration` accepts `scene?: 'list' | 'search' | 'complete' | 'chart' | 'inventory' | 'orders' | 'product' | 'permission' | 'purchase' | 'supplier' | 'team' | 'notification' | 'store' | 'task' | 'session' | 'activity' | 'payment' | 'security' | 'integration'` and the compatible `variant?: 'complete' | 'neutral'`. Explicit scene takes priority; without scene, neutral maps to list and all other calls default to complete. Accepted #830 intentionally changes both standalone default complete and EmptyState default list artwork and internal SVG structure; the outer decorative span, class and 96×104 viewBox remain. Complete draws its own decorative check without depending on CheckCircleFill. The chart, inventory, orders, list, activity, task, product, purchase, supplier, store, team, search, complete, permission, notification, session, integration, payment and security scenes use white or background-100 surface bodies, gray-700 fine outlines, and low-saturation local color fills. Every internal detail contrasts at least 3:1 with each fill it crosses, without reduced opacity. Each illustration uses at most one brand-colored element, at a narrative position. Same-shape, unblurred offset shadows provide depth; rectangle corner radii use authored SVG geometry. Geometry is identical across themes. Both variants have no icon backplate. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
545
551
|
- StatusPage defaults to a whole unavailable page with a caller-owned title, decorative SkeletonIllustration, plain-text description rendered as a paragraph, and actions. Its size="region" variant centers an icon or small illustration, title, description, and up to two actions inside a substantial content region, with a transparent background and content-driven height. ErrorCard covers a blocking compact failure; RegionErrorState is a compact retryable region message with its own status announcement; EmptyState means loading succeeded without content. StatusPage headingLevel accepts 1–6 (default 1); choose the next heading level under the host section. Existing omitted, 1 and 2 calls need no migration. StatusPage creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
546
552
|
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
547
553
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
package/contracts/design.md
CHANGED
|
@@ -377,7 +377,9 @@ components:
|
|
|
377
377
|
backgroundColor: "{semantic-colors.surface-panel}"
|
|
378
378
|
hoverBackgroundColor: "{semantic-colors.surface-hover}"
|
|
379
379
|
textColor: "{semantic-colors.text-primary}"
|
|
380
|
-
borderColor: "{semantic-colors.border}"
|
|
380
|
+
borderColor: "{semantic-colors.border-strong}"
|
|
381
|
+
hoverBorderColor: "{colors.gray-600}"
|
|
382
|
+
pressBorderColor: "{colors.gray-600}"
|
|
381
383
|
typography: "{typography.label-14}"
|
|
382
384
|
rounded: "{rounded.sm}"
|
|
383
385
|
height: 40px
|
|
@@ -413,6 +415,11 @@ components:
|
|
|
413
415
|
---
|
|
414
416
|
# q1travel Design System
|
|
415
417
|
|
|
418
|
+
只读值用 text-primary、禁用值用 text-muted、只读占位用 text-secondary(亮暗均 ≥4.5:1),disabled 优先。禁用文字在真实背景对比至少 3:1 且弱于可用态;禁用图标保留既有颜色。
|
|
419
|
+
|
|
420
|
+
Combobox、MultiSelect、Cascader 可用态仅在精细指针悬停时加深描边(亮 gray-800、暗 gray-700),底色不变;聚焦、只读、禁用与 invalid 不响应悬停。状态修复覆盖全部尺寸,尺寸与字级统一仅限 md。
|
|
421
|
+
Combobox 的 `showSelectedPrefix?: boolean` 默认 false;开启时只在选中项有 prefix 且未输入编辑时显示装饰前置内容(aria-hidden),无 prefix 不留空位。选中前置槽 sm/md/lg 分别使用 space-5/space-6/space-7(20/24/28),编辑态搜索槽等宽,图标保持原尺寸并居中。Combobox 选中值、MultiSelect 触发摘要与 Cascader 单选值单行省略,实际截断时悬停显示全文;输入聚焦编辑使用原生滚动,Cascader 多选标签不变。
|
|
422
|
+
|
|
416
423
|
## Overview
|
|
417
424
|
|
|
418
425
|
This is the light-theme source contract. It defines token names, semantic roles, and component mappings before documentation examples are rendered.
|
|
@@ -504,6 +511,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
504
511
|
|
|
505
512
|
|
|
506
513
|
- Toggle represents one on/off setting and reports a requested change immediately. Square Checkbox stages a form change that a submit applies; circle Checkbox marks task completion through the consumer’s change handler. Choosing the wrong one misstates when the change takes effect.
|
|
514
|
+
- Toggle 默认关闭轨道在亮暗主题均用 `gray-700` 填充、`gray-800` 悬停填充;暗色无边框,圆点保持 `action-on-primary`。三尺寸在面板与页面底上的非禁用轨道边界/父底及圆点边界/轨道均至少 3:1;悬停轨道/父底不弱于常态且颜色不同。开启保留主操作色,禁用保留整体 0.5 透明度;按压轨道保持对应悬停色,圆点增加既有 `--q1-shadow-popover` 阴影;轨道与圆点均无额外缩放或位移,释放恢复。
|
|
507
515
|
- Switch selects among mutually exclusive views or modes; its outer sm / md / lg heights are 32 / 40 / 48px on fine pointers, defaulting to md. Optional `variant="cards"` provides equal-width wrapping cards with a left-aligned label-12 label above caller-owned `meta` (number or ReactNode) in regular label-14 tabular figures, space-2 padding/gap and content-driven equal sm/md/lg height; cards alone use a two-pixel border-active selected border and inset line without a tint, a matching border-active label and the complete shared keyboard focus ring; disabled selected cards keep a neutral two-pixel border-strong outline with muted text, so the selection stays distinguishable. An auto-fit grid keeps any item count and its last row equally wide, with a readable 10rem minimum; the four-item example uses 全部 / 需要处理 / 更新 / 已归档 with numeric counts 128 / 12 / 8 / 32; the default `segmented` markup, dimensions and keyboard/radio semantics remain unchanged.
|
|
508
516
|
- ToggleGroup uses pressed buttons for single or multiple choices; arrow keys move focus without selecting. Icon-only items require an accessible name. See `implementation-map.md` for selection ownership.
|
|
509
517
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
@@ -541,7 +549,7 @@ CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); succ
|
|
|
541
549
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
542
550
|
|
|
543
551
|
- Banner accepts a string `title` and paragraph-inline `children` (text and inline Link, no block elements). `action: BannerAction` is either `{ label, href }` or `{ label, onClick, loading?, disabled? }`, mutually exclusive. The exported `BannerAction` derives button events and states from ButtonProps. Buttons use `type="button"`, size sm, rounded, primary for announcements and secondary for tone strips, with layout class `banner__button-action`. Loading retains focus with aria-disabled and blocks activation; disabled takes priority and leaves the Tab order. Loading adds no aria-busy; Banner adds no role or live region. Progress ignores either action. Existing links and calls omitting new capabilities retain their complete markup; no existing consumer migration is required. Remove button calls and BannerAction imports before reverting this addition. Banner announces at page scope. Note is an inline aside. ErrorMessage sits against one field. ErrorCard replaces a region that failed to load. Picking the widest one available overstates the problem.
|
|
544
|
-
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data: default `layout="center"` keeps the full empty view; `layout="inline"` is a small illustration or icon and regular
|
|
552
|
+
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data: default `layout="center"` keeps the full empty view; `layout="inline"` is a small illustration or icon and regular primary-text title row inside a section whose heading and actions belong to the host, without description, actions, or headingLevel. Inline accepts `illustration?: ReactNode`, using `--q1-space-10` (40) for its width and preserving the artwork aspect ratio; illustration takes priority over icon with a development warning when both are supplied. Without illustration, icon-only and title-only calls use the same zero horizontal padding and primary title color. Center limits the decorative illustration slot to `--q1-space-16` (64), with no extra bottom margin: the existing `--q1-space-2` container gap gives 8 between illustration and title. Standalone artwork remains 96 wide; inline built-in scenes use simplified geometry with at most two narrative details, clear strokes, left alignment and vertical centering with the title. Standard inline usage pairs the 40-wide illustration with a visible title on its right in the same row (for example, 「还没有登录记录」). Use titleHidden only in the few contexts where the host already supplies a visible title. All inline variants use zero horizontal padding; illustration, icon and title-only content align with the host section body content edge. Hosts without padding supply their own inset. Inline titles use text-primary at the unchanged copy-14 size. Center accepts a decorative `illustration?: ReactNode` in place of icon; when neither illustration nor icon is supplied, it renders `EmptyListIllustration variant="neutral"` inside the decorative `empty-state__illustration` slot. Explicit icons retain their slot with a transparent background in both themes. Both layouts accept `titleHidden?: boolean`: true adds `empty-state--title-hidden` to the existing root and `q1-sr-only` to the required title, preserving its p or h2–h6 tag, text and accessibility semantics. Center keeps illustration/icon, optional description and actions; inline keeps its small illustration or icon. The hidden title takes no layout space; remaining siblings keep the existing gap and margins, and an illustration/icon alone has no trailing margin. Omitted or false preserves the existing DOM, classes and styles exactly. Built-in `EmptyListIllustration` accepts `scene?: 'list' | 'search' | 'complete' | 'chart' | 'inventory' | 'orders' | 'product' | 'permission' | 'purchase' | 'supplier' | 'team' | 'notification' | 'store' | 'task' | 'session' | 'activity' | 'payment' | 'security' | 'integration'` and the compatible `variant?: 'complete' | 'neutral'`. Explicit scene takes priority; without scene, neutral maps to list and all other calls default to complete. Accepted #830 intentionally changes both standalone default complete and EmptyState default list artwork and internal SVG structure; the outer decorative span, class and 96×104 viewBox remain. Complete draws its own decorative check without depending on CheckCircleFill. The chart, inventory, orders, list, activity, task, product, purchase, supplier, store, team, search, complete, permission, notification, session, integration, payment and security scenes use white or background-100 surface bodies, gray-700 fine outlines, and low-saturation local color fills. Every internal detail contrasts at least 3:1 with each fill it crosses, without reduced opacity. Each illustration uses at most one brand-colored element, at a narrative position. Same-shape, unblurred offset shadows provide depth; rectangle corner radii use authored SVG geometry. Geometry is identical across themes. Both variants have no icon backplate. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
545
553
|
- StatusPage defaults to a whole unavailable page with a caller-owned title, decorative SkeletonIllustration, plain-text description rendered as a paragraph, and actions. Its size="region" variant centers an icon or small illustration, title, description, and up to two actions inside a substantial content region, with a transparent background and content-driven height. ErrorCard covers a blocking compact failure; RegionErrorState is a compact retryable region message with its own status announcement; EmptyState means loading succeeded without content. StatusPage headingLevel accepts 1–6 (default 1); choose the next heading level under the host section. Existing omitted, 1 and 2 calls need no migration. StatusPage creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
546
554
|
- RegionErrorState replaces only the failed region in place (table body under a kept header, card body, page section) while the rest of the page stays usable: neutral surface, danger color on the icon only, polite `role=status` by default, built-in retry that keeps focus while `retrying`. ErrorCard is for a failure that blocks the current task (danger card, assertive `role=alert`). EmptyState is a successful load with no data. `layout="strip"` adds a full-width neutral strip for list failures and load-more rows, with retry always inside its right edge. See `implementation-map.md` for the retry and announcement boundary.
|
|
547
555
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
|
@@ -150,7 +150,7 @@ Accepted 2026-10-08: explicitly sized single-line Button, ButtonLink, Input, Sea
|
|
|
150
150
|
|
|
151
151
|
Owner approved 2026-10-15 (#455): public `RegionErrorState` replaces only a region whose data request failed, while the host keeps its frame (table header, card title, section heading). Props: `title` (required), `description`, `layout` `center` (default; table bodies and large regions, block padding `--q1-space-16` keeps roughly five table rows of height) or `inline` (cards and page sections; the retry button wraps under the description when the component's own container is narrower than 30rem), `headingLevel` 2–6 (default renders `p`, matching EmptyState), `onRetry`, `retrying`, and `live` `polite` (default, `role=status`) or `assertive` (`role=alert`). Accepted 2026-10-25 (#659): `strip` adds `.region-error-state--strip`, a full-width neutral strip with `--q1-color-bg-hover`, `--q1-radius-2`, block padding `--q1-space-2`, inline padding `--q1-space-4`, and minimum block size twice `--q1-space-6`. It shares inline typography and announcements, but its retry always stays inside the right edge without the narrow-container wrap. There is no `size` prop.
|
|
152
152
|
|
|
153
|
-
The announcement region wraps only the icon, the `q1-sr-only` 「错误:」 prefix, the title and the description, and carries `aria-busy` while `retrying`. The retry Button sits outside that region so its loading status never nests inside another live region. Retry uses Button `loading` (focusable, `aria-disabled`, polite 「正在重试」 status) rather than `busy` (native disabled drops focus); the component stays mounted while retrying, and the
|
|
153
|
+
The announcement region wraps only the icon, the `q1-sr-only` 「错误:」 prefix, the title and the description, and carries `aria-busy` while `retrying`. The retry Button sits outside that region so its loading status never nests inside another live region. Retry uses Button `loading` (focusable, `aria-disabled`, polite 「正在重试」 status) rather than `busy` (native disabled drops focus); the component stays mounted while retrying, and the children remain 「重试」 to preserve the exact idle width and height. While retrying, the existing `aria-label` names the button 「正在重试」; idle omits that attribute. The shared Button loading rule visually hides that content and shows only its centered spinner; the accessible name remains 「正在重试」, and one loading status announces it. Icons are rendered internally from the existing icon set (AlertCircle, RotateCcw); no icon is exported. Copy is caller-owned; field errors, 403/404 and backend messages are out of scope. After a successful retry the caller moves focus to the restored content. Existing tokens only; the danger color appears only on the icon.
|
|
154
154
|
|
|
155
155
|
## Cascader 层级选择
|
|
156
156
|
|
|
@@ -162,4 +162,12 @@ Accepted 2026-11-02 (#761): ThemeSwitcher keeps a single outer group border. Opt
|
|
|
162
162
|
|
|
163
163
|
## EmptyState title visibility
|
|
164
164
|
|
|
165
|
-
Accepted #568: `EmptyState` supports `titleHidden?: boolean` in both center and inline layouts. Both layouts accept `titleHidden?: boolean`: true adds `empty-state--title-hidden` to the existing root and `q1-sr-only` to the required title, preserving its p or h2–h6 tag, text and accessibility semantics. Center keeps illustration/icon, optional description and actions; inline keeps its small illustration or icon. Inline accepts `illustration?: ReactNode` with width `--q1-space-
|
|
165
|
+
Accepted #568: `EmptyState` supports `titleHidden?: boolean` in both center and inline layouts. Both layouts accept `titleHidden?: boolean`: true adds `empty-state--title-hidden` to the existing root and `q1-sr-only` to the required title, preserving its p or h2–h6 tag, text and accessibility semantics. Center keeps illustration/icon, optional description and actions; inline keeps its small illustration or icon. Inline accepts `illustration?: ReactNode` with width `--q1-space-10` (40), with simplified geometry, left alignment and vertical centering with the title, preserving the artwork aspect ratio; illustration takes priority over icon and supplying both emits a development warning. Inline does not accept description, actions or headingLevel. Inline icon-only and title-only calls share zero horizontal padding and primary title color. The hidden title takes no layout space; remaining siblings keep the existing gap and margins, and an illustration/icon alone has no trailing margin. Omitted or false preserves the existing DOM, classes and styles exactly.
|
|
166
|
+
|
|
167
|
+
Standard inline usage pairs the 40-wide illustration with a visible title on its right in the same row (for example, 「还没有登录记录」). Use titleHidden only in the few contexts where the host already supplies a visible title. All inline variants use zero horizontal padding; illustration, icon and title-only content align with the host section body content edge. Hosts without padding supply their own inset. Inline titles use text-primary at the unchanged copy-14 size.
|
|
168
|
+
|
|
169
|
+
### EmptyState panel
|
|
170
|
+
|
|
171
|
+
Accepted #860: `layout="panel"` is a full-width compact surface for empty tab or filter results. It requires a visible `title: string`; optional `description: string`, `actions: ReactNode` and `headingLevel: 2 | 3 | 4 | 5 | 6` (default `p`) retain text and native action semantics. `icon`, `illustration` and `titleHidden` are forbidden only in this branch. Center and inline types and byte-for-byte markup remain unchanged.
|
|
172
|
+
|
|
173
|
+
DOM: `div.empty-state.empty-state--panel > div.empty-state__body` contains the title and optional `p.empty-state__desc`, followed by optional sibling `div.empty-state__actions`. Only `empty-state--panel` and `empty-state__body` are new classes. The static root has no live-region semantics. Existing subtle background, radius-3, space-4/6, copy-14, semibold primary title and regular secondary description tokens provide hierarchy in both themes, with text contrast at least 4.5:1. A one-pixel border uses the existing border-subtle token and space-1 divided by four; the edge remains distinguishable from the page in both themes without darkening the fill. Actions align vertically at the right on wide screens and below the text at the left on narrow screens; no horizontal overflow.
|
|
@@ -103,6 +103,8 @@ Adding a clause therefore means answering two questions before the wording: who
|
|
|
103
103
|
- [both] MUST pair normal text over an image with fully opaque `--q1-color-text-on-media` and `--q1-color-media-scrim` covering the entire text background, or an independently verified opaque text backing. Do not assume `overlay-scrim` provides text contrast. Verify at least 4.5:1 after actual composition; translucent text, parent opacity, filters, blending and gradients require their own measurement.
|
|
104
104
|
- [both] MUST caption spoken audio and provide a transcript for any media that carries information not available in surrounding text.
|
|
105
105
|
|
|
106
|
+
- [both] CopyButton, TextWithCopyButton, and Snippet success keep the existing check and announcement channel (CopyButton/Snippet inline status; TextWithCopyButton success Toast) and displays an aria-hidden Check +「已复制」bubble for 2 seconds using the shared anchored overlay and 浮层面(与 Popover/Menu 相同). Changing the value clears both; failures keep the existing error feedback. The bubble 优先在按钮下方,空间不足时换到上方;it escapes overflow clipping without changing button layout; it does not enter the overlay stack or intercept the enclosing surface’s Escape or outside-pointer dismissal.
|
|
107
|
+
|
|
106
108
|
## Accessibility Tiers
|
|
107
109
|
|
|
108
110
|
Every component is classified into one accessibility tier; the tier sets its bar. The tier is decided by how much keyboard and focus behavior the component must own beyond native semantics. The keyboard model source is always the matching WAI-ARIA APG pattern, not invention — established design systems follow APG too, so "matching a reference" and "following APG" do not conflict.
|
|
@@ -243,8 +245,18 @@ Component pages follow one skeleton so a reader who has used one page can find t
|
|
|
243
245
|
|
|
244
246
|
### Empty illustrations
|
|
245
247
|
|
|
246
|
-
Accepted #830: decorative empty illustrations share a 96×104 viewBox
|
|
248
|
+
Accepted #830: decorative empty illustrations share a 96×104 viewBox and ICON_STROKE_WIDTH round strokes. All scene geometry, including half the computed stroke width around each element, stays inside the viewBox. The family specimen uses one site-themed card per scene, four columns on wide screens and two on narrow screens. At most one element uses primary-700 in light and primary-900 in dark, with identical geometry. Scene selects list, search, complete, chart, inventory, orders, product, permission, purchase, supplier, team, notification, store, task, session, activity, payment, security or integration. Scene takes precedence over the compatible variant; absent scene, neutral maps to list and all other calls to complete. Keep the decorative outer span, aria-hidden=true, non-focusable SVG and text-owned state meaning. Complete owns its check; CheckCircleFill semantics are unchanged.
|
|
249
|
+
|
|
250
|
+
Accepted #839: chart, inventory, orders, list, activity, task, product, purchase, supplier, store, team, search, complete, permission, notification, session, integration, payment and security use background-100 surfaces, gray-700 outlines, local teal/amber/green/blue-300 fills and matching hue-900 detail strokes in light; dark uses gray-700 detail strokes, with inventory blue-300 to avoid a brown cast; session and security use blue-300, payment uses green-300. Adjacent documentation gallery cells use distinct hues; reorder the gallery without changing individual scene assets. Every internal detail contrasts at least 3:1 with each fill it crosses, without reduced opacity. Local color covers 20–35% of the main geometric bounding rectangle; no opaque gray body or purple backplate. At most one primary element appears, at a functional narrative position. Same-shape, unblurred shadows offset by two viewBox units use gray-alpha-100 in light and gray-alpha-200 in dark. Internal rectangle radii remain authored SVG geometry. No gradients, blur or particles.
|
|
247
251
|
|
|
248
252
|
### EmptyState illustration-only presentation
|
|
249
253
|
|
|
250
|
-
Accepted #568: `titleHidden` visually hides the required title using `q1-sr-only`, while retaining its original paragraph or heading semantics and accessible text. Illustration/icon remains decorative; description and actions are independent optional center content. Inline accepts a small decorative `illustration?: ReactNode` at `--q1-space-
|
|
254
|
+
Accepted #568: `titleHidden` visually hides the required title using `q1-sr-only`, while retaining its original paragraph or heading semantics and accessible text. Illustration/icon remains decorative; description and actions are independent optional center content. Inline accepts a small decorative `illustration?: ReactNode` at `--q1-space-10` (40) width, preserving its aspect ratio; illustration takes priority over icon, with a development warning when both are supplied. Inline description, actions and headingLevel remain unsupported. Omitted or false leaves the default presentation unchanged; inline calls without illustration share the zero horizontal padding and primary title color.
|
|
255
|
+
|
|
256
|
+
Inline built-in illustrations use space-10 (40), simplified geometry with at most two narrative details, clear strokes and no public size prop. The illustration aligns left and centers vertically with its title. Center geometry, space-16 and spacing remain unchanged. Standard inline usage pairs the 40-wide illustration with a visible title on its right in the same row (for example, 「还没有登录记录」). Use titleHidden only in the few contexts where the host already supplies a visible title. All inline variants use zero horizontal padding; illustration, icon and title-only content align with the host section body content edge. Hosts without padding supply their own inset. Inline titles use text-primary at the unchanged copy-14 size.
|
|
257
|
+
|
|
258
|
+
### EmptyState panel
|
|
259
|
+
|
|
260
|
+
Accepted #860: `layout="panel"` is a full-width compact surface for empty tab or filter results. It requires a visible `title: string`; optional `description: string`, `actions: ReactNode` and `headingLevel: 2 | 3 | 4 | 5 | 6` (default `p`) retain text and native action semantics. `icon`, `illustration` and `titleHidden` are forbidden only in this branch. Center and inline types and byte-for-byte markup remain unchanged.
|
|
261
|
+
|
|
262
|
+
DOM: `div.empty-state.empty-state--panel > div.empty-state__body` contains the title and optional `p.empty-state__desc`, followed by optional sibling `div.empty-state__actions`. Only `empty-state--panel` and `empty-state__body` are new classes. The static root has no live-region semantics. Existing subtle background, radius-3, space-4/6, copy-14, semibold primary title and regular secondary description tokens provide hierarchy in both themes, with text contrast at least 4.5:1. A one-pixel border uses the existing border-subtle token and space-1 divided by four; the edge remains distinguishable from the page in both themes without darkening the fill. Actions align vertically at the right on wide screens and below the text at the left on narrow screens; no horizontal overflow.
|
|
@@ -168,7 +168,7 @@ export declare type BannerAction = {
|
|
|
168
168
|
export declare type BannerProps = {
|
|
169
169
|
/** 粗体导语,一眼点题。 */
|
|
170
170
|
title: string;
|
|
171
|
-
/** 段落内文本或行内内容,可放 Link
|
|
171
|
+
/** 段落内文本或行内内容,可放 Link;非空白导语用「 — 」衔接,空白导语仅显示正文。 */
|
|
172
172
|
children?: ReactNode;
|
|
173
173
|
/** 未设 = 页顶公告形态;设值 = 页内卡条形态(neutral 为中性卡条)。 */
|
|
174
174
|
tone?: BannerTone;
|
|
@@ -369,7 +369,7 @@ declare type ButtonSharedProps = {
|
|
|
369
369
|
shortcut?: string;
|
|
370
370
|
className?: string;
|
|
371
371
|
children?: ReactNode;
|
|
372
|
-
/** 文字前置图标槽;14–16px、currentColor,装饰性(自带 aria-hidden)。loading
|
|
372
|
+
/** 文字前置图标槽;14–16px、currentColor,装饰性(自带 aria-hidden)。loading 时保留占位并视觉隐藏。 */
|
|
373
373
|
prefix?: ReactNode;
|
|
374
374
|
/** 文字后置图标槽,同上。 */
|
|
375
375
|
suffix?: ReactNode;
|
|
@@ -692,7 +692,7 @@ export declare function CollapsibleBreadcrumb({ items, label, linkComponent }: {
|
|
|
692
692
|
linkComponent?: LinkComponent;
|
|
693
693
|
}): JSX.Element;
|
|
694
694
|
|
|
695
|
-
export declare function Combobox({ options, searchPlacement, searchPlaceholder, footer, value, onChange, onSearch, isLoading, placeholder, size, required, disabled, readOnly, invalid, errorId, error, emptyText, clearable, id, className, name, inputMode, spellCheck, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ref, ...rest }: ComboboxProps): JSX.Element;
|
|
695
|
+
export declare function Combobox({ options, searchPlacement, showSelectedPrefix, searchPlaceholder, footer, value, onChange, onSearch, isLoading, placeholder, size, required, disabled, readOnly, invalid, errorId, error, emptyText, clearable, id, className, name, inputMode, spellCheck, 'aria-label': ariaLabel, 'aria-labelledby': ariaLabelledby, ref, ...rest }: ComboboxProps): JSX.Element;
|
|
696
696
|
|
|
697
697
|
export declare type ComboboxOption = {
|
|
698
698
|
/** 禁止空字符串(与「未选」的受控空值歧义,adapter 层保证)。 */
|
|
@@ -714,6 +714,8 @@ export declare type ComboboxProps = {
|
|
|
714
714
|
options: ComboboxOption[];
|
|
715
715
|
/** 默认在触发器输入;panel 使用按钮触发并将搜索放在弹层。 */
|
|
716
716
|
searchPlacement?: 'input' | 'panel';
|
|
717
|
+
/** 为 true 时选中值前显示该选项的 prefix;选项无 prefix 时不留空位。 */
|
|
718
|
+
showSelectedPrefix?: boolean;
|
|
717
719
|
/** 面板搜索的独立占位文案。 */
|
|
718
720
|
searchPlaceholder?: string;
|
|
719
721
|
/** 列表之外的操作区,进入正常 Tab 顺序。 */
|
|
@@ -1118,9 +1120,9 @@ export declare type EmptyListIllustrationProps = {
|
|
|
1118
1120
|
variant?: 'complete' | 'neutral';
|
|
1119
1121
|
};
|
|
1120
1122
|
|
|
1121
|
-
export declare type EmptyListIllustrationScene = 'list' | 'search' | 'complete' | 'chart' | 'inventory' | 'orders' | 'product' | 'permission' | 'purchase' | 'supplier' | 'team' | 'notification' | 'store' | 'task' | 'session' | 'activity';
|
|
1123
|
+
export declare type EmptyListIllustrationScene = 'list' | 'search' | 'complete' | 'chart' | 'inventory' | 'orders' | 'product' | 'permission' | 'purchase' | 'supplier' | 'team' | 'notification' | 'store' | 'task' | 'session' | 'activity' | 'payment' | 'security' | 'integration';
|
|
1122
1124
|
|
|
1123
|
-
export declare function EmptyState({ icon, illustration, title, titleHidden, description, actions, headingLevel, layout, }: {
|
|
1125
|
+
export declare function EmptyState({ icon, illustration, title, titleHidden, description, actions, headingLevel, layout, }: ({
|
|
1124
1126
|
icon?: ReactNode;
|
|
1125
1127
|
title: string;
|
|
1126
1128
|
/** 仅视觉隐藏标题,保留原标签与可访问文本。 */
|
|
@@ -1139,6 +1141,15 @@ export declare function EmptyState({ icon, illustration, title, titleHidden, des
|
|
|
1139
1141
|
description?: never;
|
|
1140
1142
|
actions?: never;
|
|
1141
1143
|
headingLevel?: never;
|
|
1144
|
+
}) | {
|
|
1145
|
+
layout: 'panel';
|
|
1146
|
+
title: string;
|
|
1147
|
+
description?: string;
|
|
1148
|
+
actions?: ReactNode;
|
|
1149
|
+
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
1150
|
+
icon?: never;
|
|
1151
|
+
illustration?: never;
|
|
1152
|
+
titleHidden?: never;
|
|
1142
1153
|
})): JSX.Element;
|
|
1143
1154
|
|
|
1144
1155
|
/** Public host for consumer-owned sibling portals. Package overlays share this Foundation root. */
|
|
@@ -2234,7 +2245,7 @@ export declare type RegionErrorStateProps = {
|
|
|
2234
2245
|
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
2235
2246
|
/** 传入时渲染「重试」按钮;不传则不提供重试。 */
|
|
2236
2247
|
onRetry?: () => void;
|
|
2237
|
-
/**
|
|
2248
|
+
/** 重试进行中:组件原地保留,按钮进入载入态并播报「正在重试」,焦点留在按钮上。 */
|
|
2238
2249
|
retrying?: boolean;
|
|
2239
2250
|
/** 出现时的播报级别:polite 用 role=status,assertive 用 role=alert。默认 polite。 */
|
|
2240
2251
|
live?: 'polite' | 'assertive';
|