UI Components Easy
Swap
DaisyUI-style swap component — toggle between two states (icon/text) with flip, rotate, or fade animations. CSS-only, no JavaScript.
Open in Lab
MCP
css
Targets: HTML
Code
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Inter, system-ui, sans-serif;
background: #050910;
color: #f2f6ff;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
}
/* ── Demo shell ── */
.demo {
width: min(520px, 100%);
display: flex;
flex-direction: column;
gap: 2rem;
}
.demo-title {
font-size: 1.5rem;
font-weight: 700;
letter-spacing: -0.02em;
color: #f2f6ff;
}
.demo-sub {
font-size: 0.9rem;
color: #8090b0;
line-height: 1.6;
}
/* ── Grid of swap demos ── */
.swap-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 1rem;
}
.swap-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.85rem;
padding: 1.5rem 1rem;
background: rgba(255, 255, 255, 0.03);
border: 1px solid rgba(255, 255, 255, 0.07);
border-radius: 1rem;
transition: border-color 0.2s ease, background 0.2s ease;
}
.swap-card:hover {
background: rgba(255, 255, 255, 0.05);
border-color: rgba(255, 255, 255, 0.12);
}
.swap-card__label {
font-size: 0.75rem;
color: #5a6a8a;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.07em;
}
/* ── Swap base ── */
.swap {
position: relative;
display: inline-grid;
place-items: center;
cursor: pointer;
width: 2.5rem;
height: 2.5rem;
color: #8090b0;
transition: color 0.2s ease;
}
.swap:hover {
color: #c5d3f0;
}
/* Hide native checkbox */
.swap input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.swap:focus-within {
outline: 2px solid rgba(79, 110, 247, 0.6);
outline-offset: 3px;
border-radius: 0.5rem;
}
/* Both slots overlap */
.swap-on,
.swap-off {
grid-area: 1 / 1;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 0.3s;
}
/* ── Default: swap-off visible, swap-on hidden ── */
.swap-off {
opacity: 1;
}
.swap-on {
opacity: 0;
pointer-events: none;
}
.swap input:checked ~ .swap-off {
opacity: 0;
pointer-events: none;
}
.swap input:checked ~ .swap-on {
opacity: 1;
pointer-events: auto;
}
/* ─────────────────────────── Rotate variant ─────────────────────────── */
.swap-rotate .swap-off {
transform: rotate(0deg);
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease;
}
.swap-rotate .swap-on {
transform: rotate(-180deg);
transition: transform 0.35s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease;
}
.swap-rotate input:checked ~ .swap-off {
transform: rotate(180deg);
opacity: 0;
}
.swap-rotate input:checked ~ .swap-on {
transform: rotate(0deg);
opacity: 1;
}
/* ─────────────────────────── Flip variant ─────────────────────────── */
.swap-flip {
perspective: 120px;
}
.swap-flip .swap-off {
transform: rotateY(0deg);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transition: transform 0.4s ease, opacity 0.1s ease 0.2s;
}
.swap-flip .swap-on {
transform: rotateY(180deg);
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
transition: transform 0.4s ease, opacity 0.1s ease 0.2s;
}
.swap-flip input:checked ~ .swap-off {
transform: rotateY(-180deg);
opacity: 0;
}
.swap-flip input:checked ~ .swap-on {
transform: rotateY(0deg);
opacity: 1;
}
/* ─────────────────────────── Fade variant ─────────────────────────── */
.swap-fade .swap-off,
.swap-fade .swap-on {
transition: opacity 0.25s ease;
}
/* ── Accent color for star ── */
.swap--accent input:checked ~ .swap-on {
color: #f59e0b;
}
/* Responsive */
@media (max-width: 380px) {
.swap-grid {
grid-template-columns: repeat(2, 1fr);
}
}<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Swap</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="demo">
<h2 class="demo-title">Swap</h2>
<p class="demo-sub">CSS-only toggle component. Click any button to switch states.</p>
<div class="swap-grid">
<!-- 1. Sun / Moon — rotate -->
<div class="swap-card">
<label class="swap swap-rotate" aria-label="Toggle theme">
<input type="checkbox" />
<!-- Moon (off state) -->
<svg class="swap-off" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
</svg>
<!-- Sun (on state) -->
<svg class="swap-on" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="5"/>
<line x1="12" y1="1" x2="12" y2="3"/>
<line x1="12" y1="21" x2="12" y2="23"/>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/>
<line x1="1" y1="12" x2="3" y2="12"/>
<line x1="21" y1="12" x2="23" y2="12"/>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/>
</svg>
</label>
<span class="swap-card__label">Theme</span>
</div>
<!-- 2. Play / Pause — fade -->
<div class="swap-card">
<label class="swap swap-fade" aria-label="Toggle playback">
<input type="checkbox" />
<!-- Play (off state) -->
<svg class="swap-off" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polygon points="5 3 19 12 5 21 5 3"/>
</svg>
<!-- Pause (on state) -->
<svg class="swap-on" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<rect x="6" y="4" width="4" height="16"/>
<rect x="14" y="4" width="4" height="16"/>
</svg>
</label>
<span class="swap-card__label">Media</span>
</div>
<!-- 3. Menu / Close — rotate -->
<div class="swap-card">
<label class="swap swap-rotate" aria-label="Toggle menu">
<input type="checkbox" />
<!-- Menu (off state) -->
<svg class="swap-off" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
<line x1="3" y1="6" x2="21" y2="6"/>
<line x1="3" y1="12" x2="21" y2="12"/>
<line x1="3" y1="18" x2="21" y2="18"/>
</svg>
<!-- Close (on state) -->
<svg class="swap-on" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true">
<line x1="18" y1="6" x2="6" y2="18"/>
<line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</label>
<span class="swap-card__label">Menu</span>
</div>
<!-- 4. Star — flip -->
<div class="swap-card">
<label class="swap swap-flip swap--accent" aria-label="Toggle favorite">
<input type="checkbox" />
<!-- Star outline (off state) -->
<svg class="swap-off" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
<!-- Star filled (on state) -->
<svg class="swap-on" xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
</svg>
</label>
<span class="swap-card__label">Favorite</span>
</div>
</div>
</div>
</body>
</html>Swap
A CSS-only toggle component inspired by DaisyUI’s swap pattern. Uses a hidden <input type="checkbox"> to drive state, with no JavaScript required. Three animation variants — flip, rotate, and fade — cover the most common use cases.
How it works
The .swap wrapper contains a hidden checkbox and two child elements: .swap-on (shown when checked) and .swap-off (shown when unchecked). CSS :checked selectors and sibling combinators handle the entire transition.
Variants
| Modifier | Effect |
|---|---|
.swap-flip | 3D Y-axis flip between states |
.swap-rotate | 180° rotation crossfade |
.swap-fade | Simple opacity crossfade |
Demos
- Sun / Moon — theme toggle with rotate animation
- Play / Pause — media control with fade animation
- Menu / Close — hamburger icon with rotate animation
- Star — filled/outline toggle with flip animation
When to use it
- Dark/light mode toggles
- Media player controls
- Mobile navigation hamburger menus
- Favorite/like buttons