Theming With Tailwind CSS

Nwalozie ElijahNwalozie Elijah
8 min read

As accessibility becomes increasingly important, having at least an extra theme (mostly dark) on a web page has become necessary. In this article, I will show how to create multiple themes in a Tailwind CSS application and create switches to toggle them.

Tailwind CSS is a popular, utility-first CSS framework that provides pre-designed CSS classes to quickly and easily style your web pages. Additionally, it provides customization options, allowing developers to easily tweak and adjust the styles to their specific needs.

Installation and Overview

In your terminal, enter the commands

git clone https://github.com/Complexlity/tailwind-theming.git
cd tailwind-theming
npm install
npm run watch

On Completion, you should have a similar folder structure.

-

For this tutorial, we would work with four files

  • index.html

  • input.css

  • tailwind.config.js

  • main.js

Creating Call To Action Component

  1. Update index.html with the code below
<!-- index.html -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tailwind Theming</title>
    <link rel="stylesheet" href="output.css" />
    <script src="main.js" defer></script>
  </head>
  <body class="bg-gray-400">
    <div class="wrapper mt-16 px-8">
      <div class="theme-container mx-auto max-w-[50rem] rounded-xl shadow-md bg-white">
        <div
          class="lg:py-16 lg:px-8 z-20 mx-auto w-full py-12 px-4 text-center sm:px-6"
        >
          <h2 class="theme-header text-3xl font-extrabold text-black sm:text-4xl">
            <span class="block"> Hey There ๐Ÿ‘‹ </span>
            <span class="block text-indigo-500">
              Happy Learning!!
            </span>
          </h2>
          <p class="mx-auto mt-4 max-w-md text-xl text-gray-400">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat
            necessitatibus eos consequuntur?
          </p>
          <div class="lg:mt-0 lg:flex-shrink-0">
            <div class="mt-12 inline-flex rounded-md shadow">
              <button
                type="button"
                class="w-full rounded-lg bg-indigo-600 py-4 px-6 text-center text-base font-semibold text-white shadow-md transition duration-200 ease-in hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-indigo-200"
              >
                Get started
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

Open index.html in your browser to see the output below.

-

Creating dark theme using inline class

The easiest mode of using a dark theme in Tailwind is with the dark class. Update the code in tailwind.config.js.

// tailwind.config.js
module.exports = {
  content: ["./*.html"],
  darkMode: "class",
  theme: {
    extend: {},
  },
  plugins: [],
};

darkMode:class enables the dark state to be used with Tailwind classes to set different properties between normal and dark modes.

Update index.html in the following ways:

  • Add dark class to the body (Line 13)

  • Add dark:bg-gray-800 class to div.theme-container (Line 15)

  • Add dark:text-white class to h2.theme-header (Line 19)

See the code below for an illustration

....
<body class="dark ....">
....
    <div class="theme-container dark:bg-gray-800 ....">
....
        <h2 class="theme-header dark:text-white .... ">
....

NOTE: .... represents unchanged code

To enable this, we add the dark class to the body tag. Removing it would reset it back to normal mode.

Create Toggle

Now in the current state, we have to manually add or remove the dark class from the body, but this is usually not what we want. We would want a switch to toggle between these modes.

<!-- Replace index.html with the code below -->

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tailwind Theming</title>
    <link rel="stylesheet" href="output.css" />
    <script src="main.js" defer></script>
  </head>
  <body class="bg-gray-400">
    <div class="wrapper mt-16 px-8">
      <div
        class="theme-container mx-auto max-w-[50rem] rounded-xl bg-white shadow-md dark:bg-gray-800"
      >
        <div class="mx-8 pt-4 text-end">

            <!-- Checkbox start -->
          <div
            class="relative mr-2 inline-block w-10 select-none align-middle transition duration-200 ease-in"
          >
            <input
              type="checkbox"
          name="theme-toggle"    id="theme-toggle"
              class="peer absolute block h-6 w-6 cursor-pointer appearance-none rounded-full border-4 bg-white checked:right-0 checked:border-green-400"
            />
            <label
              for="theme-toggle"
              class="block h-6 cursor-pointer overflow-hidden rounded-full bg-gray-300 peer-checked:bg-green-400"
            ></label>
          </div>
          <label
            for="toggle"
            class="theme-text text-bold text- font- font-bold tracking-wider text-gray-700 dark:text-white"
            >LIGHT</label
          >
        </div>
          <!-- Checkbox End -->

          <div
          class="lg:py-16 lg:px-8 z-20 mx-auto w-full py-12 px-4 text-center sm:px-6"
        >
          <h2
            class="theme-header text-3xl font-extrabold text-black dark:text-white sm:text-4xl"
          >
            <span class="block"> Hey There ๐Ÿ‘‹ </span>
            <span class="block text-indigo-500">
              Happy Learning!!
            </span>
          </h2>
          <p class="mx-auto mt-4 max-w-md text-xl text-gray-400">
            Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quaerat
            necessitatibus eos consequuntur?
          </p>
          <div class="lg:mt-0 lg:flex-shrink-0">
            <div class="mt-12 inline-flex rounded-md shadow">
              <button
                type="button"
                class="w-full rounded-lg bg-indigo-600 py-4 px-6 text-center text-base font-semibold text-white shadow-md transition duration-200 ease-in hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:ring-offset-indigo-200"
              >
                Get started
              </button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

