Theme

This guide will help you implementthemeing in your Nuxt 3 project. We'll use a combination of Tailwind CSS for styling and Vue's reactive system to toggle between light and dark modes.

Configure Tailwind CSS by editing the tailwind.config.js file:
module.exports = {
  darkMode: "class", // Enable dark mode support
  content: [
    "./components/**/*.{js,vue,ts}",
    "./layouts/**/*.vue",
    "./pages/**/*.vue",
    "./plugins/**/*.{js,ts}",
    "./app.vue",
    "./error.vue",
  ],
  theme: {
    extend: {
      colors: {
        primary: {
          50: "rgb(var(--primary-50) / <alpha-value>)",
          75: "rgb(var(--primary-75) / <alpha-value>)",
          100: "rgb(var(--primary-100) / <alpha-value>)",
          200: "rgb(var(--primary-200) / <alpha-value>)",
          300: "rgb(var(--primary-300) / <alpha-value>)",
          400: "rgb(var(--primary-400) / <alpha-value>)",
          500: "rgb(var(--primary-500) / <alpha-value>)",
        },
        neutral: {
          10: "rgb(var(--neutral-10) / <alpha-value>)",
          20: "rgb(var(--neutral-20) / <alpha-value>)",
          30: "rgb(var(--neutral-30) / <alpha-value>)",
          50: "rgb(var(--neutral-50) / <alpha-value>)",
          100: "rgb(var(--neutral-100) / <alpha-value>)",
          200: "rgb(var(--neutral-200) / <alpha-value>)",
          300: "rgb(var(--neutral-300) / <alpha-value>)",
          500: "rgb(var(--neutral-500) / <alpha-value>)",
        },
        success: {
          50: "rgb(var(--success-50) / <alpha-value>)",
          75: "rgb(var(--success-75) / <alpha-value>)",
          100: "rgb(var(--success-100) / <alpha-value>)",
          200: "rgb(var(--success-200) / <alpha-value>)",
          300: "rgb(var(--success-300) / <alpha-value>)",
          400: "rgb(var(--success-400) / <alpha-value>)",
          500: "rgb(var(--success-500) / <alpha-value>)",
        },
        danger: {
          50: "rgb(var(--danger-50) / <alpha-value>)",
          75: "rgb(var(--danger-75) / <alpha-value>)",
          100: "rgb(var(--danger-100) / <alpha-value>)",
          200: "rgb(var(--danger-200) / <alpha-value>)",
          300: "rgb(var(--danger-300) / <alpha-value>)",
          400: "rgb(var(--danger-400) / <alpha-value>)",
          500: "rgb(var(--danger-500) / <alpha-value>)",
        },
        warning: {
          50: "rgb(var(--warning-50) / <alpha-value>)",
          75: "rgb(var(--warning-75) / <alpha-value>)",
          100: "rgb(var(--warning-100) / <alpha-value>)",
          200: "rgb(var(--warning-200) / <alpha-value>)",
          300: "rgb(var(--warning-300) / <alpha-value>)",
          400: "rgb(var(--warning-400) / <alpha-value>)",
          500: "rgb(var(--warning-500) / <alpha-value>)",
        },
        info: {
          50: "rgb(var(--info-50) / <alpha-value>)",
          75: "rgb(var(--info-75) / <alpha-value>)",
          100: "rgb(var(--info-100) / <alpha-value>)",
          200: "rgb(var(--info-200) / <alpha-value>)",
          300: "rgb(var(--info-300) / <alpha-value>)",
          400: "rgb(var(--info-400) / <alpha-value>)",
          500: "rgb(var(--info-500) / <alpha-value>)",
        },
      },
    },
  },
  plugins: [],
};
Add this to colors.css file:
  /* assets/css/colors.css */

