Description
Icon Swap places two icons in the same grid cell and animates between them by fading, blurring, and scaling the outgoing icon down while the incoming icon scales up. It is a good fit for toggle buttons such as sun/moon, play/pause, or expand/collapse where you want a polished cross-dissolve without JavaScript animation libraries.
Example
CSS
:root {
--icon-swap-dur: 200ms;
--icon-swap-blur: 2px;
--icon-swap-start-scale: 0.25;
--icon-swap-ease: ease-in-out;
}
.t-icon-swap {
position: relative;
display: inline-grid;
}
.t-icon-swap .t-icon {
grid-area: 1 / 1;
transition:
opacity var(--icon-swap-dur) var(--icon-swap-ease),
filter var(--icon-swap-dur) var(--icon-swap-ease),
transform var(--icon-swap-dur) var(--icon-swap-ease);
will-change: opacity, filter, transform;
}
.t-icon-swap[data-state="a"] .t-icon[data-icon="a"],
.t-icon-swap[data-state="b"] .t-icon[data-icon="b"] {
opacity: 1;
filter: blur(0);
transform: scale(1);
}
.t-icon-swap[data-state="a"] .t-icon[data-icon="b"],
.t-icon-swap[data-state="b"] .t-icon[data-icon="a"] {
opacity: 0;
filter: blur(var(--icon-swap-blur));
transform: scale(var(--icon-swap-start-scale));
}
@media (prefers-reduced-motion: reduce) {
.t-icon-swap .t-icon {
transition: none !important;
}
}
React
import { useState } from "react";
import { Sun, Moon } from "lucide-react";
import "./icon-swap.css"; // paste the CSS above
export function IconSwap() {
const [state, setState] = useState<"a" | "b">("a");
return (
<button onClick={() => setState((s) => (s === "a" ? "b" : "a"))}>
<div className="t-icon-swap" data-state={state}>
<span className="t-icon" data-icon="a">
<Sun />
</span>
<span className="t-icon" data-icon="b">
<Moon />
</span>
</div>
</button>
);
}
Variables
| Variable | Default | Notes |
|---|---|---|
--icon-swap-dur | 200ms | sourced from --p5-dur |
--icon-swap-blur | 2px | sourced from --p5-blur |
--icon-swap-start-scale | 0.25 | sourced from --p5-start-scale |
--icon-swap-ease | ease-in-out | sourced from --p5-ease |
Credit
Adapted from Icon Swap on transitions.dev by Jakub Antalik. Original source: github.com/Jakubantalik/transitions.dev.