Javascript is required
·
4 min read
·
13733 views

Dark Mode Switch With Tailwind CSS & Nuxt 3

Dark Mode Switch With Tailwind CSS & Nuxt 3 Image

I am currently rewriting my portfolio website with Nuxt 3 which is still in beta. In this article, I want to show you how I implemented a dark mode switch in Nuxt 3 using Tailwind CSS that I will use in my new portfolio website.

Create Nuxt 3 project

To create a new Nuxt 3 project, we need to run this command in our terminal:

bash
npx nuxi init nuxt3-app

Add Tailwind CSS 3

Next, we add the nuxt/tailwind module, which provides a prerelease version that supports Nuxt 3 and Tailwind CSS v3:

bash
npm install --save-dev @nuxtjs/tailwindcss@5.0.0-4

Then we need to add this module to the buildModules section in nuxt.config.js:

1import { defineNuxtConfig } from 'nuxt3'
2
3// https://v3.nuxtjs.org/docs/directory-structure/nuxt.config
4export default defineNuxtConfig({
5  buildModules: ['@nuxtjs/tailwindcss'],
6})

Now, we can create the Tailwind configuration file tailwind.config.ts by running the following command:

bash
npx tailwindcss init

Let's add a basic CSS file at ./assets/css/tailwind.css (see official docs for further configuration options):

1@tailwind base;
2@tailwind components;
3@tailwind utilities;
4
5.theme-light {
6  --background: #f8f8f8;
7  --text: #313131;
8}
9
10.theme-dark {
11  --background: #313131;
12  --text: #f8f8f8;
13}

We define two CSS classes for the dark and light theme. CSS variables (indicated by --) are used to change CSS values based on the selected theme dynamically.

Therefore, we need to define these colors in our tailwind.conf.js:

1module.exports = {
2  content: [
3    `components/**/*.{vue,js,ts}`,
4    `layouts/**/*.vue`,
5    `pages/**/*.vue`,
6    `app.vue`,
7    `plugins/**/*.{js,ts}`,
8    `nuxt.config.{js,ts}`,
9  ],
10  theme: {
11    extend: {
12      colors: {
13        themeBackground: 'var(--background)',
14        themeText: 'var(--text)',
15      },
16    },
17  },
18  plugins: [],
19}

Implement Theme Switch

Let's start to implement a theme switch by adding this simple template to our app.vue component:

1<template>
2  <div
3    :class="{
4      'theme-light': !darkMode,
5      'theme-dark': darkMode,
6    }"
7    class="h-screen bg-themeBackground p-5"
8  >
9    <h1 class="text-themeText">Nuxt 3 Tailwind Dark Mode Demo</h1>
10    <Toggle v-model="darkMode" off-label="Light" on-label="Dark" />
11  </div>
12</template>

On the div container element, we dynamically set theme-light or theme-dark CSS class based on the reactive darkMode variable value, which we will implement later in the script part of the component.

The h1 and container div elements use our Tailwind CSS classes bg-themeBackground and text-themeText to use theme-specific colors for the background and text color.

Additionally, we use the Vue 3 Toggle library to switch between our themes.

Let's take a look at the script part of app.vue:

1<script setup lang="ts">
2import Toggle from '@vueform/toggle'
3import { useState } from '#app'
4import { onMounted, watch } from '@vue/runtime-core'
5
6type Theme = 'light' | 'dark'
7
8const LOCAL_STORAGE_THEME_KEY = 'theme'
9
10const darkMode = useState('theme', () => false)
11
12const setTheme = (newTheme: Theme) => {
13  localStorage.setItem(LOCAL_STORAGE_THEME_KEY, newTheme)
14  darkMode.value = newTheme === 'dark'
15}
16
17onMounted(() => {
18  const isDarkModePreferred = window.matchMedia('(prefers-color-scheme: dark)').matches
19
20  const themeFromLocalStorage = localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as Theme
21
22  if (themeFromLocalStorage) {
23    setTheme(themeFromLocalStorage)
24  } else {
25    setTheme(isDarkModePreferred ? 'dark' : 'light')
26  }
27})
28
29watch(darkMode, (selected) => {
30  setTheme(selected ? 'dark' : 'light')
31})
32</script>