:root {
  /* Primary Colors */
  --primary-50: 253 248 233;  /* #FDF8E9 */
  --primary-75: 249 235 189;  /* #F9EBBD */
  --primary-100: 245 222 146; /* #F5DE92 */
  --primary-200: 241 208 102; /* #F1D066 */
  --primary-300: 235 188 36;  /* #EBBC24 */
  --primary-400: 188 150 29;  /* #BC961D */
  --primary-500: 141 113 22;  /* #8D7116 */

  /* Neutral Colors */
  --neutral-10: 255 255 255;  /* #FFFFFF */
  --neutral-20: 250 250 250;  /* #FAFAFA */
  --neutral-30: 245 245 245;  /* #F5F5F5 */
  --neutral-50: 232 233 233;  /* #E8E9E9 */
  --neutral-100: 225 225 225; /* #E1E1E1 */
  --neutral-200: 151 152 154; /* #97989A */
  --neutral-300: 58 58 58;    /* #3A3A3A */
  --neutral-500: 18 18 18;    /* #121212 */

  /* Success Colors */
  --success-50: 233 248 238;  /* #E9F8EE */
  --success-75: 164 226 187;  /* #A4E2BB */
  --success-100: 126 214 158; /* #7ED69E */
  --success-200: 70 196 116;  /* #46C474 */
  --success-300: 32 184 88;   /* #20B858 */
  --success-400: 22 129 62;   /* #16813E */
  --success-500: 20 112 54;   /* #147036 */

  /* Danger Colors */
  --danger-50: 253 231 231;   /* #FDE7E7 */
  --danger-75: 248 158 158;   /* #F89E9E */
  --danger-100: 246 118 118;  /* #F67676 */
  --danger-200: 242 58 58;    /* #F23A3A */
  --danger-300: 239 18 18;    /* #EF1212 */
  --danger-400: 167 13 13;    /* #A70D0D */
  --danger-500: 146 11 11;    /* #920B0B */

  /* Warning Colors */
  --warning-50: 255 249 230;  /* #FFF9E6 */
  --warning-75: 255 229 151;  /* #FFE597 */
  --warning-100: 255 218 108; /* #FFDA6C */
  --warning-200: 255 203 44;  /* #FFCB2C */
  --warning-300: 255 192 1;   /* #FFC001 */
  --warning-400: 179 134 1;   /* #B38601 */
  --warning-500: 156 117 1;   /* #9C7501 */

  /* Info Colors */
  --info-50: 231 246 253;    /* #E7F6FD */
  --info-75: 156 218 246;    /* #9CDAF6 */
  --info-100: 115 203 242;   /* #73CBF2 */
  --info-200: 55 180 237;    /* #37B4ED */
  --info-300: 14 165 233;    /* #0EA5E9 */
  --info-400: 10 115 163;    /* #0A73A3 */
  --info-500: 9 101 142;     /* #09658E */
}

/* Dark mode overrides */
.dark {
  /* Primary Colors (Dark Mode) */
  --primary-50: 141 113 22;   /* #8D7116 - Darkest yellow */
  --primary-75: 188 150 29;   /* #BC961D */
  --primary-100: 235 188 36;  /* #EBBC24 */
  --primary-200: 241 208 102; /* #F1D066 */
  --primary-300: 235 188 36;  /* #EBBC24 */
  --primary-400: 249 235 189; /* #F9EBBD */
  --primary-500: 253 248 233; /* #FDF8E9 - Lightest yellow */
  
  /* Neutral Colors (Dark Mode) */
  --neutral-10: 18 18 18;     /* #121212 - Darkest gray */
  --neutral-20: 58 58 58;     /* #3A3A3A */
  --neutral-30: 151 152 154;  /* #97989A */
  --neutral-50: 225 225 225;  /* #E1E1E1 */
  --neutral-100: 232 233 233; /* #E8E9E9 */
  --neutral-200: 245 245 245; /* #F5F5F5 */
  --neutral-300: 250 250 250; /* #FAFAFA */
  --neutral-500: 255 255 255; /* #FFFFFF - White */

  /* Success, Danger, Warning, and Info colors can stay the same or be slightly adjusted */
}
Create Theme Toggle

To enable light and dark mode switching in your Nuxt 3 project, update index.vue to include a toggle switch.

This will allow users to seamlessly switch between themes.

<template>
  <div class=" bg-neutral-10 h-screen">
    <h1 class="text-3xl font-bold underline text-red-500">Hello world!</h1>
    <div class="p-4 flex items-center">
          <InputSwitch v-model="checked" class="" @change="themeChange" />
          <p class="ml-3 text-[#676A6C] dark:text-[#E8E9E9]">Light Mode</p>
    </div>
  </div>
</template>

<script setup>
const checked = ref(false);

const themeChange = () => {
  if (localStorage.getItem("color-theme")) {
    if (localStorage.getItem("color-theme") === "light") {
      document.documentElement.classList.add("dark");
      localStorage.setItem("color-theme", "dark");
    } else {
      document.documentElement.classList.remove("dark");
      localStorage.setItem("color-theme", "light");
    }
  } else {
    if (document.documentElement.classList.contains("dark")) {
      document.documentElement.classList.remove("dark");
      localStorage.setItem("color-theme", "light");
    } else {
      document.documentElement.classList.add("dark");
      localStorage.setItem("color-theme", "dark");
    }
  }
};
</script>
Retain User Preference

To ensure the theme preference persists across the entire Nuxt 3 application, update app.vue.

This ensures a seamless user experience, even after page reloads.

<script>
export default defineNuxtComponent({
  setup() {
    const config = useRuntimeConfig();
    const isProduction = config.public.env === "production";

    onBeforeMount(async () => {

    if (
    localStorage.getItem('color-theme') === 'dark' ||
    (!('color-theme' in localStorage) &&
      window.matchMedia('(prefers-color-scheme: dark)').matches)
    ) {
    document.documentElement.classList.add('dark');
    } else {
    document.documentElement.classList.remove('dark');
    }

  });

    useHead({
      script: isProduction
        ? [
            {
              src: "/newRelic.js",
              async: true,
              defer: true,
            },
          ]
        : [],
    });
  },
});
</script>

Add this also to app.vue

// app.vue
<style>
@import './assets/css/colors.css';
</style>