30 CSS Notifications21 / 30

Pure CSSMIT licensed

Pure CSS Notification Toggle Switch

The switch pattern end-to-end: a notification preferences panel where each control is a genuine checkbox restyled into a sliding switch (spring thumb via linear(), tinted row via :has(), visible focus ring), and a mute-all panel where one master switch dims, disables and un-tints every child row — pointer-events, opacity and state, all coordinated by :has() with zero JavaScript.

Published

Live Demo
Try it

The code

<div class="ntf-21-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-21a" aria-label="Notification preferences with CSS-only switches">
  <form class="ntf-21a__panel">
    <h2 class="ntf-21a__title">Notifications</h2>
    <label class="ntf-21a__row"><span class="ntf-21a__tx"><b>Product updates</b><small>Release notes, new snippet drops</small></span><span class="ntf-21a__switch"><input type="checkbox" checked><i class="ntf-21a__track" aria-hidden="true"><b></b></i></span></label>
    <label class="ntf-21a__row"><span class="ntf-21a__tx"><b>Comments</b><small>Replies on your snippets</small></span><span class="ntf-21a__switch"><input type="checkbox" checked><i class="ntf-21a__track" aria-hidden="true"><b></b></i></span></label>
    <label class="ntf-21a__row"><span class="ntf-21a__tx"><b>SMS alerts</b><small>Only for security events</small></span><span class="ntf-21a__switch"><input type="checkbox"><i class="ntf-21a__track" aria-hidden="true"><b></b></i></span></label>
    <p class="ntf-21a__cap">Real checkboxes underneath — keyboard, forms and focus rings come free. The thumb settles on a <b>linear()</b> spring.</p>
  </form>
</section>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-21b" aria-label="Master mute switch disabling child switches">
  <form class="ntf-21b__panel">
    <label class="ntf-21b__master"><span class="ntf-21b__mtx"><b>Pause everything</b><small>One switch, whole subtree — pure :has()</small></span><span class="ntf-21b__switch ntf-21b__switch--big"><input type="checkbox" id="ntf-21b-all"><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
    <div class="ntf-21b__kids">
      <label class="ntf-21b__row"><span class="ntf-21b__tx"><b>Mentions</b></span><span class="ntf-21b__switch"><input type="checkbox" checked><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
      <label class="ntf-21b__row"><span class="ntf-21b__tx"><b>Weekly digest</b></span><span class="ntf-21b__switch"><input type="checkbox" checked><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
      <label class="ntf-21b__row"><span class="ntf-21b__tx"><b>Marketing</b></span><span class="ntf-21b__switch"><input type="checkbox"><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
    </div>
    <p class="ntf-21b__cap">Pausing dims and disables the children <em>without touching their state</em> — unpause and every row remembers itself.</p>
  </form>
</section>
</div>
.ntf-21-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.ntf-21-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.ntf-21a,
.ntf-21a *,
.ntf-21a *::before,
.ntf-21a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ntf-21a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: radial-gradient(120% 100% at 50% 0%,#151a26,#0b0e15);
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.ntf-21a__panel {
  width: min(410px,100%);
  padding: 24px;
  border-radius: 20px;
  background: rgba(255,255,255,.045);
  border: 1px solid rgba(165,190,230,.16);
  box-shadow: 0 30px 70px -40px rgba(0,0,0,.9);
}

.ntf-21a__title {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: #edf2fc;
  margin-bottom: 8px;
}

.ntf-21a__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 12px;
  border-radius: 13px;
  cursor: pointer;
  transition: background .2s;
}

.ntf-21a__row:hover {
  background: rgba(255,255,255,.04);
}

.ntf-21a__row:has(input:checked) {
  background: oklch(0.75 0.14 200 / .07);
}

.ntf-21a__row+.ntf-21a__row {
  margin-top: 2px;
}

