47 CSS Toggles & Switches12 / 47

Pure CSSMIT licensed

Split-Flap Departure Board Toggle

An airport departure-board cell that flips between OFF and ON: the top half of the card hinges down over the bottom in 3D, revealing the new state exactly like a mechanical split-flap display — hinge line, half-letters and all. Pure CSS.

Published

Live Demo
Try it

The code

<section class="tgl-12" aria-label="Split flap board toggle">
  <label class="tgl-12__wrap" for="tgl-12-sw">
    <input type="checkbox" id="tgl-12-sw" class="tgl-12__sw" role="switch">
    <span class="tgl-12__board" aria-hidden="true">
      <b class="tgl-12__half tgl-12__half--topnew"><i>ON</i></b>
      <b class="tgl-12__half tgl-12__half--botold"><i>OFF</i></b>
      <b class="tgl-12__flap"><span class="tgl-12__face tgl-12__face--front"><i>OFF</i></span><span class="tgl-12__face tgl-12__face--back"><i>ON</i></span></b>
      <em class="tgl-12__hinge"></em>
    </span>
    <span class="tgl-12__lbl">Departures feed</span>
  </label>
  <p class="tgl-12__cap">Mechanical split-flap · click to flip</p>
</section>
.tgl-12,
.tgl-12 *,
.tgl-12 *::before,
.tgl-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tgl-12 {
  font-family: ui-monospace,'SF Mono',Menlo,monospace;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 60px 20px;
  background: #14130f;
}

.tgl-12__wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  cursor: pointer;
}

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

.tgl-12__board {
  position: relative;
  width: 150px;
  height: 96px;
  perspective: 500px;
  filter: drop-shadow(0 14px 24px rgba(0,0,0,.6));
}

.tgl-12__half,
.tgl-12__face {
  position: absolute;
  left: 0;
  width: 150px;
  height: 48px;
  overflow: hidden;
  background: #242220;
  color: #f5d97a;
  font-size: 44px;
  font-weight: 700;
  letter-spacing: .04em;
  text-align: center;
}

.tgl-12__half i,
.tgl-12__face i {
  font-style: normal;
  display: block;
  line-height: 96px;
}

.tgl-12__half--topnew {
  top: 0;
  border-radius: 8px 8px 0 0;
}

.tgl-12__half--botold {
  top: 48px;
  border-radius: 0 0 8px 8px;
  background: #1e1c1a;
}

.tgl-12__half--botold i,
.tgl-12__face--back i {
  transform: translateY(-48px);
}

.tgl-12__flap {
  position: absolute;
  top: 0;
  left: 0;
  width: 150px;
  height: 48px;
  transform-style: preserve-3d;
  transform-origin: bottom;
  transition: transform .6s cubic-bezier(.4,0,.6,1);
}

.tgl-12__sw:checked ~ .tgl-12__board .tgl-12__flap {
  transform: rotateX(-180deg);
}

.tgl-12__face {
  top: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  border-radius: 8px 8px 0 0;
}

.tgl-12__face--back {
  transform: rotateX(180deg);
  background: #1e1c1a;
  border-radius: 0 0 8px 8px;
}

.tgl-12__hinge {
  position: absolute;
  top: 47px;
  left: 0;
  right: 0;
  height: 2px;
  background: #0d0c0a;
  z-index: 5;
}

.tgl-12__hinge::before,
.tgl-12__hinge::after {
  content: "";
  position: absolute;
  top: -4px;
  width: 5px;
  height: 10px;
  background: #0d0c0a;
  border-radius: 2px;
}

.tgl-12__hinge::before {
  left: -2px;
}

.tgl-12__hinge::after {
  right: -2px;
}

.tgl-12__lbl {
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #8d8672;
}

.tgl-12__sw:focus-visible ~ .tgl-12__board {
  outline: 3px solid #f5d97a;
  outline-offset: 5px;
  border-radius: 10px;
}

.tgl-12__cap {
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #57523f;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-12__flap {
    transition: transform .01s;
  }
}
/* No JavaScript — a hinged flap with front/back half-word faces rotates -180° inside a perspective parent, exactly like a mechanical split-flap cell. */
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: Split-Flap Departure Board Toggle
Source: https://codefronts.com/components/css-toggles-switches/split-flap-departure-board-toggle/

