@sableui/react 0.13.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -2
- package/CHANGELOG.md +172 -0
- package/CONTRIBUTING.md +6 -4
- package/DESIGN.md +10 -3
- package/dist/blocks/approval-block.js +93 -0
- package/dist/blocks/approval-block.meta.js +23 -0
- package/dist/blocks/index.d.ts +27 -1
- package/dist/blocks/index.js +8 -0
- package/dist/blocks/message-reactions-block.js +87 -0
- package/dist/blocks/message-reactions-block.meta.js +23 -0
- package/dist/components/accordion/accordion.meta.js +8 -2
- package/dist/components/area-chart/area-chart.meta.js +10 -3
- package/dist/components/attachment/attachment.meta.js +26 -0
- package/dist/components/bar-chart/bar-chart.meta.js +11 -4
- package/dist/components/bottom-nav/bottom-nav.js +3 -1
- package/dist/components/bottom-nav/bottom-nav.meta.js +32 -0
- package/dist/components/breadcrumbs/breadcrumbs.meta.js +26 -0
- package/dist/components/bubble/bubble.js +105 -5
- package/dist/components/bubble/bubble.meta.js +42 -6
- package/dist/components/bubble/bubble.variants.js +1 -1
- package/dist/components/bubble/index.js +2 -0
- package/dist/components/button/button.js +21 -4
- package/dist/components/button/button.meta.js +7 -7
- package/dist/components/calendar/calendar.js +18 -1
- package/dist/components/calendar/calendar.meta.js +38 -0
- package/dist/components/carousel/carousel.js +6 -4
- package/dist/components/carousel/carousel.meta.js +42 -0
- package/dist/components/chat-composer/chat-composer.js +21 -21
- package/dist/components/chat-composer/chat-composer.meta.js +38 -0
- package/dist/components/chat-composer/use-eased-growth.js +70 -0
- package/dist/components/checkbox/checkbox-mark.js +8 -1
- package/dist/components/checkbox/checkbox.js +3 -1
- package/dist/components/checkbox/checkbox.meta.js +32 -0
- package/dist/components/checkbox-card/checkbox-card.js +3 -0
- package/dist/components/checkbox-card/checkbox-card.meta.js +32 -0
- package/dist/components/code-block/code-block.js +14 -3
- package/dist/components/code-block/code-block.meta.js +28 -1
- package/dist/components/collapsible/collapsible.meta.js +32 -0
- package/dist/components/color-field/color-field.meta.js +26 -0
- package/dist/components/combobox/combobox.meta.js +44 -0
- package/dist/components/command-menu/command-menu.js +8 -3
- package/dist/components/command-menu/command-menu.meta.js +38 -0
- package/dist/components/confirm-button/confirm-button.js +179 -0
- package/dist/components/confirm-button/confirm-button.meta.js +143 -0
- package/dist/components/confirm-button/index.js +7 -0
- package/dist/components/data-table/data-table.js +6 -2
- package/dist/components/data-table/data-table.meta.js +32 -0
- package/dist/components/entity/entity.meta.js +26 -0
- package/dist/components/feedback/feedback.js +120 -47
- package/dist/components/feedback/feedback.meta.js +55 -4
- package/dist/components/input-group/input-group.meta.js +27 -1
- package/dist/components/like-button/index.js +7 -0
- package/dist/components/like-button/like-button.js +89 -0
- package/dist/components/like-button/like-button.meta.js +132 -0
- package/dist/components/line-chart/line-chart.meta.js +10 -3
- package/dist/components/middle-truncate/middle-truncate.meta.js +1 -1
- package/dist/components/note/note.meta.js +32 -0
- package/dist/components/notification-badge/notification-badge.js +17 -5
- package/dist/components/notification-badge/notification-badge.meta.js +34 -2
- package/dist/components/number-ticker/index.js +7 -0
- package/dist/components/number-ticker/number-ticker.js +246 -0
- package/dist/components/number-ticker/number-ticker.meta.js +159 -0
- package/dist/components/otp-field/otp-field.js +23 -3
- package/dist/components/otp-field/otp-field.meta.js +23 -2
- package/dist/components/panel-group/panel-group.meta.js +26 -0
- package/dist/components/panel-section/panel-section.js +37 -10
- package/dist/components/panel-section/panel-section.meta.js +32 -0
- package/dist/components/pie-chart/pie-chart.js +20 -11
- package/dist/components/pie-chart/pie-chart.meta.js +17 -4
- package/dist/components/pill/pill.meta.js +32 -0
- package/dist/components/radar-chart/radar-chart.js +4 -3
- package/dist/components/radar-chart/radar-chart.meta.js +10 -3
- package/dist/components/radial-chart/radial-chart.js +22 -22
- package/dist/components/radial-chart/radial-chart.meta.js +17 -4
- package/dist/components/radio/radio.meta.js +26 -0
- package/dist/components/radio-card/radio-card.meta.js +38 -0
- package/dist/components/reaction-picker/index.js +13 -0
- package/dist/components/reaction-picker/reaction-picker.js +104 -0
- package/dist/components/reaction-picker/reaction-picker.meta.js +156 -0
- package/dist/components/relative-time/relative-time.meta.js +32 -0
- package/dist/components/scroller/scroller.meta.js +32 -0
- package/dist/components/scrub-field/scrub-field.meta.js +32 -0
- package/dist/components/search-input/search-input.js +9 -7
- package/dist/components/search-input/search-input.meta.js +26 -0
- package/dist/components/show-more/show-more.js +1 -4
- package/dist/components/show-more/show-more.meta.js +38 -0
- package/dist/components/side-nav/side-nav.js +21 -1
- package/dist/components/side-nav/side-nav.meta.js +32 -0
- package/dist/components/side-nav/side-nav.variants.js +3 -1
- package/dist/components/sidebar/sidebar.js +31 -8
- package/dist/components/sidebar/sidebar.meta.js +38 -0
- package/dist/components/slider/slider.meta.js +26 -0
- package/dist/components/sparkline/sparkline.js +7 -4
- package/dist/components/sparkline/sparkline.meta.js +9 -2
- package/dist/components/table/table.meta.js +26 -0
- package/dist/components/tabs/tabs.js +27 -8
- package/dist/components/tabs/tabs.meta.js +46 -3
- package/dist/components/theme-selector/theme-selector.meta.js +32 -0
- package/dist/components/theme-switcher/theme-switcher.js +4 -1
- package/dist/components/theme-switcher/theme-switcher.meta.js +42 -0
- package/dist/components/toast/toast.js +25 -13
- package/dist/components/toast/toast.meta.js +7 -1
- package/dist/components/toggle/toggle.js +28 -5
- package/dist/components/toggle/toggle.meta.js +44 -2
- package/dist/components/tool-call/index.js +8 -0
- package/dist/components/tool-call/tool-call.js +117 -0
- package/dist/components/tool-call/tool-call.meta.js +147 -0
- package/dist/components/tool-call/tool-call.variants.js +33 -0
- package/dist/components/tooltip/index.js +3 -1
- package/dist/components/tooltip/tooltip.js +84 -0
- package/dist/components/tooltip/tooltip.meta.js +16 -1
- package/dist/components/tree/tree.js +71 -6
- package/dist/components/tree/tree.meta.js +32 -0
- package/dist/components/video/video.meta.js +32 -0
- package/dist/file-hashes.json +130 -104
- package/dist/foundations/motion.js +69 -3
- package/dist/icon-sets/reicon.js +11 -1
- package/dist/index.d.ts +907 -104
- package/dist/index.js +6 -1
- package/dist/lib/chart/cartesian.js +9 -10
- package/dist/lib/chart/chart-center.js +30 -0
- package/dist/lib/chart/chart-frame.js +15 -8
- package/dist/lib/chart/dim.js +13 -0
- package/dist/lib/chart/format.js +7 -1
- package/dist/lib/chart/motion.js +6 -4
- package/dist/lib/highlight-glide.js +68 -10
- package/dist/lib/taxonomy.js +5 -0
- package/dist/motion/Burst.js +79 -0
- package/dist/motion/MotionProvider.js +45 -3
- package/dist/motion/Reveal.js +1 -1
- package/dist/motion/TextReveal.js +1 -1
- package/dist/motion/index.js +17 -2
- package/dist/motion/useAutoSize.js +42 -0
- package/dist/motion/useShake.js +25 -0
- package/dist/styles.css +439 -5
- package/llms/blocks/approval-block.md +128 -0
- package/llms/blocks/message-reactions-block.md +116 -0
- package/llms/components/accordion.md +3 -2
- package/llms/components/area-chart.md +4 -3
- package/llms/components/attachment.md +7 -0
- package/llms/components/bar-chart.md +5 -4
- package/llms/components/bottom-nav.md +8 -0
- package/llms/components/breadcrumbs.md +7 -0
- package/llms/components/bubble.md +17 -7
- package/llms/components/button.md +6 -6
- package/llms/components/calendar.md +9 -0
- package/llms/components/carousel.md +17 -0
- package/llms/components/chat-composer.md +9 -0
- package/llms/components/checkbox-card.md +8 -0
- package/llms/components/checkbox.md +8 -0
- package/llms/components/code-block.md +8 -0
- package/llms/components/collapsible.md +8 -0
- package/llms/components/color-field.md +7 -0
- package/llms/components/combobox.md +10 -0
- package/llms/components/command-menu.md +9 -0
- package/llms/components/confirm-button.md +92 -0
- package/llms/components/data-table.md +8 -0
- package/llms/components/entity.md +7 -0
- package/llms/components/feedback.md +23 -3
- package/llms/components/input-group.md +8 -1
- package/llms/components/like-button.md +91 -0
- package/llms/components/line-chart.md +4 -3
- package/llms/components/middle-truncate.md +1 -1
- package/llms/components/note.md +8 -0
- package/llms/components/notification-badge.md +10 -2
- package/llms/components/number-ticker.md +86 -0
- package/llms/components/otp-field.md +15 -1
- package/llms/components/panel-group.md +7 -0
- package/llms/components/panel-section.md +8 -0
- package/llms/components/pie-chart.md +6 -4
- package/llms/components/pill.md +8 -0
- package/llms/components/radar-chart.md +4 -3
- package/llms/components/radial-chart.md +6 -4
- package/llms/components/radio-card.md +9 -0
- package/llms/components/radio.md +7 -0
- package/llms/components/reaction-picker.md +115 -0
- package/llms/components/relative-time.md +8 -0
- package/llms/components/scroller.md +8 -0
- package/llms/components/scrub-field.md +8 -0
- package/llms/components/search-input.md +7 -0
- package/llms/components/show-more.md +9 -0
- package/llms/components/side-nav.md +8 -0
- package/llms/components/sidebar.md +9 -0
- package/llms/components/slider.md +7 -0
- package/llms/components/sparkline.md +3 -2
- package/llms/components/table.md +7 -0
- package/llms/components/tabs.md +20 -2
- package/llms/components/theme-selector.md +8 -0
- package/llms/components/theme-switcher.md +17 -0
- package/llms/components/toast.md +2 -1
- package/llms/components/toggle.md +19 -2
- package/llms/components/tool-call.md +115 -0
- package/llms/components/tooltip.md +11 -1
- package/llms/components/tree.md +8 -0
- package/llms/components/video.md +8 -0
- package/llms/foundations/motion.md +64 -3
- package/llms/reference/utilities.md +26 -1
- package/llms/tasks/reviewing-motion.md +14 -0
- package/llms-full.txt +1337 -69
- package/llms.txt +19 -11
- package/package.json +1 -1
- package/registry.json +3040 -124
- package/targets/ios.json +45 -0
|
@@ -35,7 +35,8 @@ Use RadarChart to compare the shape of a profile across five to eight dimensions
|
|
|
35
35
|
| tooltip | boolean \| { indicator?: "dot" \| "line" \| "dashed" \| "none"; hideLabel?: boolean; labelFormatter?: (label, datum) => ReactNode; showTotal?: boolean } | true | Show a tooltip on hover and keyboard focus, or configure it: the row marker, hiding the heading, rewriting it, adding a Total row. |
|
|
36
36
|
| valueFormatter | (value: number) => string | — | Format values everywhere (axis, labels, tooltip, table) — units like "$12,480". Default: compact on the axis, full elsewhere. |
|
|
37
37
|
| locale | string | runtime locale | BCP 47 locale for default number and date formatting. |
|
|
38
|
-
|
|
|
38
|
+
| motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the flourish, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. The polygons growing in and morphing are the flourish; the hover highlight is the feedback `subtle` keeps. |
|
|
39
|
+
| animate | boolean | true | Grow the polygons from the centre when the chart scrolls into view. `animate={false}` is `motion="subtle"`. |
|
|
39
40
|
| activeIndex | number \| null | — | The highlighted row (controlled); `null` for none. |
|
|
40
41
|
| defaultActiveIndex | number \| null | — | The row highlighted at first — a tooltip already open before anyone hovers. |
|
|
41
42
|
| onActiveIndexChange | (index: number \| null) => void | — | Called as the pointer or keyboard moves between rows. |
|
|
@@ -46,9 +47,9 @@ Use RadarChart to compare the shape of a profile across five to eight dimensions
|
|
|
46
47
|
|
|
47
48
|
## Motion
|
|
48
49
|
|
|
49
|
-
- **arrival** · Polygon · _custom (outside the recipe layer)_ — Once on screen each polygon grows from the centre and fades in, series staggered, on the emphasized easing. On the motion tokens; reduced motion shows the finished chart.
|
|
50
|
+
- **arrival** · Polygon · _custom (outside the recipe layer)_ — Once on screen each polygon grows from the centre and fades in, series staggered, on the emphasized easing. On the motion tokens; reduced motion shows the finished chart. The arrival is expressive: it plays at the full motion level only, so `motion="subtle"` (or a subtle app policy) shows the finished chart while hover feedback stays.
|
|
50
51
|
- **response** · Active spoke · _no motion_ — The hovered spoke's vertices and label highlight at once.
|
|
51
|
-
- **transition** · Polygon · _custom (outside the recipe layer)_ — Geometry eases from the old shape to the new one through a CSS `d` transition on the motion tokens (no recipe animates path geometry); browsers without `d` transitions update in place.
|
|
52
|
+
- **transition** · Polygon · _custom (outside the recipe layer)_ — Geometry eases from the old shape to the new one through a CSS `d` transition on the motion tokens (no recipe animates path geometry); browsers without `d` transitions update in place. Expressive, like the arrival: under `subtle` new data swaps in place.
|
|
52
53
|
- **activity** · RadarChart · _no motion_ — A chart has no indeterminate state; show a Skeleton while the data loads.
|
|
53
54
|
|
|
54
55
|
## Accessibility
|
|
@@ -37,7 +37,8 @@ Use RadialChart for progress toward goals (several rings), a single score or usa
|
|
|
37
37
|
| tooltip | boolean \| { indicator?: "dot" \| "line" \| "dashed" \| "none"; hideLabel?: boolean; labelFormatter?: (label, datum) => ReactNode; showTotal?: boolean } | true | Show a tooltip on hover and keyboard focus, or configure it: the row marker, hiding the heading, rewriting it, adding a Total row. |
|
|
38
38
|
| valueFormatter | (value: number) => string | — | Format values everywhere (axis, labels, tooltip, table) — units like "$12,480". Default: compact on the axis, full elsewhere. |
|
|
39
39
|
| locale | string | runtime locale | BCP 47 locale for default number and date formatting. |
|
|
40
|
-
|
|
|
40
|
+
| motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the flourish, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. The draw-on, the ring morph and the rolling centre number are the flourish; the hover dim is the feedback `subtle` keeps. |
|
|
41
|
+
| animate | boolean | true | Draw the rings round from their start when the chart scrolls into view. `animate={false}` is `motion="subtle"`. |
|
|
41
42
|
| activeIndex | number \| null | — | The highlighted row (controlled); `null` for none. |
|
|
42
43
|
| defaultActiveIndex | number \| null | — | The row highlighted at first — a tooltip already open before anyone hovers. |
|
|
43
44
|
| onActiveIndexChange | (index: number \| null) => void | — | Called as the pointer or keyboard moves between rows. |
|
|
@@ -48,9 +49,10 @@ Use RadialChart for progress toward goals (several rings), a single score or usa
|
|
|
48
49
|
|
|
49
50
|
## Motion
|
|
50
51
|
|
|
51
|
-
- **arrival** · Ring · _custom (outside the recipe layer)_ — Once on screen each ring draws round from its start (stroke-dashoffset on a round-capped arc), staggered outside in. On the motion tokens; reduced motion shows the finished chart.
|
|
52
|
-
- **response** · Active ring · _no motion_ — The hovered ring stays full strength and the others dim at once.
|
|
53
|
-
- **transition** ·
|
|
52
|
+
- **arrival** · Ring · _custom (outside the recipe layer)_ — Once on screen each ring draws round from its start (stroke-dashoffset on a round-capped arc), staggered outside in. On the motion tokens; reduced motion shows the finished chart. The arrival is expressive: it plays at the full motion level only, so `motion="subtle"` (or a subtle app policy) shows the finished chart while hover feedback stays.
|
|
53
|
+
- **response** · Active ring · _no motion_ — The hovered ring stays full strength and the others dim at once — mixed toward the surface rather than faded, so a dimmed ring stays solid over its track instead of turning muddy. Essential: `subtle` keeps it.
|
|
54
|
+
- **transition** · Centre value · _custom (outside the recipe layer)_ — A numeric centre value rolls to its new total digit by digit (NumberTicker) when the data changes, so the headline is seen moving with the rings instead of being found different afterwards. Expressive: under `subtle` it updates in place.
|
|
55
|
+
- **transition** · Ring · _custom (outside the recipe layer)_ — Geometry eases from the old shape to the new one through a CSS `d` transition on the motion tokens (no recipe animates path geometry); browsers without `d` transitions update in place. Expressive, like the arrival: under `subtle` new data swaps in place.
|
|
54
56
|
- **activity** · RadialChart · _no motion_ — A chart has no indeterminate state; show a Skeleton while the data loads.
|
|
55
57
|
|
|
56
58
|
## Accessibility
|
|
@@ -52,6 +52,15 @@ Use RadioCard for visual choice cards when users compare a small set of named op
|
|
|
52
52
|
|
|
53
53
|
`input`, `card`, `foreground`, `primary`, `muted`, `ring`, `muted-foreground`, `tone-3`, `tone-2`, `tone-1`, `tone-info`, `tone-warning`
|
|
54
54
|
|
|
55
|
+
## Motion
|
|
56
|
+
|
|
57
|
+
- **arrival** · Root · _no motion_ — Cards arrive with the form. A plan picker that animated each card in would delay the choice it exists to make.
|
|
58
|
+
- **response** · Root · `motion-interactive` — The edge steps toward the ring colour on hover, eased. On a card-sized target a hard snap across the whole border reads as the layout flickering.
|
|
59
|
+
- **response** · Root · `motion-press` — The settle on press confirms a click anywhere in the card — the radio is small, and without it a click in the card's body can feel like it missed the control.
|
|
60
|
+
- **transition** · Root · `motion-interactive` — Checking eases the tone ring and wash onto the new card as the old one releases, so the choice reads as moving between cards rather than two cards repainting.
|
|
61
|
+
- **transition** · Indicator · `motion-interactive` — The radio dot grows in on a spring, the same mark as Radio, so a card and a plain radio confirm a choice the same way.
|
|
62
|
+
- **activity** · Root · _no motion_ — A choice commits instantly. A slow write belongs to the surrounding form's pending state.
|
|
63
|
+
|
|
55
64
|
## Accessibility
|
|
56
65
|
|
|
57
66
|
- Base UI provides the radiogroup/radio roles, aria-checked, hidden native inputs, and roving tabindex.
|
package/llms/components/radio.md
CHANGED
|
@@ -40,6 +40,13 @@ Use Radio when a user must choose exactly one option from a visible set.
|
|
|
40
40
|
|
|
41
41
|
`input`, `card`, `primary`, `ring`
|
|
42
42
|
|
|
43
|
+
## Motion
|
|
44
|
+
|
|
45
|
+
- **arrival** · Root · _no motion_ — Radios arrive with the form they belong to. Animating each one in turns a question into a title sequence.
|
|
46
|
+
- **response** · Root · `motion-interactive` — The ring's edge and focus ring ease, so arrowing through a group — which moves the selection with focus — reads as one choice moving down the list.
|
|
47
|
+
- **transition** · Indicator · `motion-interactive` — The dot grows from the centre on a spring and shrinks out of the radio being left. A group's selection moves from one option to another, and the paired grow and shrink is what makes it read as moving rather than two dots blinking.
|
|
48
|
+
- **activity** · Root · _no motion_ — A radio commits instantly. A slow write belongs to the surrounding form's pending state, not the control.
|
|
49
|
+
|
|
43
50
|
## Accessibility
|
|
44
51
|
|
|
45
52
|
- Base UI provides the radiogroup/radio roles, aria-checked, hidden native inputs, and roving tabindex.
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# ReactionPicker
|
|
2
|
+
|
|
3
|
+
**Status:** stable
|
|
4
|
+
|
|
5
|
+
The quick row of emoji you react to a message with: a toolbar of large emoji toggles that bloom in one after another as it opens and magnify under the pointer, with MenuReactions as the same row inside a right-click menu.
|
|
6
|
+
|
|
7
|
+
**Keywords:** reaction, reactions, emoji, emoji picker, react, tapback, quick reactions, message actions, chat
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
Use ReactionPicker wherever a reader chooses a reaction for something: in a Popover from an add-reaction control (BubbleReactionAdd is that, ready-made), or as a hover bar over a message. Use MenuReactions for the same row as the first group of the menu a message opens on right-click or long-press, above Reply and Copy. Both report which emoji the reader has already used, so choosing one again removes it.
|
|
12
|
+
|
|
13
|
+
### Best practices
|
|
14
|
+
|
|
15
|
+
- Do: Keep the quick set short — six to eight. It is the reactions people actually use; a full emoji catalogue belongs behind a trailing "more" control passed as children.
|
|
16
|
+
- Do: Give every reaction a `label`. It is the accessible name and the menu's typeahead, and an emoji's own spoken name differs between platforms.
|
|
17
|
+
- Do: Pass `selected` so the reader's own reactions show pressed, and toggle in `onSelect`: choosing a reaction you already gave takes it back.
|
|
18
|
+
- Do: Offer the right-click menu and a visible add-reaction control together. A context menu is invisible until you know it is there; the pill is how a first-time reader finds reactions.
|
|
19
|
+
- Don't: Do not put a ReactionPicker inside a Menu or ContextMenu. A toolbar is not a menu item, so the menu's arrow keys would skip it; use MenuReactions there. → use **MenuReactions**
|
|
20
|
+
- Don't: Do not use it to rate an answer or collect a verdict. Reactions are social and many-valued; a helpful / not-helpful question is Feedback. → use **Feedback**
|
|
21
|
+
|
|
22
|
+
### Anatomy
|
|
23
|
+
|
|
24
|
+
| Part | Required | Description |
|
|
25
|
+
| --- | --- | --- |
|
|
26
|
+
| ReactionPicker | yes | The toolbar: one tab stop, arrow keys move between emoji, each a toggle with `aria-pressed`. |
|
|
27
|
+
| MenuReactions | no | The same row as a group of checkbox menu items, for MenuContent and ContextMenuContent. Choosing one closes the menu. |
|
|
28
|
+
|
|
29
|
+
## Subcomponents
|
|
30
|
+
|
|
31
|
+
| Name | Type | Description |
|
|
32
|
+
| --- | --- | --- |
|
|
33
|
+
| MenuReactions | { reactions?; selected?; onSelect?; motion? } & ComponentProps<typeof Menu.Group> | The quick row as checkbox menu items, for the first group of a MenuContent or ContextMenuContent. Same props as ReactionPicker, without children. |
|
|
34
|
+
|
|
35
|
+
## Props
|
|
36
|
+
|
|
37
|
+
| Name | Type | Default | Description |
|
|
38
|
+
| --- | --- | --- | --- |
|
|
39
|
+
| reactions | readonly { emoji: string; label: string }[] | DEFAULT_REACTIONS | The choices, in order. The default is 👍 ❤️ 😂 🎉 😮 😢 🙏. |
|
|
40
|
+
| selected | readonly string[] | [] | The emoji the reader has already reacted with: shown pressed, and choosing one again removes it. |
|
|
41
|
+
| onSelect | (emoji: string) => void | — | Called with the emoji the reader chose. Toggle it in or out of `selected`. |
|
|
42
|
+
| children | ReactNode | — | ReactionPicker only: trailing controls after the emoji, such as a button that opens a full emoji picker. |
|
|
43
|
+
| aria-label | string | "Reactions" | The name of the toolbar or menu group. |
|
|
44
|
+
| motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps the press and drops the wave of emoji blooming in and the magnify, `off` stills it. It can only turn motion down. |
|
|
45
|
+
|
|
46
|
+
## Tokens used
|
|
47
|
+
|
|
48
|
+
`accent`, `primary`, `ring`
|
|
49
|
+
|
|
50
|
+
## Motion
|
|
51
|
+
|
|
52
|
+
- **arrival** · Glyph · `motion-bloom` — Opening the picker is the reader's own action, so the emoji answer it: each blooms in 24ms after the one before, a short wave from first to last. The surface it sits in (a Popover or a menu) arrives with its own motion-pop. Expressive only — under `subtle` the emoji are simply there.
|
|
53
|
+
- **response** · Glyph · _custom (outside the recipe layer)_ — The emoji under the pointer or the keyboard highlight magnifies to 1.25 on a fast standard ease, so you can see which one a release will choose; each cell also carries motion-interactive and motion-press. The magnify rides an inner layer so it never fights the bloom over `scale`.
|
|
54
|
+
- **transition** · Cell · `motion-interactive` — Pressed and not pressed differ by a tint, eased by motion-interactive. The celebration belongs to the reaction that lands under the message (BubbleReaction), not to the picker that chose it.
|
|
55
|
+
- **activity** · Root · _no motion_ — Choosing is instant; there is no in-between state to show.
|
|
56
|
+
|
|
57
|
+
## Accessibility
|
|
58
|
+
|
|
59
|
+
- ReactionPicker is a Base UI toolbar: one tab stop, Left/Right move between emoji, Home/End jump to the ends.
|
|
60
|
+
- Each emoji is a button named by its `label` and reporting `aria-pressed`, so a screen-reader user hears whether choosing it adds or removes their reaction. The glyph itself is aria-hidden.
|
|
61
|
+
- MenuReactions renders checkbox menu items (`menuitemcheckbox` with `aria-checked`), reached by the menu's arrow keys and typeahead like every other row.
|
|
62
|
+
- Under reduced motion the emoji appear at once and do not magnify; pressed state is carried by `aria-pressed` and a tint, never motion.
|
|
63
|
+
|
|
64
|
+
## Related
|
|
65
|
+
|
|
66
|
+
- Components: Bubble, ContextMenu, Popover, Toolbar, Feedback
|
|
67
|
+
|
|
68
|
+
## Examples
|
|
69
|
+
|
|
70
|
+
### In a popover
|
|
71
|
+
|
|
72
|
+
```tsx
|
|
73
|
+
import { Button, Icon, Popover, PopoverContent, PopoverTrigger, ReactionPicker } from "@sableui/react"
|
|
74
|
+
|
|
75
|
+
<Popover>
|
|
76
|
+
<PopoverTrigger render={<Button variant="ghost" size="icon-sm" aria-label="Add reaction" />}>
|
|
77
|
+
<Icon name="emoji" />
|
|
78
|
+
</PopoverTrigger>
|
|
79
|
+
<PopoverContent side="top" className="w-auto rounded-full p-1">
|
|
80
|
+
<ReactionPicker selected={mine} onSelect={toggle} />
|
|
81
|
+
</PopoverContent>
|
|
82
|
+
</Popover>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### In a right-click menu
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
import { Bubble, ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger, MenuReactions } from "@sableui/react"
|
|
89
|
+
|
|
90
|
+
<ContextMenu>
|
|
91
|
+
<ContextMenuTrigger render={<Bubble />}>See you at 6.</ContextMenuTrigger>
|
|
92
|
+
<ContextMenuContent>
|
|
93
|
+
<MenuReactions selected={mine} onSelect={toggle} />
|
|
94
|
+
<ContextMenuSeparator />
|
|
95
|
+
<ContextMenuItem>Reply</ContextMenuItem>
|
|
96
|
+
<ContextMenuItem>Copy text</ContextMenuItem>
|
|
97
|
+
</ContextMenuContent>
|
|
98
|
+
</ContextMenu>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### A custom set
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { ReactionPicker } from "@sableui/react"
|
|
105
|
+
|
|
106
|
+
<ReactionPicker
|
|
107
|
+
reactions={[
|
|
108
|
+
{ emoji: "✅", label: "Done" },
|
|
109
|
+
{ emoji: "👀", label: "Looking" },
|
|
110
|
+
{ emoji: "🚀", label: "Shipped" },
|
|
111
|
+
]}
|
|
112
|
+
selected={mine}
|
|
113
|
+
onSelect={toggle}
|
|
114
|
+
/>
|
|
115
|
+
```
|
|
@@ -29,6 +29,14 @@ Use RelativeTime to show recent activity or timestamps in conversational relativ
|
|
|
29
29
|
|
|
30
30
|
`popover`, `popover-foreground`, `border`, `ring`
|
|
31
31
|
|
|
32
|
+
## Motion
|
|
33
|
+
|
|
34
|
+
- **arrival** · Root · _no motion_ — It is a word in a line of text and arrives with that line.
|
|
35
|
+
- **response** · Root · _no motion_ — The only hover change on the timestamp is a dotted underline, and text-decoration-line is discrete: CSS cannot tween it, so a transition here would animate nothing and imply the snap had been handled.
|
|
36
|
+
- **response** · Tooltip · `motion-pop` — The pointer's real answer is the absolute time, which arrives in a Tooltip on its own fast pop. That short entrance is what makes the full date read as belonging to the word under the cursor.
|
|
37
|
+
- **transition** · Root · _no motion_ — The label does not tick: it is computed when the component renders, so it never changes in front of the reader. A timestamp rolling from one minute to two would pull the eye off the content it dates.
|
|
38
|
+
- **activity** · Root · _no motion_ — A timestamp has nothing to wait for.
|
|
39
|
+
|
|
32
40
|
## Accessibility
|
|
33
41
|
|
|
34
42
|
- Renders a semantic <time> element with a machine-readable dateTime (ISO) attribute.
|
|
@@ -41,6 +41,14 @@ Use Scroller to create a contained overflow region for content that naturally ex
|
|
|
41
41
|
|
|
42
42
|
`ring`
|
|
43
43
|
|
|
44
|
+
## Motion
|
|
45
|
+
|
|
46
|
+
- **arrival** · Root · _no motion_ — It is a region, not an event; its content arrives with whatever fills it.
|
|
47
|
+
- **response** · Viewport · `motion-interactive` — The region is focusable so the keyboard can scroll it, and its focus ring eases in like any control's. It is the one sign the region, not the page, now takes the arrow keys.
|
|
48
|
+
- **response** · Scrollbar · _custom (outside the recipe layer)_ — The vertical bar (`scrollbar-quiet`) stays hidden until the region is hovered, focused or scrolled, then fades its thumb in on the motion tokens. A bar that appeared in one frame would flash every time the pointer crossed the list.
|
|
49
|
+
- **transition** · Edge fade · _no motion_ — The edge fades switch on and off as you scroll, in the frame the content is already moving, so the change is carried by the scroll itself. They are a mask gradient, which CSS cannot interpolate without registered custom properties.
|
|
50
|
+
- **activity** · Root · _no motion_ — A scroll region does no work of its own. Content still loading inside it brings its own Skeleton or Spinner.
|
|
51
|
+
|
|
44
52
|
## Accessibility
|
|
45
53
|
|
|
46
54
|
- The edge fades are a CSS mask-image on the scrolling element, not overlay elements — there is nothing in the DOM to intercept a pointer or be read by assistive tech, and the content under a fade stays fully interactive.
|
|
@@ -42,6 +42,14 @@ Use ScrubField for numeric properties in tool chrome — an inspector, a propert
|
|
|
42
42
|
|
|
43
43
|
`foreground`, `muted-foreground`, `ring`, `panel-field`, `panel-field-hover`
|
|
44
44
|
|
|
45
|
+
## Motion
|
|
46
|
+
|
|
47
|
+
- **arrival** · Root · _no motion_ — It arrives with the inspector row it sits in.
|
|
48
|
+
- **response** · Root · `motion-interactive` — The shell eases its focus ring like every field, so tabbing through a column of numbers shows where the keyboard is without flicker.
|
|
49
|
+
- **response** · Handle · `motion-interactive` — The handle's ink brightens while the field is hovered. It is the only sign that the icon or prefix can be dragged, which nobody would guess from a label.
|
|
50
|
+
- **transition** · Value · _no motion_ — The number follows the drag pixel for pixel. Easing it — or rolling it with a NumberTicker — would show a value the pointer has already left, in a control whose whole point is precision.
|
|
51
|
+
- **activity** · Root · _no motion_ — Scrubbing is synchronous; there is nothing to wait for.
|
|
52
|
+
|
|
45
53
|
## Accessibility
|
|
46
54
|
|
|
47
55
|
- The input carries the accessible name via `label`; the glyph is aria-hidden because it is decorative.
|
|
@@ -36,6 +36,13 @@ Use SearchInput for text queries that filter or search a known content set.
|
|
|
36
36
|
|
|
37
37
|
`input`, `card`, `foreground`, `muted-foreground`, `ring`, `destructive`
|
|
38
38
|
|
|
39
|
+
## Motion
|
|
40
|
+
|
|
41
|
+
- **arrival** · Clear button · `motion-appear` — The X mounts as the first character lands. Fading and settling in from a smaller scale keeps it from popping into the corner of the eye on every new search; removal is instant so an emptied field does not wait on it.
|
|
42
|
+
- **response** · Input · `motion-interactive` — The focus ring and invalid border ease in, so focus reads as the field answering rather than a repaint.
|
|
43
|
+
- **transition** · Root · _no motion_ — The field has no states of its own to move between — the clear button arriving is its only change, and arrival covers it.
|
|
44
|
+
- **activity** · Root · _no motion_ — Searching is the results' job: a list skeleton or a Spinner beside them, not a field that animates while you type.
|
|
45
|
+
|
|
39
46
|
## Accessibility
|
|
40
47
|
|
|
41
48
|
- Renders a native <input type="search"> — keyboard and form semantics come for free.
|
|
@@ -30,6 +30,15 @@ Use ShowMore to reveal additional content after an intentionally shortened previ
|
|
|
30
30
|
|
|
31
31
|
`background`, `foreground`, `ring`
|
|
32
32
|
|
|
33
|
+
## Motion
|
|
34
|
+
|
|
35
|
+
- **arrival** · Root · _no motion_ — The preview arrives with the content it clips. Its fade and toggle appear only once a measurement proves the content overflows.
|
|
36
|
+
- **response** · Toggle · `motion-interactive` — The toggle is a link Button, so its hover and focus ease on Button's recipe.
|
|
37
|
+
- **transition** · Content · _custom (outside the recipe layer)_ — The region eases its max-height between the collapsed and the measured full height on the slow motion token. motion-disclosure keys on Base UI's starting and ending styles, which this self-contained toggle does not have. The text unrolling in place keeps the reader's line; a snap drops them at a different scroll position.
|
|
38
|
+
- **transition** · Fade · `motion-interactive` — The bottom fade dissolves as the region opens and returns as it closes, so the cut edge and the reveal read as one movement.
|
|
39
|
+
- **transition** · Chevron · `motion-interactive` — The chevron turns 180° in step with the region (motion-interactive lists `rotate`), so the toggle visibly points at what it will do next.
|
|
40
|
+
- **activity** · Root · _no motion_ — Expanding is local and instant; nothing loads.
|
|
41
|
+
|
|
33
42
|
## Accessibility
|
|
34
43
|
|
|
35
44
|
- The toggle is a real <button> with aria-expanded reflecting the open state.
|
|
@@ -41,11 +41,19 @@ Use SideNav for vertical navigation inside a settings page, documentation area,
|
|
|
41
41
|
| --- | --- | --- | --- |
|
|
42
42
|
| size | "md" \| "lg" | — | Density shared by all items. "md" (default) = the lightweight in-content sub-nav; "lg" = chrome-weight (larger label + icon, roomier rows) for a rail that pairs with the app Sidebar. |
|
|
43
43
|
| activeVariant | "content" \| "sidebar" | "content" | Active-state token vocabulary shared by all items. "content" uses bg-accent/text-accent-foreground for local rails inside the content area; "sidebar" uses bg-sidebar-accent/text-sidebar-accent-foreground for SideNavs rendered inside app chrome. |
|
|
44
|
+
| motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` inherits the app's policy, `subtle` keeps feedback and drops the current-page glide, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. |
|
|
44
45
|
|
|
45
46
|
## Tokens used
|
|
46
47
|
|
|
47
48
|
`muted`, `muted-foreground`, `foreground`, `accent`, `accent-foreground`, `ring`, `sidebar-foreground`, `sidebar-accent`, `sidebar-accent-foreground`, `sidebar-ring`
|
|
48
49
|
|
|
50
|
+
## Motion
|
|
51
|
+
|
|
52
|
+
- **arrival** · Root · _no motion_ — In-page navigation arrives with the page it navigates.
|
|
53
|
+
- **response** · SideNavItem · `motion-interactive` — Rows ease their hover fill and focus ring, so moving down a long nav reads as one pointer travelling rather than rows flashing in turn.
|
|
54
|
+
- **transition** · SideNav · `motion-glide` — The current-item fill is one element that travels to the new row when the section changes inside a persistent layout, so the marker reads as moving rather than one row dimming while another lights. It lands in place on first paint, each row keeps its own fill as the server-render fallback, and the active row's heavier weight lands at once — font weight does not ease. Expressive: `subtle` moves it at once.
|
|
55
|
+
- **activity** · Root · _no motion_ — Navigation chrome has no working state; a loading section shows it in the content.
|
|
56
|
+
|
|
49
57
|
## Accessibility
|
|
50
58
|
|
|
51
59
|
- SideNav renders a <nav> landmark; pair multiple navs with aria-label to distinguish them.
|
|
@@ -41,6 +41,7 @@ Use Sidebar for persistent desktop navigation or application chrome outside the
|
|
|
41
41
|
| icons | boolean | true | Render the leading icon on each SidebarItem. Set false for a text-only rail. Ignored while collapsed (icon-only by definition). |
|
|
42
42
|
| variant | "app" \| "inline" | "app" | Where the rail lives. "app" is the page's chrome column — sticky, full viewport height, hidden below lg where AppHeader + BottomNav take over. "inline" is always visible and fills its parent's height, for a rail inside a bounded frame (a nested shell, an embedded preview). Use it instead of overriding the layout with !important classes. |
|
|
43
43
|
| surface | "rail" \| "page" | "rail" | Which surface the rail is painted on. "rail" is its own --sidebar-* surface with a border — right for a full-height rail that frames the app, which a theme may tint or paint dark. "page" uses the page's surface and ink with no border — use it when a full-width header sits above the rail, where a tinted column starting under a page-coloured bar reads as pasted on. Use it instead of overriding bg-/text- classes, which the rail's re-pointed tokens defeat. |
|
|
44
|
+
| motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` inherits the app's policy, `subtle` keeps feedback and drops the collapse ease and the current-page glide, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. |
|
|
44
45
|
|
|
45
46
|
## Variants
|
|
46
47
|
|
|
@@ -50,6 +51,14 @@ Use Sidebar for persistent desktop navigation or application chrome outside the
|
|
|
50
51
|
|
|
51
52
|
`sidebar`, `sidebar-foreground`, `sidebar-border`, `sidebar-accent`, `sidebar-accent-foreground`, `sidebar-ring`
|
|
52
53
|
|
|
54
|
+
## Motion
|
|
55
|
+
|
|
56
|
+
- **arrival** · Root · _no motion_ — The rail is the app's frame, present from first paint. It is not summoned, so it has no entrance.
|
|
57
|
+
- **response** · SidebarItem · `motion-interactive` — Rows ease their hover fill (SideNav's row recipe on the rail rung), so moving down the nav reads as the pointer travelling rather than rows flashing.
|
|
58
|
+
- **transition** · SidebarNav · `motion-glide` — The current-page fill is one element that travels to the new item when the page changes inside a persistent layout, so the marker reads as moving rather than one row dimming while another lights. It lands in place on first paint, and each item keeps its own fill as the server-render fallback. Expressive: `subtle` moves it at once.
|
|
59
|
+
- **transition** · Root · `motion-size` — Collapsing to the icon rail eases the width instead of jumping 192px in one frame, so the content beside it visibly takes the room the rail gave up. Nothing inside jumps — a collapsed row's centred icon and an expanded row's inset icon sit on the same column — and labels leave at once and fade back in on reopen, clipped to the rail meanwhile. It reflows the page for the length of the ease, which is acceptable for a deliberate user toggle and never happens on load. Expressive: `subtle` snaps.
|
|
60
|
+
- **activity** · Root · _no motion_ — Navigation chrome has no working state; a loading route shows it in the page.
|
|
61
|
+
|
|
53
62
|
## Accessibility
|
|
54
63
|
|
|
55
64
|
- SidebarNav renders a <nav> landmark.
|
|
@@ -60,6 +60,13 @@ Use Slider for bounded numeric choices where approximate adjustment is faster th
|
|
|
60
60
|
|
|
61
61
|
`secondary`, `primary`, `background`, `ring`
|
|
62
62
|
|
|
63
|
+
## Motion
|
|
64
|
+
|
|
65
|
+
- **arrival** · Root · _no motion_ — It arrives with the form or panel it sits in.
|
|
66
|
+
- **response** · Thumb · `motion-interactive` — The thumb's dot grows from 12px to full size on hover and keyboard focus, and a little past it while dragging, on a spring. Only the paint resizes, never the hit target, so the growth says the thumb is live without moving where you have to aim.
|
|
67
|
+
- **transition** · Thumb · _no motion_ — The thumb and the filled range follow the pointer one-to-one, and keyboard steps land at once. An eased position would trail the drag and show a value the user has already moved past.
|
|
68
|
+
- **activity** · Root · _no motion_ — A slider commits as it moves; there is nothing to wait for.
|
|
69
|
+
|
|
63
70
|
## Accessibility
|
|
64
71
|
|
|
65
72
|
- Base UI provides role=slider, aria-valuemin/max/now, and arrow-key / Page Up-Down / Home-End control.
|
|
@@ -26,7 +26,8 @@ Use Sparkline next to a number to show where it has been — a KPI's last 30 day
|
|
|
26
26
|
| variant | "line" \| "area" \| "bar" | "line" | A line, a line over a soft fill, or columns. |
|
|
27
27
|
| tone | "1" \| "2" \| "3" \| "info" \| "warning" \| "primary" \| "neutral" \| "success" \| "destructive" | "primary" | Colour by role. |
|
|
28
28
|
| className | string | "h-8 w-24" | Sizes it; the drawing stretches to fit. |
|
|
29
|
-
|
|
|
29
|
+
| motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the flourish, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. Drawing on (or rising) is the flourish, so `subtle` and `off` show the finished line. |
|
|
30
|
+
| animate | boolean | true | Draw on (or rise) when it scrolls into view. `animate={false}` is `motion="subtle"`. |
|
|
30
31
|
| locale | string | runtime locale | BCP 47 locale for default number and date formatting. |
|
|
31
32
|
|
|
32
33
|
## Variants
|
|
@@ -39,7 +40,7 @@ Use Sparkline next to a number to show where it has been — a KPI's last 30 day
|
|
|
39
40
|
|
|
40
41
|
## Motion
|
|
41
42
|
|
|
42
|
-
- **arrival** · Line / bars · _custom (outside the recipe layer)_ — Once on screen the line draws on (or the bars rise, in sequence). On the motion tokens; reduced motion shows the finished drawing.
|
|
43
|
+
- **arrival** · Line / bars · _custom (outside the recipe layer)_ — Once on screen the line draws on (or the bars rise, in sequence). On the motion tokens; reduced motion shows the finished drawing. The arrival is expressive: it plays at the full motion level only, so `motion="subtle"` (or a subtle app policy) shows the finished line.
|
|
43
44
|
- **response** · Sparkline · _no motion_ — Not interactive: it has no tooltip or focus.
|
|
44
45
|
- **transition** · Sparkline · _no motion_ — New data redraws in place; a word-sized chart animating on every update would distract from the number beside it.
|
|
45
46
|
- **activity** · Sparkline · _no motion_ — No indeterminate state.
|
package/llms/components/table.md
CHANGED
|
@@ -50,6 +50,13 @@ Use Table for structured data with consistent columns and rows. Use DataTable wh
|
|
|
50
50
|
|
|
51
51
|
`border`, `muted`, `muted-foreground`
|
|
52
52
|
|
|
53
|
+
## Motion
|
|
54
|
+
|
|
55
|
+
- **arrival** · Root · _no motion_ — A table renders with the page it sits in. Staggering rows in delays reading data someone opened to scan.
|
|
56
|
+
- **response** · TableRow · `motion-interactive` — Body rows ease to a muted wash under the pointer. Across a wide row, the wash is what keeps the eye on the right line, and eased it follows the cursor down the table rather than strobing.
|
|
57
|
+
- **transition** · TableCell · `motion-interactive` — A `data-selected` row's fill eases in on every cell together, so the selected row reads as one band being set rather than a stamp.
|
|
58
|
+
- **activity** · Root · _no motion_ — Table is markup. Loading rows are Skeletons, which carry the shimmer.
|
|
59
|
+
|
|
53
60
|
## Accessibility
|
|
54
61
|
|
|
55
62
|
- Renders semantic native table elements (thead/tbody/tfoot/th/td/caption) preserving built-in table semantics.
|
package/llms/components/tabs.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Status:** stable
|
|
4
4
|
|
|
5
|
-
Tabs over Base UI Tabs (roving focus, arrow-key nav, ARIA tablist wiring) in two styles: underline (page sections) or pill (compact switchers). Tabs (Root) + TabsList + TabsTrigger + TabsPanel.
|
|
5
|
+
Tabs over Base UI Tabs (roving focus, arrow-key nav, ARIA tablist wiring) in two styles: underline (page sections) or pill (compact switchers), with one indicator that glides to the active tab. Tabs (Root) + TabsList + TabsTrigger + TabsPanel.
|
|
6
6
|
|
|
7
7
|
**Keywords:** tabs, tablist, panels, segmented content
|
|
8
8
|
|
|
@@ -31,7 +31,7 @@ Use Tabs to switch between related panels within the same page context.
|
|
|
31
31
|
|
|
32
32
|
| Name | Type | Description |
|
|
33
33
|
| --- | --- | --- |
|
|
34
|
-
| TabsList | Base UI Tabs.List props (activateOnFocus, loopFocus) + variant?: "underline" \| "pill" + className | The tablist row. variant="underline" (default) draws a divider rule beneath the triggers; variant="pill" drops the rule and fills the active trigger, which takes the theme's button shape (--button-radius). Triggers inherit the variant. |
|
|
34
|
+
| TabsList | Base UI Tabs.List props (activateOnFocus, loopFocus) + variant?: "underline" \| "pill" + className | The tablist row. variant="underline" (default) draws a divider rule beneath the triggers; variant="pill" drops the rule and fills the active trigger, which takes the theme's button shape (--button-radius). Triggers inherit the variant. It renders the gliding indicator (Base UI Tabs.Indicator) itself, so do not add one. |
|
|
35
35
|
| TabsTrigger | Base UI Tabs.Tab props (value, disabled) + className | A single tab button. value pairs it with the panel of the same value. |
|
|
36
36
|
| TabsPanel | Base UI Tabs.Panel props (value, keepMounted) + className | The content shown when the matching trigger is active. |
|
|
37
37
|
| TabsContent | Alias of TabsPanel | Re-export of TabsPanel for consumers that prefer this name. |
|
|
@@ -41,17 +41,27 @@ Use Tabs to switch between related panels within the same page context.
|
|
|
41
41
|
| Name | Type | Default | Description |
|
|
42
42
|
| --- | --- | --- | --- |
|
|
43
43
|
| Tabs (Root) | Base UI Tabs.Root props (value, defaultValue, onValueChange, orientation) | defaultValue: 0 | Controls the active tab. Uncontrolled via defaultValue; controlled via value + onValueChange. |
|
|
44
|
+
| motion | "full" \| "subtle" \| "off" | "full" | On Tabs: how much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the glide, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. |
|
|
44
45
|
|
|
45
46
|
## Tokens used
|
|
46
47
|
|
|
47
48
|
`border`, `muted`, `muted-foreground`, `foreground`, `ring`
|
|
48
49
|
|
|
50
|
+
## Motion
|
|
51
|
+
|
|
52
|
+
- **arrival** · Root · _no motion_ — Tabs arrive with the section they organise. An entrance of their own would announce navigation chrome louder than the content it navigates.
|
|
53
|
+
- **response** · TabsTrigger · `motion-interactive` — The label steps from muted to foreground under the pointer; the ease is what makes that read as an answer rather than a repaint.
|
|
54
|
+
- **transition** · Indicator · `motion-glide` — One underline (or pill) travels to the new tab and stretches to its width. A mark that teleports makes the reader re-find the selection on every click; one that moves is tracked without looking. Expressive: motion="subtle" lands it in place. Each tab's own mark is the fallback until the indicator has measured, so the server render is already right.
|
|
55
|
+
- **transition** · TabsPanel · `animate-fade-in` — The incoming panel fades in, opacity only, so a switch between two similar panels still registers. Skipped on first render (activation direction "none") so a page of tabs does not fade in on load, and the outgoing panel leaves layout at once so a quick second click never stacks both.
|
|
56
|
+
- **activity** · Root · _no motion_ — Switching tabs is synchronous. A panel that loads data shows its own Skeleton or Spinner inside the panel, where the wait is.
|
|
57
|
+
|
|
49
58
|
## Accessibility
|
|
50
59
|
|
|
51
60
|
- Base UI provides the tablist/tab/tabpanel roles, aria-selected, and roving tabindex.
|
|
52
61
|
- Arrow keys move between tabs; Home/End jump to first/last; panels are linked via aria-controls.
|
|
53
62
|
- Active tab styling reads from the data-active attribute; disabled tabs from data-disabled. Disabled tabs are skipped by arrow-key navigation.
|
|
54
63
|
- Keyboard focus shows a ring on the focused trigger in both variants; the panel remains focusable.
|
|
64
|
+
- The gliding indicator is role="presentation" decoration; aria-selected on the tab carries the state.
|
|
55
65
|
|
|
56
66
|
## Related
|
|
57
67
|
|
|
@@ -82,3 +92,11 @@ import { Tabs, TabsList, TabsPanel, TabsTrigger } from "@sableui/react"
|
|
|
82
92
|
|
|
83
93
|
<Tabs defaultValue="overview"><TabsList><TabsTrigger value="overview">Overview</TabsTrigger><TabsTrigger value="billing" disabled>Billing</TabsTrigger></TabsList><TabsPanel value="overview">Overview content</TabsPanel></Tabs>
|
|
84
94
|
```
|
|
95
|
+
|
|
96
|
+
### Subtle motion
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { Tabs, TabsList, TabsPanel, TabsTrigger } from "@sableui/react"
|
|
100
|
+
|
|
101
|
+
<Tabs defaultValue="table" motion="subtle"><TabsList><TabsTrigger value="table">Table</TabsTrigger><TabsTrigger value="chart">Chart</TabsTrigger></TabsList><TabsPanel value="table">Rows</TabsPanel><TabsPanel value="chart">Trend</TabsPanel></Tabs>
|
|
102
|
+
```
|
|
@@ -49,6 +49,14 @@ Use ThemeSelector wherever a person picks a theme. The default `cards` layout is
|
|
|
49
49
|
|
|
50
50
|
`input`, `card`, `border`, `background`, `primary`, `primary-foreground`, `accent`, `muted`, `muted-foreground`, `ring`
|
|
51
51
|
|
|
52
|
+
## Motion
|
|
53
|
+
|
|
54
|
+
- **arrival** · Root · _no motion_ — It sits in a settings panel or a popover, which owns the entrance.
|
|
55
|
+
- **response** · Option (list) · `motion-interactive` — List rows ease their hover fill like Menu and Select rows, so a theme list beside a menu reads as the same kind of row.
|
|
56
|
+
- **response** · Name (grid) · `motion-interactive` — In the grid, the theme's name brightens on hover, eased. The preview tile's outline steps rather than easing — outline is not in the recipe's transition list — so the name carries the motion.
|
|
57
|
+
- **transition** · Check (list) · `motion-interactive` — The check fades into the chosen row and out of the old one, so the choice reads as moving, at the same moment the whole interface repaints in the new theme.
|
|
58
|
+
- **activity** · Root · _no motion_ — Applying a theme is synchronous: the whole interface repaints at once, and that repaint is the feedback.
|
|
59
|
+
|
|
52
60
|
## Accessibility
|
|
53
61
|
|
|
54
62
|
- The container is role="radiogroup" with an aria-label; each card is a role="radio" button with aria-checked reflecting the selected theme.
|
|
@@ -25,12 +25,21 @@ Use ThemeSwitcher for light, dark, and system color mode preference.
|
|
|
25
25
|
| value | "light" \| "dark" \| "system" | — | The currently selected color preference. |
|
|
26
26
|
| onValueChange | (value: ThemeMode) => void | — | Called with the next color preference when an option is chosen. |
|
|
27
27
|
| label | string | "Theme" | Accessible label for the radiogroup. |
|
|
28
|
+
| motion | "full" \| "subtle" \| "off" | "full" | How much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the glide, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. |
|
|
28
29
|
| className | string | — | Merged onto the outer radiogroup container. |
|
|
29
30
|
|
|
30
31
|
## Tokens used
|
|
31
32
|
|
|
32
33
|
`border`, `card`, `muted-foreground`, `foreground`, `accent`, `accent-foreground`, `ring`
|
|
33
34
|
|
|
35
|
+
## Motion
|
|
36
|
+
|
|
37
|
+
- **arrival** · Root · _no motion_ — It arrives with the header or settings row it lives in; a theme control that announces itself competes with the page it themes.
|
|
38
|
+
- **response** · Option · `motion-interactive` — The icon steps from muted to foreground under the pointer, which is what tells an icon-only segment it is a control.
|
|
39
|
+
- **response** · Option · `motion-press` — A settle under the press confirms the tap on a 28px target before the theme change repaints the page around it.
|
|
40
|
+
- **transition** · Active pill · `motion-glide` — One pill slides to the chosen mode, stepping by option + gap on the spacing scale, so light → dark reads as the same control moving, not three icons trading a fill. Expressive: motion="subtle" lands it in place.
|
|
41
|
+
- **activity** · Root · _no motion_ — Changing the theme is synchronous; the repaint is the feedback.
|
|
42
|
+
|
|
34
43
|
## Accessibility
|
|
35
44
|
|
|
36
45
|
- The container is role="radiogroup" with an aria-label; each option is role="radio" with aria-checked reflecting the selected value.
|
|
@@ -69,3 +78,11 @@ import { ThemeSwitcher } from "@sableui/react"
|
|
|
69
78
|
|
|
70
79
|
<ThemeSwitcher value="light" onValueChange={setTheme} />
|
|
71
80
|
```
|
|
81
|
+
|
|
82
|
+
### Subtle motion
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
import { ThemeSwitcher } from "@sableui/react"
|
|
86
|
+
|
|
87
|
+
<ThemeSwitcher value={theme} onValueChange={setTheme} motion="subtle" />
|
|
88
|
+
```
|
package/llms/components/toast.md
CHANGED
|
@@ -53,7 +53,7 @@ Use Toast for transient feedback caused by a user action: saved, failed, copied,
|
|
|
53
53
|
| closeIcon | ReactNode | <Icon name="xmark" /> | Override the dismiss-button glyph. The default already follows the injected icon set. |
|
|
54
54
|
| useToast / useToastManager | () => { toasts, add, close, update, promise } | — | Manager hook for use inside components. add({ title, description, timeout?, type?, priority?, actionProps? }) returns the new toast id and applies the same persistence default as toast.* — an error, loading, or actionProps toast stays until dismissed unless you pass a timeout. close(id), update(id, opts), and promise(p, opts) round out the API. |
|
|
55
55
|
| toast | (message, options?) => string & { success, error, warning, info, message, loading, promise, dismiss, update, add, custom } | — | Imperative singleton callable OUTSIDE React (event handlers, utilities, non-React code) — the Sonner-shaped surface that makes migrating a lib/toast wrapper a near drop-in. Requires a mounted <ToastProvider> (it binds the shared manager). Each call returns the toast id; toast.dismiss(id?) closes one or all; toast.promise(p, { loading, success, error }) and toast.update(id, opts) handle async + in-place updates. |
|
|
56
|
-
| type (toast option) | "success" \| "error" \| "warning" \| "info" \| "loading" | — | Set by the toast.* helpers. Drives a
|
|
56
|
+
| type (toast option) | "success" \| "error" \| "warning" \| "info" \| "loading" | — | Set by the toast.* helpers. Drives a leading status glyph from the shared tone list, the same one Note and InlineStatus use (success→success, error→destructive, warning→warning, info→info), which blooms in as the toast lands; loading shows a spinner, and the glyph blooms in its place when the toast settles. Both loading and error persist until updated or dismissed unless the call supplies a timeout. |
|
|
57
57
|
|
|
58
58
|
## Tokens used
|
|
59
59
|
|
|
@@ -63,6 +63,7 @@ Use Toast for transient feedback caused by a user action: saved, failed, copied,
|
|
|
63
63
|
|
|
64
64
|
- **arrival** · ToastRoot · `motion-slide-left` — A toast interrupts without being asked, so where it came from is the reader's first question. Sliding in from the viewport edge answers it and marks the toast as belonging to the edge rather than to whatever it happens to cover.
|
|
65
65
|
- **response** · ToastAction · `motion-interactive` — Toasts are on a timer, so the action has to look actionable at a glance — there is no time to hunt for the hit target.
|
|
66
|
+
- **response** · Status glyph · `motion-bloom` — The glyph is what says how it went, so it lands as its own beat: it blooms in 120ms after the card, and blooms again in the spinner's place when a loading toast settles. A 16px glyph needs that amplitude to read as an answer; the card itself only slides.
|
|
66
67
|
- **transition** · ToastRoot · `motion-slide-left` — Leaving the same way it entered keeps the stack legible when several dismiss at once; toasts that fade in place leave the remaining ones jumping to fill gaps with no visible cause.
|
|
67
68
|
- **transition** · ToastRoot (stacked) · _custom (outside the recipe layer)_ — A stacked toast arrives from the anchored edge in front of the others, which step back and shrink to make room; hovering fans the deck out into the list. Moving every card, rather than snapping them, shows that the new one went on top and the old ones are still there.
|
|
68
69
|
- **activity** · ToastRoot · _no motion_ — A toast reports a result that has already happened. Anything still in flight belongs in the surface that started it, where the reader is still looking.
|
|
@@ -30,15 +30,16 @@ Use Toggle for button-like controls that can be pressed on or off within a toolb
|
|
|
30
30
|
|
|
31
31
|
| Name | Type | Description |
|
|
32
32
|
| --- | --- | --- |
|
|
33
|
-
| ToggleGroup | Base UI ToggleGroup props (value, defaultValue, onValueChange, multiple, disabled, orientation, loopFocus) + appearance?: "default" \| "segmented" + className | Groups child Toggles and shares pressed state across them — each Toggle inside carries a value. Defaults to multiple: false (single-select) — the fit for view-mode switches and segmented controls; pass multiple to allow several pressed at once, e.g. a row of formatting toggles. |
|
|
33
|
+
| ToggleGroup | Base UI ToggleGroup props (value, defaultValue, onValueChange, multiple, disabled, orientation, loopFocus) + appearance?: "default" \| "segmented" + motion?: "full" \| "subtle" \| "off" + className | Groups child Toggles and shares pressed state across them — each Toggle inside carries a value. Defaults to multiple: false (single-select) — the fit for view-mode switches and segmented controls; pass multiple to allow several pressed at once, e.g. a row of formatting toggles. |
|
|
34
34
|
| ToggleGroupItem | Alias of Toggle | Re-export of Toggle for group composition — a Toggle carrying a value is the group item. |
|
|
35
35
|
|
|
36
36
|
## Props
|
|
37
37
|
|
|
38
38
|
| Name | Type | Default | Description |
|
|
39
39
|
| --- | --- | --- | --- |
|
|
40
|
-
| appearance | "default" \| "segmented" | "default" | On ToggleGroup: `default` is a loose row of toggles; `segmented` draws the capsule segmented control (one track, the pressed option filled) shared with ThemeSwitcher. Toggles inside inherit it. |
|
|
40
|
+
| appearance | "default" \| "segmented" | "default" | On ToggleGroup: `default` is a loose row of toggles; `segmented` draws the capsule segmented control (one track, the pressed option filled) shared with ThemeSwitcher. Single-select segmented draws ONE fill that glides to the pressed option and resizes to its label; multi-select keeps a fill per pressed option. Toggles inside inherit it. |
|
|
41
41
|
| multiple | boolean | false | On ToggleGroup: allow several toggles pressed at once. The default is single-select, which behaves like a radio group. |
|
|
42
|
+
| motion | "full" \| "subtle" \| "off" | "full" | On ToggleGroup: how much motion this component uses: `full` (default) inherits the app's policy, `subtle` keeps feedback and drops the glide, `off` stills it. It can only turn motion down — it never overrides a reader's reduce-motion setting. |
|
|
42
43
|
| Toggle | Base UI Toggle props (pressed, defaultPressed, onPressedChange, disabled, value) + className | defaultPressed: false | A two-state button. Uncontrolled via defaultPressed; controlled via pressed + onPressedChange. Pass value to identify it inside a ToggleGroup. |
|
|
43
44
|
|
|
44
45
|
## Variants
|
|
@@ -49,6 +50,14 @@ Use Toggle for button-like controls that can be pressed on or off within a toolb
|
|
|
49
50
|
|
|
50
51
|
`muted-foreground`, `foreground`, `accent`, `accent-foreground`, `card`, `border`, `ring`
|
|
51
52
|
|
|
53
|
+
## Motion
|
|
54
|
+
|
|
55
|
+
- **arrival** · Toggle · _no motion_ — Toggles arrive with the toolbar or header they sit in. Animating each one in would make a row of controls ripple for no reason.
|
|
56
|
+
- **response** · Toggle · `motion-interactive` — Hover and the pressed fill ease rather than flip, so pressing reads as the same button changing state, not a different button appearing.
|
|
57
|
+
- **response** · Toggle · `motion-press` — The small settle under the finger confirms the press landed before the state change is read — the difference matters most on touch, where there is no hover.
|
|
58
|
+
- **transition** · Segmented fill · `motion-glide` — In a single-select segmented group one fill travels to the chosen option and stretches to its label, so a view switch reads as one control now pointing elsewhere. Measured, because labels differ in width. Expressive: motion="subtle" lands it in place.
|
|
59
|
+
- **activity** · Toggle · _no motion_ — A toggle commits instantly. Work that follows from the new state shows where its result appears, not on the control.
|
|
60
|
+
|
|
52
61
|
## Accessibility
|
|
53
62
|
|
|
54
63
|
- Base UI sets aria-pressed on each Toggle and manages the pressed/disabled states.
|
|
@@ -86,6 +95,14 @@ import { Icon, Toggle, ToggleGroup } from "@sableui/react"
|
|
|
86
95
|
<ToggleGroup appearance="segmented" defaultValue={["list"]} aria-label="View"><Toggle value="list" aria-label="List view"><Icon name="list" /></Toggle><Toggle value="grid" aria-label="Grid view"><Icon name="grid" /></Toggle></ToggleGroup>
|
|
87
96
|
```
|
|
88
97
|
|
|
98
|
+
### Segmented with labels, subtle motion
|
|
99
|
+
|
|
100
|
+
```tsx
|
|
101
|
+
import { Toggle, ToggleGroup } from "@sableui/react"
|
|
102
|
+
|
|
103
|
+
<ToggleGroup appearance="segmented" motion="subtle" defaultValue={["week"]} aria-label="Range"><Toggle value="day">Day</Toggle><Toggle value="week">Week</Toggle><Toggle value="month">Month</Toggle></ToggleGroup>
|
|
104
|
+
```
|
|
105
|
+
|
|
89
106
|
### Multi-select group
|
|
90
107
|
|
|
91
108
|
```tsx
|