Dark/Light Mode


Dark Mode toggle is a must on every website nowadays. whether it maybe to for power saving abilities of your device or for easy reading at night. There are several Approaches to achieve this

Approach 1 – Using body class

<body class="dark-theme">
**body.light-theme {
  color: #222;
  background: #fff;
}
a.light-theme {
  color: #0033cc;
}

/* Dark Mode styles */
body.dark-theme {
  color: #eee;
  background: #121212;
}
body.dark-theme a {
  color: #809fff;
}**

The general idea of this approach is to style things up as we normally would, call that our “default” mode which here is the light mode. The dark theme colors should be set with the same elements styling but with a prefix of “.dark-theme” in the beginning. Then with javascript the class “dark-theme” should be toggled.

The cons of this is that it will produce a lot of redundant css code for color and background on each element.

Approach 2 – Custom properties

We can use CSS custom properties to create a dark theme! The advantage of doing it this way is avoiding to write separate style rulesets for each theme, making it a lot faster to write styles and a lot easier to make changes to a theme if we need to.

body {
  --text-color: #222;
  --bkg-color: #fff;
  --anchor-color: #0033cc;
}

body.dark-theme {
  --text-color: #eee;
  --bkg-color: #121212;
  --anchor-color: #809fff;
}
//this can also be defined inside :root{}

body {
  color: var(--text-color);
  background: var(--bkg-color);
}
a {
  color: var(--anchor-color);
}

The dark-theme class should be toggled like the previous approach using javascript.

Dark Mode when set at OS level

@media (prefers-color-scheme: dark) {
  body {
  color: var(--text-color);
  background: var(--bkg-color);
    }
    a {
      color: var(--anchor-color);
    }
}
const prefersDarkScheme = window.matchMedia("(prefers-color-scheme: dark)");

if (prefersDarkScheme.matches) {
  document.body.classList.add("dark-theme");
} else {
  document.body.classList.remove("dark-theme");
}

There is a downside to using JavaScript: there will likely be a quick flash of the light theme as JavaScript is executed after the CSS. That could be misconstrued as a bug.

Dark Mode when set at OS level but should be able to overide it.

/* Default colors */
body {
  --text-color: #222;
  --bkg-color: #fff;
}
/* Dark theme colors */
body.dark-theme {
  --text-color: #eee;
  --bkg-color: #121212;
}

/* Styles for users who prefer dark mode at the OS level */
@media (prefers-color-scheme: dark) {
  /* defaults to dark theme */
  body { 
    --text-color: #eee;
    --bkg-color: #121212;
  }
  /* Override dark mode with light mode styles if the user decides to swap */
  body.light-theme {
    --text-color: #222;
    --bkg-color: #fff;
  }
}
// Listen for a click on the button 
btn.addEventListener("click", function() {
  // If the OS is set to dark mode...
  if (prefersDarkScheme.matches) {
    // ...then apply the .light-theme class to override those styles
    document.body.classList.toggle("light-theme");
    // Otherwise...
  } else {
    // ...apply the .dark-theme class to override the default light styles
    document.body.classList.toggle("dark-theme");
  }
});

Storing user preference

Using localStorage

the only downside of this approach is that the incorrect theme will be loaded for a breif moment as javascript is run after css is executed.

// Select the button
const btn = document.querySelector(".btn-toggle");
// Select the theme preference from localStorage
const currentTheme = localStorage.getItem("theme");

// If the current theme in localStorage is "dark"...
if (currentTheme == "dark") {
  // ...then use the .dark-theme class
  document.body.classList.add("dark-theme");
}

// Listen for a click on the button 
btn.addEventListener("click", function() {
  // Toggle the .dark-theme class on each click
  document.body.classList.toggle("dark-theme");

  // Let's say the theme is equal to light
  let theme = "light";
  // If the body contains the .dark-theme class...
  if (document.body.classList.contains("dark-theme")) {
    // ...then let's make the theme dark
    theme = "dark";
  }
  // Then save the choice in localStorage
  localStorage.setItem("theme", theme);
});