.ntf-21a__tx {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.ntf-21a__tx b {
  font-size: 14px;
  font-weight: 600;
  color: #edf2fc;
}

.ntf-21a__tx small {
  font-size: 12px;
  color: rgba(212,224,246,.5);
}

.ntf-21a__switch {
  position: relative;
  flex: none;
}

.ntf-21a__switch input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.ntf-21a__track {
  display: block;
  width: 46px;
  height: 27px;
  border-radius: 999px;
  background: rgba(255,255,255,.14);
  padding: 3.5px;
  transition: background .25s;
}

.ntf-21a__track b {
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
  transition: translate .28s ease;
  transition: translate .45s linear(0,0.62 18%,1.02 38%,1.12 50%,1.02 66%,0.98 78%,1);
}

.ntf-21a__switch:has(input:checked) .ntf-21a__track {
  background: oklch(0.75 0.14 200);
}

.ntf-21a__switch:has(input:checked) .ntf-21a__track b {
  translate: 19px 0;
}

.ntf-21a__switch:has(input:focus-visible) .ntf-21a__track {
  outline: 2px solid oklch(0.75 0.14 200);
  outline-offset: 3px;
}

.ntf-21a__cap {
  margin-top: 14px;
  font-size: 11.5px;
  line-height: 1.6;
  color: rgba(212,224,246,.45);
  text-wrap: pretty;
}

.ntf-21a__cap b {
  color: rgba(237,242,252,.8);
}

@media (prefers-reduced-motion: reduce) {
  .ntf-21a__track b {
    transition-duration: .01s;
  }
}

.ntf-21b,
.ntf-21b *,
.ntf-21b *::before,
.ntf-21b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ntf-21b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#f6f3ec,#ece5d5);
  font-family: 'Outfit','Segoe UI',system-ui,sans-serif;
}

.ntf-21b__panel {
  width: min(400px,100%);
  padding: 22px;
  border-radius: 20px;
  background: #fffdf7;
  border: 1px solid rgba(36,31,23,.11);
  box-shadow: 0 28px 62px -34px rgba(36,31,23,.5);
}

.ntf-21b__master {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px;
  border-radius: 14px;
  background: #241f17;
  cursor: pointer;
}

.ntf-21b__mtx {
  display: grid;
  gap: 2px;
}

.ntf-21b__mtx b {
  font-size: 14.5px;
  font-weight: 700;
  color: #fffdf7;
}

.ntf-21b__mtx small {
  font-size: 11.5px;
  color: rgba(255,253,247,.55);
}

.ntf-21b__kids {
  margin-top: 10px;
  transition: opacity .3s;
}

.ntf-21b__panel:has(#ntf-21b-all:checked) .ntf-21b__kids {
  opacity: .4;
  pointer-events: none;
}

.ntf-21b__panel:has(#ntf-21b-all:checked) .ntf-21b__kids input {
  visibility: hidden;
}

.ntf-21b__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 14px;
  border-radius: 12px;
  cursor: pointer;
  transition: background .15s;
}

.ntf-21b__row:hover {
  background: rgba(36,31,23,.045);
}

.ntf-21b__tx b {
  font-size: 13.5px;
  font-weight: 600;
  color: #241f17;
}

.ntf-21b__switch {
  position: relative;
  flex: none;
}

.ntf-21b__switch input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.ntf-21b__track {
  display: block;
  width: 42px;
  height: 24px;
  border-radius: 999px;
  background: rgba(36,31,23,.16);
  padding: 3px;
  transition: background .25s;
}

.ntf-21b__track b {
  display: block;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fffdf7;
  box-shadow: 0 1px 3px rgba(36,31,23,.35);
  transition: translate .3s cubic-bezier(.22,1.4,.36,1);
}

.ntf-21b__switch:has(input:checked) .ntf-21b__track {
  background: oklch(0.62 0.14 155);
}

.ntf-21b__switch:has(input:checked) .ntf-21b__track b {
  translate: 18px 0;
}

.ntf-21b__switch--big .ntf-21b__track {
  background: rgba(255,253,247,.22);
  width: 48px;
  height: 27px;
}

.ntf-21b__switch--big .ntf-21b__track b {
  width: 21px;
  height: 21px;
}

.ntf-21b__switch--big:has(input:checked) .ntf-21b__track {
  background: oklch(0.72 0.16 45);
}

.ntf-21b__switch--big:has(input:checked) .ntf-21b__track b {
  translate: 21px 0;
}

.ntf-21b__switch:has(input:focus-visible) .ntf-21b__track {
  outline: 2px solid #241f17;
  outline-offset: 3px;
}

.ntf-21b__master .ntf-21b__switch:has(input:focus-visible) .ntf-21b__track {
  outline-color: #fffdf7;
}

