Advertisement

CSS :has() Notification Toggle

| by Vladimir | 2 min read | code by Paulo Nunes
A11y Ready Beginner

Tech & Dependencies

HTML CSS

Features

  • CSS :has()
  • Checkbox Hack
  • No JavaScript
  • Stateful UI

Browser Support

Chrome 105+ Edge 105+ Firefox 121+ Safari 15.4+

Core

This is a CSS :has() Notification Toggle. It provides a fully functional, state-aware UI switch for settings panels without a single line of JavaScript. Its function is to allow the user to toggle a binary state (notifications on/off) and have the entire UI — including icons, labels, and descriptive text — update instantly based purely on the state of a hidden checkbox.

Specs

  • Weight: ~2 KB. Zero dependencies. No JavaScript.
  • Performance: Exceptionally high. State changes are evaluated natively by the browser’s CSS engine, avoiding any script-based event listeners or class toggling.
  • Theming / Customization: Easily themed via CSS variables (--bg, --text, --focus). The switch itself is styled using appearance: none and pseudo-elements.
  • Responsiveness: Inherently responsive due to the simple grid layout and relative font sizes.
  • Graceful Degradation: [!] Relies entirely on the :has() pseudo-class. In older browsers that don’t support it, the toggle switch will still function visually, but the icon and text labels will not update to reflect the “on” or “off” state.

Anatomy

The component uses a hidden checkbox as a “state machine” that controls adjacent and child elements.

  • HTML (The Skeleton): A parent div with a class of .notification. Inside, an <svg> icon, a <label>, and an <input type="checkbox"> are placed as siblings.
  • CSS (The Skin): Leverages modern CSS features. @layer is used to organize styles. The switch is a custom-styled checkbox with its ::before pseudo-element acting as the sliding thumb.
  • JS (The Nervous System): Completely absent.

Logic

The core mechanism is “Parent State Selection” via :has():

.notification:has(input:checked) :is(.on, .msg-on, .icon-on) {
	display: inline-block;
}

.notification:has(input:checked) :is(.off, .msg-off, .icon-off) {
	display: none;
}

This is the power of :has() in action. Instead of listening for a change event in JavaScript and then adding/removing classes, the CSS does it declaratively. The selector reads: “Find an element with the class .notification that has a checked input inside of it. Then, within that element, find any children with the class .on, .msg-on, or .icon-on and set their display to inline-block.” At the same time, it hides the “off” state elements. This allows a single state change (the checkbox) to cascade through the entire component and update multiple, unrelated elements instantly.

Feel

Crisp, immediate, and surprisingly intelligent. Because the logic is handled by the CSS engine, the UI update is instantaneous, with zero latency. The “buzz” animation on the SVG icon when the switch is activated adds a subtle but satisfying tactile jolt, providing clear confirmation of the state change. It feels like a native OS-level toggle.

Advertisement