theme


font type

color scheme

nuxt-class-inject

Dynamic class injection before client rendering.

Fast and efficient dynamic CSS class injection. CSS classes are injected into the <html />  component before rendering avoiding flashing on client load.

features

  • Nuxt3 and Nuxt Bridge support
  • Inject CSS classes into <html />  tag before browser rendering
  • Works with client side and universal rendering
  • Injected CSS classes persist across same-browser sessions
  • Supports IE9+

live demo

You can see this module in action by using the customize button at the top right of this page.

table of contents

setup

Add the nuxt-class-inject dependency to your project.

      
yarn add --dev nuxt-class-inject
    

Then add nuxt-class-inject to the modules section of your nuxt.config.ts

ts

nuxt.config.ts

        
export default defineNuxtConfig({
  modules: [
    "nuxt-class-inject", 
  ],
});
    

usage

example

The injected classList can be accessed through $classInject directly. This helper has two properties:

  • unknown: a boolean value, true if the classList is unknown, false otherwise.
  • classList: the list of injected classes, update it to change injected classes.

vue

app.vue

        
<template>
    <div>
        <button
            v-for="theme in themes"
            @click="setTheme(theme)"
        >
            {{ theme }}
        </button>
    </div>
</template>

<script setup lang="ts">
const { $classInject } = useNuxtApp();

const themes: string[] = ["theme-light", "theme-dark"];
const setTheme = (theme: string) => {
    const current: string[] = $classInject.classList.value;

    const classList = current.filter((cls) => {
      return !cls.startsWith("theme-")
    });
    classList.push(theme);

    $classInject.classList.value = classList;
};
</script>

<style lang="css">
.theme-light {
    background-color: #1e1e1e;
    color: #fefefe;
}

.theme-dark {
    background-color: #fefefe;
    color: #1e1e1e;
}
</style>

    

configuration

You can configure the module through the classInject property in your nuxt.config.js. The default options are

ts

nuxt.config.js

        
export default defineNuxtConfig {
  modules: ["nuxt-class-inject",],
  classInject: {
    storageKey: "nuxt-class-inject",
    globalName: "__NUXT_CLASS_INJECT__",
    fallback: [""],
  },
}
    

The config options are:

  • storageKey: the storage key used to store injected classes in the browser‘s local storage
  • globalName: the global name used by the module which is used to inject JavaScript into the window
  • fallback: the classes to inject in case no classes are found in local storage

tailwind css

example

To work with Tailwind CSS change your CSS classes to change the value of variables:

vue

app.vue

        
<style lang="css">
.theme-light {
    --foreground: #1e1e1e;
    --background: #fefefe;
}

.theme-dark {
    --foreground: #fefefe;
    --background: #1e1e1e;
}
</style>

    

Then register thos variables in the tailwind.config.js file:

js

tailwind.config.js

        
/** @type {import('tailwindcss').Config} */
export default {
    theme: {
        extend: {
            colors: {
                foreground: "var(--foreground)",
                background: "var(--background)",
            },
        },
    },
};

    

notes

There are a couple of things to keep in mind when using this module.

  1. Since the “source of truth” for the injected classes is on the client side (the browser‘s storage), it means that during SSR the injected class list is not known.
  2. The exposed classList is a WriteableComputedRef<string[]> so that the only way of upating the value is by overwriting it. This is done because vue‘s reactivity for arrays works on property access and assignment and not internal array modifications. The correct manner of updating classList list is as follows:

vue

         
const classList: string[] = $classInject.classList.value;

// make changes to classList

$classInject.classList.value = classList;
    

credit

This module is “heavily inspired” by the nuxt-color-mode module. i.e. I comlpetely ripped it off, altered it slightly to better suit my needs, and called it my own.