DEV Community

rinas
rinas

Posted on • Updated on

Toggle 🌒 dark and 🔆 sunny mode using AlpineJS, and TailwindCSS

Hey 👋

Rinas here.

This is done using some Alpine JS magic 🪄 and I enjoyed implementing it.


🏎 Let's start

Update your Tailwind config to let it know need dark mode toggle based on class.

// tailwind.config.js
module.exports = {
  darkMode: 'class',
  // ...
}
Enter fullscreen mode Exit fullscreen mode

This will let you write bg-gray-50 dark:bg-black and show background colours based on the current mode.

<html class="dark">
<body>
  <div class="bg-gray-100 dark:bg-gray-600">
    <!-- ... -->
  </div>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now let's try to remove that hardcoded dark class in html tag and grab the value from localStorage using Alpine

<html
  x-data="{ darkMode: localStorage.getItem('dark') === 'true'} "
  x-init="$watch('darkMode', val => localStorage.setItem('dark', val))"
  x-bind:class="{ 'dark': darkMode }"
>
Enter fullscreen mode Exit fullscreen mode

🤔 Wait, what's going on here?

x-data="{ darkMode: localStorage.getItem('dark') === 'true' "
Enter fullscreen mode Exit fullscreen mode

x-data tells the framework to initialize a new component with the following data object. In our case, the object is:

{ darkMode: localStorage.getItem('dark') === 'true'}
Enter fullscreen mode Exit fullscreen mode

I think of it as a simple variable and setting darkMode's value to either true or false based on the localStorage item dark's value.

x-init="$watch('darkMode', val => localStorage.setItem('dark', val))"
Enter fullscreen mode Exit fullscreen mode

$watch is a magic property (yup, that's the official terminology 😁) in Alpine used to watch data we have and trigger a function.
x-init You might have already guessed it. It runs an expression when a component is initialized.

So good so far? If not, let me know. I'll try to clarify in comments and update this post accordingly

Let's add the button to toggle the theme now.

<button @click="darkMode = !darkMode">
  Toggle Theme
</button>

Enter fullscreen mode Exit fullscreen mode

🏁 Yup, that's all you need 😎


Here is the polished version I used in HIGHSCORE.domains

toggle


This is my first time using AlpineJS and got featured in Alpine.js Weekly #61 by @hugo__df

highscore.domains got a darkmode toggled powered by Alpine and TailwindUI by @onerinas
Screenshot 2021-05-31 at 8.07.54 AM


Leaving few links here which might be useful if you want to try this out in your project:

TailwindCSS:

AlpineJS:

x-init
Runs an expression when a component is initialized.
https://github.com/alpinejs/alpine#x-init

x-data
Declares a new component scope.
https://github.com/alpinejs/alpine#x-data

x-bind
Sets the value of an attribute to the result of a JS expression.
https://github.com/alpinejs/alpine#x-bind

$watch
Will fire a provided callback when a component property you "watched" gets changed.
https://github.com/alpinejs/alpine#watch

Top comments (5)

Collapse
 
guozian profile image
guozian

Hi, just found your post, and it's very useful. I have followed your tutorial and implemented in my website. However, I have one issue, if I am in dark mode, every time I refresh the page, it will first show the default light mode for 0.5 second and then quickly switch to the dark mode automatically. I can notice that 0.5 second flash, which is annoying. This is my website edutive.com.au/, can you help me have a look and maybe can help me find out what's the reason? many thanks :)

Collapse
 
elefint profile image
elefint

Might be a bit late but I found this guide very helpful:
css-tricks.com/a-complete-guide-to...

Collapse
 
guozian profile image
guozian

Hey, thank you so much. This guide is so great. I am not professional so will have to read it carefully, hopefully I can understand it and implement to my website, thanks!

Thread Thread
 
elefint profile image
elefint

No problem, a quick band aid I had used before knowing the server side stuff was to add a page loader then the user doesn't see the flicker to dark mode.

Collapse
 
apatrid profile image
Mijo

Nicely done, I'm thinking in implementing 'dark mode' in my Exclude VAT Calculator that I made with Alpine.js