The Output

-

We added a checkbox to enable the toggle.

We need to use some Javascript to make the switch work as expected.
Update main.js with the code below.

// main.js
const body = document.querySelector("body");
const themeToggle = document.querySelector("#theme-toggle");
const themeText = document.querySelector(".theme-text");
let darkTheme = false;

// Listen for clicks on the checkbox
themeToggle.addEventListener("click", () => {
  body.classList.toggle("dark"); //Add and remove the dark class from the body
  darkTheme = !darkTheme;
  themeText.textContent = darkTheme ? "DARK" : "LIGHT"; // Change the text depending on the mode
});

Here, we get the toggle and the body element and create the toggle function to change the state. We also change the toggle text when clicked.

-

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay โ€” an open-source session replay suite for developers. It can be self-hosted in minutes, giving you complete control over your customer data

OpenReplay

Happy debugging! Try using OpenReplay today.

Theming with CSS Variables

Consider a scenario where you need more than just light and dark themes. Using the tailwind dark class would not be able to handle that. With the help of CSS Variables, you can create a custom design system that handles as many themes as you want.

  • Start a new project folder

  • Open the folder on your terminal and run the following:

git clone https://github.com/Complexlity/tailwind-theming.git
cd tailwind-theming
npm install
npm run watch

Having the same folder structure and files as in the previous project, we create a different component to handle multiple themes.

Creating Call To Action Component

Copy the code below into index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Tailwind Theming</title>
    <link rel="stylesheet" href="output.css" />
    <script src="main.js" defer></script>
  </head>
  <body class="bg-gray-500">
    <div>
      <div class="wrapper mt-16 px-8">
        <div
          class="themed-background bg-gray-200 mx-auto max-w-[50rem] rounded-xl  shadow-md"
        >
          <div class="px-4 pt-4">
            <label
              for="themes"
              class="themed-label text-indigo-600 mb-2 block text-center text-sm font-medium "
              >CHOOSE A THEME</label
            >
            <select
              id="themes"
              class="block w-full cursor-pointer rounded-lg border p-2.5 text-sm outline-transparent"
            >
              <option value="" selected>Default (Light)</option>
              <option value="blue">Blue</option>
              <option value="red">Red</option>
              <option value="purple">Purple</option>
            </select>
          </div>
          <div
            class="relative mx-auto max-w-4xl overflow-hidden sm:rounded-2xl"
          >
            <div
              class="relative mx-auto max-w-2xl py-16 px-4 text-center sm:py-20 sm:px-6 lg:px-8"
            >
              <h2
                class="themed-header text-indigo-600 text-3xl font-extrabold sm:text-4xl"
              >
                <span class="block">Consistency matters.</span>
                <span class="block">Practice makes perfect.</span>
              </h2>
              <p class="themed-subheader text-gray-800 mt-4 text-lg leading-6">
                Ac euismod vel sit maecenas id pellentesque eu sed consectetur.
                Malesuada adipiscing sagittis vel nulla nec.
              </p>
              <div
                class="mx-auto mt-10 max-w-sm sm:flex sm:max-w-none sm:justify-center"
              >
                <a
                  href="#"
                  class="themed-button bg-indigo-600 text-gray-200 flex items-center justify-center rounded-md border border-transparent px-4 py-3 text-base font-medium  shadow-sm sm:px-8"
                >
                  Get started
                </a>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </body>
</html>

-

We have a similar call to action component with a select button which we would use to toggle themes.

Create CSS Variables for the colors of themed-* elements.

Replace input.css with the code below

