Dark Mode With Blazor and Tailwind Css
Tags: webdev, css, csharp, dotnet
Published: 2023-09-22 14:11:09

## Introduction
Have you ever wondered how you can add a switch between light theme and dark theme? Most modern websites have support for both themes as the vast majority of people prefer dark mode as it's less tiresome on the eyes, and you don't want to burn your end user's eyes when they are in a dark room, with your website shoving tons of bright white light straight up their eyes, so please, let me show you how you can add, the mighty **Dark Mode** to your applications.

## Requirements
**1:** A **Blazor WebAssembly Empty** project, also Blazor Server works, but the reason I'm going with the empty template is that Blazor's normal template comes equipped with Bootstrap, and a lot of boilerplate which we're definitely not interested in for our purpose.

**2:** **Tailwind Css**. If you don't know how to add **Tailwind Css** to your Blazor projects, then kindly reference my article on how you can set it up as we will be using Tailwind in this guide
https://dev.to/rasheedmozaffar/using-tailwind-css-with-net-blazor-4ng7

**3:** We'll also be using the **browser's local storage** to persist the theme our user chooses, so in case you don't know how to interact with it, I have a full article that can guide you through the setup, and will also teach you everything you need to know about dealing with the **local storage** from your **Blazor** applications 
https://dev.to/rasheedmozaffar/how-to-work-with-the-browsers-local-storage-in-blazor-58kc

> **💡 Note:** I will not go over the installation and setup processes of the local storage package and tailwind through the guide, so if you want to follow along, please reference the articles I've linked before so that you can get your Blazor project prepped up for what we'll be working with during this guide.

## Configuring Tailwind's Dark Mode Preference
By default, Tailwind Css uses a CSS media feature that is `prefers-color-scheme`, and every style made for dark mode, will appear when the user changes their system preferences over to dark theme. That's cool, but we want something better, we want a **Toggle**, A toggle that lets its clickers explore the two sides of the website, a tap that transports them from one realm to another, from the bright to the dark, from the serene to the intense. Experience the contrast and the harmony, the light and the shadow, the yin and the yang. This is not just a switch, this is an adventure.
Ok that was dramatic enough, let's move on to the configuration file.

- Open **tailwind.config.js** (In the root directory of the project)
- Add a new property called `darkMode` and set it to `class`

The file should look like this:

```javascript

/** @type {import('tailwindcss').Config} */
module.exports = {
  darkMode: 'class',
  content: ['./**/*.{razor,html}'],
  theme: {
    extend: {},
  },
  plugins: [],
}

```

Tailwind offers a **class strategy** as an alternative to the media thing we've talked about before, when the **dark** class is attached to the html parent container, all children elements with **dark styles** will come into life, like this:

```html

<main class="dark">
    <h1 class="text-3xl dark:text-white font-bold text-slate-600">Hello, World!</h1>
</main>

```
Since the dark class is present, the **h1** will be white, and so every other dark style that we apply inside the **main** element.

## The Approach
In Blazor, we have the **MainLayout**, which is most likely going to be used across all the pages, inside this layout, we will have this:

```html

<main>
    @Body
</main>

```
This main element, will wrap all of our pages' content, and thus making it the parent for all the items in a page, so what we will be doing is, we'll store our theme as a key in the local storage, and use **conditionals** to determine which theme to use, if the theme to use is dark, we'll attach the dark class to the parent, otherwise we'll just use null implying that we want to use light mode which is the default.

## Coding The Implementation
Inside MainLayout, add the following code inside the code directory:

```csharp

@code {
    private bool toggleDark = false;

    protected override async Task OnInitializedAsync()
    {
        if (!(await LocalStorage.ContainKeyAsync("theme")))
        {
            await LocalStorage.SetItemAsStringAsync("theme", "light");
        }
        else
        {
            string theme = await LocalStorage.GetItemAsStringAsync("theme");

            if (theme == "dark")
            {
                toggleDark = true;
            }
            else
            {
                toggleDark = false;
            }
        }
    }
}

```

Ok what's going on?

