Swap
Two icons, one slot, a transition between them. A sun and a moon, a hamburger and a close, a play and a pause.
swap holds no state of its own — you point it at something you already have.
Two components arguing about who is open is worse than one that simply renders
what it is told.
Usage
Give it an on slot, an off slot, and an expression naming the state:
<div x-data="{ open: false }">
<x-wirekit::button x-on:click="open = !open">
<x-wirekit::swap effect="rotate" expression="open">
<x-slot:on><x-wirekit::icon name="x-mark" /></x-slot:on>
<x-slot:off><x-wirekit::icon name="bars-3" /></x-slot:off>
</x-wirekit::swap>
</x-wirekit::button>
</div>
expression is an Alpine expression, evaluated in whatever scope surrounds the
swap. It needs no x-data of its own — Alpine resolves the name up the scope
chain, so open here is the button's open.
expression is what makes it swap. active alone cannot
active is a PHP value, resolved once when the page renders. A swap given only
active is frozen at whatever the server thought and never moves again — which is
an icon, not a swap.
Use active only for a swap the server already knows the answer to and that never
changes on the client. For anything a reader can toggle, you must use expression.
Effects
effect |
What happens |
|---|---|
fade |
Crossfade. The quiet default. |
rotate |
Half a turn — reads well for a sun/moon or a chevron. |
flip |
A card flip on the Y axis. |
Props
| Prop | Type | Default | Description |
|---|---|---|---|
expression |
string|null |
null |
Alpine expression naming the state. Required for a swap that changes on the client. |
active |
bool |
false |
Static state, for a server-rendered swap that never changes. Ignored when expression is set. |
effect |
string |
'fade' |
fade, rotate, flip. |
scope |
string|null |
null |
Class-scope override. |
Slots
| Slot | Description |
|---|---|
on |
Shown when active is true. |
off |
Shown when active is false. Falls back to the default slot. |
Accessibility
Both children stay in the DOM — you cannot crossfade between an element and
nothing. That has one consequence worth knowing about: the hidden child is
aria-hidden, so a screen reader reads only the state that is showing. Without
that, every toggle would announce both states ("sun moon").
The swap itself is not a control and takes no focus. Put it inside a real
<button> and give that the accessible name — a swap between two icons has no
text for a screen reader to read.
Under prefers-reduced-motion: reduce the state still changes; it just arrives
instead of traveling. The transform is dropped too, not only the transition — a
half-turn that snaps is still a half-turn nobody asked for.
Keyboard Interaction
None of its own. Whatever you wrap it in keeps its normal keyboard behavior.