Media & effects
Icon Swap
Smooth cross-fade rotation morphing between two icons or states.
detent-icon-swap
Port status
- Catalog status
- implemented
- Origin
- upstream
- Phase
- 4
- Wave
- phase-4-cinematic
- Group
- Media & effects
2.2 KB gzip for the class module
Variants
The small icon in the preview morphs on the button's own click; this large one starts in its
active state so both layers are visible side by side. The component renders no colour of its own — the
slotted SVGs inherit currentColor — and it emits no event: the button around it is the interaction
surface. A slotted node is never cloned, so its identity and listeners survive every toggle.
Recorded states
activerecorded state; no setter
Usage
Import
import '/components/icon-swap/define.js';
// or register it yourself
import { DetentIconSwap } from '/components/icon-swap/icon-swap.js';
Attributes
activedurationsize
Properties
No properties are recorded for this component.
Slots
fromto
Events
No events are recorded for this component.
Example
Runnable example: site/examples/icon-swap.ts. The snippet below is generated from the catalogued attributes.
<detent-icon-swap
active
duration="0.3"
size="20"
></detent-icon-swap>
Source
The pinned upstream source for this row:
src/components/ui/MorphingIcon.tsxsha256 3c194378304a…
Native files
The native module and its artifacts:
src/components/icon-swap/icon-swap.tssrc/components/icon-swap/styles.csssrc/components/icon-swap/define.tsdocs/components/icon-swap.mdsite/examples/icon-swap.tstests/browser/icon-swap.spec.tsevidence/components/icon-swap.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 |
|---|---|---|---|---|
active | active | boolean | false | Presence semantics: present means true, absent means false. |
duration | duration | number | 0.3 | Kebab-case attribute; camelCase property. |
size | size | number | 20 | Kebab-case attribute; camelCase property. |
Properties
No property-only values are recorded for this component.
Events
No custom events are recorded for this component.
Slots
| Slot | Notes |
|---|---|
from | Consumer content; rendered through a slot, never re-created. |
to | Consumer content; rendered through a slot, never re-created. |
CSS customisation
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- from and to React nodes become the from and to slots; caller content stays light DOM and is never cloned.
- className is not an attribute; styling uses the documented parts and custom properties.
- The upstream tween (duration plus cubic-bezier 0.4,0,0.2,1) becomes the same duration and curve as a CSS transition; no spring is substituted (PC-10.4 clean).
- React state and hooks become element-owned state with pre-upgrade property preservation.
- duration clamps to 0..10 s and size to 8..256 px, with one development warning on invalid input (PC-3.4).
What it does
A fixed-size icon wrapper that cross-fades, scales and rotates between two caller supplied icons.
Upstream behaviour
- Coordinated rotation, scaling, and opacity cross-fade for fluid icon transformations
- Supports any icon component (Lucide, Radix, custom SVGs)
- Zero layout shift with absolute positioning within a fixed-size container
- Configurable duration and cubic-bezier easing curve
Differences from upstream
- React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
- `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
- Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
- React state/hooks replaced by element-owned state with pre-upgrade property preservation.
- from and to React nodes become the from and to slots; caller content stays light DOM and is never cloned.
- className is not an attribute; styling uses the documented parts and custom properties.
- The upstream tween (duration plus cubic-bezier 0.4,0,0.2,1) becomes the same duration and curve as a CSS transition; no spring is substituted (PC-10.4 clean).
- React state and hooks become element-owned state with pre-upgrade property preservation.
- duration clamps to 0..10 s and size to 8..256 px, with one development warning on invalid input (PC-3.4).