Dark Mode Toggle Button without Javascript II

Written by Lucie Zdeňková on 2025-01-16

htmlcss

In the previous part Dark Mode Toggle without Javascript, we demonstrated how to set up CSS variables and style the wrapper div container. In this second part, we will focus on styling the toggle itself.


Toggle Button UI

In the image, you can see the final appearance of the toggle UI achieved using the following CSS.

UI of dark mode toggle

Hiding the Checkbox Input

The hidden checkbox input is the key principle of this solution. While the checkbox remains functional for accessibility and state management, the visible toggle UI is controlled through the <label> element. Properties display: none and visibility: hidden, along with width: 0 and height: 0 completely hide the checkbox from view.

input#darkmode-toggle {
    width: 0;
    height: 0;
    visibility: hidden;
    display: none;
}

Styling the Toggle Container

This section defines the toggle's main visual container. It acts as a clickable area styled to look like a rounded switch. The property display: block allows the label to act as a block-level element, making it possible to set width and height. The position: relative is necessary for styling the knob in next step, cursor: pointer indicates to users that this element is interactive. Properties border-radius, box-shadow define the rounded shape and shadows of the toggle, while background-color uses a previously set CSS variable var(--toggle-bg) to allow dynamic theme changes.

label.darkmode-label {
    display: block;
    position: relative;
    cursor: pointer;
    width: 100px;
    height: 40px;
    border-radius: 40px;
    box-shadow: inset 0px 1px 3px rgba(0, 0, 0, 0.4), inset 0px -1px 3px rgba(255, 255, 255, 0.4);
    background-color: var(--toggle-bg);
}

Styling the Toggle Knob

The knob is styled to move smoothly between light and dark modes. Using position: absolute; the knob is placed relative to the toggle container styled in the code snippet above. Property content: "" ensures the :after pseudo-element is rendered. We are using the CSS variables to dynamically adjust horizontal placement by using left: var(--label-left), backgroud of the knob is changed by background: linear-gradient(180deg, var(--gradient-bg1), var(--gradient-bg2)) and movement animations are controlled by transform: var(--transformantion). The remaining properties width, height, border-radius, box-shadow follow similar logic as described in the previous code snippet. Lastly, top: 2px aligns the knob vertically.

label.darkmode-label:after {
    position: absolute;
    content: "";
    left: var(--label-left);
    transform: var(--transformantion);
    background: linear-gradient(180deg, var(--gradient-bg1), var(--gradient-bg2));
    width: 36px;
    height: 36px;
    top: 2px;
    border-radius: 36px;
    box-shadow: 0px 1px 2px rgba(0, 0, 0, 0.2);
}

Animations and Additional Elements

Following code snippet adds smooth animations to the toggle's movements and interactions. First property transition: 0.3s ensures a 0.3-second animation for the toggle changes. Property width: 52px with selector label.darkmode-label:active:after slightly enlarges the knob during interaction for a tactile feedback effect.

label.darkmode-label,
label.darkmode-label:after {
    transition: 0.3s
}

label.darkmode-label:active:after {
    width: 52px;
}

Icon Styling

Following CSS code styles sun and moon illustrations mentioned in previous blog post. We need to reach icon color change according to the theme selection. Suitable solution is setting backgroud image by CSS variable background-image: var(--img-sun) and background-image: var(--img-moon). It's necessary to use <span> element inside the <label> element, so display: block allows setting dimensions width and height. Property position: absolute sets absolute position to the label container, meanwhile properties left, top sets vertical and horizontal positioning. Previously mentioned transition: 0.3s makes the animation effect. Lastly, z-index: 1 defines placement of icons above the other elements.

label.darkmode-label span.sun {
    background-image: var(--img-sun);
    display: block;
    position: absolute;
    width: 24px;
    height: 24px;
    left: 8px;
    top: 8px;
    transition: 0.3s;
    z-index: 1;
}

label.darkmode-label span.moon {
    background-image: var(--img-moon);
    display: block;
    position: absolute;
    width: 24px;
    height: 24px;
    left: 68px;
    top: 8px;
    transition: 0.3s;
    z-index: 1;

}

This code creates a functional and visually appealing toggle switch by combining CSS variables, pseudo-elements, and transitions. The hidden checkbox input manages the toggle state, while the <label> and associated pseudo-elements provide the interactive UI. CSS variable dynamic properties like --toggle-bg and --label-left allow seamless adaptation between themes. This is everyting you need to implement dark mode toggle on the static website without the need to use javascript.

Thank you for reading and see ya next time!

Resources:

  1. CSS Dark Mode Toggle Button from scratch in 6 Minutes