.ntf-21b__cap {
  margin-top: 14px;
  font-size: 11.5px;
  line-height: 1.6;
  color: rgba(36,31,23,.5);
  text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
  .ntf-21b__track b,
    .ntf-21b__kids {
    transition-duration: .01s;
  }
}
/* No JavaScript — checkboxes hold the state, :has() paints track, thumb and row tint, and the second (spring) transition declaration wins where linear() is supported, falling back to plain ease elsewhere. */

/* No JavaScript — .panel:has(#all:checked) dims the subtree, kills pointer events and hides the inputs from the tab order; child checkbox state is never mutated, so unpausing restores everything. */
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 Notification 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: Pure CSS Notification Toggle Switch
Source: https://codefronts.com/snippets/css-notifications/pure-css-notification-toggle-switch/

The switch pattern end-to-end: a notification preferences panel where each control is a genuine checkbox restyled into a sliding switch (spring thumb via linear(), tinted row via :has(), visible focus ring), and a mute-all panel where one master switch dims, disables and un-tints every child row — pointer-events, opacity and state, all coordinated by :has() with zero JavaScript.
## HTML
```html
<div class="ntf-21-group">
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-21a" aria-label="Notification preferences with CSS-only switches">
  <form class="ntf-21a__panel">
    <h2 class="ntf-21a__title">Notifications</h2>
    <label class="ntf-21a__row"><span class="ntf-21a__tx"><b>Product updates</b><small>Release notes, new snippet drops</small></span><span class="ntf-21a__switch"><input type="checkbox" checked><i class="ntf-21a__track" aria-hidden="true"><b></b></i></span></label>
    <label class="ntf-21a__row"><span class="ntf-21a__tx"><b>Comments</b><small>Replies on your snippets</small></span><span class="ntf-21a__switch"><input type="checkbox" checked><i class="ntf-21a__track" aria-hidden="true"><b></b></i></span></label>
    <label class="ntf-21a__row"><span class="ntf-21a__tx"><b>SMS alerts</b><small>Only for security events</small></span><span class="ntf-21a__switch"><input type="checkbox"><i class="ntf-21a__track" aria-hidden="true"><b></b></i></span></label>
    <p class="ntf-21a__cap">Real checkboxes underneath — keyboard, forms and focus rings come free. The thumb settles on a <b>linear()</b> spring.</p>
  </form>
</section>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400..700&display=swap" rel="stylesheet">
<section class="ntf-21b" aria-label="Master mute switch disabling child switches">
  <form class="ntf-21b__panel">
    <label class="ntf-21b__master"><span class="ntf-21b__mtx"><b>Pause everything</b><small>One switch, whole subtree — pure :has()</small></span><span class="ntf-21b__switch ntf-21b__switch--big"><input type="checkbox" id="ntf-21b-all"><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
    <div class="ntf-21b__kids">
      <label class="ntf-21b__row"><span class="ntf-21b__tx"><b>Mentions</b></span><span class="ntf-21b__switch"><input type="checkbox" checked><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
      <label class="ntf-21b__row"><span class="ntf-21b__tx"><b>Weekly digest</b></span><span class="ntf-21b__switch"><input type="checkbox" checked><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
      <label class="ntf-21b__row"><span class="ntf-21b__tx"><b>Marketing</b></span><span class="ntf-21b__switch"><input type="checkbox"><i class="ntf-21b__track" aria-hidden="true"><b></b></i></span></label>
    </div>
    <p class="ntf-21b__cap">Pausing dims and disables the children <em>without touching their state</em> — unpause and every row remembers itself.</p>
  </form>
</section>
</div>
```
## CSS
```css
.ntf-21-group {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  width: 100%;
}

.ntf-21-group > section {
  flex: 1 1 480px;
  min-width: min(100%,360px);
}

.ntf-21a,
.ntf-21a *,
.ntf-21a *::before,
.ntf-21a *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ntf-21a {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: radial-gradient(120% 100% at 50% 0%,#151a26,#0b0e15);
  font-family: 'Space Grotesk','Segoe UI',system-ui,sans-serif;
}

.ntf-21a__panel {
  width: min(410px,100%);
  padding: 24px;
  border-radius: 20px;
  background: rgba(255,255,255,.045);
  border: 1px solid rgba(165,190,230,.16);
  box-shadow: 0 30px 70px -40px rgba(0,0,0,.9);
}

.ntf-21a__title {
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: #edf2fc;
  margin-bottom: 8px;
}

.ntf-21a__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px 12px;
  border-radius: 13px;
  cursor: pointer;
  transition: background .2s;
}

.ntf-21a__row:hover {
  background: rgba(255,255,255,.04);
}

.ntf-21a__row:has(input:checked) {
  background: oklch(0.75 0.14 200 / .07);
}

.ntf-21a__row+.ntf-21a__row {
  margin-top: 2px;
}

.ntf-21a__tx {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.ntf-21a__tx b {
  font-size: 14px;
  font-weight: 600;
  color: #edf2fc;
}

.ntf-21a__tx small {
  font-size: 12px;
  color: rgba(212,224,246,.5);
}

.ntf-21a__switch {
  position: relative;
  flex: none;
}

.ntf-21a__switch input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.ntf-21a__track {
  display: block;
  width: 46px;
  height: 27px;
  border-radius: 999px;
  background: rgba(255,255,255,.14);
  padding: 3.5px;
  transition: background .25s;
}

.ntf-21a__track b {
  display: block;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 3px rgba(0,0,0,.4);
  transition: translate .28s ease;
  transition: translate .45s linear(0,0.62 18%,1.02 38%,1.12 50%,1.02 66%,0.98 78%,1);
}

.ntf-21a__switch:has(input:checked) .ntf-21a__track {
  background: oklch(0.75 0.14 200);
}

.ntf-21a__switch:has(input:checked) .ntf-21a__track b {
  translate: 19px 0;
}

.ntf-21a__switch:has(input:focus-visible) .ntf-21a__track {
  outline: 2px solid oklch(0.75 0.14 200);
  outline-offset: 3px;
}

.ntf-21a__cap {
  margin-top: 14px;
  font-size: 11.5px;
  line-height: 1.6;
  color: rgba(212,224,246,.45);
  text-wrap: pretty;
}

.ntf-21a__cap b {
  color: rgba(237,242,252,.8);
}

@media (prefers-reduced-motion: reduce) {
  .ntf-21a__track b {
    transition-duration: .01s;
  }
}

.ntf-21b,
.ntf-21b *,
.ntf-21b *::before,
.ntf-21b *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.ntf-21b {
  width: 100%;
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 48px 24px;
  background: linear-gradient(180deg,#f6f3ec,#ece5d5);
  font-family: 'Outfit','Segoe UI',system-ui,sans-serif;
}

.ntf-21b__panel {
  width: min(400px,100%);
  padding: 22px;
  border-radius: 20px;
  background: #fffdf7;
  border: 1px solid rgba(36,31,23,.11);
  box-shadow: 0 28px 62px -34px rgba(36,31,23,.5);
}

.ntf-21b__master {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 14px;
  border-radius: 14px;
  background: #241f17;
  cursor: pointer;
}

.ntf-21b__mtx {
  display: grid;
  gap: 2px;
}

.ntf-21b__mtx b {
  font-size: 14.5px;
  font-weight: 700;
  color: #fffdf7;
}

.ntf-21b__mtx small {
  font-size: 11.5px;
  color: rgba(255,253,247,.55);
}

.ntf-21b__kids {
  margin-top: 10px;
  transition: opacity .3s;
}

.ntf-21b__panel:has(#ntf-21b-all:checked) .ntf-21b__kids {
  opacity: .4;
  pointer-events: none;
}

.ntf-21b__panel:has(#ntf-21b-all:checked) .ntf-21b__kids input {
  visibility: hidden;
}

.ntf-21b__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 14px;
  border-radius: 12px;
  cursor: pointer;
  transition: background .15s;
}

.ntf-21b__row:hover {
  background: rgba(36,31,23,.045);
}

.ntf-21b__tx b {
  font-size: 13.5px;
  font-weight: 600;
  color: #241f17;
}

.ntf-21b__switch {
  position: relative;
  flex: none;
}

.ntf-21b__switch input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}

.ntf-21b__track {
  display: block;
  width: 42px;
  height: 24px;
  border-radius: 999px;
  background: rgba(36,31,23,.16);
  padding: 3px;
  transition: background .25s;
}

.ntf-21b__track b {
  display: block;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fffdf7;
  box-shadow: 0 1px 3px rgba(36,31,23,.35);
  transition: translate .3s cubic-bezier(.22,1.4,.36,1);
}

.ntf-21b__switch:has(input:checked) .ntf-21b__track {
  background: oklch(0.62 0.14 155);
}

.ntf-21b__switch:has(input:checked) .ntf-21b__track b {
  translate: 18px 0;
}

.ntf-21b__switch--big .ntf-21b__track {
  background: rgba(255,253,247,.22);
  width: 48px;
  height: 27px;
}

.ntf-21b__switch--big .ntf-21b__track b {
  width: 21px;
  height: 21px;
}

.ntf-21b__switch--big:has(input:checked) .ntf-21b__track {
  background: oklch(0.72 0.16 45);
}

.ntf-21b__switch--big:has(input:checked) .ntf-21b__track b {
  translate: 21px 0;
}

.ntf-21b__switch:has(input:focus-visible) .ntf-21b__track {
  outline: 2px solid #241f17;
  outline-offset: 3px;
}

.ntf-21b__master .ntf-21b__switch:has(input:focus-visible) .ntf-21b__track {
  outline-color: #fffdf7;
}

.ntf-21b__cap {
  margin-top: 14px;
  font-size: 11.5px;
  line-height: 1.6;
  color: rgba(36,31,23,.5);
  text-wrap: pretty;
}

@media (prefers-reduced-motion: reduce) {
  .ntf-21b__track b,
    .ntf-21b__kids {
    transition-duration: .01s;
  }
}
```

