@q1travel/design-system 0.30.3 → 0.30.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/design.dark.md +9 -2
- package/contracts/design.md +9 -2
- package/dist-pkg/design-system.d.ts +36 -4
- package/dist-pkg/design-system.js +4500 -4422
- package/dist-pkg/style.css +333 -2
- package/package.json +1 -1
package/contracts/design.dark.md
CHANGED
|
@@ -501,7 +501,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
501
501
|
|
|
502
502
|
|
|
503
503
|
- 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.
|
|
504
|
-
- Switch selects among mutually exclusive views or modes; its outer sm / md / lg heights are 32 / 40 / 48px on fine pointers, defaulting to md.
|
|
504
|
+
- 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.
|
|
505
505
|
- 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.
|
|
506
506
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
507
507
|
|
|
@@ -512,7 +512,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
512
512
|
- HoverCard presents supplemental noninteractive content on fine-pointer hover or keyboard focus after openDelay (default 0ms); closeDelay defaults to 90ms and entering the card cancels closing. Escape dismisses without moving focus into the card; aria-describedby connects the named native trigger to a nonmodal tooltip. Coarse/touch hover is ignored, trigger activation is preserved, and reduced-motion uses the shared Popover styles and anchored overlay foundation.
|
|
513
513
|
- Popover is a named anchored dialog for interactive content; it supports controlled/default open, four-side collision positioning plus controlled anchorRef placement, menu/listbox role focus, width matching, Escape focus return and outside pointer-down dismissal. Keyboard opening moves focus into content; pointer opening retains trigger focus and Tab enters the panel. It is nonmodal by default; opt-in modal locks the background and traps focus. ensurePopoverRoot exposes the same shared Foundation host for consumer-owned sibling portals; dismissExcludeRefs and nested panels preserve parent dismissal boundaries.
|
|
514
514
|
- 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.
|
|
515
|
-
- DestructiveActionModal is the required shape for irreversible actions. Do not rebuild that confirmation from a plain Modal.
|
|
515
|
+
- 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.
|
|
516
516
|
- 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.
|
|
517
517
|
- Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
|
|
518
518
|
|
|
@@ -523,9 +523,14 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
523
523
|
- Gauge is one ratio as a ring. Progress is advancement along a task, and takes segment stops.
|
|
524
524
|
- RelativeTimeCard reveals the absolute time behind a relative one. Prefer it to printing both.
|
|
525
525
|
|
|
526
|
+
CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); success adds `.check-circle-fill--success`, using the same derived solid success fill as Badge and `--q1-color-action-on-primary` foreground; existing calls retain their DOM and appearance, and adjacent text conveys completion.
|
|
527
|
+
|
|
526
528
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton.
|
|
529
|
+
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
527
530
|
|
|
528
531
|
- 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.
|
|
532
|
+
- 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; built-in `EmptyListIllustration` depicts a fully processed list or inbox, without an icon backplate. See `implementation-map.md` for the retry and announcement boundary.
|
|
533
|
+
- 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 creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
529
534
|
- 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. See `implementation-map.md` for the retry and announcement boundary.
|
|
530
535
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
|
531
536
|
- Badge and Pill label a state drawn from a known set. Ordinary metadata is text, not a badge. StatusDot is the smallest form, and needs a text label beside it.
|
|
@@ -604,6 +609,8 @@ Label, value, and detail spacing is identical across every peer in a set. One pa
|
|
|
604
609
|
|
|
605
610
|
**Copy.** Sentence case headings that state the claim rather than name the section. Simplify the language, never the claim. Keep every qualifier attached to the number it qualifies.
|
|
606
611
|
|
|
612
|
+
Second-line text in options and list rows — option descriptions, row subtitles, and secondary labels — stays empty unless it adds information nothing else provides. Add it only when it carries information the label cannot: items at the same level share the same or easily confused labels and the primary text cannot explain itself, or choosing the item has a result the user cannot see from the label, such as ordering, scope, reach, or timing. Translations, restatements of the label, and decorative notes are not information. The pull request must give the reason. Treat second-line text as an exception patch for the primary message, not as standard layout. Severe consequences such as high-risk, irreversible, or data-overwriting actions are stated in the DestructiveActionModal confirmation, not in option or row second lines. When an external product is used as a reference, its second-line information is not carried over. Body descriptions of panels such as empty states, error states, and dialogs are the main message and are not second-line text; field helper text follows the existing note and tooltip rules.
|
|
613
|
+
|
|
607
614
|
## DO
|
|
608
615
|
|
|
609
616
|
- Answer the reader's question in the first viewport, together with the one relationship the page turns on.
|
package/contracts/design.md
CHANGED
|
@@ -501,7 +501,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
501
501
|
|
|
502
502
|
|
|
503
503
|
- 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.
|
|
504
|
-
- Switch selects among mutually exclusive views or modes; its outer sm / md / lg heights are 32 / 40 / 48px on fine pointers, defaulting to md.
|
|
504
|
+
- 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.
|
|
505
505
|
- 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.
|
|
506
506
|
- RangeSlider is two thumbs over one interval, paired with numeric inputs; Slider is a single value.
|
|
507
507
|
|
|
@@ -512,7 +512,7 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
512
512
|
- HoverCard presents supplemental noninteractive content on fine-pointer hover or keyboard focus after openDelay (default 0ms); closeDelay defaults to 90ms and entering the card cancels closing. Escape dismisses without moving focus into the card; aria-describedby connects the named native trigger to a nonmodal tooltip. Coarse/touch hover is ignored, trigger activation is preserved, and reduced-motion uses the shared Popover styles and anchored overlay foundation.
|
|
513
513
|
- Popover is a named anchored dialog for interactive content; it supports controlled/default open, four-side collision positioning plus controlled anchorRef placement, menu/listbox role focus, width matching, Escape focus return and outside pointer-down dismissal. Keyboard opening moves focus into content; pointer opening retains trigger focus and Tab enters the panel. It is nonmodal by default; opt-in modal locks the background and traps focus. ensurePopoverRoot exposes the same shared Foundation host for consumer-owned sibling portals; dismissExcludeRefs and nested panels preserve parent dismissal boundaries.
|
|
514
514
|
- 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.
|
|
515
|
-
- DestructiveActionModal is the required shape for irreversible actions. Do not rebuild that confirmation from a plain Modal.
|
|
515
|
+
- 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.
|
|
516
516
|
- 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.
|
|
517
517
|
- Tooltip carries a short label for a control. Anything a reader must read to decide belongs in the page or in a ContextCard.
|
|
518
518
|
|
|
@@ -523,9 +523,14 @@ Static WCAG stroke contrast against canvas/panel (2026-10-07): light neutral 5.3
|
|
|
523
523
|
- Gauge is one ratio as a ring. Progress is advancement along a task, and takes segment stops.
|
|
524
524
|
- RelativeTimeCard reveals the absolute time behind a relative one. Prefer it to printing both.
|
|
525
525
|
|
|
526
|
+
CheckCircleFill accepts `tone?: 'default' | 'success'` (default `default`); success adds `.check-circle-fill--success`, using the same derived solid success fill as Badge and `--q1-color-action-on-primary` foreground; existing calls retain their DOM and appearance, and adjacent text conveys completion.
|
|
527
|
+
|
|
526
528
|
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton.
|
|
529
|
+
**Status and feedback.** Banner, ProjectBanner, Note, ErrorCard, RegionErrorState, ErrorMessage, EmptyState, Badge, Pill, CheckCircleFill, StatusDot, ToastProvider, useToast, Feedback, PageFeedback, Spinner, LoadingDots, Skeleton, StatusPage, SkeletonIllustration, EmptyListIllustration.
|
|
527
530
|
|
|
528
531
|
- 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.
|
|
532
|
+
- 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; built-in `EmptyListIllustration` depicts a fully processed list or inbox, without an icon backplate. See `implementation-map.md` for the retry and announcement boundary.
|
|
533
|
+
- 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 creates no live region; its host owns focus and announcements. StatusPage code is metadata only, never a message or spoken number.
|
|
529
534
|
- 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. See `implementation-map.md` for the retry and announcement boundary.
|
|
530
535
|
- Toast is for non-blocking confirmation of something that already happened. It is not where an error the reader must act on belongs.
|
|
531
536
|
- Badge and Pill label a state drawn from a known set. Ordinary metadata is text, not a badge. StatusDot is the smallest form, and needs a text label beside it.
|
|
@@ -604,6 +609,8 @@ Label, value, and detail spacing is identical across every peer in a set. One pa
|
|
|
604
609
|
|
|
605
610
|
**Copy.** Sentence case headings that state the claim rather than name the section. Simplify the language, never the claim. Keep every qualifier attached to the number it qualifies.
|
|
606
611
|
|
|
612
|
+
Second-line text in options and list rows — option descriptions, row subtitles, and secondary labels — stays empty unless it adds information nothing else provides. Add it only when it carries information the label cannot: items at the same level share the same or easily confused labels and the primary text cannot explain itself, or choosing the item has a result the user cannot see from the label, such as ordering, scope, reach, or timing. Translations, restatements of the label, and decorative notes are not information. The pull request must give the reason. Treat second-line text as an exception patch for the primary message, not as standard layout. Severe consequences such as high-risk, irreversible, or data-overwriting actions are stated in the DestructiveActionModal confirmation, not in option or row second lines. When an external product is used as a reference, its second-line information is not carried over. Body descriptions of panels such as empty states, error states, and dialogs are the main message and are not second-line text; field helper text follows the existing note and tooltip rules.
|
|
613
|
+
|
|
607
614
|
## DO
|
|
608
615
|
|
|
609
616
|
- Answer the reader's question in the first viewport, together with the one relationship the page turns on.
|
|
@@ -465,10 +465,11 @@ export declare type CheckboxProps = ControlOptionOwnProps & (FieldShellProps | F
|
|
|
465
465
|
} & Omit<ControlOptionNativeInputProps, 'size'>;
|
|
466
466
|
|
|
467
467
|
/** 装饰性圆底勾;当前或选中语义由宿主表达。 */
|
|
468
|
-
export declare function CheckCircleFill({ className, ...props }: CheckCircleFillProps): JSX.Element;
|
|
468
|
+
export declare function CheckCircleFill({ className, tone, ...props }: CheckCircleFillProps): JSX.Element;
|
|
469
469
|
|
|
470
470
|
export declare type CheckCircleFillProps = {
|
|
471
471
|
className?: string;
|
|
472
|
+
tone?: 'default' | 'success';
|
|
472
473
|
children?: never;
|
|
473
474
|
[attribute: `data-${string}`]: string | number | boolean | undefined;
|
|
474
475
|
};
|
|
@@ -998,15 +999,27 @@ export declare function Drawer({ open, onClose, title, children, footer, height,
|
|
|
998
999
|
height?: number;
|
|
999
1000
|
}): ReactNode;
|
|
1000
1001
|
|
|
1001
|
-
|
|
1002
|
+
/** 列表或收件箱内容已全部处理的装饰插画;状态含义由相邻文字表达。 */
|
|
1003
|
+
export declare function EmptyListIllustration(): JSX.Element;
|
|
1004
|
+
|
|
1005
|
+
export declare function EmptyState({ icon, illustration, title, description, actions, headingLevel, layout, }: {
|
|
1002
1006
|
icon?: ReactNode;
|
|
1003
1007
|
title: string;
|
|
1008
|
+
} & ({
|
|
1009
|
+
layout?: 'center';
|
|
1010
|
+
illustration?: ReactNode;
|
|
1004
1011
|
description?: string;
|
|
1005
1012
|
/** CTA 区:传家族 <Button> 与次要链接;最多一个主 CTA。 */
|
|
1006
1013
|
actions?: ReactNode;
|
|
1007
1014
|
/** 标题层级:缺省渲染 p(不进标题树);作为页面区块时传 2-6 渲染对应 h 标签。 */
|
|
1008
1015
|
headingLevel?: 2 | 3 | 4 | 5 | 6;
|
|
1009
|
-
}
|
|
1016
|
+
} | {
|
|
1017
|
+
layout: 'inline';
|
|
1018
|
+
illustration?: never;
|
|
1019
|
+
description?: never;
|
|
1020
|
+
actions?: never;
|
|
1021
|
+
headingLevel?: never;
|
|
1022
|
+
})): JSX.Element;
|
|
1010
1023
|
|
|
1011
1024
|
/** Public host for consumer-owned sibling portals. Package overlays share this Foundation root. */
|
|
1012
1025
|
export declare function ensurePopoverRoot(): HTMLElement | null;
|
|
@@ -2312,6 +2325,8 @@ declare type SkeletonDataAttributes = {
|
|
|
2312
2325
|
[key: `data-${string}`]: string | number | boolean | null | undefined;
|
|
2313
2326
|
};
|
|
2314
2327
|
|
|
2328
|
+
export declare function SkeletonIllustration(): JSX.Element;
|
|
2329
|
+
|
|
2315
2330
|
export declare type SkeletonProps = {
|
|
2316
2331
|
shape?: SkeletonShape;
|
|
2317
2332
|
animated?: boolean;
|
|
@@ -2395,7 +2410,23 @@ export declare function StatusDot({ tone, label, showLabel, titlePrefix, }: {
|
|
|
2395
2410
|
|
|
2396
2411
|
export declare type StatusDotTone = 'neutral' | 'warning' | 'success' | 'danger';
|
|
2397
2412
|
|
|
2398
|
-
export declare function
|
|
2413
|
+
export declare function StatusPage({ code, size, title, description, primaryAction, secondaryAction, illustration, icon, headingLevel }: StatusPageProps): JSX.Element;
|
|
2414
|
+
|
|
2415
|
+
export declare type StatusPageCode = '402' | '403' | '404' | '500' | 'offline' | 'timeout' | 'session-expired';
|
|
2416
|
+
|
|
2417
|
+
export declare type StatusPageProps = {
|
|
2418
|
+
code?: StatusPageCode;
|
|
2419
|
+
size?: 'page' | 'region';
|
|
2420
|
+
title: string;
|
|
2421
|
+
description?: string;
|
|
2422
|
+
primaryAction?: ReactNode;
|
|
2423
|
+
secondaryAction?: ReactNode;
|
|
2424
|
+
illustration?: ReactNode;
|
|
2425
|
+
icon?: ReactNode;
|
|
2426
|
+
headingLevel?: 1 | 2;
|
|
2427
|
+
};
|
|
2428
|
+
|
|
2429
|
+
export declare function Switch({ options, ariaLabel, name, defaultValue, value, onChange, size, variant, fullWidth, iconOnly, }: {
|
|
2399
2430
|
options: SwitchOption[];
|
|
2400
2431
|
ariaLabel: string;
|
|
2401
2432
|
name?: string;
|
|
@@ -2403,6 +2434,7 @@ export declare function Switch({ options, ariaLabel, name, defaultValue, value,
|
|
|
2403
2434
|
value?: string;
|
|
2404
2435
|
onChange?: (value: string) => void;
|
|
2405
2436
|
size?: 'sm' | 'md' | 'lg';
|
|
2437
|
+
variant?: 'segmented' | 'cards';
|
|
2406
2438
|
fullWidth?: boolean;
|
|
2407
2439
|
/** 纯图标模式:文字标签仍在,只是视觉隐藏(q1-sr-only),可访问名称不丢失。 */
|
|
2408
2440
|
iconOnly?: boolean;
|