/* input.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --color-text-base: #4f46e5; /* bg-indigo-600 */
    --color-text-muted: #1f2937; /* bg-gray-800 */
    --color-fill: #e5e7eb;  /* bg-gray-200 */
    --color-text-inverted: #e5e7eb; /* bg-gray-200 */
    --color-button-base: #4f46e5; /*bg-indigo-600 */
  }
}

Here, we create variables from the tailwind colors using their hex code equivalent.

Add Custom Color Configuration

Update tailwind.config.js with the code below

module.exports = {
  content: ["./*.html"],
  theme: {
    extend: {
      textColor: {
        skin: {
          base: "var(--color-text-base)",
          muted: "var(--color-text-muted)",
          inverted: "var(--color-text-inverted)",
        },
      },
      backgroundColor: {
        skin: {
          fill: "var(--color-fill)",
          button: "var(--color-button-base)",
        },
      },
    },
  },
  plugins: [],
};

Here, we use the CSS variables to make custom tailwind colors. These colors can be accessed in HTML elements. For example, the base color could be accessed by text-skin-base, and the fill color could be accessed by bg-skin-fill.

Replace hard-coded colors with the created colors

Update the following elements in index.html:

  • div.themed-background (Line 15 ): Replace bg-gray-200 with bg-skin-fill

  • label.themed-label (Line 20 ): Replace text-indigo-600 with text-skin-base

  • h2.themed-header (Line 40 ): Replace text-indigo-600 with text-skin-base

  • p.themed-subheader (Line 45): Replace text-gray-800 with text-skin-muted

  • a.themed-button (Line 54): Replace bg-indigo-600 with bg-skin-button and text-gray-200 with text-skin-inverted

Note: Depending on the text editor and code formatting, the line number may vary

....
<div class="themed-background bg-skin-fill mx-auto ...."> 
<!-- bg-gray-200 now bg-skin-fill -->
....
<label for="themes" class="themed-label text-skin-base mb-2 ....">CHOOSE A THEME</label>  
<!-- text-indigo-600 now text-skin-base -->
....
<h2 class="themed-header text-skin-base text-3xl .... "> 
<!-- text-indigo-600 now text-skin-base -->
....
<p class="themed-subheader text-skin-muted mt-4 .... ">
<!-- text-gray-800 now text-skin-muted -->
....
<a href="#" class="themed-button bg-skin-button text-skin-inverted flex ....">
<!-- bg-indigo-600 now bg-skin-button , text-gray-200 now text-skin-inverted -->

Note: .... represents unchanged code The component should still look the same without hardcoded colors on the elements.

Create Multiple Themes

Now you can create as many themes as you need using the CSS variables. We will create three more: Blue, Red, and Purple. Replace input.css with the code below

/* input.css */ 
@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --color-text-base: #4f46e5;
    --color-text-muted: #1f2937;
    --color-fill: #e5e7eb;
    --color-text-inverted: #e5e7eb;
    --color-button-base: #4f46e5;
  }

  .blue {
    --color-text-base: #fff;
    --color-text-muted: #bfdbfe;
    --color-fill: #1e40af;
    --color-text-inverted: #1d4ed8;
    --color-button-base: #fff;
  }
  .red {
    --color-text-base: #fff;
    --color-text-muted: #fecaca;
    --color-fill: #991b1b;
    --color-text-inverted: #b91c1c;
    --color-button-base: #fff;
  }
  .purple {
    --color-text-base: #fff;
    --color-text-muted: #e9d5ff;
    --color-fill: #6b21a8;
    --color-text-inverted: #7e22ce;
    --color-button-base: #fff;
  }
}

Now we created more themes. We used scoping of CSS variables to achieve it. These colors would only apply under the specific class while the :root one remains the default.

Implement Theme Toggling

We already created the select to toggle these themes, so we need to use Javascript to make it work as expected. Insert the code below into main.js.

// Insert code into main.js
const themedContainer = document.querySelector("body > div");
const selectOptions = document.querySelector("#themes");

selectOptions.addEventListener("input", (e) => {
  themedContainer.className = e.target.value;
});

In the code above, we change the parent element's class depending on the selected option's value. Now the code works the same as the image below.

-

Conclusion

In this article, we looked at how to create a dark theme and multiple themes using Tailwind CSS. You can see both methods in action here. With the ease of implementation here and for accessibility reasons, you should always create at least a dark theme for your websites and applications

newsletter

1
Subscribe to my newsletter

Read articles from Nwalozie Elijah directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Nwalozie Elijah
Nwalozie Elijah