We store the selected theme value in Local Storage and use useState to define a reactive variable called darkMode:

const darkMode = useState('theme', () => false)

If the component is mounted, we first detect if the user has requested light or dark color theme by using the CSS media feature "prefers-color-scheme":

const isDarkModePreferred = window.matchMedia('(prefers-color-scheme: dark)').matches

Then we set the theme value based on the local storage value:

1const setTheme = (newTheme: Theme) => {
2  localStorage.setItem(LOCAL_STORAGE_THEME_KEY, newTheme)
3  darkMode.value = newTheme === 'dark'
4}
5
6onMounted(() => {
7  const isDarkModePreferred = window.matchMedia('(prefers-color-scheme: dark)').matches
8
9  const themeFromLocalStorage = localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as Theme
10
11  if (themeFromLocalStorage) {
12    setTheme(themeFromLocalStorage)
13  } else {
14    setTheme(isDarkModePreferred ? 'dark' : 'light')
15  }
16})

This the complete app.vue component code:

1<template>
2  <div
3    :class="{
4      'theme-light': !darkMode,
5      'theme-dark': darkMode,
6    }"
7    class="h-screen bg-themeBackground p-5"
8  >
9    <h1 class="text-themeText">Nuxt 3 Tailwind Dark Mode Demo</h1>
10    <Toggle v-model="darkMode" off-label="Light" on-label="Dark" />
11  </div>
12</template>
13
14<script setup lang="ts">
15import Toggle from '@vueform/toggle'
16import { useState } from '#app'
17import { onMounted, watch } from '@vue/runtime-core'
18
19type Theme = 'light' | 'dark'
20
21const LOCAL_STORAGE_THEME_KEY = 'theme'
22
23const darkMode = useState('theme', () => false)
24
25const setTheme = (newTheme: Theme) => {
26  localStorage.setItem(LOCAL_STORAGE_THEME_KEY, newTheme)
27  darkMode.value = newTheme === 'dark'
28}
29
30onMounted(() => {
31  const isDarkModePreferred = window.matchMedia('(prefers-color-scheme: dark)').matches
32
33  const themeFromLocalStorage = localStorage.getItem(LOCAL_STORAGE_THEME_KEY) as Theme
34
35  if (themeFromLocalStorage) {
36    setTheme(themeFromLocalStorage)
37  } else {
38    setTheme(isDarkModePreferred ? 'dark' : 'light')
39  }
40})
41
42watch(darkMode, (selected) => {
43  setTheme(selected ? 'dark' : 'light')
44})
45</script>
46
47<style src="@vueform/toggle/themes/default.css"></style>

Now we can use run the following command to start our Nuxt app in development mode:

bash
npm run dev

Finally, we can test our dark mode theme switch at http://localhost:3000:

Theme Switch In Action

StackBlitz Demo

My simple demo is available as interactive StackBlitz demo:

Alternative

Alternatively, you could also use the color-mode module that supports Nuxt Bridge and Nuxt 3 or useDark from VueUse.

Conclusion

This article showed you how to create a simple dark mode switch in Nuxt 3 with Tailwind CSS v3. You can expect more Nuxt 3 posts in the following months as I plan to blog about interesting topics that I discover while I rewrite my portfolio website.

If you liked this article, follow me on Twitter to get notified about new blog posts and more content from me.

Alternatively (or additionally), you can also subscribe to my weekly Vue.js newsletter.

I will never share any of your personal data. You can unsubscribe at any time.

If you found this article helpful.You will love these ones as well.
Focus & Code Diff in Nuxt Content Code Blocks Image

Focus & Code Diff in Nuxt Content Code Blocks

A Comprehensive Guide to Data Fetching in Nuxt 3 Image

A Comprehensive Guide to Data Fetching in Nuxt 3

Use Shiki to Style Code Blocks in HTML Emails Image

Use Shiki to Style Code Blocks in HTML Emails

How I Replaced Revue With a Custom-Built Newsletter Service Using Nuxt 3, Supabase, Serverless, and Amazon SES Image

How I Replaced Revue With a Custom-Built Newsletter Service Using Nuxt 3, Supabase, Serverless, and Amazon SES