@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
|
@@ -89,7 +89,7 @@ Search 的角色**不由「產品核心是不是搜尋」決定**,而由——
|
|
|
89
89
|
┌──────────────────────────┐ ┆ ┌──────────────────────┐
|
|
90
90
|
│ 業務操作群組 │ ┆ │ 工具操作 溢出 │
|
|
91
91
|
└──────────────────────────┘ ┆ └──────────────────────┘
|
|
92
|
-
|
|
92
|
+
業務層(Operations) 工具層(Utilities)
|
|
93
93
|
```
|
|
94
94
|
|
|
95
95
|
兩種對齊的差異只在業務層內部排序方向,工具層位置不變:
|
|
@@ -142,7 +142,7 @@ Search 的角色**不由「產品核心是不是搜尋」決定**,而由——
|
|
|
142
142
|
|
|
143
143
|
| Context | Variant | Cite |
|
|
144
144
|
|---|---|---|
|
|
145
|
-
| **DataTable / Kanban / list 等 dense always-visible data toolbar** filter / sort / column controls | **`text` + pressed** + iconOnly + Popover wrap real panel(DataTableFilterPanel / DataTableSortManager) | `data-table.stories.tsx#WithBulkActions
|
|
145
|
+
| **DataTable / Kanban / list 等 dense always-visible data toolbar** filter / sort / column controls | **`text` + pressed** + iconOnly + Popover wrap real panel(DataTableFilterPanel / DataTableSortManager) | `data-table.stories.tsx#WithBulkActions` SSOT |
|
|
146
146
|
| **Standalone / labeled** sole state carrier(toolbar 外 / 單獨 filter UI 非 DataTable context) | `tertiary` + pressed | 視 case |
|
|
147
147
|
| 純動作工具(無 state)/ 低重量輔助 | `text` | 既有 default |
|
|
148
148
|
|
|
@@ -283,7 +283,7 @@ Icon 的目的是幫助辨識,不是視覺對稱。
|
|
|
283
283
|
|
|
284
284
|
**分隔線語意判斷**:遵守以上規則後孤立自然不出現。若出現孤立,先問「這條分隔線有語意理由嗎?」而非接受孤立。
|
|
285
285
|
|
|
286
|
-
**切換按鈕**:`text
|
|
286
|
+
**切換按鈕**:`text` + `pressed` toggle——依預設(未啟用)狀態決定位置與分隔線;切為啟用(`pressed=true`)時不移動、不調整分隔線(啟用態不是 variant,見三節)。
|
|
287
287
|
|
|
288
288
|
---
|
|
289
289
|
|
|
@@ -331,6 +331,7 @@ Icon 的目的是幫助辨識,不是視覺對稱。
|
|
|
331
331
|
### 共通規則
|
|
332
332
|
|
|
333
333
|
- 溢出按鈕不構成工具層。工具層需要有固定工具按鈕(⚙ 設定、↺ 刷新、⤢ 全螢幕等)才存在,只有 ··· 不算
|
|
334
|
+
- 溢出選單本體的浮層行為(超出 viewport 的 collision 翻轉 / 內容過長捲動 / Esc 關閉 / 鍵盤導覽)由 `DropdownMenu` own(Radix 預設),本 pattern 不重定義
|
|
334
335
|
- **如果操作列出現大量溢出按鈕,這是資訊架構的問題,應回到功能規劃層重新設計**
|
|
335
336
|
|
|
336
337
|
---
|
|
@@ -425,7 +426,7 @@ Icon 的目的是幫助辨識,不是視覺對稱。
|
|
|
425
426
|
├── 移除它,使用者無法完成任務 → 業務操作
|
|
426
427
|
│ ├── 一般操作 → label 或 icon + label
|
|
427
428
|
│ ├── 含多個子選項 → + endIcon={ChevronDown}
|
|
428
|
-
│ └── 可配置資料操作 → text
|
|
429
|
+
│ └── 可配置資料操作 → text + pressed(未配置 pressed=false / 已配置 pressed=true)
|
|
429
430
|
│ └── 需要更強可操作性提示 → tertiary(未配置)
|
|
430
431
|
└── 跟任務無關,服務環境 → 工具操作 → icon-only + tooltip
|
|
431
432
|
|
|
@@ -443,15 +444,21 @@ Icon 的目的是幫助辨識,不是視覺對稱。
|
|
|
443
444
|
|
|
444
445
|
---
|
|
445
446
|
|
|
447
|
+
## 邊界與分權
|
|
448
|
+
|
|
449
|
+
本 pattern 是組合指南(無 runtime 元件)——元件級邊界(無可見 action 時不渲染空操作列 / loading / disabled 狀態)與 a11y 細節由消費端元件 own:icon-only 必設 `aria-label` + tooltip(見第四節,Button own);溢出選單的鍵盤 / focus 行為由 `DropdownMenu` own(見第六節)。
|
|
450
|
+
|
|
446
451
|
## 關聯文件
|
|
447
452
|
|
|
448
453
|
- `button.spec.md`:Button 元件層的設計原則
|
|
454
|
+
- `../../components/DropdownMenu/dropdown-menu.spec.md`:溢出選單浮層行為 SSOT(collision / 捲動 / Esc / 鍵盤)
|
|
449
455
|
|
|
450
456
|
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
451
457
|
|
|
452
458
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
453
459
|
|
|
454
460
|
- `bulk-action-bar.spec.md`
|
|
461
|
+
- `button.spec.md`
|
|
455
462
|
- `calendar.spec.md`
|
|
456
463
|
- `data-table.spec.md`
|
|
457
464
|
- `file-viewer.spec.md`
|
|
@@ -89,7 +89,7 @@ Switch / Checkbox / Radio / Avatar / Badge / CircularProgress / ProgressBar / Sk
|
|
|
89
89
|
Dialog / Sheet / Popover / Tooltip / HoverCard / Coachmark / Command / SelectMenu / DropdownMenu
|
|
90
90
|
|
|
91
91
|
**共同約束**:
|
|
92
|
-
-
|
|
92
|
+
- 結構化 chrome 浮層(Dialog / Sheet / Popover;Coachmark 經 `PopoverHeader`)消費 `patterns/overlay-surface/` 的 `SurfaceHeader` / `SurfaceBody` / `SurfaceFooter`(padding SSOT;實際 consumer 名單見 `overlay-surface.spec.md`「Consumers」)。menu 形浮層(Command / SelectMenu / DropdownMenu)走自有 menu surface canonical(`floatingLayerClass` + `py-2`);Tooltip(純文字 surface)與 HoverCard(behavior primitive,內容由 consumer 排)不消費 Surface*(2026-05-18 M18 anchor)
|
|
93
93
|
- elevation 走 `--elevation-200`+ 的 overlay layer
|
|
94
94
|
- dismiss 走 `onClose`(語意:關閉 overlay session,見 CLAUDE.md 命名 canonical)
|
|
95
95
|
- 部分 overlay 有 modal vs non-modal 分流(Dialog / Sheet modal;Popover / HoverCard non-modal)
|
|
@@ -110,9 +110,9 @@ Tabs / Accordion / DataTable / Calendar / Carousel / Breadcrumb / Sidebar / Prof
|
|
|
110
110
|
CheckboxGroup / RadioGroup / FieldGroup(若建)
|
|
111
111
|
|
|
112
112
|
**共同約束**:
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
- spec
|
|
113
|
+
- 自己**無視覺 chrome**(無 bg / border / radius)——僅渲染 `role="group"` layout 容器 + 提供 context(`CheckboxGroupContext` 等)給 descendant 同步 state;視覺由 descendant(Checkbox / RadioGroupItem / SelectionItem)承載
|
|
114
|
+
- RadioGroup 註記:兼任 Field 家族「承載欄位值的互動控件」(見 `field-controls.spec.md`「哪些元件吃 `<Field disabled>` cascade」),有 display / empty mode 內容渲染,其 anatomy story 因此含 ColorMatrix / SizeMatrix
|
|
115
|
+
- spec 應明文「本元件無自有視覺 chrome」;純 wrapper 的 anatomy story 最小集 = `Overview` + `Inspector`
|
|
116
116
|
|
|
117
117
|
---
|
|
118
118
|
|
|
@@ -141,7 +141,7 @@ Family 4 的 Input / Select 視覺對齊 Family 1 的 menu-item(高度、字體
|
|
|
141
141
|
|
|
142
142
|
## 新元件判斷流程
|
|
143
143
|
|
|
144
|
-
1. **垂直列表裡?** → Family 1(menu 容器)/ Family 2(頁面)
|
|
144
|
+
1. **垂直列表裡?** → Family 1(menu 容器)/ Family 2(頁面)——分界看「容器 + 閱讀模式」:浮層 menu 內掃視選擇 = Family 1(scanning),頁面上閱讀內容 = Family 2(reading);深度規格詳 `item-anatomy.spec.md` Family 1 / Family 2 章節
|
|
145
145
|
2. **單行可點擊 / 可讀的 pill?** → Family 3(action trigger or data indicator)
|
|
146
146
|
3. **單行可編輯資料?** → Family 4(必須視覺對齊 Family 1)
|
|
147
147
|
4. **都不是?** → **停下討論**——新 family 還是 self-contained
|
|
@@ -174,7 +174,7 @@ CLAUDE.md `# 4-Family Layout Model` 留概要 + 判斷流程(每 session signal)
|
|
|
174
174
|
|
|
175
175
|
Consumer 自建 chrome-like div 帶 bg/border/shadow 時,參考上表或就近取 `--layout-space-*` token。**具體值允許偏離,但 breathing 本身必存在**。
|
|
176
176
|
|
|
177
|
-
**世界級對照**(DS-wide 共識):
|
|
177
|
+
**世界級對照**(DS-wide 共識): <!-- @benchmark-unverified -->
|
|
178
178
|
- Material Design 3:`<mat-card-content>` 16dp / `<mat-dialog-content>` 24dp / Drawer section padding
|
|
179
179
|
- Polaris(Shopify):`<Page>` 16-20px / `<Card>` 16-20px / `<Modal>` 20px
|
|
180
180
|
- Atlassian:`<Box padding="space.200">` / `<ModalBody>` 24px / Drawer body padding
|
|
@@ -212,4 +212,6 @@ Consumer 自建 chrome-like div 帶 bg/border/shadow 時,參考上表或就近
|
|
|
212
212
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
213
213
|
|
|
214
214
|
- `calendar.spec.md`
|
|
215
|
+
- `field.spec.md`
|
|
216
|
+
- `item-anatomy.spec.md`
|
|
215
217
|
- `time-picker.spec.md`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!-- @benchmark-cited: D5 retrofit 2026-05-18 — verified 0 world-class DS claim in body; blanket retract removed. -->
|
|
2
2
|
|
|
3
|
-
# Inline Action
|
|
3
|
+
# Inline Action 設計原則(SSOT)
|
|
4
4
|
|
|
5
5
|
**Layout Family**:non-family(hosted inline element,無 own layout — 由宿主 family 1/4 提供)。
|
|
6
6
|
|
|
@@ -153,7 +153,7 @@ Q3. Row 多大?
|
|
|
153
153
|
| Menu / TreeView / SidebarMenuButton / SelectionItem suffix | Row inline flow | Inline Action | Inline with content |
|
|
154
154
|
| SidebarGroup header chevron | Aux toggle | Inline Action | Inline header toggle |
|
|
155
155
|
| Select ChevronDown / DatePicker Calendar / Combobox ChevronDown | Field chrome(裝飾)| **Decorative**(不是 action)| Click falls through;host 是 trigger |
|
|
156
|
-
| **FileItem compact**(row 24)| Row slot |
|
|
156
|
+
| **FileItem compact**(row 24)| Row slot | Button xs iconOnly(`var(--field-height-xs)` 容器 + data-unbounded 收斂到 1lh)| 2026-04-23 user 統一 rich + compact 同 primitive,不撐高 row(見 file-item.tsx 註解)|
|
|
157
157
|
| **FileItem rich**(row 56 sm/md rich)| Row slot | Button xs iconOnly(24 固定)| ≤ 24 cap,不放大 |
|
|
158
158
|
| **DataTable header cell internal**(sort indicator / ⌄ menu / filter funnel / pin)| Header cell host internal | **Inline Action**(`ItemInlineActionButton` asChild for DropdownMenu)| Embedded inline,跟 label 一體 |
|
|
159
159
|
| **DataTable body cell internal**(display endAction / clear / edit indicator)| Cell content host internal | **Inline Action**(自動繼承 Field family endAction)| Field display 元件已對齊 |
|
|
@@ -188,6 +188,11 @@ Row action 的 affordance 是「次要功能」,不是 primary CTA。Button chro
|
|
|
188
188
|
- 尺寸:`patterns/overlay-surface/overlay-surface.spec.md`「Chrome dismiss size canonical」(overlay sm + v5 trick / banner xs explicit)
|
|
189
189
|
- variant / divider:`patterns/action-bar/action-bar.spec.md`(corner action group)
|
|
190
190
|
|
|
191
|
+
### 邊界案例
|
|
192
|
+
|
|
193
|
+
- **帶可見文字 label 的觸發**:不是 Inline Action — Inline Action 必 icon-only(label 走 tooltip + `aria-label`,見「實作要求」);需要可見文字 → `Button`(text / tertiary)。
|
|
194
|
+
- **Dark mode**:hover / active / fg 全走 semantic token(`--neutral-hover` 等)自動 adapt,Inline Action 不 own dark token;colored host 的繼承色由 host token 處理(見 `tag.spec.md`)。
|
|
195
|
+
|
|
191
196
|
### Inline action 共用元件(`ItemInlineAction` / `ItemSuffix`)
|
|
192
197
|
|
|
193
198
|
Canonical 實作於 `item-anatomy.tsx`,匯出 `ItemInlineAction` / `ItemSuffix`(從 `RowSizeContext` 自動查 icon size / hover bg / tooltip / aria-label / fg-muted → foreground 全內建)。
|
|
@@ -199,7 +204,7 @@ Canonical 實作於 `item-anatomy.tsx`,匯出 `ItemInlineAction` / `ItemSuffix`(
|
|
|
199
204
|
<ItemSuffix hoverReveal>{actions.map((a) => <ItemInlineAction action={a} />)}</ItemSuffix>
|
|
200
205
|
```
|
|
201
206
|
|
|
202
|
-
**Host 走宣告式 API**(`inlineActions?: InlineActionConfig[]` / `endAction?: InlineActionConfig` prop)— 不自刻 button JSX。已遷移:`SidebarMenuButton` / `Input` / `NumberInput`(2026-05-31 infra-audit 修 stale status:verify input.tsx/number-input.tsx 已用 endAction InlineActionConfig)
|
|
207
|
+
**Host 走宣告式 API**(`inlineActions?: InlineActionConfig[]` / `endAction?: InlineActionConfig` prop)— 不自刻 button JSX。已遷移:`SidebarMenuButton` / `Input` / `NumberInput`(2026-05-31 infra-audit 修 stale status:verify input.tsx/number-input.tsx 已用 endAction InlineActionConfig)、`Tag`(2026-05-01:宣告式 `onRemove` + 內部 `ItemInlineActionButton`,見 tag.tsx)。`LinkInput` / `Combobox` 屬下方「不適用清單」(右側 chrome 獨佔),內建 inline action 已直接消費 `ItemInlineAction` primitive——無 pending。
|
|
203
208
|
|
|
204
209
|
### Dismiss canonical — X close only
|
|
205
210
|
|
|
@@ -224,7 +229,7 @@ Canonical 實作於 `item-anatomy.tsx`,匯出 `ItemInlineAction` / `ItemSuffix`(
|
|
|
224
229
|
|
|
225
230
|
#### `dismiss` prop 觸發條件(Button 限定)
|
|
226
231
|
|
|
227
|
-
**明示**:`<Button dismiss />`
|
|
232
|
+
**明示**:`<Button dismiss />`(唯一機械觸發;Button 無 `onClose` / `onDismiss` prop)→ 觸發弱化 override(icon fg-muted → hover foreground;variant 強制 text)。語意判準:點擊語意 = X close 才用 dismiss
|
|
228
233
|
|
|
229
234
|
**不觸發**:callback = `onRemove`(collection 操作)/ `onClear`(欄位清空)/ `onDelete` — 這些不是 dismiss 語意,Button 用對應 variant 即可
|
|
230
235
|
|
|
@@ -244,7 +249,7 @@ Tag solid / branded color variant(`red / blue / orange`)→ Tag 內 X **繼承 h
|
|
|
244
249
|
|
|
245
250
|
sonner toast auto-dismiss / Radix Dialog `DialogClose` wiring — 第三方只管 state logic(何時關),**我們渲染的視覺按鈕必套對應 canonical**(chrome corner → Button dismiss;chrome padding → Inline Action)。
|
|
246
251
|
|
|
247
|
-
hook `check_story_anatomy.sh` 規則 B 已在 stories 層攔 label Button 作 dismiss。
|
|
252
|
+
hook `check_story_invariants.sh` R1(原 `check_story_anatomy.sh` 規則 B,已 folded)已在 stories 層攔 label Button 作 dismiss。
|
|
248
253
|
|
|
249
254
|
---
|
|
250
255
|
|
|
@@ -279,7 +284,7 @@ hook `check_story_anatomy.sh` 規則 B 已在 stories 層攔 label Button 作 di
|
|
|
279
284
|
- `disabled / readonly` 模式 / Sidebar `collapsible=icon` 模式跟 config 一致(slot 也要被相同條件隱藏)
|
|
280
285
|
- Reveal 模式(`actionsReveal="hover"`)、絕對定位 chrome 跟 config 共用,consumer 不需重做
|
|
281
286
|
- Padding budget(Sidebar)slot mode 預設按 1 icon 寬度預留,多 icon 寬度需 consumer 自控 className
|
|
282
|
-
- 視覺一致性 by consumer — 但 **app-code 不可直接 import L3 primitive**(`ItemInlineActionButton` / `ItemInlineAction` / `RowSizeProvider`),由 `
|
|
287
|
+
- 視覺一致性 by consumer — 但 **app-code 不可直接 import L3 primitive**(`ItemInlineActionButton` / `ItemInlineAction` / `RowSizeProvider`),由 `check_canonical_propagation.sh` E.2(原 `check_l3_primitive_import`,已 folded,P0 BLOCK)攔截。需要視覺一致 inline-action button 時,(a) 用 host 自帶 slot(此 escape hatch)、(b) 用 `<Button iconOnly variant="text" />`、(c) 走 spec rationale 例外申請
|
|
283
288
|
|
|
284
289
|
**現況清單**(2026-04-25 補完):
|
|
285
290
|
- ✅ `Input.endSlot`
|
|
@@ -289,7 +294,7 @@ hook `check_story_anatomy.sh` 規則 B 已在 stories 層攔 label Button 作 di
|
|
|
289
294
|
|
|
290
295
|
**不適用清單**:DatePicker / Combobox / Select / TimePicker / LinkInput 右側由元件自帶 chrome 獨佔(Calendar / ChevronDown / Clock / ExternalLink 等 intrinsic affordance),不開放 consumer 放置 endAction/endSlot — 這些元件的 inline-action 需求走**內建 clear X**(consumer 透過 `clearable` prop opt-in)或 `<Button iconOnly />` 放在 Field 外側。Escape hatch canonical 不涵蓋此類 host(本質上非 consumer-facing slot)。
|
|
291
296
|
|
|
292
|
-
**世界級對照**:
|
|
297
|
+
**世界級對照**: <!-- @benchmark-unverified -->
|
|
293
298
|
- Material UI:`InputAdornment` ReactNode slot(無 config,純 escape hatch 派 — 我們的 90/10 比 Material 更有結構)
|
|
294
299
|
- Polaris:`TextField.connectedRight` ReactNode 只接 Button,但無 config-based 簡化,API 一致性差
|
|
295
300
|
- Ant Design:`Input.suffix` ReactNode slot,跟 Material 同派
|
|
@@ -301,20 +306,15 @@ hook `check_story_anatomy.sh` 規則 B 已在 stories 層攔 label Button 作 di
|
|
|
301
306
|
|
|
302
307
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
303
308
|
|
|
304
|
-
- `sheet.spec.md`
|
|
305
|
-
|
|
306
|
-
## 被引用(auto-maintained,Dim 3 reciprocal audit)
|
|
307
|
-
|
|
308
|
-
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
309
|
-
|
|
310
309
|
- `alert.spec.md`
|
|
311
310
|
- `breadcrumb.spec.md`
|
|
312
311
|
- `data-table.spec.md`
|
|
313
312
|
- `field-controls.spec.md`
|
|
313
|
+
- `item-anatomy.spec.md`
|
|
314
|
+
- `overlay-surface.spec.md`
|
|
314
315
|
- `popover.spec.md`
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
319
|
-
|
|
316
|
+
- `sheet.spec.md`
|
|
317
|
+
- `sidebar.spec.md`
|
|
320
318
|
- `tag.spec.md`
|
|
319
|
+
- `tree-view.spec.md`
|
|
320
|
+
- `uiSize.spec.md`
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline
|
|
1
|
+
<!-- @benchmark-cited: D5 retrofit 2026-05-18 — body claims marked per-claim @benchmark-unverified inline(M22(d) 顯式撤回;本檔 frontmatter 無 benchmark list,來源 URL 未補)。 -->
|
|
2
2
|
|
|
3
3
|
# Item Anatomy 設計原則(Family 1 + Family 2 row 結構 SSOT)
|
|
4
4
|
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
|
|
48
48
|
| 元件 | 繼承的部分 | 不繼承的部分 |
|
|
49
49
|
|------|----------|-----------|
|
|
50
|
-
| `Tabs`(Trigger) | gap-2 slot 間距、scanning 字體 tier、hover 行為 | 結構是**橫向 inline**(非垂直 row), 高度系統對標 Button, 無 prefix/content/suffix column
|
|
50
|
+
| `Tabs`(Trigger) | gap-2 slot 間距、scanning 字體 tier、hover 行為 | 結構是**橫向 inline**(非垂直 row), 高度系統對標 Button, 無 prefix/content/suffix column;詳細對標分析見 `components/Tabs/tabs.spec.md`「Tabs 不完全屬於 item-layout」段 |
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
@@ -141,9 +141,9 @@
|
|
|
141
141
|
|
|
142
142
|
```bash
|
|
143
143
|
# 看有沒有繞過 item-anatomy 的手刻 list item
|
|
144
|
-
rg
|
|
144
|
+
rg 'className="flex.*py-[0-9].*hover:bg' packages/design-system/src -g '*.tsx'
|
|
145
145
|
# 看有沒有繞過 DescriptionList 的手刻 key-value grid
|
|
146
|
-
rg
|
|
146
|
+
rg 'grid-cols-\[[0-9]+px_1fr\]' packages/design-system/src -g '*.tsx'
|
|
147
147
|
```
|
|
148
148
|
|
|
149
149
|
非空 = drift,要改用 primitive。
|
|
@@ -198,7 +198,9 @@ rg "grid-cols-\[[0-9]+px_1fr\]" packages/design-system/src src/hooks --type tsx
|
|
|
198
198
|
|
|
199
199
|
### Card header 大 prefix 對齊(avatar > text block)
|
|
200
200
|
|
|
201
|
-
Row primitive 的 24px 閾值規則適用於**列表行**(prefix 跟 text block 接近等高)。當 prefix 遠大於 text block 時(如 64px profile avatar 搭配 1-2 行文字),改用 **card header
|
|
201
|
+
Row primitive 的 24px 閾值規則適用於**列表行**(prefix 跟 text block 接近等高)。當 prefix 遠大於 text block 時(如 64px profile avatar 搭配 1-2 行文字),改用 **card header 模式**。
|
|
202
|
+
|
|
203
|
+
**為什麼置中**:text block 遠矮於 prefix 時,若沿用列表行的 top-align(`h-[1lh]` 對第一行),文字會頂在 avatar 上緣、下方大片留白,視覺重心偏上失衡;垂直置中讓 text 與大 avatar 形成均衡的 identity pair。實作:
|
|
202
204
|
|
|
203
205
|
```tsx
|
|
204
206
|
<div className="flex items-start gap-3">
|
|
@@ -324,7 +326,7 @@ Consumer 不需手動插 Separator——把同類 items 包進 Group,自動分
|
|
|
324
326
|
|
|
325
327
|
**為什麼 state 不獨立列規則**:state 視覺(hover / focus / active / selected)跟隨 permanent layer 分類,不獨立觸發。瞬時 state(hover/focus/active)單一 item 啟用不會創造相鄰衝突;selected 的連續 bg 在 permanent flush / transparent item 上是 **multi-select feature**(Finder / Gmail / Notion idiom),在 permanent standalone card 上依然必 gap(card identity 保留)。
|
|
326
328
|
|
|
327
|
-
### 世界級 benchmark(2026-04-22 掃 6 家)
|
|
329
|
+
### 世界級 benchmark(2026-04-22 掃 6 家) <!-- @benchmark-unverified -->
|
|
328
330
|
|
|
329
331
|
| DS | menu item | file/card list | table row | settings row |
|
|
330
332
|
|----|-----------|---------------|-----------|-------------|
|
|
@@ -363,6 +365,12 @@ Consumer 不需手動插 Separator——把同類 items 包進 Group,自動分
|
|
|
363
365
|
- `file-upload.stories.tsx` rich list(standalone card)→ 改 `gap-2` + 移除 `border overflow-hidden` 外框
|
|
364
366
|
- `file-item.stories.tsx` Rich / HoverSwap rich block / Clickable:同上
|
|
365
367
|
|
|
368
|
+
### 邊界案例
|
|
369
|
+
|
|
370
|
+
- **多行 item(文字換行)**:不改變分類 — gap 判定只看永久視覺層,與 item 高度 / 行數無關
|
|
371
|
+
- **Selected 連續多 item**:見上「為什麼 state 不獨立列規則」— flush / transparent 的連續 selected bg 是 multi-select feature;standalone card 依然必 gap
|
|
372
|
+
- **Dark mode**:分隔 affordance(`border-divider` / bg token)走 semantic token 自動 adapt,分類與 gap 判定不變
|
|
373
|
+
|
|
366
374
|
### 跟 Group auto-separation 的關係
|
|
367
375
|
|
|
368
376
|
本規則處理**單一 group 內 item 之間**;「Group auto-separation」處理**相鄰 group 之間**(自帶 16px + border 分隔)。
|
|
@@ -386,7 +394,7 @@ Consumer 不需手動插 Separator——把同類 items 包進 Group,自動分
|
|
|
386
394
|
| **control** | Selection 機制(Checkbox / RadioGroupItem) | 只有 SelectionItem 有 | **永遠 inline**(`h-[1lh]`) |
|
|
387
395
|
| **prefix** | 視覺輔助(icon / avatar / thumbnail) | 可選,在所有 item 元件 | 24px 閾值規則 |
|
|
388
396
|
| **content** | label + 可選 description | 永遠存在 | 佔滿剩餘空間,`flex-1 min-w-0` |
|
|
389
|
-
| **suffix** | label 的 metadata(tag、chevron、time、count) | 可選,在所有 item 元件 |
|
|
397
|
+
| **suffix** | label 的 metadata(tag、chevron、time、count) | 可選,在所有 item 元件 | **永遠 `h-[1lh]` inline 對齊第一行 label(跟 prefix 解耦,見「24px 閾值對齊規則」suffix 段)** |
|
|
390
398
|
|
|
391
399
|
**suffix 可以塞什麼**(支援 Switch 同時遵守 24px 閾值):
|
|
392
400
|
|
|
@@ -399,7 +407,7 @@ Consumer 不需手動插 Separator——把同類 items 包進 Group,自動分
|
|
|
399
407
|
|
|
400
408
|
### 互動 control 在 suffix(Switch / Checkbox / Select)
|
|
401
409
|
|
|
402
|
-
Menu item / dropdown item 的 suffix 是**可以**塞 Switch / Checkbox / Select 的——世界級 dropdown menu(macOS / Windows / VS Code 的 View menu、Notion 的 page options)普遍用 menu item + trailing toggle/checkmark 呈現「此項 on/off」。**條件**:必須遵守 item-anatomy 的對齊 + 點擊語義規則。 <!-- @benchmark-unverified
|
|
410
|
+
Menu item / dropdown item 的 suffix 是**可以**塞 Switch / Checkbox / Select 的——世界級 dropdown menu(macOS / Windows / VS Code 的 View menu、Notion 的 page options)普遍用 menu item + trailing toggle/checkmark 呈現「此項 on/off」。**條件**:必須遵守 item-anatomy 的對齊 + 點擊語義規則。 <!-- @benchmark-unverified -->
|
|
403
411
|
|
|
404
412
|
**視覺對齊**(跟 `Tag` / `Chevron` 共用規則):
|
|
405
413
|
|
|
@@ -414,18 +422,16 @@ MenuItem 承載 Switch 時,**整列 row 的 click = toggle 這個 control**(不
|
|
|
414
422
|
|
|
415
423
|
```tsx
|
|
416
424
|
// ✅ Canonical:row click 與 suffix Switch 同步 toggle(row 可點擊、Switch 視覺反映狀態)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
onClick={() => setDark(!dark)}
|
|
420
|
-
>
|
|
425
|
+
// (DropdownMenuItem 無任意 suffix slot——trailing 互動 control 用 MenuItem endContent)
|
|
426
|
+
<MenuItem endContent={<Switch checked={dark} tabIndex={-1} aria-hidden />} onClick={() => setDark(!dark)}>
|
|
421
427
|
Dark mode
|
|
422
|
-
</
|
|
428
|
+
</MenuItem>
|
|
423
429
|
|
|
424
430
|
// ❌ 錯:row 跟 Switch 各自獨立,兩個不同 action
|
|
425
|
-
<
|
|
431
|
+
<MenuItem onClick={() => navigate('/settings')}>
|
|
426
432
|
Dark mode
|
|
427
433
|
<Switch checked={dark} onCheckedChange={setDark} /> {/* Switch 有自己的 click,搶 row click */}
|
|
428
|
-
</
|
|
434
|
+
</MenuItem>
|
|
429
435
|
```
|
|
430
436
|
|
|
431
437
|
**Switch 實作細節**:放 suffix 時 Switch 應 `tabIndex={-1}` + `aria-hidden`(或去掉自己的 click handler),讓 **row 成為唯一互動靶子**、Switch 純視覺反映狀態。Radix `DropdownMenuCheckboxItem` 的 Checkbox 就是這模式(checkmark 是視覺,整列是 trigger)。Switch 類比採用同樣規則。
|
|
@@ -439,7 +445,7 @@ MenuItem 承載 Switch 時,**整列 row 的 click = toggle 這個 control**(不
|
|
|
439
445
|
|
|
440
446
|
兩者**結構一致**(label 左 / toggle 右齊),差別只在**容器層級**(menu item = overlay row / Field horizontal = page row)。
|
|
441
447
|
|
|
442
|
-
**世界級對照**:macOS View menu「Show toolbar」+ ✓ / Figma View menu「Show layout grids」+ toggle / VS Code View menu「Toggle sidebar」+ ✓——都是 menu item + trailing toggle/check 的 canonical 應用。 <!-- @benchmark-unverified
|
|
448
|
+
**世界級對照**:macOS View menu「Show toolbar」+ ✓ / Figma View menu「Show layout grids」+ toggle / VS Code View menu「Toggle sidebar」+ ✓——都是 menu item + trailing toggle/check 的 canonical 應用。 <!-- @benchmark-unverified -->
|
|
443
449
|
|
|
444
450
|
**外層**:`flex items-start`——多行時 control / prefix / suffix 的 Y 不隨文字下移。
|
|
445
451
|
|
|
@@ -457,13 +463,11 @@ MenuItem 承載 Switch 時,**整列 row 的 click = toggle 這個 control**(不
|
|
|
457
463
|
|
|
458
464
|
---
|
|
459
465
|
|
|
466
|
+
## 24px 閾值對齊規則(Prefix)與 Suffix inline 對齊
|
|
460
467
|
|
|
461
|
-
|
|
462
|
-
|
|
468
|
+
**核心原則**:Prefix 的對齊容器高度由**自己內容物的大小**決定(24px 閾值公式);**Suffix 一律 `h-[1lh]` inline 對齊第一行 label**(實作 SSOT:`ItemSuffix` primitive 與 MenuItem suffix slot 皆固定 `h-[1lh]`,見 `menu-item.spec.md`「Suffix 對齊(實作限制)」)。兩 slot 各自獨立,不互相同步。
|
|
463
469
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
### 公式
|
|
470
|
+
### 公式(prefix slot)
|
|
467
471
|
|
|
468
472
|
| 內容物高度 | 對齊容器 | 對齊目標 |
|
|
469
473
|
|---|---|---|
|
|
@@ -484,7 +488,7 @@ MenuItem 承載 Switch 時,**整列 row 的 click = toggle 這個 control**(不
|
|
|
484
488
|
|
|
485
489
|
**2. 業界 convention 全部如此**
|
|
486
490
|
|
|
487
|
-
Apple Mail / Gmail / iOS Settings / Material 3 / Atlassian DSP / Polaris ResourceItem——**全部**是「prefix 跟 suffix 各自獨立對齊」。沒有任何一個把小 suffix 強迫對齊到大 avatar 中心。 <!-- @benchmark-unverified
|
|
491
|
+
Apple Mail / Gmail / iOS Settings / Material 3 / Atlassian DSP / Polaris ResourceItem——**全部**是「prefix 跟 suffix 各自獨立對齊」。沒有任何一個把小 suffix 強迫對齊到大 avatar 中心。 <!-- @benchmark-unverified -->
|
|
488
492
|
|
|
489
493
|
| 系統 | Prefix(大 avatar) | Suffix(小元素) |
|
|
490
494
|
|---|---|---|
|
|
@@ -497,22 +501,20 @@ Apple Mail / Gmail / iOS Settings / Material 3 / Atlassian DSP / Polaris Resourc
|
|
|
497
501
|
|
|
498
502
|
把小 suffix 強迫對齊大 avatar 中心,小 suffix 會「下沉」到 description 行,**視覺上跟 label 失聯**。對齊 label 第一行則讓 suffix 跟它修飾的對象在同一條基準線上,視覺重量自然分配。
|
|
499
503
|
|
|
500
|
-
### 大塊 suffix
|
|
504
|
+
### 大塊 suffix(> 24px)不支援 block 對齊(2026-06-11 收斂對齊實作)
|
|
501
505
|
|
|
502
|
-
`suffix > 24px`
|
|
506
|
+
`suffix > 24px`(thumbnail / stacked badge)在 DS 內**零實作、不支援 block 對齊**:`ItemSuffix` primitive 與 MenuItem suffix slot 皆固定 `h-[1lh]` inline(SSOT:`item-anatomy.tsx` ItemSuffix、`menu-item.spec.md`「Suffix 對齊(實作限制)」),本 folder anatomy story 對齊表亦明文「Suffix 所有情況 = 一行文字高度」。世界級同向:上表 Material 3 List trailing icon top-aligned(對齊第一行,非文字塊中心)。
|
|
503
507
|
|
|
504
508
|
| 場景 | Prefix | Suffix | 結果 |
|
|
505
509
|
|---|---|---|---|
|
|
506
|
-
| 標準選單(MenuItem 大宗) | icon 16px / avatar 24px | Tag / Chevron(≤24px) |
|
|
510
|
+
| 標準選單(MenuItem 大宗) | icon 16px / avatar 24px | Tag / Chevron(≤24px) | Prefix inline,suffix inline |
|
|
507
511
|
| 用戶選單(avatar + name + role) | avatar 32px(block) | Chevron(≤24px) | Prefix block,suffix inline ← **各自獨立** |
|
|
508
|
-
| 帶縮圖列表(thumbnail + title + 縮圖) | icon 16px(inline) | thumbnail 40px(block) | Prefix inline,suffix block ← **各自獨立** |
|
|
509
|
-
| 雙視覺重的卡片 | avatar 40px(block) | thumbnail 40px(block) | 兩邊都 block |
|
|
510
512
|
|
|
511
|
-
**沒有「prefix 跟 suffix 必須同步」的情況。**
|
|
513
|
+
**沒有「prefix 跟 suffix 必須同步」的情況。** 未來若需 thumbnail 級大塊 suffix,屬 API 擴充——先回本 spec 補 canonical 再動 primitive。
|
|
512
514
|
|
|
513
515
|
### Avatar 尺寸選擇
|
|
514
516
|
|
|
515
|
-
**用於 prefix slot**。Avatar 尺寸是 **consumer 依視覺重量意圖決定**的——有兩組預設可選,對齊模式跟著 size 走,**不跟著 description
|
|
517
|
+
**用於 prefix slot**。Avatar 尺寸是 **consumer 依視覺重量意圖決定**的——有兩組預設可選,對齊模式跟著 size 走,**不跟著 description 的有無走**(scope:裸 `<ItemAvatar>` 組合;經 `MenuItem avatar`(AvatarData)高階 API 時由 MenuItem 自動依 description 有無選 inline/block 尺寸——AvatarData 無 size 欄位,見 `menu-item.spec.md`「startIcon vs avatar」)。
|
|
516
518
|
|
|
517
519
|
**兩組預設尺寸**(依 row size):
|
|
518
520
|
|
|
@@ -530,7 +532,7 @@ Apple Mail / Gmail / iOS Settings / Material 3 / Atlassian DSP / Polaris Resourc
|
|
|
530
532
|
| > 24 | 無 | `h-[1lh]` inline | 大 avatar 但無 desc,仍對齊第一行(block 沒意義) |
|
|
531
533
|
| > 24 | 有 | `h-[block calc]` block | 大 avatar 視覺重,平衡 label + desc 整個文字塊 |
|
|
532
534
|
|
|
533
|
-
**關鍵**:consumer 可以自由選擇「小 avatar + description」——那是完全合法的組合,此時 avatar inline 對齊第一行 label,description 從第二行自然往下。沒有「有 desc 就必須用 block
|
|
535
|
+
**關鍵**:consumer 可以自由選擇「小 avatar + description」——那是完全合法的組合,此時 avatar inline 對齊第一行 label,description 從第二行自然往下。沒有「有 desc 就必須用 block 尺寸」的規則。此自由度屬裸 `<ItemAvatar>` 組合;MenuItem 的 AvatarData API 不暴露此選擇(自動推導,見上 scope 註)。
|
|
534
536
|
|
|
535
537
|
**程式化規則**(scope:**row context only** — Sidebar / SelectMenu / TreeView / Combobox / Menu 等 row primitive 子樹內):consumer **必須**用 `<ItemAvatar>` / `<ItemIcon>` helper 元件,**禁止** `import { AVATAR_SIZE }` 手動查表,更**禁止**硬寫 `<Avatar size={N} />`。
|
|
536
538
|
|
|
@@ -636,7 +638,7 @@ Label x 位置受 prefix 尺寸影響(icon 16 vs avatar 24,差 8px)。**對齊
|
|
|
636
638
|
|
|
637
639
|
**範例**:Sidebar footer 只有一個 user row(Alan Chen)→ 用 inline avatar 24px 的自然預設,不需要縮成 16px 去對齊上方 main nav 的 icon。Main nav 和 footer 是不同 group,本來就該各自獨立。
|
|
638
640
|
|
|
639
|
-
**業界 convention**:Apple HIG list section、Material 3 list、Atlassian DSP 全部採用「section-scoped consistency」——同一 section 內 leading element 類型一致,跨 section 變化是正常的。沒有任何世界級系統要求整個 sidebar / menu 所有 prefix 強迫同尺寸。 <!-- @benchmark-unverified
|
|
641
|
+
**業界 convention**:Apple HIG list section、Material 3 list、Atlassian DSP 全部採用「section-scoped consistency」——同一 section 內 leading element 類型一致,跨 section 變化是正常的。沒有任何世界級系統要求整個 sidebar / menu 所有 prefix 強迫同尺寸。 <!-- @benchmark-unverified -->
|
|
640
642
|
|
|
641
643
|
❌ **錯誤示範**:為了讓 footer avatar 跟 main nav icon 齊左,把 avatar 從 24 改成 16——這是用錯誤方向修了一個根本不存在的問題,且違反 Avatar 尺寸公式。
|
|
642
644
|
|
|
@@ -649,7 +651,7 @@ Label x 位置受 prefix 尺寸影響(icon 16 vs avatar 24,差 8px)。**對齊
|
|
|
649
651
|
| School | 代表 | 行為 | 我們的預設? |
|
|
650
652
|
|---|---|---|---|
|
|
651
653
|
| A | Slack / VS Code Explorer / Discord | Per-section 獨立 prefix 寬度 | **✅ 預設** |
|
|
652
|
-
| B | Notion / Linear / Atlassian Confluence | 全域對齊 | 🟡 Opt-in via `uniformPrefix` | <!-- @benchmark-unverified
|
|
654
|
+
| B | Notion / Linear / Atlassian Confluence | 全域對齊 | 🟡 Opt-in via `uniformPrefix` | <!-- @benchmark-unverified -->
|
|
653
655
|
|
|
654
656
|
兩派都是世界級慣例,沒有絕對對錯。**選 A 作為預設的理由**:
|
|
655
657
|
|
|
@@ -683,7 +685,7 @@ Label x 位置受 prefix 尺寸影響(icon 16 vs avatar 24,差 8px)。**對齊
|
|
|
683
685
|
|
|
684
686
|
### 其他 row primitive 怎麼接
|
|
685
687
|
|
|
686
|
-
要對齊 Notion 模式,在 row primitive 的頂層 wrapper(例如 `<TreeView>` 的 root)加: <!-- @benchmark-unverified
|
|
688
|
+
要對齊 Notion 模式,在 row primitive 的頂層 wrapper(例如 `<TreeView>` 的 root)加: <!-- @benchmark-unverified -->
|
|
687
689
|
|
|
688
690
|
```tsx
|
|
689
691
|
const slotStyle = getUniformPrefixSlotStyle(size)
|
|
@@ -702,12 +704,12 @@ const slotStyle = getUniformPrefixSlotStyle(size)
|
|
|
702
704
|
- ❌ 拿 escape hatch `uniformPrefix={false}` 配合 mixed prefix 沒有設計理由——這違反使用者視覺直覺,只在「刻意製造視覺斷層」時才該用
|
|
703
705
|
- ❌ 在 row primitive 的某個中層元件再做一次 `:has()` 偵測——應該由頂層容器一次處理,中層不該重複 logic
|
|
704
706
|
|
|
705
|
-
|
|
706
|
-
|
|
707
707
|
## SelectionItem:Control 跟 Prefix 同高度(不歪斜)
|
|
708
708
|
|
|
709
709
|
`SelectionItem` 永遠有 **control** slot(checkbox / radio)。當 prefix(icon / avatar)也存在時,**兩者必須在同一條 baseline**——否則視覺歪斜。
|
|
710
710
|
|
|
711
|
+
> **何時用 SelectionItem(vs 裸 RadioGroup / Checkbox group)**:SSOT → `components/SelectionControl/selection-item.spec.md`「何時用」;本節只 own control + prefix 對齊規則。
|
|
712
|
+
|
|
711
713
|
### 規則:control 跟著 prefix 的對齊走
|
|
712
714
|
|
|
713
715
|
| prefix 模式 | Control 對齊 | Prefix 對齊 | 結果 |
|
|
@@ -788,10 +790,10 @@ Block 模式時 checkbox 不在 label 第一行——它跟 avatar 在同一高
|
|
|
788
790
|
|
|
789
791
|
| Consumer | Primitive 消費 | Rationale |
|
|
790
792
|
|----------|--------------|-----------|
|
|
791
|
-
| **FileItem**(rich + compact) | ✅ ItemContent + ItemPrefix
|
|
793
|
+
| **FileItem**(rich + compact) | ✅ ItemContent + ItemPrefix(hover action 為 Button xs iconOnly,非 ItemInlineActionButton——2026-04-23 user 統一,見 file-item.tsx)| 純 row-item layout,完美 fit |
|
|
792
794
|
| **Notice / Alert / Toast** | ✅ ItemContent + ItemPrefix | title+desc 標準 row-item 結構 |
|
|
793
795
|
| **ProfileCard** | ✅ ItemContent(+ `labelTruncate=false` + `labelClassName` escape hatch) | 偏離 rationale:card context 用 `text-body-lg font-medium`,非一般 body label |
|
|
794
|
-
| **MenuItem** | ✅ ItemContent(`mode="scanning"\|"
|
|
796
|
+
| **MenuItem** | ✅ ItemContent(`mode="scanning"` + `size="md"\|"lg"` 正交)+ `itemPrefixAlignVariants` cva SSOT | Content 用 ItemContent 配合 size-aware scanning mode(sm/md = caption / lg = body-compact);label+desc clamp 透過 className escape hatch(MenuItem 特化 labelMaxLines / descMaxLines 語意) |
|
|
795
797
|
| **Sidebar / TreeView** | ✅ ItemPrefix(+ 其他 primitives) | Label-only(無 description),不需 ItemContent |
|
|
796
798
|
| **Empty** | ❌ token-direct only | **結構限制**:centered stack pattern(icon → title → desc → action 垂直居中),非 row-item 佈局。保持手刻 |
|
|
797
799
|
| **Dialog**(DialogTitle / DialogDescription) | ❌ token-direct only | **結構限制**:title 跟 desc 是獨立 Radix `<DialogTitle>` / `<DialogDescription>` component(aria-labelledby / aria-describedby 語義),非同層 span 配對 |
|
|
@@ -800,7 +802,7 @@ Block 模式時 checkbox 不在 label 第一行——它跟 avatar 在同一高
|
|
|
800
802
|
| **Switch**(standalone mode with label+desc) | ❌ token-direct only | **a11y 限制(同 SelectionItem)**:standalone mode 用 `<label htmlFor={inputId}>` 包 label+desc+switch,form control 關聯必要。Control-only mode(放 MenuItem suffix 等)不涉及 label+desc,不在 ItemContent scope |
|
|
801
803
|
| **Steps**(`StepDescription`) | ❌ token-direct only | **API 限制**:StepLabel 跟 StepDescription 是獨立 public forwardRef subcomponent,consumer 分開寫;遷 ItemContent(single-component API)= breaking API |
|
|
802
804
|
|
|
803
|
-
**共 5 consumer 消費 primitive(FileItem / Notice / ProfileCard / MenuItem / TreeView・Sidebar),
|
|
805
|
+
**共 5 consumer 消費 primitive(FileItem / Notice / ProfileCard / MenuItem / TreeView・Sidebar),6 consumer 保 token-direct(Empty / Dialog / DescriptionList / SelectionItem / Switch / Steps)各有 a11y / semantic / API 結構限制**。Token 層 SSOT 100% 覆蓋(改 `--item-gap-label-desc-<mode>[-lg]` 4 變體 → 全 DS 同步);primitive 層覆蓋結構能 fit 的 consumer。
|
|
804
806
|
|
|
805
807
|
**Switch 跟 SelectionItem 同 blocker**(2026-04-23 user 糾正):兩者都是 `<label htmlFor>` 表單 control 關聯結構,ItemContent 用 `<span>` 渲染 label 會破壞 form a11y。這類結構**只能 token-level SSOT**(實際已做)。
|
|
806
808
|
|
|
@@ -820,7 +822,7 @@ World-class benchmark(6 家 DS):
|
|
|
820
822
|
- Polaris / Atlassian / Apple HIG:typography token 手選(無 density prop)
|
|
821
823
|
- 6 家共識:14/20 vs 16/24 兩擋 body 表達 scanning vs reading
|
|
822
824
|
|
|
823
|
-
本 DS 採 density-prop 派(A 派),跟 Material / Carbon / Ant 對齊。`mode` 比 `dense` 更 self-documenting。 <!-- @benchmark-unverified
|
|
825
|
+
本 DS 採 density-prop 派(A 派),跟 Material / Carbon / Ant 對齊。`mode` 比 `dense` 更 self-documenting。 <!-- @benchmark-unverified -->
|
|
824
826
|
|
|
825
827
|
**未來改 scanning typography(e.g. 從 caption 12 改到 caption 13)**:只改 `--font-caption-size` token 一處 → 所有 `mode="scanning"` 的 consumer 同步動。
|
|
826
828
|
|
|
@@ -921,7 +923,6 @@ World-class benchmark(6 家 DS):
|
|
|
921
923
|
|
|
922
924
|
---
|
|
923
925
|
|
|
924
|
-
|
|
925
926
|
## Inline Action 設計規格 → `inline-action.spec.md`(2026-04-24 抽出,獨立 SSOT)
|
|
926
927
|
|
|
927
928
|
嵌入 Tag dismiss / Field endAction / Row suffix action 的 icon primitive。完整 265 行視覺規格 / API / predicate / Icon 色 / same-row consistency → 見 `patterns/element-anatomy/inline-action.spec.md`。
|
|
@@ -1022,19 +1023,23 @@ Selection control(Dropdown / Menu / List / SegmentedControl / Chip)的 item 視
|
|
|
1022
1023
|
> 本節由 `scripts/add-reciprocal-pointers.mjs` 自動維護,列出在 SSOT 語境下指向本 spec 的其他 spec。若要手動補充,寫在本節之前。
|
|
1023
1024
|
|
|
1024
1025
|
- `alert.spec.md`
|
|
1026
|
+
- `color.spec.md`
|
|
1025
1027
|
- `command.spec.md`
|
|
1026
|
-
- `data-table.spec.md`
|
|
1027
1028
|
- `dropdown-menu.spec.md`
|
|
1029
|
+
- `element-anatomy.spec.md`
|
|
1028
1030
|
- `empty.spec.md`
|
|
1029
1031
|
- `field.spec.md`
|
|
1030
1032
|
- `file-item.spec.md`
|
|
1033
|
+
- `header-canonical.spec.md`
|
|
1034
|
+
- `inline-action.spec.md`
|
|
1031
1035
|
- `menu-item.spec.md`
|
|
1032
1036
|
- `notice.spec.md`
|
|
1033
|
-
- `overlay-surface.spec.md`
|
|
1034
1037
|
- `select-menu.spec.md`
|
|
1035
1038
|
- `select.spec.md`
|
|
1036
1039
|
- `selection-item.spec.md`
|
|
1037
1040
|
- `sidebar.spec.md`
|
|
1041
|
+
- `steps.spec.md`
|
|
1042
|
+
- `tabs.spec.md`
|
|
1038
1043
|
- `tag.spec.md`
|
|
1039
1044
|
- `time-picker.spec.md`
|
|
1040
1045
|
- `toast.spec.md`
|
|
@@ -812,7 +812,7 @@ const InspectorInner = () => {
|
|
|
812
812
|
{hasPx && <span><strong style={{ color: Z.pad.text }}>padding-x</strong> = {pxPx}</span>}
|
|
813
813
|
{effectiveHasPrefix && <span><strong style={{ color: Z.icon.text }}>prefix-content gap</strong> = {gapPx}</span>}
|
|
814
814
|
<span><strong style={{ color: Z.label.text }}>py</strong> = {preset.py.includes('calc') ? `calc((field-height-${size} − 一行文字高度) / 2)` : preset.py}</span>
|
|
815
|
-
{hasDescription && <span>label-desc gap = --item-gap-label-desc-
|
|
815
|
+
{hasDescription && <span>{`label-desc gap = --item-gap-label-desc-${preset.mode}${size === 'lg' ? '-lg' : ''} (2px)`}</span>}
|
|
816
816
|
</div>
|
|
817
817
|
</div>
|
|
818
818
|
</div>
|
|
@@ -839,7 +839,7 @@ const InspectorInner = () => {
|
|
|
839
839
|
<TkVal token={preset.gap} value={preset.gapDesc} />
|
|
840
840
|
</PropRow>
|
|
841
841
|
{hasDescription && (
|
|
842
|
-
<PropRow label="label-desc" dot={Z.gap.text}><TkVal token=
|
|
842
|
+
<PropRow label="label-desc" dot={Z.gap.text}><TkVal token={`--item-gap-label-desc-${preset.mode}${size === 'lg' ? '-lg' : ''}`} value="2px" /></PropRow>
|
|
843
843
|
)}
|
|
844
844
|
{effectiveHasSuffix && (
|
|
845
845
|
<PropRow label="suffix gap" dot={Z.suffix.text}>
|
|
@@ -1507,7 +1507,7 @@ export const IconActionPrimitiveDecision = {
|
|
|
1507
1507
|
<tr><Td>Tag dismiss X</Td><Td>Pill body</Td><Td mono>Inline Action</Td><Td>Colored host,繼承顏色</Td></tr>
|
|
1508
1508
|
<tr><Td>Menu / TreeView suffix action</Td><Td>Row inline flow</Td><Td mono>Inline Action</Td><Td>Inline with content</Td></tr>
|
|
1509
1509
|
<tr><Td>SidebarGroup header chevron</Td><Td>Aux toggle</Td><Td mono>Inline Action</Td><Td>Inline header toggle</Td></tr>
|
|
1510
|
-
<tr><Td><strong>FileItem compact</strong>(row 24)</Td><Td>Row slot</Td><Td mono>
|
|
1510
|
+
<tr><Td><strong>FileItem compact</strong>(row 24)</Td><Td>Row slot</Td><Td mono>Button xs iconOnly</Td><Td>2026-04-23 user 統一 rich + compact 同 primitive(field-height-xs 容器 + data-unbounded 收斂)</Td></tr>
|
|
1511
1511
|
<tr><Td><strong>FileItem rich</strong>(row 56)</Td><Td>Row slot</Td><Td mono>Button xs iconOnly</Td><Td>≤ 24 cap,不放大</Td></tr>
|
|
1512
1512
|
<tr><Td><strong>DataTable row action</strong></Td><Td>Row dedicated column</Td><Td mono>Button xs iconOnly</Td><Td>≤ 24 cap(跨 tier 固定)</Td></tr>
|
|
1513
1513
|
<tr><Td><strong>Dialog / Sheet chrome corner close</strong></Td><Td>Action group region</Td><Td mono>Button sm iconOnly dismiss</Td><Td>corner 屬 action group,可與 refresh / share 並排</Td></tr>
|
|
@@ -1591,8 +1591,8 @@ export const IconActionPrimitiveDecision = {
|
|
|
1591
1591
|
</div>
|
|
1592
1592
|
</div>
|
|
1593
1593
|
<div className="flex items-center gap-3 px-3 py-2 rounded-md border border-border bg-surface">
|
|
1594
|
-
<span className="flex-1 text-caption text-fg-secondary">✅ compact row:都用 Inline Action(icon 16,hover-bg 18,size="sm")</span>
|
|
1595
|
-
<div className="flex items-center gap-
|
|
1594
|
+
<span className="flex-1 text-caption text-fg-secondary">✅ compact row:都用 Inline Action(icon 16,hover-bg 18,size="sm",gap-2)</span>
|
|
1595
|
+
<div className="flex items-center gap-2">
|
|
1596
1596
|
<ItemInlineActionButton icon={RotateCw} size="sm" aria-label="重試" />
|
|
1597
1597
|
<ItemInlineActionButton icon={Download} size="sm" aria-label="下載" />
|
|
1598
1598
|
<ItemInlineActionButton icon={MoreVertical} size="sm" aria-label="更多" />
|
|
@@ -1600,7 +1600,7 @@ export const IconActionPrimitiveDecision = {
|
|
|
1600
1600
|
</div>
|
|
1601
1601
|
<div className="flex items-center gap-3 px-3 py-2 rounded-md border border-error bg-surface">
|
|
1602
1602
|
<span className="flex-1 text-caption text-error-text">❌ 禁止混 Inline Action + Button(box 尺寸不一致)</span>
|
|
1603
|
-
<div className="flex items-center gap-
|
|
1603
|
+
<div className="flex items-center gap-2">
|
|
1604
1604
|
<ItemInlineActionButton icon={RotateCw} size="sm" aria-label="重試" />
|
|
1605
1605
|
<Button variant="text" size="xs" iconOnly startIcon={Download} aria-label="下載" />
|
|
1606
1606
|
</div>
|
|
@@ -215,7 +215,7 @@ ItemPrefix.displayName = "ItemPrefix"
|
|
|
215
215
|
* 直接用 `<ItemPrefix>` 就好,不需此 cva。
|
|
216
216
|
*
|
|
217
217
|
* ── SSOT 傳播 ──
|
|
218
|
-
* gap 用 `var(--item-gap-label-desc)` token;font-size 用 token-awareness
|
|
218
|
+
* gap 用 `var(--item-gap-label-desc-scanning[-lg])` token(bare 變體已不存在,4 個 mode×size 變體);font-size 用 token-awareness
|
|
219
219
|
* (`var(--font-caption-size)` / `var(--font-body-size)`)。改 token → 公式同步。
|
|
220
220
|
*/
|
|
221
221
|
// code-quality-allow: long-function — cva variant/styles table — 拆 fn 會失去 type inference + 跨 fn 傳 config 反而難讀
|
|
@@ -270,7 +270,7 @@ ItemLabel.displayName = "ItemLabel"
|
|
|
270
270
|
* `<ItemContent>` — Row primitive 的 label + optional description 內容區(SSOT)。
|
|
271
271
|
*
|
|
272
272
|
* ── 存在的唯一理由 ──
|
|
273
|
-
* 封裝「flex-col + label + description + `mt-[var(--item-gap-label-desc)]` gap」結構,
|
|
273
|
+
* 封裝「flex-col + label + description + `mt-[var(--item-gap-label-desc-<mode>[-lg])]` gap」結構,
|
|
274
274
|
* 避免 13+ 消費者各自 hard-code `mt-0.5`。改 token 一處,全 DS 同步。
|
|
275
275
|
*
|
|
276
276
|
* ── Consumer 偏離 canonical ──
|
|
@@ -278,7 +278,7 @@ ItemLabel.displayName = "ItemLabel"
|
|
|
278
278
|
* 該元件 `spec.md` 明文寫下 rationale(對齊 item-anatomy canonical「偏離必明文」)。
|
|
279
279
|
* 合法偏離範例:
|
|
280
280
|
* - MenuItem 的 `leading-compact + text-caption` scanning-mode typography
|
|
281
|
-
* → MenuItem spec 明文 rationale + 消費 `--item-gap-label-desc` token 直接用
|
|
281
|
+
* → MenuItem spec 明文 rationale + 消費 `--item-gap-label-desc-scanning[-lg]` token 直接用
|
|
282
282
|
* - SelectionItem 的 control slot 跟 block formula 綁定
|
|
283
283
|
* → SelectionItem spec 明文 rationale
|
|
284
284
|
*
|
|
@@ -55,8 +55,9 @@ export interface ChromeHeaderProps
|
|
|
55
55
|
* Tabs row slot(2026-05-18 加 per W2/W4 真實能用 + user-mandated fix)。
|
|
56
56
|
* 提供時 ChromeHeader 自動 column 結構:
|
|
57
57
|
* row 1 = children(h-chrome-header-height 固定,px-loose,跟 single-row 模式同)
|
|
58
|
-
* row 2 = tabsSlot 包在
|
|
59
|
-
*
|
|
58
|
+
* row 2 = tabsSlot 包在 `HEADER_TABS_SLOT_WRAPPER_CLASS` wrapper
|
|
59
|
+
* (`[&>[role=tablist]]:w-full` + 注入 TabsList 內 px-loose;wrapper 本身
|
|
60
|
+
* 無 padding 無 border——border 由 TabsList 自畫,W1 全寬 paint 一條線)
|
|
60
61
|
*
|
|
61
62
|
* Consumer 傳:`tabsSlot={<TabsList>...</TabsList>}`,TabsContent 放 ChromeHeader 之外。
|
|
62
63
|
* Standalone Tabs(無 chrome header)該直接用 `<TabsList>` 不需 wrapper。
|