- We're creating a boolean variable called `toggleDark`, this variable will determine whether we want to toggle dark mode on or not.
- We're overriding `OnInitializedAsync` to do two things:
    - When the app is used for the first time on a browser, there'll be no **theme** key in the local storage, so we're checking to see if such a key is there, if not, add it and give the value **light**, this value is honestly meaningless, it's just there because using `null` as a value for the key results in an error

    - If the theme key has a value, grab it, and check if it's set to `dark`, then change the value of `toggleDark` to `true` therefore enabling dark mode. However, if the value is something else, `light` in this case, then flip `toggleDark` to `false`, and that means light mode is the theme of choice.

With that done, we now want to make use of `toggleDark` to display the appropriate theme, to do that, we need to add a conditional class to the **main** element wrapping the `@Body` directive, we can do that as follows:

```html

<main class='@(toggleDark ? "dark" : null)'>
    @Body
</main>

```
Here, we're using the conditional operator aka **ternary operator** aka **inline if operator**, whoa that dude got names!
When the value of `toggleDark` is set to `true`, then we want to attach the `dark` class, however if `toggleDark` is `false`, then just don't add any class to the element.

Now we're almost done, what's left for us to do, is have a switch that will let us control which theme to use, because now if we want to switch modes, we'd have to do that from the local storage by changing the value of the `theme` key by hand to `dark`, and of course that makes no sense.

## Adding The Toggle Button
Now we'll design some basic button and set an `@onclick` event on it, when the event fires, the `toggleDark` gets changed to the opposite of its current value, and the new theme is changed inside the local storage, to keep the state in sync with our theme of choice.

Inside MainLayout, inside `main`, add a `header` element, I don't think it's the right way to add the header inside of main, I think a better practice would be adding a div wrapping both the header and main, and on that div attach the conditional operator which would determine the theme, but for now, we'll do it like that.
Add this code in the header to create a basic navigation bar with a toggle:

```html

<header class="p-4 flex justify-between ... ">
    <div>
        <h1 class="text-xl font-bold text-slate-700">Theming Demo</h1>
    </div>

    <div>
        <button class="rounded-md bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900">
            Toggle Dark/Light Mode
        </button>
    </div>
</header>

```
 Don't bother too much if this looks confusing, it's just for aesthetic purposes and has nothing to do with the actual implementation.

## Writing The Change Theme Logic
Below `OnInitializedAsync`, we'll create a new method called `ChangeTheme`, the content of which is as follows:

```csharp

private async Task ChangeTheme()
{
    toggleDark = !toggleDark;
    string theme = await LocalStorage.GetItemAsStringAsync("theme");

    if (theme == "dark")
    {
        await LocalStorage.SetItemAsStringAsync("theme", "light");
    }
    else
    {
        await LocalStorage.SetItemAsStringAsync("theme", "dark");
    }
}

```
Let's explain it:
- We begin by inverting the value of `toggleDark`, so if's set to `true`, after this assignment, it'll become `false` and vice versa
- After that, we grab the value of the key `theme` from the local storage of the browser
    - If `theme` is dark, we want to flip it to `light`
    - If it's something else, we want to set it to dark

Now we have to hook this method so that it gets called when the **button** aka **toggle** is clicked, for that, simply add this to the button we created earlier:

```html

<button @onclick="ChangeTheme"
class="rounded-md ...">
    Toggle Dark/Light Mode
</button>

```

## And There You Have It 🌟
All you have to do right now, is when designing your components, you just add the dark styles to them, when in light mode, those styles won't be visible, but when you click that toggle, and switch to dark mode, your components will now use their dark styles.

## Results
If you go to Index, and add the following markup:

```html

<div class="h-[100vh] p-4 w-full bg-violet-200 dark:bg-slate-800">
    <h1 class="text-3xl text-blue-500 dark:text-white font-bold">Hello, World!</h1>
</div>

```

You run the project, and click on the toggle, you should see the background and text colors changing, so your output should be something similar to this

## **Light Theme**

![Light theme result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ymayqyg7wkaj7g1a0ros.png)

## **Dark Theme**

![Dark theme result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0h4ykui0f7oz2r58j6vo.png)

## Conclusion
In this post, you've learned how we can get dark mode to work along side light mode, we also saw how we can persist the theme the user chooses in the local storage so that if the user closes your application and re open it later, the theme they were using the last time they visited your site would be used again, making your app more flexible and more modern with the help of toggling between themes.
If you have any questions, suggestions, or something you would like to point out, please add it in the comments down below.

**Thanks for reading along!**