ExpandToggle
An expand/collapse affordance. The disc fills as the glyph turns a quarter turn and the second stroke grows out of the first.
Usage
<ExpandToggle
size={56}
intensity="normal"
spring="smooth"
/>How the motion works
The two strokes are never cross-faded and the icon is never swapped: the minus is the plus with its vertical stroke scaled to nothing, which leaves the whole move as one progress value driving two properties. Rotating is what supplies the motion — a bar that merely sprouted a second bar in place would read as a glyph being edited rather than an object turning — and a plus is unchanged by a quarter turn, so the rotation costs nothing at the far end and exists purely as travel. Halfway through, the grown-but-still-short stroke crossing the turned horizontal is the diagonal cross the move is recognisable by, and it falls out of the construction rather than being a keyframe anyone placed. Angle takes the spring's raw output and stroke length a clamped copy, because angle is the one quantity here with room to ring: overshoot past the quarter turn is visible and reads as the icon settling, whereas a stroke that overshot its length would simply grow longer than the icon it belongs to. Ring and fill trade places on that same clamped value — the border fades out from underneath the fill arriving over it — so the disc changes state once instead of stacking two changes that happen to coincide.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| expanded | boolean | — | Controlled state. Omit for uncontrolled use. |
| defaultExpanded | boolean | false | Initial uncontrolled state. False shows the filled plus. |
| onChange | (expanded: boolean) => void | — | Fires when toggled. |
| size | number | 56 | Diameter in px. Stroke and ring scale with it. |
| intensity | "subtle" | "normal" | "strong" | "normal" | Scales the press squash. |
| label | string | "Expand" | Accessible name for the icon-only button. |
