@q1travel/design-system 0.30.15 → 0.31.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.
- package/contracts/context-contrast-migration.md +9 -7
- package/contracts/design.dark.md +4 -1
- package/contracts/design.md +4 -1
- package/contracts/interface-guidelines.md +4 -0
- package/dist-pkg/component-enums.json +6 -0
- package/dist-pkg/design-system.d.ts +18 -2
- package/dist-pkg/design-system.js +1656 -1575
- package/dist-pkg/style.css +87 -60
- package/package.json +2 -2
|
@@ -54,9 +54,10 @@ brand-text semantic token is not introduced in this migration.
|
|
|
54
54
|
The existing `presentation: { type: 'stacked' }` adopts one consistent visual
|
|
55
55
|
behavior, without new props. Grouped and drilldown keep their presentations.
|
|
56
56
|
|
|
57
|
-
- A selected `menuitemradio` retains `aria-checked`, with a
|
|
58
|
-
and
|
|
59
|
-
remains visible.
|
|
57
|
+
- A selected `menuitemradio` retains `aria-checked`, with a neutral trailing
|
|
58
|
+
check and regular-weight labels, without selected fill or a stronger selected
|
|
59
|
+
press layer. Pending feedback remains visible. Hover, focus, and submenu
|
|
60
|
+
expansion retain their own feedback.
|
|
60
61
|
- `depth: 1` uses a connector to the preceding depth-0 context within the same
|
|
61
62
|
group. The last child ends the branch at its center. Search results keep their
|
|
62
63
|
existing filtering behavior; when the parent is absent, retain indentation
|
|
@@ -84,10 +85,11 @@ carries.
|
|
|
84
85
|
step (16px) from its parent's inline padding, in every presentation. The
|
|
85
86
|
connector lines are removed; `aria-describedby` still names the visible
|
|
86
87
|
parent, so the parent relationship survives for assistive technology.
|
|
87
|
-
- A selected `menuitemradio` keeps `aria-checked`,
|
|
88
|
-
|
|
89
|
-
drilldown. The inline-start accent rail is removed.
|
|
90
|
-
layer; `:focus-visible` keeps its own focus shadow
|
|
88
|
+
- A selected `menuitemradio` keeps `aria-checked`, a neutral trailing check,
|
|
89
|
+
and regular-weight labels, without selected fill or a stronger selected press
|
|
90
|
+
layer, as in grouped and drilldown. The inline-start accent rail is removed.
|
|
91
|
+
Hover remains a background layer; `:focus-visible` keeps its own focus shadow.
|
|
92
|
+
Submenu expansion retains its own feedback.
|
|
91
93
|
|
|
92
94
|
## Acceptance and delivery boundary
|
|
93
95
|
|
package/contracts/design.dark.md
CHANGED
|
@@ -516,6 +516,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
516
516
|
- Modal blocks and demands an answer. Use it for confirmation, not for a long form; a long form belongs in a Drawer or its own route.
|
|
517
517
|
- DestructiveActionModal is the required shape for irreversible actions. Do not rebuild that confirmation from a plain Modal. It is also where high-risk, irreversible, or data-overwriting consequences are stated; do not move that warning into option second lines.
|
|
518
518
|
- Sheet slides in from an edge with a focus trap; opt-in `mobileFullscreen` fills narrow viewports with safe-area padding and no outer inset or radius, while default floating spacing remains unchanged; Drawer is the wider panel form. Menu is anchored and flips on collision; ContextMenu and DotsMenu share its keyboard engine.
|
|
519
|
+
- Menu, DotsMenu and ContextMenu item entries use the existing `danger: true` (no separate tone). Dangerous text and icons use `--q1-color-status-danger-text`, with no border or resting fill and no inverted foreground. Hover and visible focus inherit the neutral `--q1-color-bg-hover`; dark-theme danger active also uses that neutral hover background to keep both themes at AA. Ordinary active retains its text-primary 10% transparent mix. Disabled or locked dangerous entries use secondary text/icon color and have no hover or active feedback.
|
|
519
520
|
- Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
|
|
520
521
|
|
|
521
522
|
**Data and evidence.** Table, Pagination, DocsPagination, ShowMore, Chart, Gauge, Progress, JsonView, Entity, Description, RelativeTimeCard.
|
|
@@ -530,10 +531,12 @@ CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); succ
|
|
|
530
531
|
|
|
531
532
|
**Images and proportions.** AspectRatio requires a finite positive width/height ratio (invalid values render as 1 with one development warning) and only reserves layout space. Image requires alt (empty for decoration), defaults to contain and eager, accepts optional src, fit, loading, sm/md/lg square sizes (32/40/48), onError and root className; omitted size follows host width and natural image ratio. Loading keeps img mounted under a decorative Skeleton; missing and failed sources show a single named image fallback, and source changes isolate stale events. Decorative states are hidden from assistive technology; neither Image nor its host becomes inert. Inside AspectRatio, omit size: the Image root and img fill the ratio frame and object-fit controls complete centered containment or cropping. Image also accepts opt-in frame="none": only none emits data-frame="none" on the existing .image root, removing its border and radius while preserving background, dimensions, fit, loading, empty/error text and accessible names. Omitted frame and frame="default" preserve the previous markup and appearance exactly; the host may provide the outer frame. No new tokens or existing-consumer migration; remove new usages before reverting the additive exports.
|
|
532
533
|
|
|
534
|
+
**Stat.** Tier C read-only single statistic: `label: string` and `value: ReactNode` are required; `unit?: ReactNode` and `placeholder?: ReactNode` are optional. Only null or undefined values use the placeholder (default `—`); 0, "0", and the empty string remain unchanged. Native HTMLDListElement attributes and className pass through the root. Structure is `<dl class="stat"><dt class="stat__label">label</dt><dd class="stat__value">value<span class="stat__unit">unit</span></dd></dl>`; omit the span when no unit is supplied. Label and unit use label-14 with text-muted; the value uses heading-20-strong with text-primary and tabular-nums. Unit text aligns on the baseline; long content wraps within narrow hosts. The placeholder is visual content without additional ARIA. Stat creates no headings, live regions or interactive elements; hosts own layout, Card surfaces, trends, links and loading. No new tokens or existing-consumer migration; remove Stat usages before reverting this additive export.
|
|
535
|
+
|
|
533
536
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
534
537
|
|
|
535
538
|
- 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.
|
|
536
|
-
- 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 an icon and regular secondary title row inside a section whose heading and actions belong to the host, without description, actions, headingLevel, or illustration. Center accepts a decorative `illustration?: ReactNode` in place of icon;
|
|
539
|
+
- 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 an icon and regular secondary title row inside a section whose heading and actions belong to the host, without description, actions, headingLevel, or illustration. 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. Built-in `EmptyListIllustration` accepts `scene?: 'list' | 'search' | 'complete' | 'chart' | 'inventory' | 'orders' | 'product' | 'permission'` 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. All scenes use opaque neutral bodies, token radii, canonical stroke width, and at most one brand-colored element (light primary-700, dark primary-900); geometry is identical across themes. Body contrast against the actual panel is at least 1.3:1. 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.
|
|
537
540
|
- 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.
|
|
538
541
|
- 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.
|
|
539
542
|
- 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
|
@@ -516,6 +516,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
516
516
|
- Modal blocks and demands an answer. Use it for confirmation, not for a long form; a long form belongs in a Drawer or its own route.
|
|
517
517
|
- DestructiveActionModal is the required shape for irreversible actions. Do not rebuild that confirmation from a plain Modal. It is also where high-risk, irreversible, or data-overwriting consequences are stated; do not move that warning into option second lines.
|
|
518
518
|
- Sheet slides in from an edge with a focus trap; opt-in `mobileFullscreen` fills narrow viewports with safe-area padding and no outer inset or radius, while default floating spacing remains unchanged; Drawer is the wider panel form. Menu is anchored and flips on collision; ContextMenu and DotsMenu share its keyboard engine. DotsMenu accepts contextual `label` (default 更多操作) for trigger and menu, plus `{type:'separator'}` entries using the shared Menu separator; roving focus and typeahead skip separators and disabled items.
|
|
519
|
+
- Menu, DotsMenu and ContextMenu item entries use the existing `danger: true` (no separate tone). Dangerous text and icons use `--q1-color-status-danger-text`, with no border or resting fill and no inverted foreground. Hover and visible focus inherit the neutral `--q1-color-bg-hover`; dark-theme danger active also uses that neutral hover background to keep both themes at AA. Ordinary active retains its text-primary 10% transparent mix. Disabled or locked dangerous entries use secondary text/icon color and have no hover or active feedback.
|
|
519
520
|
- Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
|
|
520
521
|
|
|
521
522
|
**Data and evidence.** Table, Pagination, DocsPagination, ShowMore, Chart, Gauge, Progress, JsonView, Entity, Description, RelativeTimeCard.
|
|
@@ -530,10 +531,12 @@ CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); succ
|
|
|
530
531
|
|
|
531
532
|
**Images and proportions.** AspectRatio requires a finite positive width/height ratio (invalid values render as 1 with one development warning) and only reserves layout space. Image requires alt (empty for decoration), defaults to contain and eager, accepts optional src, fit, loading, sm/md/lg square sizes (32/40/48), onError and root className; omitted size follows host width and natural image ratio. Loading keeps img mounted under a decorative Skeleton; missing and failed sources show a single named image fallback, and source changes isolate stale events. Decorative states are hidden from assistive technology; neither Image nor its host becomes inert. Inside AspectRatio, omit size: the Image root and img fill the ratio frame and object-fit controls complete centered containment or cropping. Image also accepts opt-in frame="none": only none emits data-frame="none" on the existing .image root, removing its border and radius while preserving background, dimensions, fit, loading, empty/error text and accessible names. Omitted frame and frame="default" preserve the previous markup and appearance exactly; the host may provide the outer frame. No new tokens or existing-consumer migration; remove new usages before reverting the additive exports.
|
|
532
533
|
|
|
534
|
+
**Stat.** Tier C read-only single statistic: `label: string` and `value: ReactNode` are required; `unit?: ReactNode` and `placeholder?: ReactNode` are optional. Only null or undefined values use the placeholder (default `—`); 0, "0", and the empty string remain unchanged. Native HTMLDListElement attributes and className pass through the root. Structure is `<dl class="stat"><dt class="stat__label">label</dt><dd class="stat__value">value<span class="stat__unit">unit</span></dd></dl>`; omit the span when no unit is supplied. Label and unit use label-14 with text-muted; the value uses heading-20-strong with text-primary and tabular-nums. Unit text aligns on the baseline; long content wraps within narrow hosts. The placeholder is visual content without additional ARIA. Stat creates no headings, live regions or interactive elements; hosts own layout, Card surfaces, trends, links and loading. No new tokens or existing-consumer migration; remove Stat usages before reverting this additive export.
|
|
535
|
+
|
|
533
536
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
534
537
|
|
|
535
538
|
- 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.
|
|
536
|
-
- 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 an icon and regular secondary title row inside a section whose heading and actions belong to the host, without description, actions, headingLevel, or illustration. Center accepts a decorative `illustration?: ReactNode` in place of icon;
|
|
539
|
+
- 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 an icon and regular secondary title row inside a section whose heading and actions belong to the host, without description, actions, headingLevel, or illustration. 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. Built-in `EmptyListIllustration` accepts `scene?: 'list' | 'search' | 'complete' | 'chart' | 'inventory' | 'orders' | 'product' | 'permission'` 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. All scenes use opaque neutral bodies, token radii, canonical stroke width, and at most one brand-colored element (light primary-700, dark primary-900); geometry is identical across themes. Body contrast against the actual panel is at least 1.3:1. 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.
|
|
537
540
|
- 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.
|
|
538
541
|
- 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.
|
|
539
542
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
|
@@ -240,3 +240,7 @@ Component pages follow one skeleton so a reader who has used one page can find t
|
|
|
240
240
|
- This file is the machine-readable mirror of the public 界面准则 page (`/guidelines`); keep them in sync.
|
|
241
241
|
- `AGENTS.md` at the repository root constrains agent generation and modification behavior.
|
|
242
242
|
- `npm run check` (build + shell contract + browser contract) is the merge gate for any interface change.
|
|
243
|
+
|
|
244
|
+
### Empty illustrations
|
|
245
|
+
|
|
246
|
+
Accepted #830: decorative empty illustrations share a 96×104 viewBox, radius-3 outer frames, radius-1 clips, and ICON_STROKE_WIDTH round strokes. All scene geometry, including half the computed stroke width around each element, stays inside the viewBox. Inventory integrates the bell into the shelf; permission centers a complete padlock on the panel; product uses a front-facing label. No detached brand decorations. The family specimen uses one site-themed card per scene, four columns on wide screens and two on narrow screens. Opaque neutral bodies must contrast at least 1.3:1 against the actual card background. Use no gradients, shadows or particles; 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 or permission. Scene takes precedence over the compatible variant; absent scene, neutral maps to list and all other calls to complete. Standalone complete and EmptyState list defaults intentionally receive new artwork and internal SVG structure. Keep the decorative outer span, aria-hidden=true, non-focusable SVG and text-owned state meaning. Complete owns its check; CheckCircleFill semantics are unchanged.
|
|
@@ -1099,8 +1099,15 @@ export declare function Drawer({ open, onClose, title, children, footer, height,
|
|
|
1099
1099
|
height?: number;
|
|
1100
1100
|
}): ReactNode;
|
|
1101
1101
|
|
|
1102
|
-
/**
|
|
1103
|
-
export declare function EmptyListIllustration(): JSX.Element;
|
|
1102
|
+
/** 场景空态的装饰插画;状态含义由相邻文字表达。 */
|
|
1103
|
+
export declare function EmptyListIllustration({ scene, variant }?: EmptyListIllustrationProps): JSX.Element;
|
|
1104
|
+
|
|
1105
|
+
export declare type EmptyListIllustrationProps = {
|
|
1106
|
+
scene?: EmptyListIllustrationScene;
|
|
1107
|
+
variant?: 'complete' | 'neutral';
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
export declare type EmptyListIllustrationScene = 'list' | 'search' | 'complete' | 'chart' | 'inventory' | 'orders' | 'product' | 'permission';
|
|
1104
1111
|
|
|
1105
1112
|
export declare function EmptyState({ icon, illustration, title, description, actions, headingLevel, layout, }: {
|
|
1106
1113
|
icon?: ReactNode;
|
|
@@ -2528,6 +2535,15 @@ export declare type SplitButtonEntry = {
|
|
|
2528
2535
|
onSelect?: () => void;
|
|
2529
2536
|
};
|
|
2530
2537
|
|
|
2538
|
+
export declare function Stat({ label, value, unit, placeholder, className, ...rest }: StatProps): JSX.Element;
|
|
2539
|
+
|
|
2540
|
+
export declare type StatProps = Omit<HTMLAttributes<HTMLDListElement>, 'children'> & {
|
|
2541
|
+
label: string;
|
|
2542
|
+
value: ReactNode;
|
|
2543
|
+
unit?: ReactNode;
|
|
2544
|
+
placeholder?: ReactNode;
|
|
2545
|
+
};
|
|
2546
|
+
|
|
2531
2547
|
export declare function StatusDot({ tone, label, showLabel, titlePrefix, }: {
|
|
2532
2548
|
tone: StatusDotTone;
|
|
2533
2549
|
label: string;
|