47 CSS Toggles & Switches19 / 47

Pure CSSMIT licensed

3D Flip Card Toggle

A toggle that flips like a card on a table: the whole control rotates 180° in 3D to reveal its other face — muted gray 'Paused' on the front, vivid 'Active' on the back — turning a boolean into a satisfying physical object.

Published

Live Demo
Try it

The code

<section class="tgl-19" aria-label="3D flip card toggle">
  <input type="checkbox" id="tgl-19-sw" class="tgl-19__sw" role="switch" aria-label="Campaign active">
  <label class="tgl-19__persp" for="tgl-19-sw" aria-hidden="true">
    <span class="tgl-19__card">
      <span class="tgl-19__face tgl-19__face--front"><b>PAUSED</b><small>Campaign is not spending</small></span>
      <span class="tgl-19__face tgl-19__face--back"><b>ACTIVE</b><small>Ads are live · $184/day</small></span>
    </span>
  </label>
  <p class="tgl-19__cap">Click the card to flip state</p>
</section>
.tgl-19,
.tgl-19 *,
.tgl-19 *::before,
.tgl-19 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tgl-19 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 60px 20px;
  background: #f1f2f6;
}

.tgl-19__sw {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

.tgl-19__persp {
  perspective: 900px;
  cursor: pointer;
  display: block;
}

.tgl-19__card {
  position: relative;
  display: block;
  width: 230px;
  height: 130px;
  transform-style: preserve-3d;
  transition: transform .65s cubic-bezier(.4,.2,.2,1.15);
}

.tgl-19__sw:checked ~ .tgl-19__persp .tgl-19__card {
  transform: rotateY(180deg);
}

.tgl-19__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-radius: 16px;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  box-shadow: 0 18px 36px -20px rgba(30,35,60,.55);
}

.tgl-19__face b {
  font-size: 24px;
  font-weight: 800;
  letter-spacing: .12em;
}

.tgl-19__face small {
  font-size: 12.5px;
  opacity: .75;
}

.tgl-19__face--front {
  background: linear-gradient(150deg,#fdfdfe,#e7e9ef);
  color: #5a6172;
}

.tgl-19__face--back {
  background: linear-gradient(150deg,oklch(0.62 0.17 155),oklch(0.5 0.14 170));
  color: #fff;
  transform: rotateY(180deg);
}

.tgl-19__sw:focus-visible ~ .tgl-19__persp {
  outline: 3px solid oklch(0.6 0.17 250);
  outline-offset: 5px;
  border-radius: 18px;
}

.tgl-19__cap {
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #9aa0af;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-19__card {
    transition: transform .15s;
  }
}
/* No JavaScript — a preserve-3d card rotates rotateY(180deg) on :checked, with backface-hidden faces so each side lands upright. */
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Toggles & Switche from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: 3D Flip Card Toggle
Source: https://codefronts.com/components/css-toggles-switches/3d-flip-card-toggle/

A toggle that flips like a card on a table: the whole control rotates 180° in 3D to reveal its other face — muted gray 'Paused' on the front, vivid 'Active' on the back — turning a boolean into a satisfying physical object.
## HTML
```html
<section class="tgl-19" aria-label="3D flip card toggle">
  <input type="checkbox" id="tgl-19-sw" class="tgl-19__sw" role="switch" aria-label="Campaign active">
  <label class="tgl-19__persp" for="tgl-19-sw" aria-hidden="true">
    <span class="tgl-19__card">
      <span class="tgl-19__face tgl-19__face--front"><b>PAUSED</b><small>Campaign is not spending</small></span>
      <span class="tgl-19__face tgl-19__face--back"><b>ACTIVE</b><small>Ads are live · $184/day</small></span>
    </span>
  </label>
  <p class="tgl-19__cap">Click the card to flip state</p>
</section>
```
## CSS
```css
.tgl-19,
.tgl-19 *,
.tgl-19 *::before,
.tgl-19 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tgl-19 {
  font-family: 'Segoe UI',system-ui,sans-serif;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 60px 20px;
  background: #f1f2f6;
}

.tgl-19__sw {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
}

.tgl-19__persp {
  perspective: 900px;
  cursor: pointer;
  display: block;
}

.tgl-19__card {
  position: relative;
  display: block;
  width: 230px;
  height: 130px;
  transform-style: preserve-3d;
  transition: transform .65s cubic-bezier(.4,.2,.2,1.15);
}

.tgl-19__sw:checked ~ .tgl-19__persp .tgl-19__card {
  transform: rotateY(180deg);
}

.tgl-19__face {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  border-radius: 16px;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  box-shadow: 0 18px 36px -20px rgba(30,35,60,.55);
}

.tgl-19__face b {
  font-size: 24px;
  font-weight: 800;
  letter-spacing: .12em;
}

.tgl-19__face small {
  font-size: 12.5px;
  opacity: .75;
}

.tgl-19__face--front {
  background: linear-gradient(150deg,#fdfdfe,#e7e9ef);
  color: #5a6172;
}

.tgl-19__face--back {
  background: linear-gradient(150deg,oklch(0.62 0.17 155),oklch(0.5 0.14 170));
  color: #fff;
  transform: rotateY(180deg);
}

.tgl-19__sw:focus-visible ~ .tgl-19__persp {
  outline: 3px solid oklch(0.6 0.17 250);
  outline-offset: 5px;
  border-radius: 18px;
}

.tgl-19__cap {
  font-size: 12px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: #9aa0af;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-19__card {
    transition: transform .15s;
  }
}
```

## JavaScript
```js
/* No JavaScript — a preserve-3d card rotates rotateY(180deg) on :checked, with backface-hidden faces so each side lands upright. */
```

How this works

Standard flip-card architecture bound to a checkbox: a perspective wrapper, an inner card with transform-style:preserve-3d that rotates rotateY(180deg) when checked, and two absolutely-stacked faces with backface-visibility:hidden — the back face pre-rotated 180° so it lands upright.

An overshoot bezier gives the flip a flick-of-the-wrist settle, and a shadow beneath the wrapper squashes at mid-flip (via a width-scale on its own transform) to ground the card. The checkbox is sr-only-but-focusable; the visible card is its label, so the entire card is the hit target.

Make it yours

  • Flip vertically (rotateX) for a flashcard feel.
  • Put real content on faces — a stat that changes when enabled sells the metaphor.
  • Slow to .8s and raise perspective to 1200px for a bigger, heavier card.
  • Add a corner fold with a clip-path triangle on each face.

Gotchas — read before shipping

  • backface-visibility:hidden on BOTH faces, and preserve-3d on the inner — miss either and faces ghost through each other.
  • Perspective belongs on the wrapper; on the inner card the rotation flattens.
  • Both faces must state their meaning in text — mid-flip, color alone is unreadable.

Browser support

ChromeSafariFirefoxEdge
111+15.4+113+111+

Floor set by oklch() as this demo is written. The core technique itself goes back further — Chrome 36+.

Classic 3D transform stack; universal in modern browsers.

Techniques used in this demo

3D transforms (perspective + preserve-3d)role="" (ARIA)MDN ↗oklch()MDN ↗

Search CodeFronts

Loading…