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 toclass
The file should look like this:
/** @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:
<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:
<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:
@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 oftoggleDark
totrue
therefore enabling dark mode. However, if the value is something else,light
in this case, then fliptoggleDark
tofalse
, and that means light mode is the theme of choice.
- 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
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:
<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:
<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:
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 totrue
, after this assignment, it'll becomefalse
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 tolight
- If it's something else, we want to set it to dark
- If
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:
<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:
<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
Dark Theme
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!