DEV Community

Cover image for Building a Smooth Dark/Light Mode Switch with Modern CSS Features
Muhammad Usman
Muhammad Usman

Posted on

Building a Smooth Dark/Light Mode Switch with Modern CSS Features

I have built this simple project just to give you the idea that how you can build functional light/dark mode toggle using only HTML & CSS. So you can use it in your own projects.

Hey there, Dev Community! Let me show you this cool and simple project I made—a website with a light/dark mode toggle! It’s a simple example of how you can switch between light/dark themes using just HTML and CSS. No JavaScript used in this example. Perfect for beginners who are learning theme-switching basics, or as a starter template for projects the needs dark mode functionality.

Here is the visual example of the code:

What exactly is happening in this code?

1. A Simple and Basic HTML Structure:

  • The page has a header with a logo and a light/dark toggle switch.
  • A "hero" section with a title, text, and a button.
  • Four colorful blocks at the bottom of the hero section (just for visual better effect for users!).

2. CSS Variables (Light/Dark Mode):

  • Colors are stored in CSS variables (like --bg-color or --text-color).
  • When the toggle button is clicked by the user, these variables swap between light/dark values throughout the page.
  • Color Examples: Light mode uses soft white backgrounds, Dark mode uses deep blues.

03. How's the Toggle Switch Trick Working:

  • The toggle is a hidden checkbox. When a user checked/clicked, it triggers between light & dark modes.
  • The sliding circle effect? It’s a CSS ::before pseudo-element that moves when the checkbox is checked.

04. Responsive Styling of the Elements:

  • Fonts from Google Fonts (Poppins, Merriweather) make the text look cleaner & make it more visually appealing.
  • Buttons and blocks changing colors smoothly using CSS transitions effect.
  • The color blocks use different color variables for light/dark mode (pink, yellow, green, blue).

05. Responsive Layout:

  • The color blocks adjust into a grid on smaller screens.
  • The hero section centers content neatly.

Why I think It’s Cool:

  • No JavaScript: Everything happens using only pure CSS!
  • Smooth Transitions: Colors fade gently between light/dark modes.
  • Easy to use & customize—change the color variables to match your brand identity!

Try copying the code into an HTML file and play with the colors. Toggle that switch and watch how well the effect happen with your colors!

Don’t forget to share it with your fellow developers and friends, so it can reach to a wide audience.

📍 Find me on: LinkedIn | Medium | Bluesky

The Complete HTML & CSS Code:


html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Dark Mode Toggle</title>
    <link
      rel="stylesheet"
      href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600&family=Merriweather:wght@700&family=JetBrains+Mono&display=swap"
    />
    <style>
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      :root {
        --bg-color: #f8fafc;
        --text-color: #1e293b;
        --header-bg: #e2e8f0;
        --btn-bg: #3b82f6;
        --btn-text: #ffffff;
        --toggle-bg: #94a3b8;
        --toggle-circle: #ffffff;
        --block-1: #ec4899;
        --block-2: #facc15;
        --block-3: #22c55e;
        --block-4: #6366f1;
      }

      :has(#dark-mode-toggle:checked) {
        --bg-color: #0f172a;
        --text-color: #e2e8f0;
        --header-bg: #1e293b;
        --btn-bg: #14b8a6;
        --btn-text: #0f172a;
        --toggle-bg: #475569;
        --toggle-circle: #0f172a;
        --block-1: #f43f5e;
        --block-2: #f97316;
        --block-3: #84cc16;
        --block-4: #3b82f6;
      }

      body {
        font-family: "Poppins", sans-serif;
        background-color: var(--bg-color);
        color: var(--text-color);
        display: flex;
        flex-direction: column;
        min-height: 100vh;
        transition: background 0.3s, color 0.3s;
      }

      .header {
        font-family: "Merriweather", serif;
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 20px;
        background: var(--header-bg);
        box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
        transition: background 0.3s;
      }

      .logo {
        font-size: 1.5em;
        font-weight: bold;
      }

      #dark-mode-toggle {
        display: none;
      }

      .toggle {
        display: inline-block;
        width: 60px;
        height: 30px;
        background: var(--toggle-bg);
        border-radius: 30px;
        position: relative;
        cursor: pointer;
        transition: background 0.3s;
      }

      .toggle::before {
        content: "";
        position: absolute;
        top: 3px;
        left: 3px;
        width: 24px;
        height: 24px;
        background: var(--toggle-circle);
        border-radius: 50%;
        transition: transform 0.3s;
      }

      #dark-mode-toggle:checked + .toggle::before {
        transform: translateX(30px);
      }

      .hero {
        display: flex;
        justify-content: center;
        align-items: center;
        flex-grow: 1;
        padding: 50px 20px;
        text-align: center;
      }

      .hero .content {
        max-width: 800px;
      }

      .hero h1 {
        font-family: "Merriweather", serif;
        font-size: 2.5em;
        margin-bottom: 20px;
      }

      .hero p {
        font-family: "Poppins", sans-serif;
        font-size: 1.2em;
        margin-bottom: 30px;
      }

      .btn {
        font-family: "Poppins", sans-serif;
        padding: 10px 20px;
        font-size: 1em;
        background: var(--btn-bg);
        color: var(--btn-text);
        text-decoration: none;
        border-radius: 8px;
        transition: background 0.3s, color 0.3s;
      }

      .btn:hover {
        background: var(--btn-text);
        color: var(--btn-bg);
      }

      .color-blocks {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
        gap: 20px;
        padding: 20px;
      }

      .block {
        height: 200px;
        border-radius: 12px;
        transition: background 0.3s;
      }

      .block-1 {
        background: var(--block-1);
      }

      .block-2 {
        background: var(--block-2);
      }

      .block-3 {
        background: var(--block-3);
      }

      .block-4 {
        background: var(--block-4);
      }
    </style>
  </head>
  <body>
    <header class="header">
      <div class="logo">Dark Mode</div>
      <input type="checkbox" id="dark-mode-toggle" />
      <label for="dark-mode-toggle" class="toggle"></label>
    </header>

    <section class="hero">
      <div class="content">
        <h1>We Build Stunning Digital Experiences</h1>
        <p>
          Innovative designs, seamless functionality, and performance-driven
          development tailored to your brand.
        </p>
        <a href="#" class="btn">Get Started</a>
      </div>
    </section>

    <section class="color-blocks">
      <div class="block block-1"></div>
      <div class="block block-2"></div>
      <div class="block block-3"></div>
      <div class="block block-4"></div>
    </section>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

Top comments (1)

Collapse
 
madsstoumann profile image
Mads Stoumann

You can also use the light-dark() function in CSS, and default to the user's preferred color-scheme:

body {
  background: light-dark(#FFF, #333);
  color: light-dark(#222, #EEE);
  color-scheme: light dark;
Enter fullscreen mode Exit fullscreen mode

Then, you can make your toggle behave like a "select the opposite of my preferred scheme":

<label>
  <input type="checkbox" name="toggle-color-scheme">
  <span>Toggle color-scheme</span>
</label>
Enter fullscreen mode Exit fullscreen mode

And in CSS:

@media (prefers-color-scheme: dark) {
  &:has([name="toggle-color-scheme"]:checked) {
    color-scheme: light;
  }
}
@media (prefers-color-scheme: light) {
  &:has([name="toggle-color-scheme"]:checked) {
    color-scheme: dark;
  }
}
Enter fullscreen mode Exit fullscreen mode

You can, of course, still use custom properties with light-dark():

:root {
  --color-bg: light-dark(#FFF, #333);
  --color-text: light-dark(#222, #EEE);
}
Enter fullscreen mode Exit fullscreen mode