An airport departure-board cell that flips between OFF and ON: the top half of the card hinges down over the bottom in 3D, revealing the new state exactly like a mechanical split-flap display — hinge line, half-letters and all. Pure CSS.
## HTML
```html
<section class="tgl-12" aria-label="Split flap board toggle">
  <label class="tgl-12__wrap" for="tgl-12-sw">
    <input type="checkbox" id="tgl-12-sw" class="tgl-12__sw" role="switch">
    <span class="tgl-12__board" aria-hidden="true">
      <b class="tgl-12__half tgl-12__half--topnew"><i>ON</i></b>
      <b class="tgl-12__half tgl-12__half--botold"><i>OFF</i></b>
      <b class="tgl-12__flap"><span class="tgl-12__face tgl-12__face--front"><i>OFF</i></span><span class="tgl-12__face tgl-12__face--back"><i>ON</i></span></b>
      <em class="tgl-12__hinge"></em>
    </span>
    <span class="tgl-12__lbl">Departures feed</span>
  </label>
  <p class="tgl-12__cap">Mechanical split-flap · click to flip</p>
</section>
```
## CSS
```css
.tgl-12,
.tgl-12 *,
.tgl-12 *::before,
.tgl-12 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.tgl-12 {
  font-family: ui-monospace,'SF Mono',Menlo,monospace;
  width: 100%;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  padding: 60px 20px;
  background: #14130f;
}

.tgl-12__wrap {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  cursor: pointer;
}

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

.tgl-12__board {
  position: relative;
  width: 150px;
  height: 96px;
  perspective: 500px;
  filter: drop-shadow(0 14px 24px rgba(0,0,0,.6));
}

.tgl-12__half,
.tgl-12__face {
  position: absolute;
  left: 0;
  width: 150px;
  height: 48px;
  overflow: hidden;
  background: #242220;
  color: #f5d97a;
  font-size: 44px;
  font-weight: 700;
  letter-spacing: .04em;
  text-align: center;
}

.tgl-12__half i,
.tgl-12__face i {
  font-style: normal;
  display: block;
  line-height: 96px;
}

.tgl-12__half--topnew {
  top: 0;
  border-radius: 8px 8px 0 0;
}

.tgl-12__half--botold {
  top: 48px;
  border-radius: 0 0 8px 8px;
  background: #1e1c1a;
}

.tgl-12__half--botold i,
.tgl-12__face--back i {
  transform: translateY(-48px);
}

.tgl-12__flap {
  position: absolute;
  top: 0;
  left: 0;
  width: 150px;
  height: 48px;
  transform-style: preserve-3d;
  transform-origin: bottom;
  transition: transform .6s cubic-bezier(.4,0,.6,1);
}

.tgl-12__sw:checked ~ .tgl-12__board .tgl-12__flap {
  transform: rotateX(-180deg);
}

.tgl-12__face {
  top: 0;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  border-radius: 8px 8px 0 0;
}

.tgl-12__face--back {
  transform: rotateX(180deg);
  background: #1e1c1a;
  border-radius: 0 0 8px 8px;
}

.tgl-12__hinge {
  position: absolute;
  top: 47px;
  left: 0;
  right: 0;
  height: 2px;
  background: #0d0c0a;
  z-index: 5;
}

.tgl-12__hinge::before,
.tgl-12__hinge::after {
  content: "";
  position: absolute;
  top: -4px;
  width: 5px;
  height: 10px;
  background: #0d0c0a;
  border-radius: 2px;
}

.tgl-12__hinge::before {
  left: -2px;
}

.tgl-12__hinge::after {
  right: -2px;
}

.tgl-12__lbl {
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #8d8672;
}

.tgl-12__sw:focus-visible ~ .tgl-12__board {
  outline: 3px solid #f5d97a;
  outline-offset: 5px;
  border-radius: 10px;
}

.tgl-12__cap {
  font-size: 11px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: #57523f;
}

@media (prefers-reduced-motion: reduce) {
  .tgl-12__flap {
    transition: transform .01s;
  }
}
```

## JavaScript
```js
/* No JavaScript — a hinged flap with front/back half-word faces rotates -180° inside a perspective parent, exactly like a mechanical split-flap cell. */
```

How this works

The board is four layers: a static top half showing the NEW word's upper half, a static bottom half showing the OLD word's lower half, and a hinged flap with two faces — front = old word's top half, back (pre-rotated 180°) = new word's bottom half. Each half shows exactly half a line of text via a fixed-height, overflow:hidden box; bottom halves shift the text up by 50%.

Checking the box transitions the flap rotateX(0 → −180deg) inside a perspective parent with backface-visibility:hidden, so the front face falls away and the back face lands as the new bottom half. Unchecking plays the same hinge in reverse. A dark hinge line and side pins sell the mechanism.

Make it yours

  • Change the words (e.g. OPEN/SHUT) — keep them the same length for the authentic board look.
  • Slow the flap to .8s and add a tiny end bounce with an overshoot bezier.
  • Stack several cells with staggered transition-delays for a full board ripple.
  • Swap the palette for amber-on-black classic airport styling.

Gotchas — read before shipping

  • The back face must be pre-rotated with rotateX(180deg) INSIDE a preserve-3d flap, or it renders mirrored.
  • All four text layers must use identical font metrics and line-height, or the halves misalign at the hinge.
  • Put perspective on the parent, not the flap, so the fold has depth.

Browser support

ChromeSafariFirefoxEdge
36+9+16+36+

3D transforms + backface-visibility; universal in modern browsers. Falls back to an instant swap if 3D is unavailable.

Techniques used in this demo

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

Search CodeFronts

Loading…