Using Cookies

// Select the button
const btn = document.querySelector(".btn-toggle");

// Listen for a click on the button 
btn.addEventListener("click", function() {
  // Toggle the .dark-theme class on the body
  document.body.classList.toggle("dark-theme");

  // Let's say the theme is equal to light
  let theme = "light";
  // If the body contains the .dark-theme class...
  if (document.body.classList.contains("dark-theme")) {
    // ...then let's make the theme dark
    theme = "dark";
  }
  // Then save the choice in a cookie
  document.cookie = "theme=" + theme;
});
<?php
$themeClass = '';
if (!empty($_COOKIE['theme']) && $_COOKIE['theme'] == 'dark') {
  $themeClass = 'dark-theme';
}
?>

<!DOCTYPE html>
<html lang="en">
<!-- etc. -->
<body class="<?php echo $themeClass; ?>">
<!-- etc. -->
</body>
</html>

If your site has user accounts then the way to go about it is to store the the settings in the user db. Pick the preference either from session or from cookie. that is when the user is not logged in the value of cookie is taken

<?php
// Login action
if (!empty($_POST['login'])) {
  // etc.

  // If the uuser is authenticated...
  if ($loginSuccess) {
    // ... save their theme preference to a session variable
    $_SESSION['user_theme'] = $userData['theme'];
  }
}

// Pick the session variable first if it's set; otherwise pick the cookie
$themeChoice = $_SESSION['user_theme'] ?? $_COOKIE['theme'] ?? null;
$themeClass = '';
if ($themeChoice == 'dark') {
  $themeClass = 'dark-theme';
}
?>

<!DOCTYPE html>
<html lang="en">
<!-- etc. -->
<body class="<?php echo $themeClass; ?>">
<!-- etc. -->
</body>
</html>

Some Design considerations

Dark Mode Images

A good rule is to decrease the brightness and contrast of images a bit so that it looks comfortable to the eyes when it’s against a dark background.

@media (prefers-color-scheme: dark) {
  img {
    filter: brightness(.8) contrast(1.2);
  }
}

or this can be done using different versions of the image

<picture>
  <!-- Use this image if the user's OS setting is light or unset -->
  <source srcset="photo-light.png" media="(prefers-color-scheme: light) or (prefers-color-scheme: no-preference)">
  <!-- Use this image if the user's OS setting is dark -->
  <source srcset="photo-dark.png" media="(prefers-color-scheme: dark)">
</picture>

Dark Mode Typography

The trick here is a lot like images: we’ve gotta balance the contrast. Use too heavy of a font and we get blaring text that’s makes us want to move away from the screen. Use too light of a font and we’ll strain our eyes while inching toward the screen to get a closer look.The balance is somewhere in the middle.

Dark Mode Icons

Icons fall into this “tricky” category because they’re sort of a cross between text and images. If we’re working with SVG icons, though, we can change the fill with CSS. On the other hand, if we’re using font icons, we can simply change the color property instead.

Dark Mode Colors

Pure white text on a pure black background will look jarring. The trick here is to use an off-white for the text and off-black for the background. eg #121212

Dark Mode Color Palettes

Most things boil down to one thing: contrast. That’s why the first tip before settling on any color is to run ideas through a contrast checker to ensure color ratios conform to WCAG’s guidelines for at least a AA rating which is 4.5:1. That means desaturated colors are our friends when working with a dark mode design.

Next, remember that accent colors are meant to be enhancements. They’re likely brighter than the dark theme background color, so using them like a primary color or the background color of a large container is just as jarring and hard on the eyes as a bright image or heavy white text.

If contrast is the balance we’re trying to strike, then remember that dark mode is more than blacks and grays. What about dark blue background with pale yellow text? Or dark brown with tan?

some eg. #152028, #202945, #232B32