## JavaScript
```js
/* No JavaScript — checkboxes hold the state, :has() paints track, thumb and row tint, and the second (spring) transition declaration wins where linear() is supported, falling back to plain ease elsewhere. */

/* No JavaScript — .panel:has(#all:checked) dims the subtree, kills pointer events and hides the inputs from the tab order; child checkbox state is never mutated, so unpausing restores everything. */
```

How this works

A trustworthy CSS switch is a real <input type="checkbox"> hidden but focusable, with the costume drawn on a sibling span:

.switch input{ position:absolute; opacity:0 }
.track{ width:44px; height:26px; border-radius:999px;
  background:var(--off); transition:background .25s }
.thumb{ width:20px; height:20px; border-radius:50%;
  transition:translate .4s linear(0,1.15 60%,.98 80%,1) }
.switch:has(input:checked) .track{ background:var(--on) }
.switch:has(input:checked) .thumb{ translate:18px 0 }

The linear() spring gives the thumb a physical overshoot-and-settle no cubic-bezier can fake (beziers can't change direction twice). Because the input is real, you get keyboard toggling, form submission, and :focus-visible for free — the ring draws on the track via .switch:has(input:focus-visible). The mute-all variant shows :has() as panel logic: .panel:has(#all:checked) .row{ opacity:.45; pointer-events:none } — one selector disables a subtree, and unchecking restores every row's own remembered state, because the children never actually changed.

Make it yours

  • Thumb travel = track width − thumb − 2×padding: 44−20−6 = 18px here; keep the formula and any size works.
  • Row tinting on :has(:checked) gives scan-ability — a settings page reads as a barcode of what's on.
  • Swap the spring for translate .2s ease for corporate contexts; keep the spring for consumer products.
  • Add a third 'scheduled' state? Switches are binary by contract — reach for radios/segmented controls instead of inventing tri-state switches.

Gotchas — read before shipping

  • display:none on the input kills keyboard access — hide with opacity:0 (still focusable), never display or visibility.
  • pointer-events:none disables pointers but NOT the keyboard — the mute-all variant also sets visibility on inputs so tabbing skips disabled rows honestly.
  • Don't put on/off text INSIDE the thumb — 20px text fails contrast and clips in German; the row label carries meaning.
  • iOS Safari needs the label wrapping input + costume (as here) for reliable tap toggling — orphaned labels with for= sometimes miss.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by :has(), oklch(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 113+.

:has() (Chrome 105 / Safari 15.4 / Firefox 121) drives all state styling. linear() easing is Chrome 113 / Safari 17.2 / Firefox 112 — older browsers fall back to the plain transition declared first. The checkbox itself works in every browser ever made.

Techniques used in this demo

Search CodeFronts

Loading…