Buttons & actions
Split Button
A primary action with a chevron trigger for related actions.
detent-split-button
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 2
- Wave
- phase-2-controls
- Group
- Buttons & actions
6.8 KB gzip for the class module · inlines the shared spring
Variants
Recorded states
disabledrecorded state; no setteropenrecorded state; no setterexpandedrecorded state; no settercollapsedrecorded state; no setterreduced motionrecorded state; no setteractiverecorded state; no setter
Usage
Import
import '/components/split-button/define.js';
// or register it yourself
import { DetentSplitButton } from '/components/split-button/split-button.js';
Attributes
disabledlabelsizevariant
Properties
labeloptionssizevariantdisabledopenonClick
Slots
No slots are recorded for this component.
Events
detent-select— { value: string; label: string }detent-click— {}
Example
Runnable example: site/examples/split-button.ts. The snippet below is generated from the catalogued attributes.
<detent-split-button
disabled
label
size="md"
variant="primary"
></detent-split-button>
Property-only values
const element = document.querySelector('detent-split-button');
if (element !== null) {
// onClick: () => void
element.onClick = () => {};
// options: SplitButtonOption[]
element.options = [];
}
Source
The pinned upstream source for this row:
src/components/ui/SplitButton.tsxsha256 0747f656021b…
Native files
The native module and its artifacts:
src/components/split-button/split-button.tssrc/components/split-button/styles.csssrc/components/split-button/define.tsdocs/components/split-button.mdsite/examples/split-button.tstests/browser/split-button.spec.tsevidence/components/split-button.md
API
Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.
Attributes
| Attribute | Property | Type | Default | Notes |
|---|---|---|---|---|
disabled | disabled | boolean | false | Presence semantics: present means true, absent means false. |
label | label | string | — | Kebab-case attribute; camelCase property. |
size | size | 'sm' | 'md' | 'lg' | 'md' | Kebab-case attribute; camelCase property. |
variant | variant | 'primary' | 'secondary' | 'primary' | Kebab-case attribute; camelCase property. |
Properties
| Property | Type | Default | Notes |
|---|---|---|---|
label | string | — | Property-only: assign from script; not accepted as an attribute. |
options | SplitButtonOption[] | — | Property-only: assign from script; not accepted as an attribute. |
size | 'sm' | 'md' | 'lg' | 'md' | Property-only: assign from script; not accepted as an attribute. |
variant | 'primary' | 'secondary' | 'primary' | Property-only: assign from script; not accepted as an attribute. |
disabled | boolean | false | Property-only: assign from script; not accepted as an attribute. |
open | — | — | Recorded in the catalog without a top-level type; pinned from source when the row is ported. |
onClick | () => void | — | Property-only: assign from script; not accepted as an attribute. |
Events
| Event | Upstream prop | Payload type | Notes |
|---|---|---|---|
detent-select | options[].onSelect | () => void | Composed, bubbles; fires only for a user action. |
detent-click | onClick | () => void | Composed, bubbles; fires only for a user action. |
Slots
No slots are recorded for this component.
CSS customisation
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- `onSelect` becomes the composed `detent-select` event with a plain-data detail; the React `icon` node and the per-option callback are dropped from `options`.
- Real roving DOM focus over enabled menuitem buttons replaces upstream's activeIndex highlight on the menu container; Tab closes the menu and hands focus to the next tabbable element outside the control (PC-12.6).
- Framer Motion springs and AnimatePresence become the shared spring helper with the pinned springSnappy values (400/25/0.5); the menu closes without an exit animation.
- The upstream hard-coded light/dark hex pairs and opacity-30 disabled treatment become theme tokens and muted tokens at full opacity (PC-9.5).
- `open` is a reflected state attribute, not a controlled prop.
- The pinned source also styles danger and info variants; both are ported (danger #b91c1c fill, info #1d4ed8 fill, hover one step lighter) so the variant list matches the source rather than the previously recorded two.
What it does
A primary action fused with a chevron trigger that reveals related actions in a menu. Two real buttons, a real role="menu" with roving focus, one shared pill silhouette.
Upstream behaviour
- Primary action and chevron trigger share a single rounded pill silhouette
- Chevron rotates with a snappy spring on open/close
- Menu flips above the trigger when there is not enough room below
- Supports primary and secondary visual variants
- Closes on outside click and on Escape
Differences from upstream
- React callback props become typed composed CustomEvents; programmatic state assignment emits none.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- `lucide-react` icon components replaced with local SVG assets (ISC notice retained).
- `onSelect` becomes the composed `detent-select` event with a plain-data detail; the React `icon` node and the per-option callback are dropped from `options`.
- Real roving DOM focus over enabled menuitem buttons replaces upstream's activeIndex highlight on the menu container; Tab closes the menu and hands focus to the next tabbable element outside the control (PC-12.6).
- Framer Motion springs and AnimatePresence become the shared spring helper with the pinned springSnappy values (400/25/0.5); the menu closes without an exit animation.
- The upstream hard-coded light/dark hex pairs and opacity-30 disabled treatment become theme tokens and muted tokens at full opacity (PC-9.5).
- `open` is a reflected state attribute, not a controlled prop.
- The pinned source also styles danger and info variants; both are ported (danger #b91c1c fill, info #1d4ed8 fill, hover one step lighter) so the variant list matches the source rather than the previously recorded two.