@seekalfred/primitives 0.5.1 → 0.7.1
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/README.md +682 -19
- package/dist/accordion/accordion.js +10 -4
- package/dist/accordion/accordion.js.map +1 -1
- package/dist/action-button/action-button.d.ts +50 -0
- package/dist/action-button/action-button.js +159 -0
- package/dist/action-button/action-button.js.map +1 -0
- package/dist/action-button/index.d.ts +2 -0
- package/dist/action-button/index.js +2 -0
- package/dist/activity-heatmap/activity-heatmap.d.ts +92 -0
- package/dist/activity-heatmap/activity-heatmap.js +699 -0
- package/dist/activity-heatmap/activity-heatmap.js.map +1 -0
- package/dist/activity-heatmap/index.d.ts +2 -0
- package/dist/activity-heatmap/index.js +2 -0
- package/dist/alert/alert.d.ts +19 -1
- package/dist/alert/alert.js +180 -23
- package/dist/alert/alert.js.map +1 -1
- package/dist/alert-dialog/alert-dialog.d.ts +45 -18
- package/dist/alert-dialog/alert-dialog.js +128 -9
- package/dist/alert-dialog/alert-dialog.js.map +1 -1
- package/dist/alert-dialog/index.d.ts +2 -2
- package/dist/alert-dialog/index.js +2 -2
- package/dist/animated-counter/animated-counter.d.ts +38 -0
- package/dist/animated-counter/animated-counter.js +230 -0
- package/dist/animated-counter/animated-counter.js.map +1 -0
- package/dist/animated-counter/index.d.ts +2 -0
- package/dist/animated-counter/index.js +2 -0
- package/dist/announcement-bar/announcement-bar.d.ts +67 -0
- package/dist/announcement-bar/announcement-bar.js +364 -0
- package/dist/announcement-bar/announcement-bar.js.map +1 -0
- package/dist/announcement-bar/index.d.ts +2 -0
- package/dist/announcement-bar/index.js +2 -0
- package/dist/avatar/avatar.d.ts +6 -0
- package/dist/avatar/avatar.js +47 -16
- package/dist/avatar/avatar.js.map +1 -1
- package/dist/badge/badge.d.ts +10 -3
- package/dist/badge/badge.js +35 -5
- package/dist/badge/badge.js.map +1 -1
- package/dist/bar-chart/bar-chart.d.ts +51 -0
- package/dist/bar-chart/bar-chart.js +565 -0
- package/dist/bar-chart/bar-chart.js.map +1 -0
- package/dist/bar-chart/index.d.ts +2 -0
- package/dist/bar-chart/index.js +2 -0
- package/dist/breadcrumb/breadcrumb.d.ts +2 -29
- package/dist/breadcrumb/breadcrumb.js +32 -9
- package/dist/breadcrumb/breadcrumb.js.map +1 -1
- package/dist/brush-chart/brush-chart.d.ts +64 -0
- package/dist/brush-chart/brush-chart.js +1056 -0
- package/dist/brush-chart/brush-chart.js.map +1 -0
- package/dist/brush-chart/index.d.ts +2 -0
- package/dist/brush-chart/index.js +2 -0
- package/dist/button/button.d.ts +36 -23
- package/dist/button/button.js +198 -45
- package/dist/button/button.js.map +1 -1
- package/dist/button-group/button-group.d.ts +76 -0
- package/dist/button-group/button-group.js +402 -0
- package/dist/button-group/button-group.js.map +1 -0
- package/dist/button-group/index.d.ts +2 -0
- package/dist/button-group/index.js +2 -0
- package/dist/calendar/calendar.d.ts +1 -1
- package/dist/calendar/calendar.js +257 -30
- package/dist/calendar/calendar.js.map +1 -1
- package/dist/card/card.d.ts +9 -2
- package/dist/card/card.js +16 -5
- package/dist/card/card.js.map +1 -1
- package/dist/card-stack/card-stack.d.ts +54 -0
- package/dist/card-stack/card-stack.js +492 -0
- package/dist/card-stack/card-stack.js.map +1 -0
- package/dist/card-stack/index.d.ts +2 -0
- package/dist/card-stack/index.js +2 -0
- package/dist/carousel/carousel.d.ts +56 -0
- package/dist/carousel/carousel.js +389 -0
- package/dist/carousel/carousel.js.map +1 -0
- package/dist/carousel/index.d.ts +2 -0
- package/dist/carousel/index.js +2 -0
- package/dist/checkbox/checkbox.d.ts +0 -7
- package/dist/checkbox/checkbox.js +26 -29
- package/dist/checkbox/checkbox.js.map +1 -1
- package/dist/chip-group/chip-group.d.ts +37 -0
- package/dist/chip-group/chip-group.js +148 -0
- package/dist/chip-group/chip-group.js.map +1 -0
- package/dist/chip-group/index.d.ts +2 -0
- package/dist/chip-group/index.js +2 -0
- package/dist/confirm-morph/confirm-morph.d.ts +85 -0
- package/dist/confirm-morph/confirm-morph.js +652 -0
- package/dist/confirm-morph/confirm-morph.js.map +1 -0
- package/dist/confirm-morph/index.d.ts +2 -0
- package/dist/confirm-morph/index.js +2 -0
- package/dist/copy-button/copy-button.d.ts +32 -0
- package/dist/copy-button/copy-button.js +134 -0
- package/dist/copy-button/copy-button.js.map +1 -0
- package/dist/copy-button/index.d.ts +2 -0
- package/dist/copy-button/index.js +2 -0
- package/dist/countdown/countdown.d.ts +45 -0
- package/dist/countdown/countdown.js +298 -0
- package/dist/countdown/countdown.js.map +1 -0
- package/dist/countdown/index.d.ts +2 -0
- package/dist/countdown/index.js +2 -0
- package/dist/date-picker/date-picker.d.ts +51 -0
- package/dist/date-picker/date-picker.js +241 -0
- package/dist/date-picker/date-picker.js.map +1 -0
- package/dist/date-picker/date-range-picker.d.ts +78 -0
- package/dist/date-picker/date-range-picker.js +1015 -0
- package/dist/date-picker/date-range-picker.js.map +1 -0
- package/dist/date-picker/index.d.ts +4 -0
- package/dist/date-picker/index.js +4 -0
- package/dist/date-picker/picker-parts.d.ts +46 -0
- package/dist/date-picker/picker-parts.js +135 -0
- package/dist/date-picker/picker-parts.js.map +1 -0
- package/dist/dialog/dialog.d.ts +10 -14
- package/dist/dialog/dialog.js +40 -12
- package/dist/dialog/dialog.js.map +1 -1
- package/dist/donut-chart/donut-chart.d.ts +64 -0
- package/dist/donut-chart/donut-chart.js +576 -0
- package/dist/donut-chart/donut-chart.js.map +1 -0
- package/dist/donut-chart/index.d.ts +2 -0
- package/dist/donut-chart/index.js +2 -0
- package/dist/empty-state/empty-state.d.ts +55 -0
- package/dist/empty-state/empty-state.js +89 -0
- package/dist/empty-state/empty-state.js.map +1 -0
- package/dist/empty-state/index.d.ts +2 -0
- package/dist/empty-state/index.js +2 -0
- package/dist/expanding-button-group/expanding-button-group.d.ts +54 -0
- package/dist/expanding-button-group/expanding-button-group.js +350 -0
- package/dist/expanding-button-group/expanding-button-group.js.map +1 -0
- package/dist/expanding-button-group/index.d.ts +2 -0
- package/dist/expanding-button-group/index.js +2 -0
- package/dist/file-drop-zone/file-drop-zone.d.ts +71 -0
- package/dist/file-drop-zone/file-drop-zone.js +116 -0
- package/dist/file-drop-zone/file-drop-zone.js.map +1 -0
- package/dist/file-drop-zone/index.d.ts +2 -0
- package/dist/file-drop-zone/index.js +2 -0
- package/dist/filter-bar/filter-bar.d.ts +142 -0
- package/dist/filter-bar/filter-bar.js +416 -0
- package/dist/filter-bar/filter-bar.js.map +1 -0
- package/dist/filter-bar/index.d.ts +2 -0
- package/dist/filter-bar/index.js +2 -0
- package/dist/floating-button-group/floating-button-group.d.ts +61 -0
- package/dist/floating-button-group/floating-button-group.js +340 -0
- package/dist/floating-button-group/floating-button-group.js.map +1 -0
- package/dist/floating-button-group/index.d.ts +2 -0
- package/dist/floating-button-group/index.js +2 -0
- package/dist/gauge/gauge.d.ts +45 -0
- package/dist/gauge/gauge.js +219 -0
- package/dist/gauge/gauge.js.map +1 -0
- package/dist/gauge/index.d.ts +2 -0
- package/dist/gauge/index.js +2 -0
- package/dist/icon-morph/icon-morph.d.ts +55 -0
- package/dist/icon-morph/icon-morph.js +331 -0
- package/dist/icon-morph/icon-morph.js.map +1 -0
- package/dist/icon-morph/index.d.ts +2 -0
- package/dist/icon-morph/index.js +2 -0
- package/dist/in-view-title/in-view-title.d.ts +33 -0
- package/dist/in-view-title/in-view-title.js +119 -0
- package/dist/in-view-title/in-view-title.js.map +1 -0
- package/dist/in-view-title/index.d.ts +2 -0
- package/dist/in-view-title/index.js +2 -0
- package/dist/index.d.ts +71 -6
- package/dist/index.js +73 -11
- package/dist/input/input.d.ts +22 -0
- package/dist/input/input.js +36 -12
- package/dist/input/input.js.map +1 -1
- package/dist/lib/cn.js +11 -1
- package/dist/lib/cn.js.map +1 -1
- package/dist/lib/count-to.js +113 -0
- package/dist/lib/count-to.js.map +1 -0
- package/dist/lib/eye-morph.js +62 -0
- package/dist/lib/eye-morph.js.map +1 -0
- package/dist/lib/field-message.js +354 -0
- package/dist/lib/field-message.js.map +1 -0
- package/dist/lib/field-shell.js +81 -0
- package/dist/lib/field-shell.js.map +1 -0
- package/dist/lib/field-surface.js +11 -8
- package/dist/lib/field-surface.js.map +1 -1
- package/dist/lib/flag-emoji.js +50 -0
- package/dist/lib/flag-emoji.js.map +1 -0
- package/dist/lib/flip.js +88 -0
- package/dist/lib/flip.js.map +1 -0
- package/dist/lib/height-follow.js +59 -0
- package/dist/lib/height-follow.js.map +1 -0
- package/dist/lib/icon-swap.js +47 -0
- package/dist/lib/icon-swap.js.map +1 -0
- package/dist/lib/leaving-list.js +57 -0
- package/dist/lib/leaving-list.js.map +1 -0
- package/dist/lib/menu-highlight.js +64 -0
- package/dist/lib/menu-highlight.js.map +1 -0
- package/dist/lib/merge-refs.js +3 -3
- package/dist/lib/merge-refs.js.map +1 -1
- package/dist/lib/morph-text.js +128 -0
- package/dist/lib/morph-text.js.map +1 -0
- package/dist/lib/motion.js +169 -0
- package/dist/lib/motion.js.map +1 -0
- package/dist/lib/pausable-timeout.js +67 -0
- package/dist/lib/pausable-timeout.js.map +1 -0
- package/dist/lib/rolling-value.js +31 -0
- package/dist/lib/rolling-value.js.map +1 -0
- package/dist/lib/spring-progress.js +114 -0
- package/dist/lib/spring-progress.js.map +1 -0
- package/dist/lib/spring-tween.js +500 -0
- package/dist/lib/spring-tween.js.map +1 -0
- package/dist/lib/svg-id.js +10 -0
- package/dist/lib/svg-id.js.map +1 -0
- package/dist/lib/swap-text.js +79 -0
- package/dist/lib/swap-text.js.map +1 -0
- package/dist/lib/use-copy-feedback.js +36 -0
- package/dist/lib/use-copy-feedback.js.map +1 -0
- package/dist/line-chart/index.d.ts +2 -0
- package/dist/line-chart/index.js +2 -0
- package/dist/line-chart/line-chart.d.ts +76 -0
- package/dist/line-chart/line-chart.js +611 -0
- package/dist/line-chart/line-chart.js.map +1 -0
- package/dist/mention-input/index.d.ts +2 -0
- package/dist/mention-input/index.js +2 -0
- package/dist/mention-input/mention-input.d.ts +98 -0
- package/dist/mention-input/mention-input.js +576 -0
- package/dist/mention-input/mention-input.js.map +1 -0
- package/dist/menu/menu.d.ts +2 -0
- package/dist/menu/menu.js +23 -12
- package/dist/menu/menu.js.map +1 -1
- package/dist/money-input/index.d.ts +2 -0
- package/dist/money-input/index.js +2 -0
- package/dist/money-input/money-input.d.ts +65 -0
- package/dist/money-input/money-input.js +607 -0
- package/dist/money-input/money-input.js.map +1 -0
- package/dist/morph-select/index.d.ts +2 -0
- package/dist/morph-select/index.js +2 -0
- package/dist/morph-select/morph-select.d.ts +71 -0
- package/dist/morph-select/morph-select.js +693 -0
- package/dist/morph-select/morph-select.js.map +1 -0
- package/dist/multi-select/index.d.ts +2 -0
- package/dist/multi-select/index.js +2 -0
- package/dist/multi-select/multi-select.d.ts +36 -0
- package/dist/multi-select/multi-select.js +152 -0
- package/dist/multi-select/multi-select.js.map +1 -0
- package/dist/number-field/index.d.ts +2 -0
- package/dist/number-field/index.js +2 -0
- package/dist/number-field/number-field.d.ts +42 -0
- package/dist/number-field/number-field.js +199 -0
- package/dist/number-field/number-field.js.map +1 -0
- package/dist/onboarding-checklist/index.d.ts +2 -0
- package/dist/onboarding-checklist/index.js +2 -0
- package/dist/onboarding-checklist/onboarding-checklist.d.ts +55 -0
- package/dist/onboarding-checklist/onboarding-checklist.js +577 -0
- package/dist/onboarding-checklist/onboarding-checklist.js.map +1 -0
- package/dist/page-header/index.d.ts +2 -0
- package/dist/page-header/index.js +2 -0
- package/dist/page-header/page-header.d.ts +75 -0
- package/dist/page-header/page-header.js +83 -0
- package/dist/page-header/page-header.js.map +1 -0
- package/dist/password-field/index.d.ts +2 -0
- package/dist/password-field/index.js +2 -0
- package/dist/password-field/password-field.d.ts +37 -0
- package/dist/password-field/password-field.js +101 -0
- package/dist/password-field/password-field.js.map +1 -0
- package/dist/password-strength/index.d.ts +2 -0
- package/dist/password-strength/index.js +2 -0
- package/dist/password-strength/password-strength.d.ts +127 -0
- package/dist/password-strength/password-strength.js +455 -0
- package/dist/password-strength/password-strength.js.map +1 -0
- package/dist/phone-input/index.d.ts +3 -0
- package/dist/phone-input/index.js +3 -0
- package/dist/phone-input/phone-countries.d.ts +29 -0
- package/dist/phone-input/phone-countries.js +170 -0
- package/dist/phone-input/phone-countries.js.map +1 -0
- package/dist/phone-input/phone-input.d.ts +66 -0
- package/dist/phone-input/phone-input.js +440 -0
- package/dist/phone-input/phone-input.js.map +1 -0
- package/dist/popover/popover.d.ts +20 -0
- package/dist/popover/popover.js +33 -9
- package/dist/popover/popover.js.map +1 -1
- package/dist/progress/progress.js +46 -5
- package/dist/progress/progress.js.map +1 -1
- package/dist/radio-group/radio-group.js +8 -7
- package/dist/radio-group/radio-group.js.map +1 -1
- package/dist/reactions/index.d.ts +2 -0
- package/dist/reactions/index.js +2 -0
- package/dist/reactions/reactions.d.ts +50 -0
- package/dist/reactions/reactions.js +315 -0
- package/dist/reactions/reactions.js.map +1 -0
- package/dist/rich-text-editor/index.d.ts +3 -0
- package/dist/rich-text-editor/index.js +2 -0
- package/dist/rich-text-editor/rich-text-editor.d.ts +70 -0
- package/dist/rich-text-editor/rich-text-editor.js +909 -0
- package/dist/rich-text-editor/rich-text-editor.js.map +1 -0
- package/dist/rich-text-editor/rich-text-engine.d.ts +16 -0
- package/dist/rich-text-editor/rich-text-engine.js +366 -0
- package/dist/rich-text-editor/rich-text-engine.js.map +1 -0
- package/dist/rich-text-editor/rich-text-html.js +174 -0
- package/dist/rich-text-editor/rich-text-html.js.map +1 -0
- package/dist/rich-text-editor/rich-text-icons.js +43 -0
- package/dist/rich-text-editor/rich-text-icons.js.map +1 -0
- package/dist/ridgeline/index.d.ts +2 -0
- package/dist/ridgeline/index.js +2 -0
- package/dist/ridgeline/ridgeline.d.ts +55 -0
- package/dist/ridgeline/ridgeline.js +701 -0
- package/dist/ridgeline/ridgeline.js.map +1 -0
- package/dist/scroll-area/index.d.ts +2 -2
- package/dist/scroll-area/index.js +2 -2
- package/dist/scroll-area/scroll-area.d.ts +75 -28
- package/dist/scroll-area/scroll-area.js +212 -41
- package/dist/scroll-area/scroll-area.js.map +1 -1
- package/dist/scrub-input/evaluate.d.ts +14 -0
- package/dist/scrub-input/evaluate.js +72 -0
- package/dist/scrub-input/evaluate.js.map +1 -0
- package/dist/scrub-input/index.d.ts +3 -0
- package/dist/scrub-input/index.js +3 -0
- package/dist/scrub-input/scrub-input.d.ts +74 -0
- package/dist/scrub-input/scrub-input.js +486 -0
- package/dist/scrub-input/scrub-input.js.map +1 -0
- package/dist/search-field/index.d.ts +2 -0
- package/dist/search-field/index.js +2 -0
- package/dist/search-field/search-field.d.ts +27 -0
- package/dist/search-field/search-field.js +155 -0
- package/dist/search-field/search-field.js.map +1 -0
- package/dist/select/select.d.ts +3 -1
- package/dist/select/select.js +95 -25
- package/dist/select/select.js.map +1 -1
- package/dist/separator/separator.d.ts +1 -1
- package/dist/sheet/sheet.js +4 -2
- package/dist/sheet/sheet.js.map +1 -1
- package/dist/shortcut-recorder/index.d.ts +3 -0
- package/dist/shortcut-recorder/index.js +3 -0
- package/dist/shortcut-recorder/shortcut-model.d.ts +27 -0
- package/dist/shortcut-recorder/shortcut-model.js +206 -0
- package/dist/shortcut-recorder/shortcut-model.js.map +1 -0
- package/dist/shortcut-recorder/shortcut-recorder.d.ts +93 -0
- package/dist/shortcut-recorder/shortcut-recorder.js +489 -0
- package/dist/shortcut-recorder/shortcut-recorder.js.map +1 -0
- package/dist/side-nav/index.d.ts +2 -0
- package/dist/side-nav/index.js +2 -0
- package/dist/side-nav/side-nav.d.ts +72 -0
- package/dist/side-nav/side-nav.js +142 -0
- package/dist/side-nav/side-nav.js.map +1 -0
- package/dist/skeleton/skeleton.d.ts +8 -2
- package/dist/skeleton/skeleton.js +6 -2
- package/dist/skeleton/skeleton.js.map +1 -1
- package/dist/slider/index.d.ts +2 -2
- package/dist/slider/slider.d.ts +46 -14
- package/dist/slider/slider.js +188 -50
- package/dist/slider/slider.js.map +1 -1
- package/dist/slope-chart/index.d.ts +2 -0
- package/dist/slope-chart/index.js +2 -0
- package/dist/slope-chart/slope-chart.d.ts +54 -0
- package/dist/slope-chart/slope-chart.js +568 -0
- package/dist/slope-chart/slope-chart.js.map +1 -0
- package/dist/sparkline/index.d.ts +2 -0
- package/dist/sparkline/index.js +2 -0
- package/dist/sparkline/sparkline.d.ts +46 -0
- package/dist/sparkline/sparkline.js +390 -0
- package/dist/sparkline/sparkline.js.map +1 -0
- package/dist/split-bar/index.d.ts +2 -0
- package/dist/split-bar/index.js +2 -0
- package/dist/split-bar/split-bar.d.ts +62 -0
- package/dist/split-bar/split-bar.js +513 -0
- package/dist/split-bar/split-bar.js.map +1 -0
- package/dist/split-button/index.d.ts +2 -0
- package/dist/split-button/index.js +2 -0
- package/dist/split-button/split-button.d.ts +46 -0
- package/dist/split-button/split-button.js +123 -0
- package/dist/split-button/split-button.js.map +1 -0
- package/dist/stat-card/index.d.ts +2 -0
- package/dist/stat-card/index.js +2 -0
- package/dist/stat-card/stat-card.d.ts +101 -0
- package/dist/stat-card/stat-card.js +249 -0
- package/dist/stat-card/stat-card.js.map +1 -0
- package/dist/stepper/index.d.ts +2 -0
- package/dist/stepper/index.js +2 -0
- package/dist/stepper/stepper.d.ts +51 -0
- package/dist/stepper/stepper.js +215 -0
- package/dist/stepper/stepper.js.map +1 -0
- package/dist/streamgraph/index.d.ts +2 -0
- package/dist/streamgraph/index.js +2 -0
- package/dist/streamgraph/streamgraph.d.ts +70 -0
- package/dist/streamgraph/streamgraph.js +613 -0
- package/dist/streamgraph/streamgraph.js.map +1 -0
- package/dist/styles.css +1 -1
- package/dist/swipe-actions/index.d.ts +2 -0
- package/dist/swipe-actions/index.js +2 -0
- package/dist/swipe-actions/swipe-actions.d.ts +67 -0
- package/dist/swipe-actions/swipe-actions.js +552 -0
- package/dist/swipe-actions/swipe-actions.js.map +1 -0
- package/dist/switch/switch.js +10 -9
- package/dist/switch/switch.js.map +1 -1
- package/dist/table/index.d.ts +2 -2
- package/dist/table/index.js +2 -2
- package/dist/table/table.d.ts +87 -4
- package/dist/table/table.js +441 -165
- package/dist/table/table.js.map +1 -1
- package/dist/tabs/tabs.js +143 -7
- package/dist/tabs/tabs.js.map +1 -1
- package/dist/tag-input/index.d.ts +2 -0
- package/dist/tag-input/index.js +2 -0
- package/dist/tag-input/tag-input.d.ts +40 -0
- package/dist/tag-input/tag-input.js +269 -0
- package/dist/tag-input/tag-input.js.map +1 -0
- package/dist/text-morph/index.d.ts +2 -0
- package/dist/text-morph/index.js +2 -0
- package/dist/text-morph/text-morph.d.ts +23 -0
- package/dist/text-morph/text-morph.js +21 -0
- package/dist/text-morph/text-morph.js.map +1 -0
- package/dist/text-reveal/index.d.ts +2 -0
- package/dist/text-reveal/index.js +2 -0
- package/dist/text-reveal/text-reveal.d.ts +27 -0
- package/dist/text-reveal/text-reveal.js +51 -0
- package/dist/text-reveal/text-reveal.js.map +1 -0
- package/dist/text-shimmer/index.d.ts +2 -0
- package/dist/text-shimmer/index.js +2 -0
- package/dist/text-shimmer/text-shimmer.d.ts +29 -0
- package/dist/text-shimmer/text-shimmer.js +29 -0
- package/dist/text-shimmer/text-shimmer.js.map +1 -0
- package/dist/textarea/textarea.d.ts +23 -0
- package/dist/textarea/textarea.js +24 -2
- package/dist/textarea/textarea.js.map +1 -1
- package/dist/theme-switch/index.d.ts +2 -0
- package/dist/theme-switch/index.js +2 -0
- package/dist/theme-switch/theme-switch.d.ts +37 -0
- package/dist/theme-switch/theme-switch.js +123 -0
- package/dist/theme-switch/theme-switch.js.map +1 -0
- package/dist/time-picker/index.d.ts +2 -0
- package/dist/time-picker/index.js +2 -0
- package/dist/time-picker/time-picker.d.ts +54 -0
- package/dist/time-picker/time-picker.js +201 -0
- package/dist/time-picker/time-picker.js.map +1 -0
- package/dist/toast/index.d.ts +2 -2
- package/dist/toast/index.js +2 -2
- package/dist/toast/toast.d.ts +6 -0
- package/dist/toast/toast.js +98 -36
- package/dist/toast/toast.js.map +1 -1
- package/dist/tooltip/tooltip.d.ts +10 -3
- package/dist/tooltip/tooltip.js +67 -18
- package/dist/tooltip/tooltip.js.map +1 -1
- package/dist/treemap/index.d.ts +2 -0
- package/dist/treemap/index.js +2 -0
- package/dist/treemap/treemap.d.ts +53 -0
- package/dist/treemap/treemap.js +699 -0
- package/dist/treemap/treemap.js.map +1 -0
- package/dist/usage-meter/index.d.ts +2 -0
- package/dist/usage-meter/index.js +2 -0
- package/dist/usage-meter/usage-meter.d.ts +50 -0
- package/dist/usage-meter/usage-meter.js +440 -0
- package/dist/usage-meter/usage-meter.js.map +1 -0
- package/dist/waffle-chart/index.d.ts +2 -0
- package/dist/waffle-chart/index.js +2 -0
- package/dist/waffle-chart/waffle-chart.d.ts +57 -0
- package/dist/waffle-chart/waffle-chart.js +426 -0
- package/dist/waffle-chart/waffle-chart.js.map +1 -0
- package/package.json +245 -3
package/README.md
CHANGED
|
@@ -61,6 +61,29 @@ Two deliberate behaviour differences:
|
|
|
61
61
|
`hidden` attribute (`display: none`), which CSS cannot transition out of.
|
|
62
62
|
The chevron still rotates.
|
|
63
63
|
|
|
64
|
+
### Motion (Accordion)
|
|
65
|
+
|
|
66
|
+
A panel opens and closes in place with plain CSS: height follows the content on the smooth spring, opacity eases over 240ms on the way open and 160ms on the way shut, and the answer settles down from 6px up with a little blur. The chevron turns on the snappy spring. React Aria marks a collapsed panel `hidden`, which cannot transition, so the CSS keeps it as a box of no height and takes it out of the accessibility tree with `content-visibility: hidden`. Height needs `interpolate-size` (Chromium); elsewhere it snaps open, still correct. The panel's children now sit in an `accordion-panel-content` wrapper. Nothing animates at mount; reduced motion is instant.
|
|
67
|
+
|
|
68
|
+
## ActionButton
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
<ActionButton onAction={save} pendingLabel="Saving" successLabel="Saved">
|
|
72
|
+
Save changes
|
|
73
|
+
</ActionButton>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
A button for one async commit. Its label morphs letter by letter, from the
|
|
77
|
+
resting text to a pending one to a success one: letters both share keep their
|
|
78
|
+
place, new ones rise in from a soft blur, removed ones lift away, and the width
|
|
79
|
+
springs to fit. The arrow becomes a spinner and then a tick that draws itself.
|
|
80
|
+
|
|
81
|
+
`onAction` is awaited. A rejection returns the button to rest and goes to
|
|
82
|
+
`onActionError` (without it the error is dropped, because the button has
|
|
83
|
+
already returned to rest). It stays focusable while pending (`aria-disabled`,
|
|
84
|
+
`aria-busy`), a live region announces the pending and success labels, and the
|
|
85
|
+
accessible name is always the resting label. `variant` and `size` are Button's.
|
|
86
|
+
|
|
64
87
|
## Alert
|
|
65
88
|
|
|
66
89
|
```tsx
|
|
@@ -86,6 +109,10 @@ No `success` / `warning` / `info` variants yet — one faked call site is b
|
|
|
86
109
|
the Rule of Two. Until then, a `className` override is the supported route;
|
|
87
110
|
consumer CSS always wins.
|
|
88
111
|
|
|
112
|
+
### Motion (Alert)
|
|
113
|
+
|
|
114
|
+
A string `title` or description swaps as a whole when its text changes (the new text rises in, the old lifts away); a different `icon` grows in from small and blurred; the copy column springs to its new height. New optional `open` and `onDismiss` props make the alert come and go: hiding it collapses its height with the fade (the spring, opacity 240ms in and 160ms out), and `onDismiss` adds a Dismiss button (`dismissLabel`). An uncontrolled alert (`onDismiss` without `open`) collapses first and then reports; with `open`, the owner decides. An alert open on first render does not animate, and one with neither prop is the same DOM as before.
|
|
115
|
+
|
|
89
116
|
## AlertDialog
|
|
90
117
|
|
|
91
118
|
```tsx
|
|
@@ -138,6 +165,10 @@ Sizes: `xs` (20px) · `sm` (24px) · `md` (32px, default) · `lg` (40px)
|
|
|
138
165
|
| `className="h-10 w-10"` | `size="lg"` | six ad-hoc sizes became five named ones |
|
|
139
166
|
| `<AvatarImage src />` with no `alt` | `alt` is **required** | `""` for decorative; 15 of 38 legacy uses had none, which fails axe |
|
|
140
167
|
|
|
168
|
+
### Motion (Avatar)
|
|
169
|
+
|
|
170
|
+
A photo that is still downloading after mount fades in and un-blurs over 240ms; one already decoded shows at once. The initials fade in over 240ms while a photo loads or after it fails. New optional `presence` (`"online" | "offline"`) draws a dot on the corner that grows in on the snappy spring, swaps when it changes and shrinks away; its word is read beside the name. The root no longer clips (the dot sits on its edge); the photo clips itself to the circle.
|
|
171
|
+
|
|
141
172
|
## Badge
|
|
142
173
|
|
|
143
174
|
```tsx
|
|
@@ -184,13 +215,17 @@ the product's `--primary-*` scale, value for value — `periwinkle.50` is
|
|
|
184
215
|
No `asChild` — zero call sites, and the legacy `[a&]:hover:` rules existed only
|
|
185
216
|
for the anchor it produced.
|
|
186
217
|
|
|
218
|
+
### Motion (Badge)
|
|
219
|
+
|
|
220
|
+
A new string label swaps as a whole and the pill's width springs to the new size on the morph spring; colour, border and fill ease over 240ms. New optional `icon` swaps in place when it changes. The label now sits in a wrapper inside the badge, so query the root through `[data-slot="badge"]`, not through its text. The reference's hover tint is not ported.
|
|
221
|
+
|
|
187
222
|
## Breadcrumbs
|
|
188
223
|
|
|
189
224
|
```tsx
|
|
190
225
|
<Breadcrumbs>
|
|
191
226
|
<BreadcrumbItem href="/">Home</BreadcrumbItem>
|
|
192
|
-
<BreadcrumbItem href="/design-thinking">Design
|
|
193
|
-
<BreadcrumbItem>User
|
|
227
|
+
<BreadcrumbItem href="/design-thinking">Design thinking</BreadcrumbItem>
|
|
228
|
+
<BreadcrumbItem>User personas</BreadcrumbItem>
|
|
194
229
|
</Breadcrumbs>
|
|
195
230
|
```
|
|
196
231
|
|
|
@@ -224,6 +259,11 @@ Two smaller deliberate changes: the separator is `/` rather than a lucide
|
|
|
224
259
|
separators were the default), and hovering a crumb underlines it as well as
|
|
225
260
|
darkening it — colour alone disappears under forced-colors.
|
|
226
261
|
|
|
262
|
+
|
|
263
|
+
### Motion
|
|
264
|
+
|
|
265
|
+
A crumb added after the first render slides in 8px from the left over 240ms and the crumbs that stay slide to their new place; link colour eases over 160ms. The trail present at first render is still. A removed crumb disappears at once (it would need a leaving copy), and the reference's underline fade is not ported: Alfred's links carry no underline at rest by design.
|
|
266
|
+
|
|
227
267
|
## Button
|
|
228
268
|
|
|
229
269
|
| variant | |
|
|
@@ -246,17 +286,54 @@ Sizes: `sm` (32px) · `md` (36px, default) · `lg` (40px) · `icon` (36px
|
|
|
246
286
|
one it has no accessible name and fails axe's `button-name` rule — the
|
|
247
287
|
`IconOnly` story exists to keep that true.
|
|
248
288
|
|
|
249
|
-
`
|
|
250
|
-
|
|
289
|
+
Give it an `href` and it is a link: a real `<a>`, announced as a link, with the
|
|
290
|
+
same classes and the same hover, press and focus-ring states. Use it whenever
|
|
291
|
+
activating the control navigates.
|
|
251
292
|
|
|
252
293
|
```tsx
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
<a href="/reports" className={buttonVariants({ variant: "outline" })}>
|
|
294
|
+
<Button href="/reports" variant="outlineNeutral">
|
|
256
295
|
View reports
|
|
257
|
-
</
|
|
296
|
+
</Button>
|
|
258
297
|
```
|
|
259
298
|
|
|
299
|
+
For client-side routing, mount React Aria's `RouterProvider` once above it;
|
|
300
|
+
without one an `href` is an ordinary page load, which is what an external or
|
|
301
|
+
download link wants anyway. `target`, `rel` and `download` pass through.
|
|
302
|
+
|
|
303
|
+
`buttonVariants` is still exported, but it is no longer the way to style a
|
|
304
|
+
link. On your own `<a>` its hover, press and focus classes never apply: they key
|
|
305
|
+
on React Aria data attributes a plain anchor never receives, so keyboard focus
|
|
306
|
+
is invisible.
|
|
307
|
+
|
|
308
|
+
### Motion
|
|
309
|
+
|
|
310
|
+
A Button moves, and every motion is pure CSS plus a little plain React where CSS
|
|
311
|
+
cannot reach (no animation library):
|
|
312
|
+
|
|
313
|
+
- **Press.** It scales down while pressed (0.97; 0.96 for an icon button; 0.985
|
|
314
|
+
above 220px wide) and springs back. Menu and popover triggers
|
|
315
|
+
(`aria-haspopup`, `role="combobox"`, `data-state`), disabled and pending
|
|
316
|
+
buttons never scale.
|
|
317
|
+
- **A changing label.** A different text, or a different icon, crossfades: the
|
|
318
|
+
old one is popped out of flow where it was, `aria-hidden`, and lifts away
|
|
319
|
+
while the new one rises in, and the width springs to fit. Nothing animates on
|
|
320
|
+
first render. While it runs the OUTGOING label is still in the DOM, so read a
|
|
321
|
+
button's text after the change settles, and keep `id`s, inputs and anything
|
|
322
|
+
stateful out of a label that changes.
|
|
323
|
+
- **`isPending`.** A spinner scales in over a label that fades; the button
|
|
324
|
+
stays focusable (`aria-disabled`) and announces itself (`aria-busy`).
|
|
325
|
+
- **Hover and press** on `primary` step its opacity (0.91, 0.84) and every
|
|
326
|
+
filled and outlined variant gains a resting shadow on hover. A disabled
|
|
327
|
+
`primary` fades; the other variants stay opaque and mute their colours.
|
|
328
|
+
|
|
329
|
+
Reduced motion keeps the opacity change and drops the movement and blur. The
|
|
330
|
+
spring easings are `linear()` curves (Chrome 113, Safari 17.2, Firefox 112);
|
|
331
|
+
older browsers get a cubic-bezier with the same overshoot.
|
|
332
|
+
|
|
333
|
+
The `@seekalfred/styles` motion tokens behind it are `--ease-*` and
|
|
334
|
+
`--duration-*` (`settle`, `enter`, `snappy`, `morph`, `gentle`, `calm`,
|
|
335
|
+
`smooth`, `quick`, `instant`, …).
|
|
336
|
+
|
|
260
337
|
## Migrating from the legacy shadcn Button
|
|
261
338
|
|
|
262
339
|
The variant and size vocabulary is unchanged, so most call sites move by
|
|
@@ -269,7 +346,7 @@ React Aria Components rather than a bare `<button>`:
|
|
|
269
346
|
| `disabled` | `isDisabled` | |
|
|
270
347
|
| `variant="default"` | `variant="primary"` | `default` named nothing. |
|
|
271
348
|
| `size="default"` | `size="md"` | Same 36px height. |
|
|
272
|
-
| `<Button asChild><a /></Button>` | `<
|
|
349
|
+
| `<Button asChild><a /></Button>` | `<Button href>` | Renders React Aria's Link, so the states still work. Needs `RouterProvider` for client-side routes. |
|
|
273
350
|
|
|
274
351
|
The first four are mechanical. `asChild` is not — each site needs a decision
|
|
275
352
|
about whether it wanted a link or a button.
|
|
@@ -287,6 +364,34 @@ Two things that changed on purpose:
|
|
|
287
364
|
`[data-theme="dark"]`. Every `dark:` class in the original is gone, not
|
|
288
365
|
reimplemented.
|
|
289
366
|
|
|
367
|
+
## ButtonGroup
|
|
368
|
+
|
|
369
|
+
```tsx
|
|
370
|
+
<ButtonGroup
|
|
371
|
+
label="Document actions"
|
|
372
|
+
items={[
|
|
373
|
+
{ id: "edit", label: "Edit", icon: <PencilIcon aria-hidden /> },
|
|
374
|
+
{ id: "share", label: "Share", reserve: ["Copied"], onSelect: copyLink },
|
|
375
|
+
]}
|
|
376
|
+
menu={{ label: "More actions", items: [{ id: "del", label: "Delete", destructive: true }] }}
|
|
377
|
+
/>
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
Related actions joined into one surface: a shared border and radius, hairline
|
|
381
|
+
dividers, no gaps. One soft highlight glides between segments under the pointer or
|
|
382
|
+
keyboard focus (it appears in place, and only moves between segments travel), the
|
|
383
|
+
pressed segment answers inside itself, and an optional chevron segment opens more
|
|
384
|
+
actions in a menu that grows from the group's edge.
|
|
385
|
+
|
|
386
|
+
A segment's label can change in place without resizing it: `reserve` lists the
|
|
387
|
+
other labels it can show and the segment sizes to the widest. A label that
|
|
388
|
+
changes right after its segment is pressed (`Share` to `Copied`) is announced
|
|
389
|
+
once. When the row does not fit, segments with an icon drop their label and keep
|
|
390
|
+
the icon (`collapseLabels`). Tab visits every segment; the arrow keys along the
|
|
391
|
+
orientation, Home and End move between them. `variant` is `outline` or
|
|
392
|
+
`solid`, `size` is `sm` or `md`, `orientation` is `horizontal` or
|
|
393
|
+
`vertical`.
|
|
394
|
+
|
|
290
395
|
## Calendar
|
|
291
396
|
|
|
292
397
|
**Maturity: `experimental`.** The API may still move; `DateRangePicker` is its
|
|
@@ -328,6 +433,18 @@ so a September-focused window opens on August. Range pickers usually want
|
|
|
328
433
|
`"start"`. It is inherited from React Aria rather than defaulted here, because
|
|
329
434
|
either alignment is legitimate.
|
|
330
435
|
|
|
436
|
+
### Motion
|
|
437
|
+
|
|
438
|
+
Built in plain CSS and a little plain React, with no animation library.
|
|
439
|
+
|
|
440
|
+
- A one-month calendar shows its month on a strip with the neighbours beside it. Moving by a month slides the strip on a spring; the other months are faded back and `inert`. A jump of more than a month (Shift + Page Down) brings the new month in from one month away instead of scrolling through the ones between. The window follows the height of the month in view.
|
|
441
|
+
- The month title, now in the header beside the buttons, crosses in place: the new month arrives from the side the strip travels to while the old one leaves toward the other. React Aria's own heading stays, visually hidden, as the text.
|
|
442
|
+
- In a single-date `Calendar`, the selected day's fill is one disc that glides on a straight line between days, grows in when a selection appears in a month and fades out when it leaves. Each number flips colour as the disc passes it: the day it lands on waits longer on long hops, the day it leaves waits longer on short ones.
|
|
443
|
+
- Day and header buttons fill on hover, press to .94 and .96 and spring back. Nothing animates on first render. `CalendarRange` and calendars with several months keep their layout and get the press and hover motion; the range band has no motion yet.
|
|
444
|
+
- Reduced motion swaps the slides and glides for plain fades or nothing.
|
|
445
|
+
|
|
446
|
+
**`DatePicker`** rolls its value part by part (a later date rises in, an earlier one drops in, a part that did not change holds still, and parts after a wider or narrower one slide along). The chevron turns while the list is open, the list grows from the field and leaves quicker, and the trigger fills muted while it is open. A pick is seen for 300ms (the disc glides, the footer confirms it) before the list closes; Clear closes at once. The footer gains a status line (`selectedStatusLabel`, `emptyStatusLabel`) and takes Clear (`clearable`, `clearLabel`) out of the calendar. Reduced motion closes at once and fades.
|
|
447
|
+
|
|
331
448
|
### Loading the tokens is not optional
|
|
332
449
|
|
|
333
450
|
`@seekalfred/primitives/styles.css` defines **no** token values — not
|
|
@@ -346,6 +463,54 @@ not — and the two that hurt are `--focus-ring-width` and `--focus-ring-offset`
|
|
|
346
463
|
because their absence removes the focus indicator rather than merely shifting a
|
|
347
464
|
colour. `apps/smoke` is the worked example of a correct consumer.
|
|
348
465
|
|
|
466
|
+
|
|
467
|
+
## DateRangePicker
|
|
468
|
+
|
|
469
|
+
```tsx
|
|
470
|
+
<DateRangePicker label="Invoice dates" value={range} onChange={setRange} />
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
A trigger that **grows into the panel it opens**: one surface whose width, height,
|
|
474
|
+
corners and offset ride springs from the trigger's box to the panel's and back,
|
|
475
|
+
while the panel's content arrives just behind it and leaves just ahead of it. The
|
|
476
|
+
panel holds a rail of presets (`presets`, with `defaultDateRangePresets` from today
|
|
477
|
+
back to the start of the year), the typed start and end, and two months side by side
|
|
478
|
+
(one month under a scrolling row of presets when there is no room; `months` forces
|
|
479
|
+
either). **Nothing is applied until Apply**: the panel edits a draft, `onChange`
|
|
480
|
+
fires on Apply, and Cancel or Escape throws the draft away. Values are `DateValue`
|
|
481
|
+
objects, either end may be `null`, and `messages` renames every string.
|
|
482
|
+
|
|
483
|
+
**Motion** (plain CSS and a little plain React, no animation library; the five
|
|
484
|
+
springs are sampled from the reference solver):
|
|
485
|
+
|
|
486
|
+
- The surface springs open on the grow spring and shrinks on a slower one; the
|
|
487
|
+
trigger's icons and its value step aside as it grows (120ms, a little blur) and
|
|
488
|
+
come back after it has shrunk; the root stays on top for half a second after
|
|
489
|
+
closing so the shrinking box never slips under a neighbour.
|
|
490
|
+
- The formatted range **flies** from the trigger into the footer as the surface
|
|
491
|
+
grows and back into the trigger as it shrinks; its words roll one by one (a later
|
|
492
|
+
date rises, an earlier one drops, a word that did not change holds still).
|
|
493
|
+
- Months slide together in the direction you travel: the new pair arrives from the
|
|
494
|
+
side you travel to with a little blur while the old one is kept where it stood and
|
|
495
|
+
slides away, `inert`.
|
|
496
|
+
- The highlight is one bar per week under the days, which stretches from the edge the
|
|
497
|
+
range arrives from as you hover or arrow to the end day, plus a solid end at each
|
|
498
|
+
side which glides from day to day. A month that has just arrived shows its
|
|
499
|
+
highlight in place; only a change after that animates.
|
|
500
|
+
- One highlight glides between the presets, and the compact row scrolls to keep the
|
|
501
|
+
active one in view. Days press to .9, buttons to .97 and the header arrows to .96.
|
|
502
|
+
|
|
503
|
+
The calendar is React Aria's `RangeCalendar`, so keyboard, locale and calendar-system
|
|
504
|
+
behaviour are its own; the bars and ends are measured from the cells it renders.
|
|
505
|
+
`clearable` adds a Clear button in the footer. Reduced motion jumps the surface and
|
|
506
|
+
swaps the slides and rolls for plain fades.
|
|
507
|
+
|
|
508
|
+
**Breaking from the earlier DateRangePicker:** the panel now edits a draft that
|
|
509
|
+
Apply commits (it used to commit when the second day was pressed), the calendar and
|
|
510
|
+
the typed fields sit beside a preset rail, and `DateRangePicker` is its own module
|
|
511
|
+
(still exported from `/date-picker`). It reads the viewer's local today for the
|
|
512
|
+
presets, so `@internationalized/date` (a peer dependency) is imported at runtime.
|
|
513
|
+
|
|
349
514
|
## Card
|
|
350
515
|
|
|
351
516
|
```tsx
|
|
@@ -385,6 +550,10 @@ so `onClick` works exactly as it did — and a `div` with a click handler i
|
|
|
385
550
|
focusable and has no role. One legacy call site does this. Use a `Button` or a
|
|
386
551
|
link inside the card instead.
|
|
387
552
|
|
|
553
|
+
### Motion (Card)
|
|
554
|
+
|
|
555
|
+
`interactive` makes a card respond to a mouse or pen: it floats 2px on the snappy spring while pointed at, its border darkens (160ms) and its shadow deepens (240ms). Touch never lifts, reduced motion does not lift, and a card without `interactive` never moves. Not ported: a media zoom, a status line and the quick-look morph, which need slots Alfred's card does not have.
|
|
556
|
+
|
|
388
557
|
## Checkbox
|
|
389
558
|
|
|
390
559
|
```tsx
|
|
@@ -413,6 +582,45 @@ other form controls will take.
|
|
|
413
582
|
| *(no way to set it)* | `isIndeterminate` | `indeterminate` is a DOM property, not an attribute |
|
|
414
583
|
| *(no label at all)* | `children` | the legacy control had no accessible name |
|
|
415
584
|
|
|
585
|
+
### Motion (Checkbox)
|
|
586
|
+
|
|
587
|
+
The chosen colour grows in from small on the snappy spring while it fades in, the mark draws itself, and the check morphs into the dash and back on the morph spring (the `d` property transitions in Chromium; elsewhere it swaps). The square (not the hit area) presses to .95 in 100ms and springs back; its border eases over 160ms and darkens on hover while unchosen. **DOM:** the two glyph svgs are now one `checkbox-mark` (with `data-mark` of `check` or `indeterminate`) and a `checkbox-fill` element carries the colour, so the box's own background no longer turns primary.
|
|
588
|
+
|
|
589
|
+
## ConfirmMorph
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
<ConfirmMorph label="Delete project" onConfirm={deleteProject} onUndo={restoreProject} />
|
|
593
|
+
<ConfirmMorph label="Delete project" confirmMode="hold" onConfirm={deleteProject} />
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
A button for destructive or important actions that asks in place. The same pill
|
|
597
|
+
morphs into a question with Cancel and Confirm, then a spinner, then a result with
|
|
598
|
+
Undo; its width springs to each face while the faces crossfade at their natural
|
|
599
|
+
width. Use it where a dialog would be heavy.
|
|
600
|
+
|
|
601
|
+
`confirmMode` is `press`, `hold` (a press held for `holdDuration` fills the
|
|
602
|
+
button, and drains back if let go early) or `countdown` (a draining ring and
|
|
603
|
+
ticking seconds that Cancel or Escape can still stop). Escape, a press outside,
|
|
604
|
+
or `confirmTimeout` answer no; resting the pointer on it, or a hidden tab,
|
|
605
|
+
pauses the clock. A press right after the question appears is the tail of the
|
|
606
|
+
opening press, not an answer. A rejection shows an error face with Retry.
|
|
607
|
+
Controlled with `state` and `onStateChange`. A live region announces each step,
|
|
608
|
+
and focus follows the control between faces only when it was already inside.
|
|
609
|
+
|
|
610
|
+
## CopyButton
|
|
611
|
+
|
|
612
|
+
```tsx
|
|
613
|
+
<CopyButton value={token} />
|
|
614
|
+
<CopyButton value={url} iconOnly variant="plain" />
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
Copies a value and says so, calmly: the icon trades places in one soft breath
|
|
618
|
+
(copy, then a tick that draws itself, or an alert if the clipboard refused), the
|
|
619
|
+
label's changed letters rise in, and the button never changes width because its
|
|
620
|
+
label cell reserves the widest of `Copy`, `Copied` and `Failed`. It returns to
|
|
621
|
+
rest by itself, a live region announces the result, and the accessible name is
|
|
622
|
+
always the resting label.
|
|
623
|
+
|
|
416
624
|
## Dialog
|
|
417
625
|
|
|
418
626
|
```tsx
|
|
@@ -458,6 +666,128 @@ Two legacy behaviours are deliberately not carried: the enter/exit animation
|
|
|
458
666
|
and `data-exiting` for whoever adds it), and the footer's `flex-col-reverse`,
|
|
459
667
|
which put DOM order and visual order in disagreement below `sm` — WCAG 1.3.2.
|
|
460
668
|
|
|
669
|
+
### Motion (Dialog)
|
|
670
|
+
|
|
671
|
+
The overlay fades in over 240ms and out over 160ms and blurs the page a little; the panel rises 8px and grows from .96 on the smooth spring while it fades in, and leaves over 160ms. A string `title` or `description` swaps as a whole when its text changes. The close button fills on hover and presses to .96. While the dialog leaves, the overlay lets clicks through. New `entrance` prop (`"rise"` or `"none"`) lets an edge-anchored preset opt out; Sheet does. Reduced motion is a plain fade.
|
|
672
|
+
|
|
673
|
+
## EmptyState
|
|
674
|
+
|
|
675
|
+
```tsx
|
|
676
|
+
<EmptyState action={<Button size="sm">Clear filters</Button>}>
|
|
677
|
+
No invoices match these filters.
|
|
678
|
+
</EmptyState>
|
|
679
|
+
```
|
|
680
|
+
|
|
681
|
+
What a list shows when it has nothing to show: one muted line, centred in a
|
|
682
|
+
bordered box the width of the list. `action` is an optional next step under the
|
|
683
|
+
text and is omitted from the DOM when absent.
|
|
684
|
+
|
|
685
|
+
### Motion (EmptyState)
|
|
686
|
+
|
|
687
|
+
New optional `icon` settles in once when the state first appears (small and transparent to rest over 480ms); a different icon component later swaps in place. A string message swaps as a whole when it changes and the message column follows its height on the smooth spring. The message now sits in an `empty-state-copy` frame.
|
|
688
|
+
|
|
689
|
+
## ExpandingButtonGroup
|
|
690
|
+
|
|
691
|
+
```tsx
|
|
692
|
+
<ExpandingButtonGroup
|
|
693
|
+
label="Message actions"
|
|
694
|
+
items={[
|
|
695
|
+
{ id: "reply", label: "Reply", icon: <ReplyIcon aria-hidden /> },
|
|
696
|
+
{ id: "archive", label: "Archive", doneLabel: "Archived", icon: <ArchiveIcon aria-hidden /> },
|
|
697
|
+
{ id: "delete", label: "Delete", tone: "danger", icon: <TrashIcon aria-hidden /> },
|
|
698
|
+
]}
|
|
699
|
+
/>
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
A compact pill of icon buttons. One action always shows its label; the one you
|
|
703
|
+
point at or focus takes over the label while its neighbours slide aside, on a
|
|
704
|
+
spring. Moving between actions needs the pointer a few pixels inside the next one,
|
|
705
|
+
so resting on a boundary never flips between them. An action can confirm in place
|
|
706
|
+
(`doneLabel`): the icon draws a check and the label becomes a past tense word
|
|
707
|
+
without the width changing. A promise from `onSelect` keeps the button busy.
|
|
708
|
+
|
|
709
|
+
On touch, the first tap reveals the label and a second tap runs the action. A
|
|
710
|
+
disabled action stays focusable and still reveals its label. Arrow keys, Home and
|
|
711
|
+
End rove focus.
|
|
712
|
+
|
|
713
|
+
## FileDropZone
|
|
714
|
+
|
|
715
|
+
```tsx
|
|
716
|
+
<FileDropZone
|
|
717
|
+
label="Contract"
|
|
718
|
+
prompt="Drop a contract here"
|
|
719
|
+
hint="PDF only, up to 20 MB."
|
|
720
|
+
acceptedFileTypes={["application/pdf"]}
|
|
721
|
+
onFiles={(files) => upload(files[0])}
|
|
722
|
+
/>
|
|
723
|
+
```
|
|
724
|
+
|
|
725
|
+
A dashed drop area with a "Choose file" button, on React Aria's `DropZone` and
|
|
726
|
+
`FileTrigger`. The browser's own file control ("Choose Files / No file chosen")
|
|
727
|
+
is never shown: the real input is visually hidden and the button opens it, so
|
|
728
|
+
Tab reaches the button and Enter or Space opens the dialog. `hint` is where the
|
|
729
|
+
accepted types and the size limit go, stated before a file is chosen.
|
|
730
|
+
`acceptedFileTypes` and `allowsMultiple` reach the dialog; a drop is not
|
|
731
|
+
filtered, so the consumer decides what it can read. Without `allowsMultiple`
|
|
732
|
+
only the first file is handed over.
|
|
733
|
+
|
|
734
|
+
### Motion (FileDropZone)
|
|
735
|
+
|
|
736
|
+
The surface eases its fill over 240ms and its edge over 160ms. With a file over it, the zone breathes (swells 1.2% and settles over 2.2s, repeating) and light follows the file: a wash inside and a brighter stretch of the edge nearest it, which lean toward the pointer on a short ease (React Aria's `onDropMove` gives the position). New optional `dropPrompt` swaps in for the prompt while a file is over. Reduced motion does not breathe. Not ported: the upload list, progress bars, retry, thumbnails, paste, the compact prompt, the sheets illustration and the dashes closing into a solid edge.
|
|
737
|
+
|
|
738
|
+
## FilterBar
|
|
739
|
+
|
|
740
|
+
```tsx
|
|
741
|
+
<FilterBar filters={filters} inline={<Input aria-label="Search" />} />
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
A list toolbar: every dropdown filter sits behind one Filters button with a count badge, and each set filter shows as a removable chip with Clear all.
|
|
745
|
+
|
|
746
|
+
### Motion
|
|
747
|
+
|
|
748
|
+
A chip added after the first render opens its slot from nothing while it fades up from a blur and scales in, so the chips beside it glide over; a removed chip stays while it fades and shrinks over 120ms and its slot collapses on a spring; a changed value swaps its text and the chip's width springs to it; Clear all comes and goes in its own slot; and the row follows its height when a chip makes a line wrap. Nothing animates at first render, and a removed chip is `aria-hidden` and `inert` while it leaves. The slot widths animate from and to `auto`, which needs `interpolate-size` (Chromium); elsewhere they snap, still correct. Not ported: the Add-filter button that grows into a field-and-value menu (Alfred's Filters button opens a popover of selects, which gets the Popover motion below).
|
|
749
|
+
|
|
750
|
+
## FloatingButtonGroup
|
|
751
|
+
|
|
752
|
+
```tsx
|
|
753
|
+
<FloatingButtonGroup
|
|
754
|
+
label="Board actions"
|
|
755
|
+
items={[
|
|
756
|
+
{ id: "undo", label: "Undo", icon: <UndoIcon aria-hidden />, iconOnly: true, shortcut: "⌘Z" },
|
|
757
|
+
{ type: "separator" },
|
|
758
|
+
{ id: "present", label: "Present", isPressed: presenting, onSelect: toggle },
|
|
759
|
+
]}
|
|
760
|
+
/>
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
Separate soft buttons in a quiet tray (a `role="toolbar"`). One shared highlight
|
|
764
|
+
morphs, in position and size, to whichever button the pointer or keyboard focus is
|
|
765
|
+
on, fades when the pointer leaves, and settles darker and a touch smaller while a
|
|
766
|
+
button is pressed. Arrow keys move focus (skipping disabled buttons), Home and End
|
|
767
|
+
jump to the ends, and only one button is a tab stop.
|
|
768
|
+
|
|
769
|
+
`reserveLabels` lists the other labels an item can show, so a label change
|
|
770
|
+
(`Share` to `Copied`) never moves anything. Icon-only items put their label,
|
|
771
|
+
and any `shortcut` hint, in a tooltip. `isPressed` marks a mode toggle.
|
|
772
|
+
`variant` is `muted` or `floating` (raised, for an overlay).
|
|
773
|
+
|
|
774
|
+
## IconMorph
|
|
775
|
+
|
|
776
|
+
```tsx
|
|
777
|
+
<Button aria-label="Menu" aria-pressed={open} onPress={toggle}>
|
|
778
|
+
<IconMorph name="menu" state={open} />
|
|
779
|
+
</Button>
|
|
780
|
+
```
|
|
781
|
+
|
|
782
|
+
Icons that morph between two states instead of swapping: `menu` to close, `play`
|
|
783
|
+
to pause, `plus` to check, `copy` to check, `theme` (sun to moon), `chevron`
|
|
784
|
+
(turned), `bell` (notifications on) and `heart` (liked). Lines rotate and slide,
|
|
785
|
+
shapes interpolate their outlines, and two carry a moment of their own: the bell
|
|
786
|
+
rings when it turns on, and the heart bursts when liked. Every morph rides an
|
|
787
|
+
interruptible spring, so toggling quickly reverses from wherever the icon is. The
|
|
788
|
+
icons are decorative: put them in a button that carries the name and
|
|
789
|
+
`aria-pressed`. Each is also exported on its own (`MenuIcon`, `HeartIcon`, …).
|
|
790
|
+
|
|
461
791
|
## Input and Textarea
|
|
462
792
|
|
|
463
793
|
```tsx
|
|
@@ -483,6 +813,7 @@ invalid styling on only one of them.
|
|
|
483
813
|
| `value` / `onChange` | unchanged | still a real DOM event |
|
|
484
814
|
| `disabled` | unchanged | native |
|
|
485
815
|
| `aria-invalid` | unchanged | drives the invalid border with no prop of our own |
|
|
816
|
+
| hand-rolled `<label>` + helper + error text | `label` / `description` / `error` | optional; see below |
|
|
486
817
|
| `h-9` (36px) | density scale | **46px at the default**, matching Button |
|
|
487
818
|
| `text-base md:text-sm` | `text-base` | one size; 16px avoids iOS Safari zooming on focus |
|
|
488
819
|
| `min-h-[80px]` | `min-h-20` | same 80px, from the spacing scale |
|
|
@@ -537,7 +868,90 @@ second name source makes it announce twice.
|
|
|
537
868
|
|
|
538
869
|
**An invalid field needs a message.** The border changes colour, and colour
|
|
539
870
|
alone is not a state cue (WCAG 1.4.1). Pair `aria-invalid` with
|
|
540
|
-
`aria-describedby` pointing at the error text
|
|
871
|
+
`aria-describedby` pointing at the error text, or use the `error` prop below,
|
|
872
|
+
which does both for you.
|
|
873
|
+
|
|
874
|
+
### Label, description and error
|
|
875
|
+
|
|
876
|
+
```tsx
|
|
877
|
+
<Input
|
|
878
|
+
label="Email"
|
|
879
|
+
description="We only use it to send receipts."
|
|
880
|
+
error={touched && !valid ? "Enter a valid email address." : undefined}
|
|
881
|
+
value={v}
|
|
882
|
+
onChange={(e) => set(e.target.value)}
|
|
883
|
+
/>
|
|
884
|
+
```
|
|
885
|
+
|
|
886
|
+
`Input` and `Textarea` take three optional props. `label` is a `ReactNode`
|
|
887
|
+
shown above the control. `description` (helper text) and `error` are plain
|
|
888
|
+
**strings**, because the library animates their copy word by word. An empty or
|
|
889
|
+
`undefined` `error` closes the error row.
|
|
890
|
+
|
|
891
|
+
**Passing any of the three wraps the control in a field wrapper, even when the
|
|
892
|
+
value is `undefined`. Passing none leaves the DOM exactly what it was before
|
|
893
|
+
these props existed:** the bare control, with no generated `id`. The wrapper is
|
|
894
|
+
decided by whether the prop is present, not by its value. That is deliberate:
|
|
895
|
+
`error={maybeMessage}` is the common shape, and if the wrapper came and went
|
|
896
|
+
with the message, the input would move in the tree every time validation
|
|
897
|
+
changed. React would remount it, losing focus and any uncontrolled value.
|
|
898
|
+
Toggling `error` never remounts the input.
|
|
899
|
+
|
|
900
|
+
The wiring, done for you:
|
|
901
|
+
|
|
902
|
+
- `label` is tied to the control with `htmlFor`. The control keeps your `id`, or
|
|
903
|
+
gets a generated one.
|
|
904
|
+
- `description` and `error` are named in `aria-describedby`, after any value
|
|
905
|
+
you passed yourself.
|
|
906
|
+
- A non-empty `error` sets `aria-invalid`, so the field takes its invalid border
|
|
907
|
+
with no extra prop. Without `error`, your own `aria-invalid` still applies.
|
|
908
|
+
- The error row has `role="alert"`.
|
|
909
|
+
- Screen readers get the plain copy. The animated words are `aria-hidden`.
|
|
910
|
+
|
|
911
|
+
Limits worth knowing:
|
|
912
|
+
|
|
913
|
+
- **Do not pass `aria-label` together with `label`.** `aria-label` wins, so the
|
|
914
|
+
visible name is no longer the accessible one (WCAG 2.5.3).
|
|
915
|
+
- **Inside a React Aria `TextField` the three props are ignored.** The
|
|
916
|
+
`TextField` supplies its own label and messages.
|
|
917
|
+
- The alert node is inserted together with its text. Whether a given screen
|
|
918
|
+
reader announces it on insertion is not tested here, so check your own
|
|
919
|
+
combinations if announcement timing matters.
|
|
920
|
+
|
|
921
|
+
### Motion
|
|
922
|
+
|
|
923
|
+
The rows move, in pure CSS plus a little plain React (no animation library):
|
|
924
|
+
|
|
925
|
+
- **A row opens on a spring** when `description` or `error` appears after the
|
|
926
|
+
field has mounted, and **closes the same way** when it goes away, still
|
|
927
|
+
showing its last text while it shuts. A row present on first render is just
|
|
928
|
+
there: nothing animates on mount.
|
|
929
|
+
- **Changed words rise in** from a soft blur, and the old ones lift away from
|
|
930
|
+
where they stood. Words are matched by position, so changing a word near the
|
|
931
|
+
front replaces every word after it.
|
|
932
|
+
- **Numbers roll digit by digit.** In copy such as "12 characters", only the
|
|
933
|
+
digits that changed move. They arrive from below when the number grew and from
|
|
934
|
+
above when it shrank. A gained digit opens its width and a lost one closes it.
|
|
935
|
+
- **The row follows the copy's height**, so text that wraps opens its next line
|
|
936
|
+
instead of being clipped.
|
|
937
|
+
- **Reduced motion swaps instantly.** There is no movement and no collapsed
|
|
938
|
+
frame.
|
|
939
|
+
|
|
940
|
+
The `Textarea` lifts replaced words away slightly faster than `Input` (160ms
|
|
941
|
+
against 180ms).
|
|
942
|
+
|
|
943
|
+
### The look of a field
|
|
944
|
+
|
|
945
|
+
These changes are shared by `Input`, `Textarea`, `Select` and `DatePicker`,
|
|
946
|
+
because they all use `fieldSurfaceVariants`:
|
|
947
|
+
|
|
948
|
+
- The border and background transition in 160ms on the settle curve. Only those
|
|
949
|
+
two properties animate; the focus outline switches without a transition.
|
|
950
|
+
- **Hover and keyboard focus darken the border** to the text colour. The focus
|
|
951
|
+
ring stays, because forced-colors drops border colours and the ring is the
|
|
952
|
+
visible focus indicator.
|
|
953
|
+
- A disabled field takes the muted background and does not darken on hover.
|
|
954
|
+
- An invalid field always keeps its invalid border, hovered or focused.
|
|
541
955
|
|
|
542
956
|
## Label
|
|
543
957
|
|
|
@@ -589,6 +1003,91 @@ the dropped nine turns out to be wanted.
|
|
|
589
1003
|
from `useLocale()`, so `dir` on the document does **not** reach a portalled
|
|
590
1004
|
menu.
|
|
591
1005
|
|
|
1006
|
+
## PageHeader
|
|
1007
|
+
|
|
1008
|
+
```tsx
|
|
1009
|
+
<PageHeader
|
|
1010
|
+
title="Projects"
|
|
1011
|
+
description="Everything your team is working on."
|
|
1012
|
+
meta="updated 14 Oct 2026"
|
|
1013
|
+
actions={<Button>New project</Button>}
|
|
1014
|
+
/>
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
The page's single `<h1>`, in the display face at the `h3` step of the scale.
|
|
1018
|
+
`description` sits under it, muted and capped at a readable measure. `actions`
|
|
1019
|
+
share the title's row at its end and wrap below it on a narrow screen. `meta`
|
|
1020
|
+
sits inline after the title on its baseline. `back` is a slot above the title
|
|
1021
|
+
for a detail page's way back: pass your router's link, the header only sizes
|
|
1022
|
+
and colours it. Every slot is omitted from the DOM when its prop is absent.
|
|
1023
|
+
|
|
1024
|
+
## PasswordField
|
|
1025
|
+
|
|
1026
|
+
```tsx
|
|
1027
|
+
<PasswordField label="Password" description="At least eight characters." />
|
|
1028
|
+
<PasswordField
|
|
1029
|
+
label="Contraseña"
|
|
1030
|
+
messages={{ showPassword: "Mostrar contraseña", hidePassword: "Ocultar contraseña" }}
|
|
1031
|
+
/>
|
|
1032
|
+
```
|
|
1033
|
+
|
|
1034
|
+
A password input with a show and hide toggle inside the field's border. Switching
|
|
1035
|
+
resolves the text in place (the dots or characters settle in from a soft blur, on
|
|
1036
|
+
every toggle but never on mount), and the eye is one outline that a slash draws
|
|
1037
|
+
across and undraws, cutting the outline beneath it, instead of swapping two
|
|
1038
|
+
icons. The toggle presses to 0.96 and springs back; the helper text opens and
|
|
1039
|
+
changes the way `Input`'s does (words only, so a count in it does not roll).
|
|
1040
|
+
|
|
1041
|
+
`label` is required and visible. `description` is optional helper text.
|
|
1042
|
+
`messages` renames the toggle (`showPassword`, `hidePassword`, English by
|
|
1043
|
+
default). Native input props apply to the input, and `className` goes to the
|
|
1044
|
+
input; `disabled` also disables the toggle, and `aria-invalid` gives the box its
|
|
1045
|
+
invalid border. The box shares the field look of `Input` and `Textarea`.
|
|
1046
|
+
|
|
1047
|
+
**The toggle's name changes with its state and it is also `aria-pressed`**, so a
|
|
1048
|
+
screen reader may announce both ("Hide password, pressed"); this is not tested
|
|
1049
|
+
with a screen reader. Reduced motion drops every transition and the press.
|
|
1050
|
+
|
|
1051
|
+
## PasswordStrength
|
|
1052
|
+
|
|
1053
|
+
```tsx
|
|
1054
|
+
<PasswordStrength label="New password" onValueChange={(value, strength) => setLevel(strength.level)} />
|
|
1055
|
+
<PasswordStrength label="Account password" rules={createPasswordRules({ minLength: 10 })} error={error} />
|
|
1056
|
+
```
|
|
1057
|
+
|
|
1058
|
+
A new-password field that shows how strong the password is while it is typed.
|
|
1059
|
+
Use it when creating or changing a password; use `PasswordField` for sign in.
|
|
1060
|
+
Scoring happens on the device and nothing is sent anywhere.
|
|
1061
|
+
|
|
1062
|
+
It is the `PasswordField` box and eye toggle plus three things: a four-segment
|
|
1063
|
+
meter, the strength word beside it, and a checklist. The segments slide in on a
|
|
1064
|
+
spring and change tone with the level (`destructive-strong`, `warning-strong`,
|
|
1065
|
+
`success-strong`); a pasted password fills left to right in a short wave and
|
|
1066
|
+
clearing empties right to left. The word swaps in place, rising when strength
|
|
1067
|
+
improves and dropping when it falls. Each rule checks off with a tick that draws
|
|
1068
|
+
itself and a tint that grows under it (rules that change together check off in a
|
|
1069
|
+
wave), and the length rule shows a rolling "N more" count while it is unmet. A
|
|
1070
|
+
new `error` opens its row on a spring, swaps as a whole when it changes, and
|
|
1071
|
+
shakes the field once.
|
|
1072
|
+
|
|
1073
|
+
Strength is the share of rules met, spread over four steps, held at the first
|
|
1074
|
+
step while the password is shorter than `shortLength` (8). `rules` takes your own
|
|
1075
|
+
list, or `createPasswordRules({ minLength, labels })` for the built-in four
|
|
1076
|
+
(length, mixed case, number, symbol); `estimateStrength` scores a password
|
|
1077
|
+
without the component. `value` and `defaultValue` work as on an input, and
|
|
1078
|
+
`onValueChange` receives the new value and its strength. `revealed` and
|
|
1079
|
+
`onRevealedChange` control the toggle. `messages` renames every string (the
|
|
1080
|
+
strength words, the toggle, the meter, the list, the "N more" words and the
|
|
1081
|
+
summary) and merges with the English defaults.
|
|
1082
|
+
|
|
1083
|
+
Native input props apply to the input, and `className` goes to the input;
|
|
1084
|
+
`disabled` also disables the toggle, and `aria-invalid` gives the box its invalid
|
|
1085
|
+
border. The error is an alert and is named in `aria-describedby` together with the
|
|
1086
|
+
requirements list. The toggle is a pressed button whose name does not change. A
|
|
1087
|
+
polite live region reads a summary when the level or the count of met rules
|
|
1088
|
+
changes. Reduced motion drops the transitions, the shake and the press, and the
|
|
1089
|
+
swapped text fades instead of moving. Screen-reader behaviour is not tested.
|
|
1090
|
+
|
|
592
1091
|
## Popover
|
|
593
1092
|
|
|
594
1093
|
```tsx
|
|
@@ -608,6 +1107,10 @@ dialog. React Aria labels it from the trigger.
|
|
|
608
1107
|
| `<PopoverContent align side>` | `<Popover placement>` | `align="start"` → `"bottom start"` |
|
|
609
1108
|
| `<PopoverAnchor>` | *no equivalent* | zero call sites |
|
|
610
1109
|
|
|
1110
|
+
### Motion (Popover)
|
|
1111
|
+
|
|
1112
|
+
The panel grows out of its trigger: it starts 5px toward it (the side it opened on) and a little small, and settles on the gentle spring while it fades in over 160ms; it leaves in 160ms, half the offset and a hair smaller, with clicks passing through. A close that interrupts an open reverses from where the panel is. New `entrance` prop (`"rise"` default, `"none"`) lets a picker that animates its own popover opt out; DatePicker does. Reduced motion is a plain fade.
|
|
1113
|
+
|
|
611
1114
|
## Progress
|
|
612
1115
|
|
|
613
1116
|
```tsx
|
|
@@ -632,6 +1135,10 @@ and no number. `formatOptions` reaches it too, so a currency or "3 of 7" needs n
|
|
|
632
1135
|
second prop. `trackClassName` and `fillClassName` are escape hatches for a colour
|
|
633
1136
|
no tone covers.
|
|
634
1137
|
|
|
1138
|
+
### Motion (Progress)
|
|
1139
|
+
|
|
1140
|
+
The fill follows the value on the smooth spring (it was a 200ms ease) and the printed percentage counts to the new number on the same spring, so the number always matches the bar. When a default-toned bar completes it eases to the success colour (after 200ms) and a check lands beside the count; an explicit `tone` is never overridden, and a bar that is full at mount shows its check with no motion. A value that is not a percentage (a price) is shown as it is. A changed string label swaps as a whole.
|
|
1141
|
+
|
|
635
1142
|
## RadioGroup
|
|
636
1143
|
|
|
637
1144
|
```tsx
|
|
@@ -651,6 +1158,10 @@ no tone covers.
|
|
|
651
1158
|
**The label moves inside the item.** That is the one rename a call site
|
|
652
1159
|
makes, and it removes the id bookkeeping Radix required.
|
|
653
1160
|
|
|
1161
|
+
### Motion (RadioGroup)
|
|
1162
|
+
|
|
1163
|
+
The chosen dot springs in from small on the snappy spring (opacity 160ms in, 120ms out) while the old one shrinks away, and the mark's colours ease over 160ms. A press dips the mark to .88 in 100ms and it springs back on the gentle spring. **DOM:** the `radio-indicator` is always rendered (with `data-selected`), not only while chosen. The reference's travelling highlight between bordered rows is not ported: Alfred's radios are not row cards.
|
|
1164
|
+
|
|
654
1165
|
## ScrollArea
|
|
655
1166
|
|
|
656
1167
|
```tsx
|
|
@@ -673,6 +1184,33 @@ name — the three go together.
|
|
|
673
1184
|
pixel width, radius, hover state or margins. Those need `::-webkit-scrollbar`,
|
|
674
1185
|
which Firefox has never supported.
|
|
675
1186
|
|
|
1187
|
+
### Motion (ScrollArea)
|
|
1188
|
+
|
|
1189
|
+
New opt-in `fade` (px, default `0` = off, so an area is unchanged) fades the content out at an edge by exactly as much as there is content hidden past it, up to that many pixels: the fade grows in with the first pixels of scroll and is gone at the ends. It is written on each scroll frame straight to the element, so scrolling never re-renders. Not ported: the overlay scrollbars that fade in and out and thicken on hover (they replace the native scrollbar with a script-drawn one).
|
|
1190
|
+
|
|
1191
|
+
## SearchField
|
|
1192
|
+
|
|
1193
|
+
```tsx
|
|
1194
|
+
<SearchField label="Search contacts" value={query} onValueChange={setQuery} />
|
|
1195
|
+
```
|
|
1196
|
+
|
|
1197
|
+
A search input with a clear button. It is controlled: `value` and
|
|
1198
|
+
`onValueChange(text)`, which is called with every edit and with `""` when the
|
|
1199
|
+
clear button is pressed (so there is no `onChange`). The clear button has a
|
|
1200
|
+
reserved box, so the field never changes width as it comes and goes; it scales
|
|
1201
|
+
in on the snappy spring while it fades and unblurs, leaves a little smaller,
|
|
1202
|
+
presses to 0.96, and pressing it clears the text and returns focus to the
|
|
1203
|
+
input. A field that is filled on first render shows the button with no
|
|
1204
|
+
animation, and while the button leaves it cannot be pressed or focused. The
|
|
1205
|
+
magnifier is muted at rest and takes full contrast while the field has focus or
|
|
1206
|
+
a value.
|
|
1207
|
+
|
|
1208
|
+
`label` is required and visible; `clearLabel` renames the button (default "Clear
|
|
1209
|
+
search"). Native input props apply to the input, `className` goes to the input,
|
|
1210
|
+
and `disabled` also disables the clear button. The browser's own clear button
|
|
1211
|
+
and search decoration are hidden. The box shares the field look of `Input`.
|
|
1212
|
+
Reduced motion fades the button in over 120ms and removes it at once.
|
|
1213
|
+
|
|
676
1214
|
## Select
|
|
677
1215
|
|
|
678
1216
|
```tsx
|
|
@@ -686,7 +1224,20 @@ which Firefox has never supported.
|
|
|
686
1224
|
Four legacy parts collapse into one. The trigger shares
|
|
687
1225
|
`fieldSurfaceVariants` with `Input`, so a select and a text field in the
|
|
688
1226
|
same form row are the same box — the legacy had `h-10` here against
|
|
689
|
-
`h-9` on the input.
|
|
1227
|
+
`h-9` on the input. Its label uses `Label`'s classes, so a Select and a
|
|
1228
|
+
labelled `Input` in one form show one label style. The trigger also shares the
|
|
1229
|
+
field look described under Input and Textarea (border transition, darker border
|
|
1230
|
+
on hover and keyboard focus, muted background when disabled).
|
|
1231
|
+
|
|
1232
|
+
### Motion
|
|
1233
|
+
|
|
1234
|
+
Built in plain CSS and a little plain React, with no animation library.
|
|
1235
|
+
|
|
1236
|
+
- The shown value rolls when the selection changes: a later option rises in from below and an earlier one drops in from above (the direction is the option's place in the list), while the old value lifts away. A value that is already chosen on first mount does not animate. The visible copy is `aria-hidden`; React Aria's own value, visually hidden beside it, is what a screen reader reads.
|
|
1237
|
+
- The chevron turns half a turn on a spring while the list is open.
|
|
1238
|
+
- The list grows from the trigger edge (offset 6px toward it, scaled to 0.97, 240ms) and leaves faster (120ms), and a selected option's tick pops in. An option's highlight fades in over 120ms.
|
|
1239
|
+
- Hover, press and an open list fill the trigger muted as well as darkening its border. An invalid or disabled trigger is not repainted.
|
|
1240
|
+
- Reduced motion replaces the roll, the list's grow and the tick's pop with plain fades, and the chevron turns without a transition.
|
|
690
1241
|
|
|
691
1242
|
### Migrating from the legacy Select
|
|
692
1243
|
|
|
@@ -793,17 +1344,66 @@ replaced.
|
|
|
793
1344
|
|
|
794
1345
|
The pulse is gated behind `motion-safe:`.
|
|
795
1346
|
|
|
1347
|
+
### Motion (Skeleton)
|
|
1348
|
+
|
|
1349
|
+
The pulse dims a block to about half and back over 1.8s, 11 times (about 20 seconds), then rests, so a stalled load does not pulse forever. The new optional `index` prop starts a block's pulse 90ms after the one above it (`<Skeleton index={2} />`), so a stack dims in a wave. Reduced motion shows the still block. The reference's placeholder-to-content crossfade is not ported: it needs a wrapper with a `loading` prop, which Alfred does not have.
|
|
1350
|
+
|
|
796
1351
|
## Slider
|
|
797
1352
|
|
|
798
1353
|
```tsx
|
|
799
|
-
<Slider label="
|
|
1354
|
+
<Slider label="Volume" defaultValue={60} formatOptions={{ style: "unit", unit: "percent" }} />
|
|
1355
|
+
<Slider label="Budget" defaultValue={[2200, 8000]} minValue={0} maxValue={10000} step={100} description="Per month" />
|
|
1356
|
+
<Slider label="Volume" step={25} marks={[0, 25, 50, 75, 100].map((v) => ({ value: v, label: `${v}%` }))} start={<Button size="icon" aria-label="Mute">...</Button>} />
|
|
800
1357
|
```
|
|
801
1358
|
|
|
802
1359
|
**`label` is required** — all four legacy call sites are unnamed sliders.
|
|
803
1360
|
|
|
1361
|
+
The look follows the design spec: a thin, rounded, light-grey track with a solid ink (`foreground`) fill, and a solid white thumb with a soft shadow. The label sits left and the value right. `value`/`defaultValue` may be a `[low, high]` pair, which gives two thumbs and a range readout ("$2,200 – $8,000"); `description` adds grey helper text; `marks` draws tick dots (a mark with a `label` prints it under the track); `start`/`end` hold content beside the track, such as an icon button.
|
|
1362
|
+
|
|
804
1363
|
The thumb is 24×24 (WCAG 2.5.8); the legacy's was 16. `aria-valuetext` comes
|
|
805
1364
|
free, so a scrubber can announce a time instead of a raw second count.
|
|
806
1365
|
|
|
1366
|
+
### Motion (Slider)
|
|
1367
|
+
|
|
1368
|
+
A value that arrives from outside a drag (a key, a prop, a click on the track) moves the thumb and the fill on the snappy spring; while dragging they stay under the pointer. The thumb swells to 1.16 while held. A bubble with the value rises in over the thumb (from small and blurred) while it is dragged or keyboard focused, and drops away in 120ms; the readout and the bubble roll their value up or down as it changes. The bubble is `aria-hidden`: the output beside the label carries the value. Reduced motion keeps opacity changes only. Not ported: clicking a mark label to move there, the springing catch-up on a click of the track (React Aria starts a drag at once), rubber-banding past the limits and release momentum.
|
|
1369
|
+
|
|
1370
|
+
## SplitButton
|
|
1371
|
+
|
|
1372
|
+
```tsx
|
|
1373
|
+
<SplitButton
|
|
1374
|
+
onPress={save}
|
|
1375
|
+
actions={[{ label: "Save as draft", onSelect: draft }, { label: "Delete", destructive: true }]}
|
|
1376
|
+
>
|
|
1377
|
+
Save
|
|
1378
|
+
</SplitButton>
|
|
1379
|
+
```
|
|
1380
|
+
|
|
1381
|
+
One pill, two actions: a main action and a menu of nearby alternatives. Change
|
|
1382
|
+
`children` (or `icon`) and the main half morphs in place: the letters that differ
|
|
1383
|
+
rise in while the old ones lift away, the icon swaps by scaling, and the width
|
|
1384
|
+
springs. The whole pill presses in for the main action; the menu half answers in
|
|
1385
|
+
colour only, so the menu opens from a still anchor, growing from its trigger with
|
|
1386
|
+
its items arriving one after another. `variant` is `primary` or `secondary`.
|
|
1387
|
+
|
|
1388
|
+
## StatCard
|
|
1389
|
+
|
|
1390
|
+
```tsx
|
|
1391
|
+
<div className="grid gap-4 sm:grid-cols-3">
|
|
1392
|
+
<StatCard label="Open" value="$12,400" sub="18 invoices" />
|
|
1393
|
+
<StatCard label="Overdue" value="$3,100" sub="4 invoices" tone="negative" />
|
|
1394
|
+
<StatCard label="Unmapped" value="$1,200" isSelected={filter === "unmapped"} onPress={() => setFilter("unmapped")} />
|
|
1395
|
+
</div>
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
One headline figure. The label is one line (an ellipsis, the full text on
|
|
1399
|
+
hover), so a row's numbers sit level and cards in a row share one height.
|
|
1400
|
+
`tone` is a meaning, not a colour: `neutral` (default, and right for every
|
|
1401
|
+
zero), `positive`, `negative`, `warning`, `attention`. `href` makes the card a
|
|
1402
|
+
link, `onPress` a button, and `isSelected` with `onPress` a filter toggle with
|
|
1403
|
+
`aria-pressed` and one selected border. For a status pill next to the figure,
|
|
1404
|
+
use `Badge`, whose `tone` already covers success, warning, danger, info,
|
|
1405
|
+
primary and neutral.
|
|
1406
|
+
|
|
807
1407
|
## Switch
|
|
808
1408
|
|
|
809
1409
|
```tsx
|
|
@@ -811,6 +1411,10 @@ free, so a scrubber can announce a time instead of a raw second count.
|
|
|
811
1411
|
<Switch aria-label="Mute" isSelected={muted} onChange={setMuted} />
|
|
812
1412
|
```
|
|
813
1413
|
|
|
1414
|
+
### Motion (Switch)
|
|
1415
|
+
|
|
1416
|
+
The thumb travels on a no-overshoot spring (559ms), stretches 5px toward its travel while pressed, and swells once (`scaleX` 1.16, 340ms) when the user changes the state; a switch that mounts on, or whose controlled value changes from outside, does not swell. Reduced motion keeps the colour change only.
|
|
1417
|
+
|
|
814
1418
|
### Migrating from the legacy Switch
|
|
815
1419
|
|
|
816
1420
|
| legacy | Alfred | |
|
|
@@ -822,8 +1426,9 @@ free, so a scrubber can announce a time instead of a raw second count.
|
|
|
822
1426
|
|
|
823
1427
|
**The track colours are not the legacy's, and could not be.** A white thumb
|
|
824
1428
|
on `--color-primary` is 2.44:1 and on `--color-muted` is 1.13:1 — both fail
|
|
825
|
-
WCAG 1.4.11. The track is `
|
|
826
|
-
which clears 3:1 in both themes.
|
|
1429
|
+
WCAG 1.4.11. The track is `control-edge` unchecked and `primary-strong`
|
|
1430
|
+
checked, which clears 3:1 in both themes. (`control-edge`, not the light
|
|
1431
|
+
page `border`, which is a decorative hairline.)
|
|
827
1432
|
|
|
828
1433
|
## Table
|
|
829
1434
|
|
|
@@ -926,9 +1531,10 @@ already agree on every value that matters:
|
|
|
926
1531
|
| cell padding | 16px / 12px | same |
|
|
927
1532
|
| typeface | Satoshi | same |
|
|
928
1533
|
|
|
929
|
-
The text colours were never a gap: the product's
|
|
930
|
-
`#6B6B78` and
|
|
931
|
-
`--color-muted-foreground` and `--color-foreground
|
|
1534
|
+
The text colours were never a gap at the time: the product's
|
|
1535
|
+
`--muted-foreground` (`#6B6B78`) and body ink (`#02021e`) matched this
|
|
1536
|
+
system's `--color-muted-foreground` and `--color-foreground`. Since the 2026
|
|
1537
|
+
colour refresh these roles follow the design tokens (ink 600 and ink 900). An earlier draft of
|
|
932
1538
|
this section claimed otherwise and prescribed `#0f172b` / `#6b6b6b`; those come
|
|
933
1539
|
from `--table-text-*`, which the AI-visibility tables do not use. Do not apply
|
|
934
1540
|
them.
|
|
@@ -970,6 +1576,10 @@ one consumer or none across the six legacy tables, and they belong to a future
|
|
|
970
1576
|
</Tabs>
|
|
971
1577
|
```
|
|
972
1578
|
|
|
1579
|
+
### Motion (Tabs)
|
|
1580
|
+
|
|
1581
|
+
One selection highlight (a pseudo-element of the tab list) glides to the selected tab on the morph spring; it is placed without a transition at first. The panel arrives fading in over 240ms while it travels 8px from the side the new tab sits on (it flips in RTL). **DOM:** none added, but once the row has placed its highlight (`data-hl` on the list) the selected tab no longer paints its own fill, border or shadow; the list's `::before` does. Reduced motion jumps the highlight and fades the panel. Not ported: the outgoing panel's exit, the panel height morph, and scroll buttons.
|
|
1582
|
+
|
|
973
1583
|
### Migrating from the legacy Tabs
|
|
974
1584
|
|
|
975
1585
|
| legacy | Alfred | |
|
|
@@ -987,10 +1597,52 @@ uncommitted form state, a running video or a scroll position will lose it
|
|
|
987
1597
|
on tab change — pass `shouldForceMount` on that `TabPanel`. With 31 legacy
|
|
988
1598
|
panels, some will need it.
|
|
989
1599
|
|
|
990
|
-
The selected tab carries a **border**, not just a fill:
|
|
991
|
-
|
|
1600
|
+
The selected tab carries a **border**, not just a fill: a surface on a
|
|
1601
|
+
`bg-muted` list is barely distinguishable, and WCAG 1.4.11 wants 3:1 for the thing
|
|
992
1602
|
telling you which tab is active.
|
|
993
1603
|
|
|
1604
|
+
## ThemeSwitch
|
|
1605
|
+
|
|
1606
|
+
```tsx
|
|
1607
|
+
<ThemeSwitch theme={theme} variant="eclipse" onThemeChange={(next, variant, trigger) => setTheme(next)} />
|
|
1608
|
+
```
|
|
1609
|
+
|
|
1610
|
+
A light/dark switch whose sun and moon turn the same way (clockwise into dark and
|
|
1611
|
+
back out of it), so the swap reads as one rotation. The icon is nudged the way the
|
|
1612
|
+
page transition travels (`variant`: `reveal`, `eclipse`, `split`, `rise`) and
|
|
1613
|
+
eases back when the theme returns. A page that loads in dark mode never spins its
|
|
1614
|
+
switches: only changes after the first painted frames animate.
|
|
1615
|
+
|
|
1616
|
+
It is controlled: it shows `theme` and calls `onThemeChange` with the theme to
|
|
1617
|
+
move to, the variant and the button (the origin a page transition can grow from).
|
|
1618
|
+
The page transition itself, and the theme, are the caller's.
|
|
1619
|
+
|
|
1620
|
+
## TimePicker
|
|
1621
|
+
|
|
1622
|
+
```tsx
|
|
1623
|
+
import { Time } from "@internationalized/date";
|
|
1624
|
+
|
|
1625
|
+
<TimePicker label="Start time" value={time} onChange={setTime} minuteStep={30} />
|
|
1626
|
+
```
|
|
1627
|
+
|
|
1628
|
+
A field that shows a time and opens a list of times to pick from. It is a React
|
|
1629
|
+
Aria `Select` underneath (a listbox with typeahead, arrow keys and a roving
|
|
1630
|
+
highlight), so it reads and behaves as one. **Values are `TimeValue`**, from
|
|
1631
|
+
`@internationalized/date` like `Calendar`'s and `DatePicker`'s dates (ADR 0006),
|
|
1632
|
+
not `"HH:MM"` strings; `onChange` hands back a `Time`. Unlike `Calendar`, this one
|
|
1633
|
+
builds a `Time` at runtime, so the peer dependency is a real import here.
|
|
1634
|
+
`label` is required. `minuteStep` sets the gap between the times in the list
|
|
1635
|
+
(1, 5, 10, 15 or 30; a time between steps is added to the list so it is never lost),
|
|
1636
|
+
and `hourCycle` chooses 12 or 24 hour times.
|
|
1637
|
+
|
|
1638
|
+
**Motion** (plain CSS and a little plain React, no animation library): the shown
|
|
1639
|
+
time rolls like a clock face (a later time rises in from below, an earlier one
|
|
1640
|
+
drops in from above, and the old one lifts away); the chevron turns half a turn on
|
|
1641
|
+
a spring while the list is open; the list grows from the field and leaves quicker;
|
|
1642
|
+
it opens already centred on the chosen time, with no scroll to watch; the
|
|
1643
|
+
highlight follows the arrows in 80ms; the trigger fills muted while open. Nothing
|
|
1644
|
+
animates on first render, and reduced motion swaps the grow for a plain fade.
|
|
1645
|
+
|
|
994
1646
|
## Toast
|
|
995
1647
|
|
|
996
1648
|
```tsx
|
|
@@ -1022,6 +1674,10 @@ user must act on.
|
|
|
1022
1674
|
`tone` matches Badge's vocabulary. The legacy had `default` and `destructive`
|
|
1023
1675
|
only, so 38 of 67 calls passed a variant that could not say "this worked".
|
|
1024
1676
|
|
|
1677
|
+
### Motion (Toast)
|
|
1678
|
+
|
|
1679
|
+
A toast rises 16px from below and grows from .96 on the morph spring while it fades in over 240ms. Pressing its close control sinks it out (8px, .97, 180ms) before it leaves the queue; the control dips to .96 under a press and springs back. Reduced motion fades only. React Aria 1.21 unmounts a closed toast at once and has no exit of its own, so a toast that leaves by its timeout or by `toastQueue.close` still leaves without the exit. Not ported: swipe-to-dismiss, the status icon.
|
|
1680
|
+
|
|
1025
1681
|
## Tooltip
|
|
1026
1682
|
|
|
1027
1683
|
```tsx
|
|
@@ -1031,6 +1687,8 @@ only, so 38 of 67 calls passed a variant that could not say "this worked".
|
|
|
1031
1687
|
</TooltipTrigger>
|
|
1032
1688
|
```
|
|
1033
1689
|
|
|
1690
|
+
The bubble is solid ink with `background` text (so it inverts in dark mode), a large corner and comfortable padding; long text wraps at 15rem. It opens above its trigger with **no arrow**; `placement` and `showArrow` are opt-in.
|
|
1691
|
+
|
|
1034
1692
|
**`delay` defaults to 0**, matching the legacy provider. React Aria ships
|
|
1035
1693
|
1500ms — taking that default would have put a 1.5-second warm-up on every
|
|
1036
1694
|
tooltip in the app. Focus opens it immediately regardless.
|
|
@@ -1044,3 +1702,8 @@ tooltip in the app. Focus opens it immediately regardless.
|
|
|
1044
1702
|
**RTL differs between the two.** Popover stamps its own `dir` from
|
|
1045
1703
|
`useLocale()`, so it needs an `I18nProvider`. Tooltip sets no `dir` at all and
|
|
1046
1704
|
inherits from the document. Set both if you support RTL.
|
|
1705
|
+
|
|
1706
|
+
### Motion (Tooltip)
|
|
1707
|
+
|
|
1708
|
+
A tooltip rises 3px from its trigger and settles from .97 over 160ms (offset follows the side it opens on), and leaves in 120ms. While any tooltip is open, and for 300ms after the last one closes, the next one only fades in (no travel). The open delay stays 0. Reduced motion fades only. **DOM:** an empty hidden `span` inside the tooltip marks the warm case. Not ported: the crossfade and size spring when a string tooltip's text changes while open.
|
|
1709
|
+
|