@qijenchen/design-system 0.1.0-beta.61 → 0.1.0-beta.63
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/CLAUDE.md +6 -6
- package/dist/components/Accordion/accordion.d.ts +1 -1
- package/dist/components/Accordion/accordion.js +1 -1
- package/dist/components/Accordion/accordion.js.map +1 -1
- package/dist/components/Alert/alert.d.ts +2 -2
- package/dist/components/Alert/alert.js +4 -3
- package/dist/components/Alert/alert.js.map +1 -1
- package/dist/components/AppShell/app-shell.d.ts.map +1 -1
- package/dist/components/AppShell/app-shell.js +4 -3
- package/dist/components/AppShell/app-shell.js.map +1 -1
- package/dist/components/Avatar/avatar.d.ts +1 -1
- package/dist/components/Avatar/avatar.js +2 -1
- package/dist/components/Avatar/avatar.js.map +1 -1
- package/dist/components/Badge/badge.d.ts +1 -1
- package/dist/components/Badge/badge.js +2 -1
- package/dist/components/Badge/badge.js.map +1 -1
- package/dist/components/Breadcrumb/breadcrumb.d.ts +1 -1
- package/dist/components/Breadcrumb/breadcrumb.js.map +1 -1
- package/dist/components/BulkActionBar/bulk-action-bar.js.map +1 -1
- package/dist/components/Button/button.d.ts +5 -5
- package/dist/components/Button/button.d.ts.map +1 -1
- package/dist/components/Button/button.js +4 -3
- package/dist/components/Button/button.js.map +1 -1
- package/dist/components/Calendar/calendar.d.ts +1 -1
- package/dist/components/Calendar/calendar.d.ts.map +1 -1
- package/dist/components/Calendar/calendar.js +80 -69
- package/dist/components/Calendar/calendar.js.map +1 -1
- package/dist/components/Carousel/carousel.d.ts.map +1 -1
- package/dist/components/Carousel/carousel.js +5 -3
- package/dist/components/Carousel/carousel.js.map +1 -1
- package/dist/components/Checkbox/checkbox-group.d.ts +2 -1
- package/dist/components/Checkbox/checkbox-group.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox-group.js.map +1 -1
- package/dist/components/Checkbox/checkbox.d.ts +9 -3
- package/dist/components/Checkbox/checkbox.d.ts.map +1 -1
- package/dist/components/Checkbox/checkbox.js +8 -3
- package/dist/components/Checkbox/checkbox.js.map +1 -1
- package/dist/components/Chip/chip.d.ts +2 -2
- package/dist/components/Chip/chip.js +3 -2
- package/dist/components/Chip/chip.js.map +1 -1
- package/dist/components/CircularProgress/circular-progress.d.ts +1 -1
- package/dist/components/CircularProgress/circular-progress.js +2 -1
- package/dist/components/CircularProgress/circular-progress.js.map +1 -1
- package/dist/components/Coachmark/coachmark.d.ts +4 -4
- package/dist/components/Coachmark/coachmark.js +2 -1
- package/dist/components/Coachmark/coachmark.js.map +1 -1
- package/dist/components/Combobox/combobox.d.ts.map +1 -1
- package/dist/components/Combobox/combobox.js +17 -13
- package/dist/components/Combobox/combobox.js.map +1 -1
- package/dist/components/Command/command.d.ts +4 -0
- package/dist/components/Command/command.d.ts.map +1 -1
- package/dist/components/Command/command.js.map +1 -1
- package/dist/components/DataTable/data-table.d.ts.map +1 -1
- package/dist/components/DataTable/data-table.js.map +1 -1
- package/dist/components/DateGrid/date-grid.d.ts +8 -4
- package/dist/components/DateGrid/date-grid.d.ts.map +1 -1
- package/dist/components/DateGrid/date-grid.js +2 -2
- package/dist/components/DateGrid/date-grid.js.map +1 -1
- package/dist/components/DatePicker/date-picker.d.ts.map +1 -1
- package/dist/components/DatePicker/date-picker.js +6 -4
- package/dist/components/DatePicker/date-picker.js.map +1 -1
- package/dist/components/DescriptionList/description-list.js +1 -1
- package/dist/components/DescriptionList/description-list.js.map +1 -1
- package/dist/components/Dialog/dialog.d.ts.map +1 -1
- package/dist/components/Dialog/dialog.js.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.d.ts.map +1 -1
- package/dist/components/DropdownMenu/dropdown-menu.js +0 -1
- package/dist/components/DropdownMenu/dropdown-menu.js.map +1 -1
- package/dist/components/Empty/empty.d.ts +1 -1
- package/dist/components/Empty/empty.js.map +1 -1
- package/dist/components/Field/field-wrapper.js +1 -1
- package/dist/components/Field/field-wrapper.js.map +1 -1
- package/dist/components/Field/field.d.ts.map +1 -1
- package/dist/components/Field/field.js +1 -0
- package/dist/components/Field/field.js.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.d.ts.map +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js +1 -1
- package/dist/components/FieldControlGroup/field-control-group.js.map +1 -1
- package/dist/components/FileUpload/file-upload.d.ts +8 -1
- package/dist/components/FileUpload/file-upload.d.ts.map +1 -1
- package/dist/components/FileUpload/file-upload.js +7 -3
- package/dist/components/FileUpload/file-upload.js.map +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts +1 -1
- package/dist/components/FileViewer/file-viewer.d.ts.map +1 -1
- package/dist/components/FileViewer/file-viewer.js +25 -14
- package/dist/components/FileViewer/file-viewer.js.map +1 -1
- package/dist/components/HoverCard/hover-card.d.ts +5 -1
- package/dist/components/HoverCard/hover-card.d.ts.map +1 -1
- package/dist/components/HoverCard/hover-card.js +2 -1
- package/dist/components/HoverCard/hover-card.js.map +1 -1
- package/dist/components/Menu/menu-item.d.ts +6 -2
- package/dist/components/Menu/menu-item.d.ts.map +1 -1
- package/dist/components/Menu/menu-item.js.map +1 -1
- package/dist/components/Notice/notice.d.ts +5 -1
- package/dist/components/Notice/notice.d.ts.map +1 -1
- package/dist/components/Notice/notice.js +2 -1
- package/dist/components/Notice/notice.js.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts +4 -0
- package/dist/components/OverflowIndicator/overflow-indicator.d.ts.map +1 -1
- package/dist/components/OverflowIndicator/overflow-indicator.js +2 -2
- package/dist/components/OverflowIndicator/overflow-indicator.js.map +1 -1
- package/dist/components/PeoplePicker/people-picker.d.ts.map +1 -1
- package/dist/components/PeoplePicker/people-picker.js +6 -2
- package/dist/components/PeoplePicker/people-picker.js.map +1 -1
- package/dist/components/Popover/popover.d.ts +1 -1
- package/dist/components/Popover/popover.js +2 -1
- package/dist/components/Popover/popover.js.map +1 -1
- package/dist/components/ProfileCard/profile-card.d.ts +9 -4
- package/dist/components/ProfileCard/profile-card.d.ts.map +1 -1
- package/dist/components/ProfileCard/profile-card.js.map +1 -1
- package/dist/components/RadioGroup/radio-group.d.ts +6 -0
- package/dist/components/RadioGroup/radio-group.d.ts.map +1 -1
- package/dist/components/RadioGroup/radio-group.js +4 -0
- package/dist/components/RadioGroup/radio-group.js.map +1 -1
- package/dist/components/ScrollArea/scroll-area.js +1 -1
- package/dist/components/ScrollArea/scroll-area.js.map +1 -1
- package/dist/components/Select/select.d.ts.map +1 -1
- package/dist/components/Select/select.js +14 -4
- package/dist/components/Select/select.js.map +1 -1
- package/dist/components/SelectMenu/select-menu.d.ts +4 -0
- package/dist/components/SelectMenu/select-menu.d.ts.map +1 -1
- package/dist/components/SelectMenu/select-menu.js +6 -3
- package/dist/components/SelectMenu/select-menu.js.map +1 -1
- package/dist/components/SelectionControl/selection-item.d.ts +5 -1
- package/dist/components/SelectionControl/selection-item.d.ts.map +1 -1
- package/dist/components/SelectionControl/selection-item.js +3 -1
- package/dist/components/SelectionControl/selection-item.js.map +1 -1
- package/dist/components/Sheet/sheet.d.ts +6 -5
- package/dist/components/Sheet/sheet.d.ts.map +1 -1
- package/dist/components/Sheet/sheet.js.map +1 -1
- package/dist/components/Sidebar/sidebar.d.ts +1 -1
- package/dist/components/Sidebar/sidebar.d.ts.map +1 -1
- package/dist/components/Sidebar/sidebar.js +3 -2
- package/dist/components/Sidebar/sidebar.js.map +1 -1
- package/dist/components/Skeleton/skeleton.d.ts +1 -1
- package/dist/components/Skeleton/skeleton.js +2 -1
- package/dist/components/Skeleton/skeleton.js.map +1 -1
- package/dist/components/Slider/slider.d.ts +1 -1
- package/dist/components/Slider/slider.d.ts.map +1 -1
- package/dist/components/Slider/slider.js +7 -3
- package/dist/components/Slider/slider.js.map +1 -1
- package/dist/components/Steps/steps.d.ts +1 -1
- package/dist/components/Steps/steps.d.ts.map +1 -1
- package/dist/components/Steps/steps.js +9 -6
- package/dist/components/Steps/steps.js.map +1 -1
- package/dist/components/Switch/switch.d.ts +5 -5
- package/dist/components/Switch/switch.js +3 -3
- package/dist/components/Switch/switch.js.map +1 -1
- package/dist/components/Tabs/tabs.js +1 -1
- package/dist/components/Tabs/tabs.js.map +1 -1
- package/dist/components/Textarea/textarea.js +1 -1
- package/dist/components/Textarea/textarea.js.map +1 -1
- package/dist/components/TimePicker/time-columns.js +1 -1
- package/dist/components/TimePicker/time-columns.js.map +1 -1
- package/dist/components/TimePicker/time-picker.d.ts.map +1 -1
- package/dist/components/TimePicker/time-picker.js +2 -1
- package/dist/components/TimePicker/time-picker.js.map +1 -1
- package/dist/components/Toast/toast.js.map +1 -1
- package/dist/components/Tooltip/tooltip.d.ts +1 -1
- package/dist/components/Tooltip/tooltip.js +2 -1
- package/dist/components/Tooltip/tooltip.js.map +1 -1
- package/dist/components/TreeView/tree-view.d.ts +4 -4
- package/dist/components/TreeView/tree-view.d.ts.map +1 -1
- package/dist/components/TreeView/tree-view.js +3 -3
- package/dist/components/TreeView/tree-view.js.map +1 -1
- package/dist/patterns/element-anatomy/item-anatomy.d.ts +3 -3
- package/dist/patterns/element-anatomy/item-anatomy.js.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.d.ts +3 -2
- package/dist/patterns/header-canonical/chrome-header.d.ts.map +1 -1
- package/dist/patterns/header-canonical/chrome-header.js.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.d.ts +3 -2
- package/dist/patterns/overlay-surface/overlay-surface.d.ts.map +1 -1
- package/dist/patterns/overlay-surface/overlay-surface.js.map +1 -1
- package/ds-canonical/hooks/_log-fire.sh +3 -1
- package/ds-canonical/hooks/check_audit_post_report_validator.sh +21 -1
- package/ds-canonical/hooks/check_audit_sample_escape.sh +6 -3
- package/ds-canonical/hooks/check_chrome_header_avatar_canonical.sh +8 -0
- package/ds-canonical/hooks/check_consumer_app_invariants.sh +321 -0
- package/ds-canonical/hooks/check_datatable_invariants.sh +75 -12
- package/ds-canonical/hooks/check_escape_marker_abuse.sh +15 -1
- package/ds-canonical/hooks/check_field_family_invariants.sh +12 -2
- package/ds-canonical/hooks/check_plugin_fork_health.sh +137 -0
- package/ds-canonical/hooks/check_propose_discipline.sh +147 -0
- package/ds-canonical/hooks/check_solo_workflow.sh +11 -1
- package/ds-canonical/hooks/check_story_invariants.sh +63 -9
- package/ds-canonical/hooks/check_storybook_addon_packaging.sh +151 -0
- package/ds-canonical/hooks/check_tailwind_wildcard_in_docs.sh +8 -2
- package/ds-canonical/hooks/lib/_overlay_handcraft.sh +25 -4
- package/ds-canonical/hooks/lib/_token_hygiene.sh +9 -1
- package/ds-canonical/hooks/session_start_governance_check.sh +7 -2
- package/ds-canonical/hooks/stop_self_audit.sh +2 -2
- package/ds-canonical/hooks/tests/test_check_addon_subdir_ship.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_consumer_app_invariants.sh +12 -0
- package/ds-canonical/hooks/tests/test_check_consumer_app_story_title.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_consumer_ds_primitive_misuse.sh +6 -4
- package/ds-canonical/hooks/tests/test_check_consumer_no_ds_catalog.sh +7 -4
- package/ds-canonical/hooks/tests/test_check_consumer_story_baseline.sh +6 -4
- package/ds-canonical/hooks/tests/test_check_data_table_size_num_to_meta_width.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_fork_user_plugin_install.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_plugin_fork_health.sh +9 -0
- package/ds-canonical/hooks/tests/test_check_propose_cite_required.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_propose_discipline.sh +10 -0
- package/ds-canonical/hooks/tests/test_check_propose_plain_chinese.sh +3 -2
- package/ds-canonical/hooks/tests/test_check_storybook_addon_packaging.sh +10 -0
- package/ds-canonical/hooks/tests/test_check_storybook_addon_preset_cjs.sh +3 -2
- package/ds-canonical/references/build-ui-canonicals.md +2 -2
- package/ds-canonical/references/composition-fidelity.md +3 -3
- package/ds-canonical/references/naming-conventions.md +1 -0
- package/ds-canonical/references/ssot-consultation.md +1 -1
- package/ds-canonical/references/ssot-index.md +7 -7
- package/ds-canonical/rules/meta-patterns.md +5 -5
- package/ds-canonical/rules/self-verify.md +1 -1
- package/ds-canonical/rules/story-rules.md +2 -0
- package/ds-canonical/rules/ui-development.md +1 -1
- package/ds-canonical/skills/deep-audit-cross-codex/SKILL.md +11 -2
- package/ds-canonical/skills/deep-audit-cross-codex/references/triage-rubric.md +1 -1
- package/ds-canonical/skills/design-system-audit/SKILL.md +15 -15
- package/ds-canonical/skills/design-system-audit/references/audit-prompts.md +4 -3
- package/ds-story-manifest.json +15 -21
- package/llms-full.txt +9 -5
- package/llms.txt +2 -2
- package/package.json +1 -1
- package/src/components/Accordion/accordion.principles.stories.tsx +1 -1
- package/src/components/Accordion/accordion.spec.md +22 -5
- package/src/components/Accordion/accordion.tsx +1 -1
- package/src/components/Alert/alert.anatomy.stories.tsx +4 -4
- package/src/components/Alert/alert.principles.stories.tsx +5 -5
- package/src/components/Alert/alert.spec.md +16 -11
- package/src/components/Alert/alert.stories.tsx +5 -5
- package/src/components/Alert/alert.tsx +2 -2
- package/src/components/AppShell/app-shell.principles.stories.tsx +68 -21
- package/src/components/AppShell/app-shell.spec.md +17 -24
- package/src/components/AppShell/app-shell.tsx +4 -3
- package/src/components/AspectRatio/aspect-ratio.anatomy.stories.tsx +3 -3
- package/src/components/AspectRatio/aspect-ratio.spec.md +15 -1
- package/src/components/Avatar/avatar.anatomy.stories.tsx +1 -1
- package/src/components/Avatar/avatar.principles.stories.tsx +8 -8
- package/src/components/Avatar/avatar.spec.md +25 -54
- package/src/components/Avatar/avatar.tsx +2 -2
- package/src/components/Badge/badge.principles.stories.tsx +2 -2
- package/src/components/Badge/badge.spec.md +22 -13
- package/src/components/Badge/badge.tsx +1 -1
- package/src/components/Breadcrumb/breadcrumb.principles.stories.tsx +5 -2
- package/src/components/Breadcrumb/breadcrumb.spec.md +25 -25
- package/src/components/Breadcrumb/breadcrumb.stories.tsx +7 -32
- package/src/components/Breadcrumb/breadcrumb.tsx +1 -1
- package/src/components/BulkActionBar/bulk-action-bar.principles.stories.tsx +2 -2
- package/src/components/BulkActionBar/bulk-action-bar.spec.md +20 -6
- package/src/components/BulkActionBar/bulk-action-bar.tsx +2 -2
- package/src/components/Button/button.anatomy.stories.tsx +2 -2
- package/src/components/Button/button.spec.md +19 -17
- package/src/components/Button/button.stories.tsx +1 -1
- package/src/components/Button/button.tsx +10 -9
- package/src/components/Calendar/calendar.anatomy.stories.tsx +1 -1
- package/src/components/Calendar/calendar.spec.md +37 -17
- package/src/components/Calendar/calendar.tsx +26 -22
- package/src/components/Carousel/carousel.anatomy.stories.tsx +6 -6
- package/src/components/Carousel/carousel.principles.stories.tsx +1 -1
- package/src/components/Carousel/carousel.spec.md +23 -3
- package/src/components/Carousel/carousel.tsx +19 -4
- package/src/components/Chart/chart.anatomy.stories.tsx +1 -1
- package/src/components/Chart/chart.spec.md +20 -1
- package/src/components/Checkbox/checkbox-group.tsx +2 -1
- package/src/components/Checkbox/checkbox.anatomy.stories.tsx +4 -4
- package/src/components/Checkbox/checkbox.principles.stories.tsx +2 -2
- package/src/components/Checkbox/checkbox.spec.md +35 -15
- package/src/components/Checkbox/checkbox.stories.tsx +1 -1
- package/src/components/Checkbox/checkbox.tsx +14 -3
- package/src/components/Chip/chip.anatomy.stories.tsx +5 -5
- package/src/components/Chip/chip.principles.stories.tsx +9 -3
- package/src/components/Chip/chip.spec.md +8 -9
- package/src/components/Chip/chip.tsx +2 -2
- package/src/components/CircularProgress/circular-progress.anatomy.stories.tsx +6 -6
- package/src/components/CircularProgress/circular-progress.spec.md +10 -11
- package/src/components/CircularProgress/circular-progress.stories.tsx +36 -43
- package/src/components/CircularProgress/circular-progress.tsx +1 -1
- package/src/components/Coachmark/coachmark.anatomy.stories.tsx +1 -1
- package/src/components/Coachmark/coachmark.principles.stories.tsx +14 -29
- package/src/components/Coachmark/coachmark.spec.md +21 -13
- package/src/components/Coachmark/coachmark.tsx +4 -4
- package/src/components/Combobox/combobox.anatomy.stories.tsx +28 -28
- package/src/components/Combobox/combobox.principles.stories.tsx +1 -1
- package/src/components/Combobox/combobox.spec.md +25 -7
- package/src/components/Combobox/combobox.tsx +12 -3
- package/src/components/Command/command.anatomy.stories.tsx +1 -1
- package/src/components/Command/command.principles.stories.tsx +8 -8
- package/src/components/Command/command.spec.md +18 -8
- package/src/components/Command/command.tsx +8 -3
- package/src/components/DataTable/data-table-sort-manager.tsx +1 -1
- package/src/components/DataTable/data-table.anatomy.stories.tsx +9 -7
- package/src/components/DataTable/data-table.principles.stories.tsx +3 -3
- package/src/components/DataTable/data-table.spec.md +15 -11
- package/src/components/DataTable/data-table.stories.tsx +58 -13
- package/src/components/DataTable/data-table.tsx +13 -0
- package/src/components/DataTable/filter-operators.spec.md +19 -27
- package/src/components/DateGrid/date-grid.anatomy.stories.tsx +8 -8
- package/src/components/DateGrid/date-grid.principles.stories.tsx +7 -7
- package/src/components/DateGrid/date-grid.spec.md +17 -14
- package/src/components/DateGrid/date-grid.tsx +8 -4
- package/src/components/DatePicker/date-picker.anatomy.stories.tsx +11 -15
- package/src/components/DatePicker/date-picker.principles.stories.tsx +8 -8
- package/src/components/DatePicker/date-picker.spec.md +40 -36
- package/src/components/DatePicker/date-picker.stories.tsx +23 -53
- package/src/components/DatePicker/date-picker.tsx +12 -6
- package/src/components/DescriptionList/description-list.anatomy.stories.tsx +2 -2
- package/src/components/DescriptionList/description-list.principles.stories.tsx +4 -18
- package/src/components/DescriptionList/description-list.spec.md +9 -3
- package/src/components/DescriptionList/description-list.stories.tsx +2 -18
- package/src/components/DescriptionList/description-list.tsx +1 -1
- package/src/components/Dialog/dialog.principles.stories.tsx +1 -1
- package/src/components/Dialog/dialog.spec.md +26 -11
- package/src/components/Dialog/dialog.stories.tsx +159 -184
- package/src/components/Dialog/dialog.tsx +2 -1
- package/src/components/DropdownMenu/dropdown-menu.anatomy.stories.tsx +2 -2
- package/src/components/DropdownMenu/dropdown-menu.principles.stories.tsx +16 -17
- package/src/components/DropdownMenu/dropdown-menu.spec.md +10 -15
- package/src/components/DropdownMenu/dropdown-menu.stories.tsx +13 -15
- package/src/components/DropdownMenu/dropdown-menu.tsx +12 -2
- package/src/components/Empty/empty.spec.md +19 -18
- package/src/components/Empty/empty.tsx +1 -1
- package/src/components/Field/field-controls.spec.md +52 -41
- package/src/components/Field/field-wrapper.tsx +2 -2
- package/src/components/Field/field.anatomy.stories.tsx +6 -5
- package/src/components/Field/field.principles.stories.tsx +8 -7
- package/src/components/Field/field.spec.md +42 -20
- package/src/components/Field/field.stories.tsx +9 -7
- package/src/components/Field/field.tsx +6 -3
- package/src/components/Field/form-validation.spec.md +20 -19
- package/src/components/FieldControlGroup/field-control-group.anatomy.stories.tsx +16 -15
- package/src/components/FieldControlGroup/field-control-group.principles.stories.tsx +5 -1
- package/src/components/FieldControlGroup/field-control-group.spec.md +17 -1
- package/src/components/FieldControlGroup/field-control-group.stories.tsx +9 -9
- package/src/components/FieldControlGroup/field-control-group.tsx +4 -3
- package/src/components/FileItem/file-item.anatomy.stories.tsx +7 -17
- package/src/components/FileItem/file-item.principles.stories.tsx +7 -7
- package/src/components/FileItem/file-item.spec.md +24 -24
- package/src/components/FileUpload/file-upload.anatomy.stories.tsx +4 -2
- package/src/components/FileUpload/file-upload.principles.stories.tsx +10 -12
- package/src/components/FileUpload/file-upload.spec.md +37 -8
- package/src/components/FileUpload/file-upload.tsx +5 -3
- package/src/components/FileViewer/file-viewer.anatomy.stories.tsx +16 -11
- package/src/components/FileViewer/file-viewer.principles.stories.tsx +4 -4
- package/src/components/FileViewer/file-viewer.spec.md +7 -19
- package/src/components/FileViewer/file-viewer.stories.tsx +3 -3
- package/src/components/FileViewer/file-viewer.tsx +42 -16
- package/src/components/HoverCard/hover-card.spec.md +20 -10
- package/src/components/HoverCard/hover-card.stories.tsx +1 -1
- package/src/components/HoverCard/hover-card.tsx +12 -1
- package/src/components/Input/input.anatomy.stories.tsx +4 -4
- package/src/components/Input/input.principles.stories.tsx +4 -31
- package/src/components/Input/input.spec.md +16 -4
- package/src/components/LinkInput/link-input.anatomy.stories.tsx +1 -1
- package/src/components/LinkInput/link-input.principles.stories.tsx +2 -2
- package/src/components/LinkInput/link-input.spec.md +14 -1
- package/src/components/Menu/menu-item.principles.stories.tsx +5 -5
- package/src/components/Menu/menu-item.spec.md +24 -12
- package/src/components/Menu/menu-item.tsx +7 -3
- package/src/components/Notice/notice.principles.stories.tsx +7 -7
- package/src/components/Notice/notice.spec.md +7 -5
- package/src/components/Notice/notice.tsx +5 -1
- package/src/components/NumberInput/number-input.anatomy.stories.tsx +4 -4
- package/src/components/NumberInput/number-input.principles.stories.tsx +13 -35
- package/src/components/NumberInput/number-input.spec.md +19 -0
- package/src/components/OverflowIndicator/overflow-indicator.anatomy.stories.tsx +4 -3
- package/src/components/OverflowIndicator/overflow-indicator.principles.stories.tsx +2 -5
- package/src/components/OverflowIndicator/overflow-indicator.spec.md +9 -1
- package/src/components/OverflowIndicator/overflow-indicator.stories.tsx +6 -42
- package/src/components/OverflowIndicator/overflow-indicator.tsx +9 -3
- package/src/components/PeoplePicker/people-picker.anatomy.stories.tsx +11 -2
- package/src/components/PeoplePicker/people-picker.spec.md +26 -12
- package/src/components/PeoplePicker/people-picker.stories.tsx +14 -15
- package/src/components/PeoplePicker/people-picker.tsx +12 -4
- package/src/components/Popover/popover.spec.md +20 -11
- package/src/components/Popover/popover.tsx +2 -2
- package/src/components/ProfileCard/profile-card.anatomy.stories.tsx +1 -1
- package/src/components/ProfileCard/profile-card.principles.stories.tsx +29 -25
- package/src/components/ProfileCard/profile-card.spec.md +30 -10
- package/src/components/ProfileCard/profile-card.tsx +12 -7
- package/src/components/ProgressBar/progress-bar.anatomy.stories.tsx +1 -1
- package/src/components/ProgressBar/progress-bar.principles.stories.tsx +7 -5
- package/src/components/ProgressBar/progress-bar.spec.md +9 -1
- package/src/components/ProgressBar/progress-bar.stories.tsx +1 -1
- package/src/components/RadioGroup/radio-group.anatomy.stories.tsx +6 -3
- package/src/components/RadioGroup/radio-group.principles.stories.tsx +6 -13
- package/src/components/RadioGroup/radio-group.spec.md +7 -1
- package/src/components/RadioGroup/radio-group.tsx +10 -0
- package/src/components/Rating/rating.anatomy.stories.tsx +1 -0
- package/src/components/Rating/rating.principles.stories.tsx +9 -6
- package/src/components/Rating/rating.spec.md +13 -7
- package/src/components/ScrollArea/scroll-area.anatomy.stories.tsx +44 -24
- package/src/components/ScrollArea/scroll-area.principles.stories.tsx +51 -57
- package/src/components/ScrollArea/scroll-area.spec.md +5 -7
- package/src/components/ScrollArea/scroll-area.stories.tsx +110 -60
- package/src/components/ScrollArea/scroll-area.tsx +1 -1
- package/src/components/SegmentedControl/segmented-control.anatomy.stories.tsx +1 -1
- package/src/components/SegmentedControl/segmented-control.spec.md +15 -14
- package/src/components/SegmentedControl/segmented-control.stories.tsx +1 -1
- package/src/components/Select/select.anatomy.stories.tsx +12 -11
- package/src/components/Select/select.principles.stories.tsx +3 -3
- package/src/components/Select/select.spec.md +34 -8
- package/src/components/Select/select.stories.tsx +2 -2
- package/src/components/Select/select.tsx +18 -3
- package/src/components/SelectMenu/select-menu.anatomy.stories.tsx +2 -2
- package/src/components/SelectMenu/select-menu.principles.stories.tsx +5 -5
- package/src/components/SelectMenu/select-menu.spec.md +19 -12
- package/src/components/SelectMenu/select-menu.stories.tsx +34 -27
- package/src/components/SelectMenu/select-menu.tsx +14 -3
- package/src/components/SelectionControl/selection-item.anatomy.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.spec.md +13 -2
- package/src/components/SelectionControl/selection-item.stories.tsx +1 -1
- package/src/components/SelectionControl/selection-item.tsx +8 -2
- package/src/components/Separator/separator.principles.stories.tsx +2 -2
- package/src/components/Separator/separator.spec.md +11 -5
- package/src/components/Sheet/sheet.anatomy.stories.tsx +7 -6
- package/src/components/Sheet/sheet.principles.stories.tsx +18 -38
- package/src/components/Sheet/sheet.spec.md +9 -11
- package/src/components/Sheet/sheet.stories.tsx +2 -2
- package/src/components/Sheet/sheet.tsx +6 -5
- package/src/components/Sidebar/sidebar.anatomy.stories.tsx +42 -33
- package/src/components/Sidebar/sidebar.principles.stories.tsx +4 -4
- package/src/components/Sidebar/sidebar.spec.md +22 -17
- package/src/components/Sidebar/sidebar.stories.tsx +16 -0
- package/src/components/Sidebar/sidebar.tsx +15 -7
- package/src/components/Skeleton/skeleton.anatomy.stories.tsx +2 -2
- package/src/components/Skeleton/skeleton.principles.stories.tsx +20 -7
- package/src/components/Skeleton/skeleton.spec.md +6 -4
- package/src/components/Skeleton/skeleton.stories.tsx +34 -26
- package/src/components/Skeleton/skeleton.tsx +1 -1
- package/src/components/Slider/slider.principles.stories.tsx +3 -3
- package/src/components/Slider/slider.spec.md +16 -12
- package/src/components/Slider/slider.stories.tsx +2 -18
- package/src/components/Slider/slider.tsx +6 -2
- package/src/components/Steps/steps.anatomy.stories.tsx +3 -3
- package/src/components/Steps/steps.principles.stories.tsx +9 -12
- package/src/components/Steps/steps.spec.md +31 -25
- package/src/components/Steps/steps.stories.tsx +5 -40
- package/src/components/Steps/steps.tsx +17 -12
- package/src/components/Switch/switch.principles.stories.tsx +1 -1
- package/src/components/Switch/switch.spec.md +13 -0
- package/src/components/Switch/switch.tsx +3 -3
- package/src/components/Tabs/tabs.anatomy.stories.tsx +4 -4
- package/src/components/Tabs/tabs.principles.stories.tsx +1 -1
- package/src/components/Tabs/tabs.spec.md +13 -6
- package/src/components/Tabs/tabs.stories.tsx +3 -3
- package/src/components/Tabs/tabs.tsx +2 -2
- package/src/components/Tag/tag.anatomy.stories.tsx +15 -15
- package/src/components/Tag/tag.principles.stories.tsx +1 -1
- package/src/components/Tag/tag.spec.md +25 -5
- package/src/components/Textarea/textarea.anatomy.stories.tsx +3 -3
- package/src/components/Textarea/textarea.principles.stories.tsx +7 -7
- package/src/components/Textarea/textarea.spec.md +15 -3
- package/src/components/Textarea/textarea.tsx +1 -1
- package/src/components/TimePicker/time-columns.tsx +1 -1
- package/src/components/TimePicker/time-picker.anatomy.stories.tsx +8 -2
- package/src/components/TimePicker/time-picker.spec.md +23 -3
- package/src/components/TimePicker/time-picker.tsx +5 -3
- package/src/components/Toast/toast.anatomy.stories.tsx +1 -1
- package/src/components/Toast/toast.spec.md +7 -4
- package/src/components/Toast/toast.stories.tsx +12 -6
- package/src/components/Toast/toast.tsx +1 -1
- package/src/components/Tooltip/tooltip.anatomy.stories.tsx +1 -1
- package/src/components/Tooltip/tooltip.spec.md +11 -8
- package/src/components/Tooltip/tooltip.tsx +1 -1
- package/src/components/TreeView/tree-view.anatomy.stories.tsx +25 -9
- package/src/components/TreeView/tree-view.principles.stories.tsx +5 -5
- package/src/components/TreeView/tree-view.spec.md +11 -9
- package/src/components/TreeView/tree-view.stories.tsx +5 -3
- package/src/components/TreeView/tree-view.tsx +8 -5
- package/src/patterns/action-bar/action-bar.spec.md +11 -4
- package/src/patterns/element-anatomy/element-anatomy.spec.md +8 -6
- package/src/patterns/element-anatomy/inline-action.spec.md +18 -18
- package/src/patterns/element-anatomy/item-anatomy.spec.md +47 -42
- package/src/patterns/element-anatomy/item-anatomy.stories.tsx +6 -6
- package/src/patterns/element-anatomy/item-anatomy.tsx +3 -3
- package/src/patterns/header-canonical/chrome-header.tsx +3 -2
- package/src/patterns/header-canonical/header-canonical.spec.md +24 -11
- package/src/patterns/header-canonical/header-canonical.stories.tsx +9 -9
- package/src/patterns/horizontal-overflow/horizontal-overflow.spec.md +22 -12
- package/src/patterns/overlay-surface/overlay-surface.spec.md +43 -39
- package/src/patterns/overlay-surface/overlay-surface.tsx +3 -2
- package/src/patterns/resize-handle/resize-handle.spec.md +10 -1
- package/src/patterns/resize-handle/resize-handle.stories.tsx +7 -7
- package/src/tokens/README.md +5 -2
- package/src/tokens/color/color.spec.md +17 -10
- package/src/tokens/color/semantic.css +2 -0
- package/src/tokens/density/density.spec.md +4 -14
- package/src/tokens/elevation/elevation.spec.md +7 -8
- package/src/tokens/layoutSpace/layoutSpace.spec.md +20 -27
- package/src/tokens/motion/motion.spec.md +15 -7
- package/src/tokens/opacity/opacity.spec.md +15 -26
- package/src/tokens/orphan-tokens.spec.md +14 -3
- package/src/tokens/radius/radius.spec.md +13 -15
- package/src/tokens/token-system.spec.md +10 -13
- package/src/tokens/typography/typography.spec.md +8 -12
- package/src/tokens/uiSize/uiSize.spec.md +28 -16
- /package/ds-canonical/hooks/{check_addon_subdir_ship.sh → retired/2026-06-11-prune-merge/check_addon_subdir_ship.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_app_story_title.sh → retired/2026-06-11-prune-merge/check_consumer_app_story_title.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_ds_primitive_misuse.sh → retired/2026-06-11-prune-merge/check_consumer_ds_primitive_misuse.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_no_ds_catalog.sh → retired/2026-06-11-prune-merge/check_consumer_no_ds_catalog.sh} +0 -0
- /package/ds-canonical/hooks/{check_consumer_story_baseline.sh → retired/2026-06-11-prune-merge/check_consumer_story_baseline.sh} +0 -0
- /package/ds-canonical/hooks/{check_data_table_size_num_to_meta_width.sh → retired/2026-06-11-prune-merge/check_data_table_size_num_to_meta_width.sh} +0 -0
- /package/ds-canonical/hooks/{check_fork_user_plugin_install.sh → retired/2026-06-11-prune-merge/check_fork_user_plugin_install.sh} +0 -0
- /package/ds-canonical/hooks/{check_plugin_freshness.sh → retired/2026-06-11-prune-merge/check_plugin_freshness.sh} +0 -0
- /package/ds-canonical/hooks/{check_propose_cite_required.sh → retired/2026-06-11-prune-merge/check_propose_cite_required.sh} +0 -0
- /package/ds-canonical/hooks/{check_propose_plain_chinese.sh → retired/2026-06-11-prune-merge/check_propose_plain_chinese.sh} +0 -0
- /package/ds-canonical/hooks/{check_storybook_addon_preset_cjs.sh → retired/2026-06-11-prune-merge/check_storybook_addon_preset_cjs.sh} +0 -0
|
@@ -63,7 +63,7 @@ benchmark:
|
|
|
63
63
|
|
|
64
64
|
### 3. Vertical rhythm
|
|
65
65
|
|
|
66
|
-
- **A 家族**:`py-[var(--layout-space-tight)]`(md=
|
|
66
|
+
- **A 家族**:`py-[var(--layout-space-tight)]`(md=12px / lg=16px)+ `data-unbounded` slot trick → 實際高度對齊 `--chrome-header-height`
|
|
67
67
|
- **B 家族**:`h-[var(--chrome-header-height)]`(md=48px / lg=56px)+ `items-center`
|
|
68
68
|
|
|
69
69
|
### 4. Dismiss icon button(close X)
|
|
@@ -76,9 +76,9 @@ benchmark:
|
|
|
76
76
|
|
|
77
77
|
**Canonical**:Chrome header brand mark avatar = **24px,density-fixed,row-size-fixed**。
|
|
78
78
|
|
|
79
|
-
對齊 5 家世界級共識(Linear / Notion / Figma / Slack / Polaris chrome header brand mark 皆固定 24px,不 density-scale 也不 row-size-scale)。設計理由:avatar 是品牌識別 mark,視覺穩定優於 density 緊鬆 / row size 調整(button 跟 density 走 touch target 邏輯,avatar 不該綁同邏輯)。
|
|
79
|
+
對齊 5 家世界級共識(Linear / Notion / Figma / Slack / Polaris chrome header brand mark 皆固定 24px,不 density-scale 也不 row-size-scale)。設計理由:avatar 是品牌識別 mark,視覺穩定優於 density 緊鬆 / row size 調整(button 跟 density 走 touch target 邏輯,avatar 不該綁同邏輯)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
80
80
|
|
|
81
|
-
**Chrome header 不是 row context**:無 sm/md/lg row size lookup 需求 → chrome header 內 avatar 用 **raw `<Avatar size={24}>`**,**不用 `<ItemAvatar>`**(後者是 row primitive anatomy helper,scope 是 row context — 詳 `item-anatomy.spec.md
|
|
81
|
+
**Chrome header 不是 row context**:無 sm/md/lg row size lookup 需求 → chrome header 內 avatar 用 **raw `<Avatar size={24}>`**,**不用 `<ItemAvatar>`**(後者是 row primitive anatomy helper,scope 是 row context — 詳 `item-anatomy.spec.md`「Scope 例外:Chrome header 不是 row context」段)。
|
|
82
82
|
|
|
83
83
|
**程式表達**(local pattern token,對齊 `data-table.css` 既有 local-token 先例):
|
|
84
84
|
- CSS:`--chrome-header-avatar-size: 1.5rem`(`header-canonical.css`)— 供 SidebarHeader 收合對齊公式消費
|
|
@@ -112,7 +112,9 @@ benchmark:
|
|
|
112
112
|
- `_demo-helpers.tsx:131` PageHeader 自刻 `<header>` + `bg-surface` → migrate 消費 ChromeHeader + 保 `bg-surface`(top-level 合法)
|
|
113
113
|
- `sidebar.stories.tsx:113` Sidebar IconCollapse demo 自刻 header + `bg-surface` → 後續一併 migrate(同 PageHeader 路徑)
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
### 7. Element contract(實作層,2026-05-20)
|
|
116
|
+
|
|
117
|
+
ChromeHeader 內部用 `<header>` element(非 `<div>`)。HTML5 spec 允許 sectional content 多次 `<header>`(`role="banner"` 只 page top 一個,sectional 內 default `<header>` 不自動有 banner role)。統一 element contract 消除「何時用 header / 何時用 div」consumer drift。(與規則 6 背景所有權是兩個層級:6 是設計原則,本條是 code contract。)
|
|
116
118
|
|
|
117
119
|
---
|
|
118
120
|
|
|
@@ -131,9 +133,9 @@ benchmark:
|
|
|
131
133
|
**世界級對照**:
|
|
132
134
|
- GitHub Primer PageHeader verbatim:「`hasBorder` defaults true,**but border NOT rendered if Navigation child contains UnderlineNav**;UnderlineNav itself provides bottom border」(`primer.style/components/page-header/react`)
|
|
133
135
|
- 我們既有 spec:`tabs.spec.md` 「出現在 Dialog / Sidebar / 任何 header」段「Consumer 把 Tabs 放在 header 區域內、移除 header 自己的 `border-b`,讓 Tabs 接管」
|
|
134
|
-
- 我們既有禁止:`tabs.spec.md
|
|
136
|
+
- 我們既有禁止:`tabs.spec.md`「禁止事項」段「Dialog / Sidebar header 內不得同時有 header `border-b` 和 TabsList `border-b`——會出現雙線」
|
|
135
137
|
|
|
136
|
-
**Counter-pattern(reject)**:Material UI 走「container 畫 border + tabs 不畫」(`<Box sx={{ borderBottom: 1 }}><Tabs>...</Tabs></Box>`)— 本 DS reject 此方向,理由:tabs underline 需 base line(MUI 2px indicator 浮空是另一派視覺語言,跨派遷移成本高且既有 spec 已 codify Primer 派)。
|
|
138
|
+
**Counter-pattern(reject)**:Material UI 走「container 畫 border + tabs 不畫」(`<Box sx={{ borderBottom: 1 }}><Tabs>...</Tabs></Box>`)— 本 DS reject 此方向,理由:tabs underline 需 base line(MUI 2px indicator 浮空是另一派視覺語言,跨派遷移成本高且既有 spec 已 codify Primer 派)。 <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
137
139
|
|
|
138
140
|
### Rule W2:Tabs 水平 padding 對齊 header(by inheritance,不重複設)
|
|
139
141
|
|
|
@@ -189,9 +191,9 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
|
|
|
189
191
|
|
|
190
192
|
| Header family | Tabs size | Rationale |
|
|
191
193
|
|---|---|---|
|
|
192
|
-
| **A. Overlay header(Padding-based)** | `sm`(32/40)| 對齊 Ant verbatim「small size could be used in Modal」;overlay 內 close X 也是 sm 統一 |
|
|
194
|
+
| **A. Overlay header(Padding-based)** | `sm`(32/40)| 對齊 Ant verbatim「small size could be used in Modal」;overlay 內 close X 也是 sm 統一 | <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
193
195
|
| **B. Chrome header(Fixed-h)** | `sm`(32/40)| chrome 內 buttons 全部 sm(real grep:`Sidebar / FileViewer toolbar / InfoPanel` 所有 buttons size=sm),tabs 跟齊 |
|
|
194
|
-
| **C. Standalone(tabs 取代 chrome header)** | `lg`(48/56)| Tabs 直接取代 header 用,**tab 高 = chrome-header-height 像素相等**(48/56)— Ant verbatim「Large size tabs are usually used in page header」 |
|
|
196
|
+
| **C. Standalone(tabs 取代 chrome header)** | `lg`(48/56)| Tabs 直接取代 header 用,**tab 高 = chrome-header-height 像素相等**(48/56)— Ant verbatim「Large size tabs are usually used in page header」 | <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
195
197
|
|
|
196
198
|
**Token alignment 強連動**:`--tab-height-lg` md=48 / lg=56 與 `--chrome-header-height` md=48 / lg=56 **像素完全相等**(皆定義於 `uiSize.css`:`--chrome-header-height` L50/L98、`--tab-height-lg` 同檔;`globals.css` 僅 21 行 aggregator,不定義此 token)— 這是「lg tabs 取代 chrome header」的 SSOT linkage 鐵證。**禁止任何一方獨自調整**(動 chrome-header-height 必同步 tab-height-lg,反之同)。
|
|
197
199
|
|
|
@@ -212,7 +214,7 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
|
|
|
212
214
|
**Rationale**:
|
|
213
215
|
- Header 內 tabs 對應 button-sm → tabs sm(W3 已 cover)
|
|
214
216
|
- 獨立 chrome-replacement tabs → tabs lg(W3 已 cover)
|
|
215
|
-
- 中間階梯(md=40)無清楚場景 — 多家世界級 DS(Material / MUI / Primer / Polaris)只有 1 個 default size(無 sm/md/lg 階梯)= sweet spot 只需 2 tier
|
|
217
|
+
- 中間階梯(md=40)無清楚場景 — 多家世界級 DS(Material / MUI / Primer / Polaris)只有 1 個 default size(無 sm/md/lg 階梯)= sweet spot 只需 2 tier <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
216
218
|
|
|
217
219
|
**Conservative path**:token 不刪(避免 breaking change 與 stories 維護成本);spec 明寫 future tier。
|
|
218
220
|
|
|
@@ -220,6 +222,8 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
|
|
|
220
222
|
|
|
221
223
|
**規則**:Tabs `cva defaultVariants.size = 'sm'` — **已 land**(`tabs.tsx:135` TabsList param `size = 'sm'` + `:394` trigger cva `defaultVariants.size = 'sm'`,inline comment「2026-05-17 從 'md' 改 'sm'」)。
|
|
222
224
|
|
|
225
|
+
**邊界 — tabs 超寬溢出**:header 內 TabsList 溢出處理走 `components/Tabs/tabs.spec.md`「Overflow 模式」(`TabsList` `overflow` prop:none / scroll / menu),非本 SSOT scope。
|
|
226
|
+
|
|
223
227
|
**Rationale**:overlay / chrome / dense toolbar 用 sm(tab 高 32/40)對齊 Ant「small size could be used in Modal」。production consumer 主要在 stories;cva default 改 sm 已過 visual baseline diff merge。
|
|
224
228
|
|
|
225
229
|
---
|
|
@@ -279,7 +283,7 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
|
|
|
279
283
|
## 禁止事項
|
|
280
284
|
|
|
281
285
|
- ❌ Header 內同時有 header `border-b` 和 TabsList `border-b`(雙線)— per W1
|
|
282
|
-
- ❌ Tabs underline 用 `border-b-2` 在 trigger 上實作(雙線)— per `tabs.spec.md
|
|
286
|
+
- ❌ Tabs underline 用 `border-b-2` 在 trigger 上實作(雙線)— per `tabs.spec.md`「禁止事項」段(trigger 禁 `border-b-2`)
|
|
283
287
|
- ❌ Negative margin 接 header 與 tabs row(脆弱 chain)— per W4
|
|
284
288
|
- ❌ Hardcode `h-16` / `h-14` / `h-12` 自寫 chrome header 高度 — 必 `--chrome-header-height`
|
|
285
289
|
- ❌ Header 內 close X 用 `size="md"` 或 `size="lg"`(real grep 100% sm)
|
|
@@ -289,7 +293,7 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
|
|
|
289
293
|
|
|
290
294
|
## A11y 預設
|
|
291
295
|
|
|
292
|
-
- Header 用 `<header>` semantic element;**僅 page-top top-level chrome header** 暴露 `role="banner"` landmark。nested 在 sectioning content(`<aside>` / `<section>` / overlay)內的 `<header>` 對 SR **不暴露 banner role**(per WAI-ARIA APG + MDN:`<header>` 只在最近 sectioning ancestor 為 `<body>` 時才是 banner)。ChromeHeader 是 layout primitive,**不硬塞 banner role**(
|
|
296
|
+
- Header 用 `<header>` semantic element;**僅 page-top top-level chrome header** 暴露 `role="banner"` landmark。nested 在 sectioning content(`<aside>` / `<section>` / overlay)內的 `<header>` 對 SR **不暴露 banner role**(per WAI-ARIA APG + MDN:`<header>` 只在最近 sectioning ancestor 為 `<body>` 時才是 banner)。ChromeHeader 是 layout primitive,**不硬塞 banner role**(與「7. Element contract」段一致)
|
|
293
297
|
- Tabs 走 Radix Tabs(原生 `role="tablist" / role="tab" / aria-selected`)
|
|
294
298
|
- Dismiss button `aria-label="關閉"`(close X)
|
|
295
299
|
- 鍵盤:Tabs 左右方向鍵切換(Radix);Esc 觸發 dismiss(overlay 家族)
|
|
@@ -308,3 +312,12 @@ ChromeHeader / SurfaceHeader 新增 `tabsSlot?: ReactNode` prop。提供時自
|
|
|
308
312
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
309
313
|
|
|
310
314
|
- `app-shell.spec.md`
|
|
315
|
+
- `dialog.spec.md`
|
|
316
|
+
- `file-viewer.spec.md`
|
|
317
|
+
- `item-anatomy.spec.md`
|
|
318
|
+
- `overlay-surface.spec.md`
|
|
319
|
+
- `popover.spec.md`
|
|
320
|
+
- `sheet.spec.md`
|
|
321
|
+
- `sidebar.spec.md`
|
|
322
|
+
- `tabs.spec.md`
|
|
323
|
+
- `uiSize.spec.md`
|
|
@@ -68,7 +68,7 @@ export const SingleRow = () => (
|
|
|
68
68
|
<Separator orientation="vertical" className="h-6 mx-1" />
|
|
69
69
|
<Button iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />
|
|
70
70
|
</ChromeHeader>
|
|
71
|
-
<div className="p-6 text-body text-fg-
|
|
71
|
+
<div className="p-6 text-body text-fg-secondary">文件預覽內容區…</div>
|
|
72
72
|
</Panel>
|
|
73
73
|
)
|
|
74
74
|
|
|
@@ -92,13 +92,13 @@ export const WithTabs = () => (
|
|
|
92
92
|
<Button iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />
|
|
93
93
|
</ChromeHeader>
|
|
94
94
|
<TabsContent value="overview">
|
|
95
|
-
<div className="p-6 text-body text-fg-
|
|
95
|
+
<div className="p-6 text-body text-fg-secondary">「概覽」分頁內容…</div>
|
|
96
96
|
</TabsContent>
|
|
97
97
|
<TabsContent value="activity">
|
|
98
|
-
<div className="p-6 text-body text-fg-
|
|
98
|
+
<div className="p-6 text-body text-fg-secondary">「活動」分頁內容…</div>
|
|
99
99
|
</TabsContent>
|
|
100
100
|
<TabsContent value="files">
|
|
101
|
-
<div className="p-6 text-body text-fg-
|
|
101
|
+
<div className="p-6 text-body text-fg-secondary">「檔案」分頁內容…</div>
|
|
102
102
|
</TabsContent>
|
|
103
103
|
</Tabs>
|
|
104
104
|
</Panel>
|
|
@@ -112,21 +112,21 @@ export const Overview = () => (
|
|
|
112
112
|
<div className="flex flex-col gap-8">
|
|
113
113
|
<section className="flex flex-col gap-2">
|
|
114
114
|
<div className="text-body font-medium text-foreground">Single-row(標題 + 動作列)</div>
|
|
115
|
-
<div className="text-body-sm text-fg-
|
|
115
|
+
<div className="text-body-sm text-fg-secondary">
|
|
116
116
|
最常見的 chrome header。對標 FileViewer Toolbar / Sidebar header。border-b 自畫、px-loose、dismiss size=sm。
|
|
117
117
|
</div>
|
|
118
118
|
<SingleRow />
|
|
119
119
|
</section>
|
|
120
120
|
<section className="flex flex-col gap-2">
|
|
121
121
|
<div className="text-body font-medium text-foreground">withTabs(標題列 + 分頁列)</div>
|
|
122
|
-
<div className="text-body-sm text-fg-
|
|
122
|
+
<div className="text-body-sm text-fg-secondary">
|
|
123
123
|
header 內含分頁。對標 FileViewer InfoPanel。傳 tabsSlot,border 改由 TabsList 全寬畫一條線。
|
|
124
124
|
</div>
|
|
125
125
|
<WithTabs />
|
|
126
126
|
</section>
|
|
127
127
|
<section className="flex flex-col gap-2">
|
|
128
128
|
<div className="text-body font-medium text-foreground">A 家族 overlay(SurfaceHeader)— 浮層標題列</div>
|
|
129
|
-
<div className="text-body-sm text-fg-
|
|
129
|
+
<div className="text-body-sm text-fg-secondary">
|
|
130
130
|
Dialog / Sheet / Popover / Coachmark 的頂部標題列。高度 padding-based(由內容撐、對齊 chrome-header-height),跟
|
|
131
131
|
chrome 家族共用同一組契約:border-b、px-loose、dismiss size=sm、tabs 連動。consumer 多用 DialogHeader /
|
|
132
132
|
SheetHeader / PopoverHeader wrapper,此處用底層 SurfaceHeader 展示 anatomy。
|
|
@@ -136,12 +136,12 @@ export const Overview = () => (
|
|
|
136
136
|
<h2 className="flex-1 truncate text-body-lg font-medium text-foreground">編輯專案設定</h2>
|
|
137
137
|
<Button iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />
|
|
138
138
|
</SurfaceHeader>
|
|
139
|
-
<div className="p-6 text-body text-fg-
|
|
139
|
+
<div className="p-6 text-body text-fg-secondary">浮層內容區…(Dialog / Sheet / Popover body)</div>
|
|
140
140
|
</div>
|
|
141
141
|
</section>
|
|
142
142
|
<section className="flex flex-col gap-2">
|
|
143
143
|
<div className="text-body font-medium text-foreground">leadingRail — 見 AppShell(非此處)</div>
|
|
144
|
-
<div className="text-body-sm text-fg-
|
|
144
|
+
<div className="text-body-sm text-fg-secondary">
|
|
145
145
|
ChromeHeader 另有 leadingRail slot(寬 = sidebar 收合寬,專為跟 sidebar 收合 icon 對齊)。因它只在「有
|
|
146
146
|
sidebar」時有意義,canonical demo 放在 AppShell primary-header(leadingRail = SidebarTrigger),不在此孤立展示。
|
|
147
147
|
</div>
|
|
@@ -22,7 +22,7 @@ scope: utility primitives module (use-overflow-items hooks: useScrollEdges + use
|
|
|
22
22
|
|
|
23
23
|
`ScrollArrow` 元件在 `Tabs/tabs.tsx` 和 `Chip/chip.tsx` **逐行 copy-paste**,`buildFadeMask` / `ARROW_BUTTON_WIDTH` / `SCROLL_PAGE_RATIO` 常數也是複製的。任何修改都要手動改兩處,是漂移溫床——實際上已經造成了一個 bug:
|
|
24
24
|
|
|
25
|
-
**Chip 的 menu trigger
|
|
25
|
+
**Chip 的 menu trigger 曾漂移成可選 chip 的視覺語言,使用者無法區分「點擊 = 選中」與「點擊 = 打開選單」;同期 Tabs 正確用 text button 表達工具層——同一個 overflow affordance 在兩元件自相矛盾。**
|
|
26
26
|
|
|
27
27
|
### Canonical 規則
|
|
28
28
|
|
|
@@ -30,12 +30,21 @@ scope: utility primitives module (use-overflow-items hooks: useScrollEdges + use
|
|
|
30
30
|
|
|
31
31
|
- 左右 scroll arrow → text button,ChevronLeft / ChevronRight
|
|
32
32
|
- Menu trigger → text button,ChevronDown
|
|
33
|
-
- **禁止**用 item 自身的視覺語言(chip 形狀、tab 底線等)來渲染 overflow trigger——overflow affordance 是「工具層」,不是「業務層」,不該跟內容爭視覺重量(
|
|
33
|
+
- **禁止**用 item 自身的視覺語言(chip 形狀、tab 底線等)來渲染 overflow trigger——overflow affordance 是「工具層」,不是「業務層」,不該跟內容爭視覺重量(「工具層必須是視覺重量最低的一層」— 本 spec codify 此原則,owner SSOT 在此)。
|
|
34
34
|
|
|
35
35
|
這條規則讓使用者看到向下 chevron 或向右 arrow 時,心智是一致的:「這是 overflow 的工具,不是可選內容」,不論這排 items 是什麼類型。
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
+
## 何時用 / 近親分界 / 常見誤解 / A11y
|
|
40
|
+
|
|
41
|
+
- **何時用**:任何「一排水平 items 可能塞不下容器」的 DS 元件要加 overflow affordance 時 import 本 module。scroll vs menu 模式的抉擇 SSOT 在 `tabs.spec.md`「Overflow 模式」表(none / scroll / menu 三策略 + 何時用),此處不重複
|
|
42
|
+
- **何時不用 / 近親分界**:設計上拒絕 overflow 的元件不消費——SegmentedControl / Steps(見「消費者清單 Non-consumers」,各自 spec 明文)
|
|
43
|
+
- **常見誤解**:「在新元件自刻 ScrollArrow / fade mask 比較快」——禁止(見「禁止事項」),copy-paste 漂移正是本 module 存在的歷史教訓
|
|
44
|
+
- **A11y**:arrow / trigger 內建 `aria-label`(arrow 預設繁中、trigger 必填)+ iconOnly Button 自動 tooltip(見「元件保證行為」);menu 開啟後的 a11y 由消費者的 DropdownMenu(Radix)承擔
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
39
48
|
## 提供的 primitive
|
|
40
49
|
|
|
41
50
|
### 常數
|
|
@@ -49,7 +58,7 @@ scope: utility primitives module (use-overflow-items hooks: useScrollEdges + use
|
|
|
49
58
|
### Helpers
|
|
50
59
|
|
|
51
60
|
- `buildFadeMask({ canScroll, atStart, atEnd, reserveArrowWidth })`
|
|
52
|
-
回傳 `linear-gradient` 字串。`reserveArrowWidth` > 0 時 fade 會延伸到 arrow button 底下(Material 3 scrim 原理);= 0 時直接從邊緣 fade。
|
|
61
|
+
回傳 `linear-gradient` 字串。`reserveArrowWidth` > 0 時 fade 會延伸到 arrow button 底下(Material 3 scrim 原理);= 0 時直接從邊緣 fade。 <!-- @benchmark-unverified -->
|
|
53
62
|
- `useScrollByPage(scrollRef)` → `(direction: 'left' | 'right') => void`
|
|
54
63
|
點一次滑動 `clientWidth × SCROLL_PAGE_RATIO`,含 `behavior: 'smooth'`。
|
|
55
64
|
|
|
@@ -66,7 +75,7 @@ scope: utility primitives module (use-overflow-items hooks: useScrollEdges + use
|
|
|
66
75
|
- 必填 `aria-label`(直接傳給 Button 的 `aria-label`,無 `label` alias;由消費者決定,例如「頁籤選單(共 5 個)」)
|
|
67
76
|
- `forwardRef` + `...props` spread 讓 Radix `DropdownMenuTrigger asChild` 可以接管
|
|
68
77
|
|
|
69
|
-
> **元件保證行為:auto-Tooltip**——`OverflowScrollArrow` 與 `OverflowMenuTriggerButton` 都是 `iconOnly` + string `aria-label` + 非 `asChild`,因此都會觸發 Button 內建的 auto-Tooltip wrap(button.tsx
|
|
78
|
+
> **元件保證行為:auto-Tooltip**——`OverflowScrollArrow` 與 `OverflowMenuTriggerButton` 都是 `iconOnly` + string `aria-label` + 非 `asChild`,因此都會觸發 Button 內建的 auto-Tooltip wrap(`button.tsx` iconOnly auto-Tooltip wrap)。亦即 hover 時 arrow / trigger 會自動冒出對應 `aria-label` 的 tooltip(「向左捲動」/「向右捲動」/「頁籤選單(共 N 個)」),共享全域 `Tooltip` 的 delay 與 warm-up。`DropdownMenuTrigger asChild` 仍正常運作:Radix 的 `asChild` 由 Trigger 自身消費(套到 `OverflowMenuTriggerButton` 的 forwardRef),不會傳成 Button 的 `asChild` prop,所以 tooltip wrap 與 trigger 接管互不衝突。
|
|
70
79
|
|
|
71
80
|
### Hook re-export
|
|
72
81
|
|
|
@@ -158,6 +167,13 @@ return (
|
|
|
158
167
|
|
|
159
168
|
---
|
|
160
169
|
|
|
170
|
+
## 邊界案例
|
|
171
|
+
|
|
172
|
+
- **內容塞得下(`canScroll=false`)**:`buildFadeMask` 回傳 `undefined`(無 mask),消費者依 `canScroll` 條件不渲染 arrows / menu trigger(Tabs / Chip 組裝範例皆如此)——items 為 0 或恰好全部塞進容器同理,overflow affordance 完全不出現
|
|
173
|
+
- **鍵盤操作**:arrow / trigger 都是標準 `<Button>`(Tab 聚焦、Enter / Space 觸發);menu 開啟後的 Escape / 方向鍵由消費者的 `DropdownMenu`(Radix)own,本 module 無自訂 keyboard handler
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
161
177
|
## 禁止事項
|
|
162
178
|
|
|
163
179
|
❌ **禁止** copy `ScrollArrow` 到其他元件。要在新元件加 overflow → import 本 module 的 `<OverflowScrollArrow>`。
|
|
@@ -174,7 +190,7 @@ return (
|
|
|
174
190
|
|
|
175
191
|
**Non-consumers**(2026-05-10 retire from「未來」清單 — prior prediction stale,spec 已明文不消費):
|
|
176
192
|
- ~~`components/Steps/steps.tsx`~~ — `steps.spec.md:342`「水平空間不夠塞 content 區,強塞會破壞 stepper 的掃視節奏」+ L353「步驟 ≤ 5、水平空間充足」→ 設計上不 overflow
|
|
177
|
-
- ~~`components/SegmentedControl/*`~~ — `segmented-control.spec.md
|
|
193
|
+
- ~~`components/SegmentedControl/*`~~ — `segmented-control.spec.md`「規模限制與邊界案例」段「**不支援 overflow / scroll**——若選項可能超出容器寬度,代表選錯元件了」+「最多 5 個 item」→ 設計上拒絕 overflow
|
|
178
194
|
|
|
179
195
|
新消費者必須加到這個清單,並且**只從本 module import** overflow 相關的 primitive,不允許自己複製。
|
|
180
196
|
|
|
@@ -183,7 +199,6 @@ return (
|
|
|
183
199
|
## 反向引用
|
|
184
200
|
|
|
185
201
|
- `hooks/use-overflow-items.ts` — 底層 scroll / overflow 計算 hooks(本 module re-export)
|
|
186
|
-
- `CLAUDE.md`「選擇 / 狀態視覺」規則 — 為什麼 overflow trigger 不該用 selection-like 視覺
|
|
187
202
|
- `components/Button/button.tsx` — text variant 的 canonical 樣式
|
|
188
203
|
- `components/DropdownMenu/dropdown-menu.spec.md` — menu item 的 selection 指示器規則
|
|
189
204
|
|
|
@@ -191,11 +206,6 @@ return (
|
|
|
191
206
|
|
|
192
207
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
193
208
|
|
|
209
|
+
- `chip.spec.md`
|
|
194
210
|
- `file-viewer.spec.md`
|
|
195
211
|
- `overflow-indicator.spec.md`
|
|
196
|
-
|
|
197
|
-
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
198
|
-
|
|
199
|
-
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
200
|
-
|
|
201
|
-
- `chip.spec.md`
|
|
@@ -34,6 +34,7 @@ Dialog 和 Popover 的**結構化 sub-components 共用 primitive**——提供
|
|
|
34
34
|
- **永遠套 `flex-1 min-h-0 overflow-y-auto`**(viewport-aware scroll:視窗太小時 body 內捲動;非 flex-col parent 內 flex-1/min-h-0 為 no-op,backward compat)。2026-05-31 infra-audit 修:原寫「無額外 flex 屬性」與 code(overlay-surface.tsx:168 恆套)矛盾。consumer 依浮層類型:
|
|
35
35
|
- **Popover**:多數 bare consume,padding 即是總 padding
|
|
36
36
|
- **Dialog / Sheet**:consumer **不直接 wrap SurfaceBody**——走 ScrollArea canonical(下節),padding 搬到 ScrollArea viewport 內層 div
|
|
37
|
+
- **邊界**:空 children → 不渲染 placeholder、無最小高度(定高浮層內仍被 `flex-1` 撐滿剩餘空間);viewport 極小 → 見「Viewport-aware scroll chain invariant」節(body 內捲動);loading / error / empty 屬 consumer 內容層職責,非本 structural pattern(見 `dialog.spec.md`「狀態處理的職責邊界」)
|
|
37
38
|
|
|
38
39
|
---
|
|
39
40
|
|
|
@@ -112,7 +113,7 @@ chrome 邊 ─ hover bg 左邊 ─────── [ loose breathing ] ──
|
|
|
112
113
|
| Slack channel list | 0 | loose | ✓ |
|
|
113
114
|
| Raycast / Spotlight | 0 | loose | ✓ |
|
|
114
115
|
| VS Code Quick Pick | 0 | loose | ✓ |
|
|
115
|
-
| Material M3 / Polaris Modal+List | px only | item inset | ✗(另一合法家族,鬆散版)|
|
|
116
|
+
| Material M3 / Polaris Modal+List | px only | item inset | ✗(另一合法家族,鬆散版)| <!-- @benchmark-unverified -->
|
|
116
117
|
|
|
117
118
|
本 DS 選 Linear-family。
|
|
118
119
|
|
|
@@ -171,7 +172,6 @@ list 不再是 body 唯一 region → **不該撤 body chrome padding**(撤了
|
|
|
171
172
|
- Item `px=0` 讓 content 直接觸 hover bg 邊(content-inside-bg breathing 違反)
|
|
172
173
|
- list outer 重複 `py-4` + item 各自 `py-2`(過鬆)
|
|
173
174
|
- 不對稱 padding 無 rationale
|
|
174
|
-
```
|
|
175
175
|
|
|
176
176
|
### SurfaceFooter
|
|
177
177
|
- `border-t border-divider`
|
|
@@ -193,7 +193,7 @@ Overlay family 的 header title typography 依「modal vs non-modal」分級,**
|
|
|
193
193
|
- **Modal**:title 是決策 anchor,user 必須 process → 需視覺重量(16px + 重字重)
|
|
194
194
|
- **Non-modal**:title 是輔助標籤,user 可忽略 → 輕量字級(14px)不搶 body content
|
|
195
195
|
|
|
196
|
-
**世界級對照**(7 家 DS 同 split):
|
|
196
|
+
**世界級對照**(7 家 DS 同 split): <!-- @benchmark-unverified -->
|
|
197
197
|
| DS | Modal title | Non-modal popover title |
|
|
198
198
|
|----|-------------|------------------------|
|
|
199
199
|
| Material M3 | Headline 24 / body 16 | Smaller 14-16 |
|
|
@@ -216,7 +216,7 @@ Overlay family 的 header title typography 依「modal vs non-modal」分級,**
|
|
|
216
216
|
|
|
217
217
|
## 為什麼 SurfaceHeader 是 padding-based(而非 fixed-h)(2026-04-22 設計原則)
|
|
218
218
|
|
|
219
|
-
**SurfaceHeader 永遠 padding-based**(`py-tight
|
|
219
|
+
**SurfaceHeader 永遠 padding-based**(`py-tight`,不鎖固定高度)。Header 高度 = `max(title line-height, button slot)+ 2×py`,**slot 設計為 ≤ title line-height** 讓 title 主導 → header 高度 = title 決定(slot 縮位 / CSS var 參數化等實作細節詳下方「Chrome dismiss size canonical」)。SurfaceFooter **不**套 unbounded slot trick(僅 padding + `border-t`,慣例放 bounded action buttons)。**語義宣告**:
|
|
220
220
|
|
|
221
221
|
**Padding-based 宣告**:「本 chrome **可以成長** — title 可換行、可附 subtitle / description」。適用 overlay family(Dialog / Sheet / Popover / Coachmark),這些都是 modal/semi-modal 情境,title 有可能長(例如「確認要永久刪除 Marketing Q4 Campaign 這個專案嗎?」兩行 title),或有 subtitle(「這個操作無法復原」補充說明)。
|
|
222
222
|
|
|
@@ -230,7 +230,7 @@ Overlay family 的 header title typography 依「modal vs non-modal」分級,**
|
|
|
230
230
|
- **會** → padding-based(SurfaceHeader 或自刻 py-tight)
|
|
231
231
|
- **不會** → fixed-h(`h-[var(--chrome-header-height)]`)
|
|
232
232
|
|
|
233
|
-
**完整 canonical + 世界級對照**:`tokens/uiSize/uiSize.spec.md
|
|
233
|
+
**完整 canonical + 世界級對照**:`tokens/uiSize/uiSize.spec.md`「Chrome header 選型 canonical」節(含「2 種 pattern 對照」)
|
|
234
234
|
|
|
235
235
|
---
|
|
236
236
|
|
|
@@ -238,7 +238,7 @@ Overlay family 的 header title typography 依「modal vs non-modal」分級,**
|
|
|
238
238
|
|
|
239
239
|
> **背景**:Popover / HoverCard 是「輕量浮層」(chrome 45 / slot 20 / title 小一級 / density 鎖 md);Dialog / Sheet 是「heavy commitment chrome」(chrome 48 / slot 24)。Per-surface body+footer button/field default size 跟「輕量 vs heavy」分化對應。
|
|
240
240
|
>
|
|
241
|
-
> **依據**:Linear / Notion / Airtable / Carbon / Material Menu / Atlassian Popup popover bodies 共識用 sm-density;Material AlertDialog / Carbon Modal / shadcn Dialog 共識用 md。world-class 雙派分明依「浮層密度」分。
|
|
241
|
+
> **依據**:Linear / Notion / Airtable / Carbon / Material Menu / Atlassian Popup popover bodies 共識用 sm-density;Material AlertDialog / Carbon Modal / shadcn Dialog 共識用 md。world-class 雙派分明依「浮層密度」分。 <!-- @benchmark-unverified -->
|
|
242
242
|
|
|
243
243
|
| Overlay | Header chrome | Body field controls | Body inline action(drag handle / trash / +CTA)| Footer action Buttons |
|
|
244
244
|
|--|--|--|--|--|
|
|
@@ -305,9 +305,11 @@ const CHROME_UNBOUNDED_SLOT =
|
|
|
305
305
|
```tsx
|
|
306
306
|
// Dialog / Sheet / Popover / Coachmark(透過 SurfaceHeader)
|
|
307
307
|
<Button data-dismiss iconOnly dismiss size="sm" startIcon={X} aria-label="關閉" />
|
|
308
|
+
// `dismiss` prop → Button 自動標 `data-unbounded`(縮位 trick 判定詞);`data-dismiss` 用途不同,
|
|
309
|
+
// 是 openAutoFocus 排除 marker(popover.tsx / dialog.tsx autofocus selector)。
|
|
308
310
|
// SurfaceHeader 自動套負 my,無需 consumer 手動 y 調整
|
|
309
311
|
|
|
310
|
-
// Header 若塞 bounded button(primary sm)→
|
|
312
|
+
// Header 若塞 bounded button(primary sm)→ Button 不自動標 `data-unbounded`,不套負 my → 自然長高
|
|
311
313
|
<Button variant="primary" size="sm">套用</Button>
|
|
312
314
|
|
|
313
315
|
// Notification banner(Notice / Alert / Toast):px-4 py-3 fixed,dismiss 用 xs 簡化(無 margin trick)
|
|
@@ -315,13 +317,28 @@ const CHROME_UNBOUNDED_SLOT =
|
|
|
315
317
|
```
|
|
316
318
|
|
|
317
319
|
**Consumer 實際高度範例**:
|
|
318
|
-
- Dialog header 只有 title + close X(sm
|
|
319
|
-
- Dialog header 有 refresh/share/close
|
|
320
|
-
- Dialog header 塞 primary sm(
|
|
320
|
+
- Dialog header 只有 title + close X(sm,`dismiss` → 自動 `data-unbounded`)→ layout 佔位 24 → header = 48 md / 56 lg ✓
|
|
321
|
+
- Dialog header 有 refresh/share(text variant)/ close(dismiss)sm → 全自動標 `data-unbounded` → 全部 layout 佔位 24 → header 仍 48/56
|
|
322
|
+
- Dialog header 塞 primary sm(bounded,無 `data-unbounded`)→ primary layout 佔 28 → header = 52 md(自然長)
|
|
321
323
|
- Popover header 同 pattern:48 md / 56 lg
|
|
322
324
|
|
|
323
325
|
**Rationale**:Unbounded 無 bg/border → 2×py-tight 過大,縮 layout 佔位至 xs(24)讓 header = chrome-header-height = 48 自然閉合,跟 Sidebar / page header / top bar 對齊。Bounded 自帶視覺重量,自然長到 52+,跟 footer 一致。
|
|
324
326
|
|
|
327
|
+
**共通 rationale**(全 overlay + banner 家族):corner close X 屬 **action group region**,必用 `<Button>` primitive(不自刻 `<button><X /></button>` 繞 DS token / a11y,不用 `ItemInlineActionButton`)。
|
|
328
|
+
|
|
329
|
+
**本 session 震盪歷史備忘(M12 FP 記憶)**:
|
|
330
|
+
- ❌ v1「chrome dismiss 全 xs(DS-wide 統一)」→ 錯:過度簡化 rationale
|
|
331
|
+
- ❌ v2「三家族 modal sm / non-modal xs / banner xs」→ 錯:overlay 內部不必分化
|
|
332
|
+
- ❌ v3「overlay 統一 sm + min-h chrome-header-height 強鎖 48/56」→ 錯:強鎖會讓 bounded button 被鎖死 slot
|
|
333
|
+
- ❌ v4「padding-based + unbounded=xs / bounded=natural」→ 錯:xs 縮小 button 連 touch target 也變 24(違反 a11y / user 意圖)
|
|
334
|
+
- ✅ v5「padding-based + unbounded `data-unbounded` 套負 my(native size sm 不變)/ bounded natural」→ 對:button native size 與 touch target 保 sm,僅 layout 佔位縮回 24,48/56 chrome-header-height 自然達成
|
|
335
|
+
|
|
336
|
+
**SSOT 關聯**:
|
|
337
|
+
- `tokens/uiSize/uiSize.spec.md`「--chrome-header-height」+ `globals.css` 聲明(md=3rem / lg=3.5rem)
|
|
338
|
+
- `tokens/layoutSpace/layoutSpace.spec.md` tight = 12 md / 16 lg
|
|
339
|
+
- `patterns/element-anatomy/inline-action.spec.md`「Dismiss canonical — X close only」
|
|
340
|
+
- `components/Button/button.spec.md`「Dismiss 視覺類」+ unbounded / bounded 判斷
|
|
341
|
+
|
|
325
342
|
---
|
|
326
343
|
|
|
327
344
|
## Viewport-aware scroll chain invariant(2026-05-04 K11 升 SSOT)
|
|
@@ -343,21 +360,6 @@ const CHROME_UNBOUNDED_SLOT =
|
|
|
343
360
|
|
|
344
361
|
**DS-wide consumer 必檢點**:`grep '<PopoverContent\|<HoverCardContent\|<DialogContent\|<SheetContent'` 內第一層 wrapper 是否含 `flex flex-col h-full`(若該 panel 用 SurfaceBody)。Hook `check_pattern_invariants.sh` C.1(原 `check_overlay_panel_scroll_chain.sh` 已 folded;2026-05-31 infra-audit 修 dangling ref)機械化攔截。
|
|
345
362
|
|
|
346
|
-
**共通 rationale**(全 overlay + banner 家族):corner close X 屬 **action group region**,必用 `<Button>` primitive(不自刻 `<button><X /></button>` 繞 DS token / a11y,不用 `ItemInlineActionButton`)。
|
|
347
|
-
|
|
348
|
-
**本 session 震盪歷史備忘(M12 FP 記憶)**:
|
|
349
|
-
- ❌ v1「chrome dismiss 全 xs(DS-wide 統一)」→ 錯:過度簡化 rationale
|
|
350
|
-
- ❌ v2「三家族 modal sm / non-modal xs / banner xs」→ 錯:overlay 內部不必分化
|
|
351
|
-
- ❌ v3「overlay 統一 sm + min-h chrome-header-height 強鎖 48/56」→ 錯:強鎖會讓 bounded button 被鎖死 slot
|
|
352
|
-
- ❌ v4「padding-based + unbounded=xs / bounded=natural」→ 錯:xs 縮小 button 連 touch target 也變 24(違反 a11y / user 意圖)
|
|
353
|
-
- ✅ v5「padding-based + unbounded `data-dismiss` 套負 my(native size sm 不變)/ bounded natural」→ 對:button native size 與 touch target 保 sm,僅 layout 佔位縮回 24,48/56 chrome-header-height 自然達成
|
|
354
|
-
|
|
355
|
-
**SSOT 關聯**:
|
|
356
|
-
- `tokens/uiSize/uiSize.spec.md`「--chrome-header-height」+ `globals.css` 聲明(md=3rem / lg=3.5rem)
|
|
357
|
-
- `tokens/layoutSpace/layoutSpace.spec.md` tight = 12 md / 16 lg
|
|
358
|
-
- `patterns/element-anatomy/inline-action.spec.md`「Dismiss canonical — X close only」
|
|
359
|
-
- `components/Button/button.spec.md`「Dismiss 視覺類」+ unbounded / bounded 判斷
|
|
360
|
-
|
|
361
363
|
---
|
|
362
364
|
|
|
363
365
|
## Control + List 視覺對稱原則 → SSOT 規則 3 補充
|
|
@@ -402,6 +404,16 @@ overlay-surface 是 **layout pattern**(`SurfaceHeader` / `SurfaceBody` / `Surfac
|
|
|
402
404
|
- **Toast / Alert**(Family 2 List item 視覺對齊):那是 row-item layout 不是 surface-section,不要套本 pattern。
|
|
403
405
|
- **Tooltip**(純文字短提示):無結構化需求,不包 Header/Body/Footer。
|
|
404
406
|
- **HoverCard**(自由組合互動浮層):目前 consumer 自行組合內容,視未來是否引入 Header/Body/Footer 需求再納入 consumer。
|
|
407
|
+
- **近親分界**:固定高度 app chrome(Sidebar header / toolbar / top bar)屬 **Chrome header(Fixed-h)家族**,用 `<ChromeHeader>` 非 SurfaceHeader——跨家族契約 SSOT 見 `patterns/header-canonical/header-canonical.spec.md`。
|
|
408
|
+
|
|
409
|
+
## 常見誤解
|
|
410
|
+
|
|
411
|
+
| 誤解 | 正解 |
|
|
412
|
+
|------|------|
|
|
413
|
+
| 「Dialog / Sheet body 直接 wrap SurfaceBody」 | 否——走 ScrollArea canonical(見「Body overflow canonical」) |
|
|
414
|
+
| 「list 場景需要 `flush` variant」 | 已撤回;用 className override + 自管 list wrapper(見「List-as-region」) |
|
|
415
|
+
| 「SurfaceHeader 自帶 close X」 | 否——close 由 consumer 包 `<Button iconOnly dismiss>`(見「不屬本 primitive 的職責」) |
|
|
416
|
+
| 「SurfaceFooter 也套 unbounded slot trick」 | 否——footer 慣例放 bounded action buttons,僅 padding + border-t |
|
|
405
417
|
|
|
406
418
|
---
|
|
407
419
|
|
|
@@ -416,20 +428,12 @@ overlay-surface 是 **layout pattern**(`SurfaceHeader` / `SurfaceBody` / `Surfac
|
|
|
416
428
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
417
429
|
|
|
418
430
|
- `app-shell.spec.md`
|
|
419
|
-
- `header-canonical.spec.md`
|
|
420
|
-
- `motion.spec.md`
|
|
421
|
-
|
|
422
|
-
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
423
|
-
|
|
424
|
-
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
425
|
-
|
|
426
431
|
- `coachmark.spec.md`
|
|
432
|
+
- `dialog.spec.md`
|
|
427
433
|
- `element-anatomy.spec.md`
|
|
428
|
-
- `notice.spec.md`
|
|
429
|
-
- `sheet.spec.md`
|
|
430
|
-
|
|
431
|
-
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
432
|
-
|
|
433
|
-
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
434
|
-
|
|
435
434
|
- `file-item.spec.md`
|
|
435
|
+
- `header-canonical.spec.md`
|
|
436
|
+
- `motion.spec.md`
|
|
437
|
+
- `popover.spec.md`
|
|
438
|
+
- `sheet.spec.md`
|
|
439
|
+
- `uiSize.spec.md`
|
|
@@ -78,8 +78,9 @@ export interface SurfaceHeaderProps
|
|
|
78
78
|
* Tabs row slot(2026-05-18 加 per header-canonical.spec.md W2/W4 真實能用 + user-mandated fix)。
|
|
79
79
|
* 提供時 SurfaceHeader 自動 column 結構:
|
|
80
80
|
* row 1 = children(title + actions/dismiss,px-loose py-tight)
|
|
81
|
-
* row 2 = tabsSlot 包在
|
|
82
|
-
*
|
|
81
|
+
* row 2 = tabsSlot 包在 `HEADER_TABS_SLOT_WRAPPER_CLASS` wrapper(v3:wrapper 自身
|
|
82
|
+
* 無 px / 無 border;TabsList 自己 w-full + px-loose,border-b 延展全寬 —
|
|
83
|
+
* W1 視覺一條線。詳 chrome-header.tsx v3 fix 註解)
|
|
83
84
|
*
|
|
84
85
|
* Consumer 傳:`tabsSlot={<TabsList>...</TabsList>}`,TabsContent 仍放 DialogBody 內。
|
|
85
86
|
* `<Tabs>` root 必須 wrap 整 DialogContent(Radix TabsList ↔ TabsContent 同 root 連動)。
|
|
@@ -56,6 +56,8 @@ benchmark:
|
|
|
56
56
|
|
|
57
57
|
## 視覺 canonical(對齊 DataTable v11)
|
|
58
58
|
|
|
59
|
+
取值依據:7px 命中區 / 1px line 非自創——來自下方「世界級對照細節」5 家共識(hit zone 7-8px fingertip-friendly / 1px line non-intrusive)+ DataTable v11 已 ship 的既有 canonical(本 primitive 抽取自它,M17)。
|
|
60
|
+
|
|
59
61
|
- **命中區**:7px 寬(horizontal)/ 高(vertical),`-3px` outward offset 跨 boundary 抓得到
|
|
60
62
|
- **Visual line**:1px,positioned `right-[3px]` / `bottom-[3px]`,default full-extent
|
|
61
63
|
- **idle**:`bg-divider`
|
|
@@ -80,6 +82,13 @@ benchmark:
|
|
|
80
82
|
- Image crop 邊界(專用 `<ImageCropTool>` 領域)
|
|
81
83
|
- Splitter pane (`resizable-panes` style)— 雙端 drag with linked state(future 評估抽 `<SplitPane>` 上層 primitive 消費本 handle)
|
|
82
84
|
|
|
85
|
+
## 邊界案例
|
|
86
|
+
|
|
87
|
+
- **Disabled**:仍渲染 1px line(`bg-divider`,無 hover affordance),但無 cursor、無 `select-none`、不掛 role / orientation / label(見 a11y 段)。disabled 切換不改 DOM 結構,只增減 attribute。
|
|
88
|
+
- **拖到 min / max 卡住**:primitive 不持 width state(不耦合 drag math),邊界 clamp 與卡住回饋由 consumer 的 resize handler 管;`isResizing` 期間 line 維持 `bg-primary` 不另示警。
|
|
89
|
+
- **同列多 handle 並存**(多欄 column resize):各 handle 為獨立 `<span>`,無互相協調;`-3px` outward offset 使相鄰欄命中區可能相接,先命中者(DOM 順序 / pointer target)收事件,衝突仲裁屬 consumer drag math。
|
|
90
|
+
- **RTL**:未特化——命中區與 line 用 physical `left/right` 定位(`resize-handle.tsx`),RTL 鏡像需另案。
|
|
91
|
+
|
|
83
92
|
## Roadmap(用 user 既有的 v2 framing)
|
|
84
93
|
|
|
85
94
|
| Phase | Scope | Status |
|
|
@@ -96,7 +105,7 @@ Phase 2-4 需獨立 RFC + 各別 user approval,本 spec 只 ship Phase 1 + 鎖
|
|
|
96
105
|
| DS / Library | 命中區 | Line | Cursor | a11y |
|
|
97
106
|
|---|---|---|---|---|
|
|
98
107
|
| **AG Grid** | 7-8px | 1px primary on drag | col-resize | role="separator" |
|
|
99
|
-
| **Material X-DataGrid** | column-separator(resize 命中區)~8px | 1px hairline(`MuiDataGrid-iconSeparator` 是純裝飾分隔 icon,非命中區也非 a11y 來源)| col-resize | aria-label "Resize column" |
|
|
108
|
+
| **Material X-DataGrid** | column-separator(resize 命中區)~8px | 1px hairline(`MuiDataGrid-iconSeparator` 是純裝飾分隔 icon,非命中區也非 a11y 來源)| col-resize | aria-label "Resize column" | <!-- @benchmark-unverified: see frontmatter benchmark list for canonical DS source URL -->
|
|
100
109
|
| **Notion(column / sidebar)** | ~6-8px | 1px line | col-resize | (DOM-only,無 role)|
|
|
101
110
|
| **VS Code** | 8px(activity bar)| bg highlight on drag | col-resize | aria-label "Resize" |
|
|
102
111
|
| **Figma** | 8px | 1px line | col-resize | role separator |
|
|
@@ -4,7 +4,7 @@ import * as React from 'react'
|
|
|
4
4
|
import { ResizeHandle } from './resize-handle'
|
|
5
5
|
|
|
6
6
|
const meta: Meta<typeof ResizeHandle> = {
|
|
7
|
-
title: 'Design System/Patterns/
|
|
7
|
+
title: 'Design System/Patterns/Resize Handle',
|
|
8
8
|
component: ResizeHandle,
|
|
9
9
|
parameters: { layout: 'centered' },
|
|
10
10
|
}
|
|
@@ -12,10 +12,10 @@ export default meta
|
|
|
12
12
|
|
|
13
13
|
type Story = StoryObj<typeof ResizeHandle>
|
|
14
14
|
|
|
15
|
-
// Demo
|
|
15
|
+
// Demo:DataTable「訂單金額」欄頭右緣的 resize handle(spec 首要 consumer 場景)
|
|
16
16
|
const ColumnDemo: React.FC<{ isResizing?: boolean; disabled?: boolean }> = ({ isResizing, disabled }) => (
|
|
17
17
|
<div className="relative inline-block bg-surface border border-divider px-[var(--layout-space-loose)] py-2 w-[200px]">
|
|
18
|
-
<span className="text-body"
|
|
18
|
+
<span className="text-body font-medium">訂單金額</span>
|
|
19
19
|
<ResizeHandle
|
|
20
20
|
direction="horizontal"
|
|
21
21
|
position="end"
|
|
@@ -31,7 +31,7 @@ export const Default: Story = {
|
|
|
31
31
|
render: () => (
|
|
32
32
|
<div className="flex flex-col gap-4">
|
|
33
33
|
<ColumnDemo />
|
|
34
|
-
<p className="text-caption text-fg-
|
|
34
|
+
<p className="text-caption text-fg-secondary">滑鼠移到右邊緣時,游標變成左右拖拉樣式,分隔線顏色加深提示可以拖拉</p>
|
|
35
35
|
</div>
|
|
36
36
|
),
|
|
37
37
|
}
|
|
@@ -41,7 +41,7 @@ export const Dragging: Story = {
|
|
|
41
41
|
render: () => (
|
|
42
42
|
<div className="flex flex-col gap-4">
|
|
43
43
|
<ColumnDemo isResizing />
|
|
44
|
-
<p className="text-caption text-fg-
|
|
44
|
+
<p className="text-caption text-fg-secondary">拖拉進行中時,整條分隔線變成主色高亮,讓使用者清楚知道正在調整尺寸</p>
|
|
45
45
|
</div>
|
|
46
46
|
),
|
|
47
47
|
}
|
|
@@ -51,7 +51,7 @@ export const Disabled: Story = {
|
|
|
51
51
|
render: () => (
|
|
52
52
|
<div className="flex flex-col gap-4">
|
|
53
53
|
<ColumnDemo disabled />
|
|
54
|
-
<p className="text-caption text-fg-
|
|
54
|
+
<p className="text-caption text-fg-secondary">停用時不顯示拖拉游標,輔助技術也會忽略此元件,分隔線維持原本的灰色</p>
|
|
55
55
|
</div>
|
|
56
56
|
),
|
|
57
57
|
}
|
|
@@ -60,7 +60,7 @@ export const Vertical: Story = {
|
|
|
60
60
|
name: '垂直方向',
|
|
61
61
|
render: () => (
|
|
62
62
|
<div className="relative bg-surface border border-divider px-[var(--layout-space-loose)] py-2 w-[300px] h-[120px]">
|
|
63
|
-
<span className="text-body"
|
|
63
|
+
<span className="text-body">查詢結果面板</span>
|
|
64
64
|
<ResizeHandle
|
|
65
65
|
direction="vertical"
|
|
66
66
|
position="end"
|
package/src/tokens/README.md
CHANGED
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
|
|
17
17
|
## 當前居民
|
|
18
18
|
|
|
19
|
-
`color/` / `typography/` / `uiSize/` / `layoutSpace/` / `density/` / `elevation/` / `radius/` / `opacity/`
|
|
19
|
+
`color/` / `typography/` / `uiSize/` / `layoutSpace/` / `density/` / `elevation/` / `radius/` / `opacity/` / `motion/`
|
|
20
|
+
|
|
21
|
+
例外居民:`categorical-color.ts` 住 tokens/ 根層(非 folder)— `CATEGORICAL_HUES` 的 TypeScript SSOT,由 `color/color.spec.md` 指認(Tag / Avatar 等 categorical 色相消費)。
|
|
20
22
|
|
|
21
23
|
## Consumer scope:public vs internal token(2026-05-17 codified)
|
|
22
24
|
|
|
@@ -24,8 +26,9 @@
|
|
|
24
26
|
|---|---|---|
|
|
25
27
|
| `color/` / `typography/` / `radius/` / `elevation/` / `opacity/` / `layoutSpace/` / `density/` | **Public** — consumer-layer(`src/app` / `src/explorations`)直接消費 | UI 開發者寫 `text-body` / `bg-primary` / `--layout-space-loose` 等 |
|
|
26
28
|
| `uiSize/`(`--field-height-*` / `--tab-height-*` / `--table-row-*` / `--tree-indent-*`)| **Internal primitive** — DS 內部 component 消費,consumer 不直接用 | Button / Input / Tabs / DataTable / TreeView 等 primitive 內部消費;consumer 用 `<Button size="sm">` 不用 `h-[var(--field-height-sm)]` |
|
|
29
|
+
| `motion/`(`--hover-delay-*`)| **Internal primitive** — DS overlay 元件內部消費,consumer 不直接用 | Tooltip / HoverCard / Avatar / OverflowIndicator 的 hover open / close delay;consumer 用元件預設,不直接讀 token |
|
|
27
30
|
|
|
28
|
-
**Internal token 規則**(`uiSize/` 適用):
|
|
31
|
+
**Internal token 規則**(`uiSize/` / `motion/` 適用):
|
|
29
32
|
- **不必補 Storybook stories**(consumer 不直接看;審查 grep 結果:`src/app` + `src/explorations` 0 個檔案直接消費 uiSize token,2026-05-17 verified)
|
|
30
33
|
- 但仍有 `.spec.md` codify family / 命名 / 派生公式
|
|
31
34
|
- 出現在 consumer-layer code = anti-pattern(該用 component prop 不該 raw token)
|