Advertisement

Pure CSS Morphing Dark Mode Toggle

| | 2 min read | code by Jon Kantner
A11y Ready Intermediate

Tech & Dependencies

HTML CSS

Features

  • Morphing Animation
  • Parent Selection
  • No JavaScript
  • Accessibility Focus

Browser Support

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

Core

This Pure CSS Morphing Dark Mode Toggle is a sophisticated implementation of a theme switcher that eliminates the need for JavaScript. By utilizing the modern CSS :has() relational selector and complex transform logic, the component morphs a sun icon into a moon icon seamlessly. It offers a premium feel through the use of custom cubic-bezier timing and modular CSS construction.

Core Technique

The component achieves its goal through two main technical pillars: parent-state detection via :has() and a multi-part geometric icon system.

1. Parent Theming via :has()

Traditionally, switching themes without JS required the “Checkbox Hack” where only sibling elements could be styled. With the :has() selector, the state of the checkbox can now control the body or any parent container directly.

/* Toggling the entire page theme based on checkbox state */
body:has(.switch__input:checked) {
	background-color: hsl(var(--hue), 10%, 10%);
	color: hsl(var(--hue), 10%, 90%);
}

2. The Morphing Icon System

The icon isn’t an SVG; it’s a collection of 11 span elements (icon parts).

  • Sun Rays: Parts 4 through 11 are positioned around a center point using transform-origin: 50% 0 and rotated in 45-degree increments.
  • Transition: When checked, the sun rays scale to zero (scale(0)) while the center core (part--3) changes its box-shadow to create the crescent moon shape.
.switch__icon-part--3 {
	box-shadow: 0 0 0 0.625em hsl(var(--hue),10%,10%) inset;
	transform: scale(0.25);
}

/* Morphing into the moon shape */
.switch__input:checked ~ .switch__icon .switch__icon-part--3 {
	box-shadow: 0 0 0 0.25em hsl(var(--hue),10%,10%) inset;
	transform: scale(1);
}

Browser Support

The critical factor for this snippet is the :has() selector. While supported in the latest versions of all major browsers, it is a relatively new addition to the CSS specification.

This code works perfectly in all modern browsers. Users on older versions of Firefox (pre-December 2023) or legacy browsers will see the functional checkbox but the theme switching logic and morphing animation will not trigger.

Advertisement