Skip to content

Nuxt Styling and Theming — CSS, Tailwind CSS, and Dark Mode

DodaTech Updated 2026-06-28 6 min read

In this tutorial, you will learn about Nuxt Styling and Theming. We cover key concepts, practical examples, and best practices to help you master this topic.

Learn Nuxt styling and theming — configure Tailwind CSS, write global and scoped styles, implement dark mode, and create a consistent design system.

In this lesson, you'll understand how to add CSS frameworks, organize styles, implement theme switching, and build reusable design components in Nuxt.

What You'll Learn

How to integrate Tailwind CSS with Nuxt, use global and scoped styles, implement dark mode with the color-mode module, create CSS variables for theming, and build a consistent design system across your application.

Why It Matters

A consistent visual theme improves user trust, reduces cognitive load, and speeds up development. Proper styling architecture prevents CSS conflicts and makes theme changes manageable across hundreds of components.

Real-World Use

A multi-tenant SaaS platform uses Nuxt with CSS custom properties and color-mode to let each customer customize colors and switch between light and dark themes without redeploying the application.

flowchart TD
    A[Nuxt Styling] --> B[Tailwind CSS]
    A --> C[Global Styles]
    A --> D[Scoped Styles]
    A --> E[CSS Variables]
    B --> F[Utility Classes]
    C --> G[Base Reset]
    D --> H[Component Styles]
    E --> I[Theme System]
    I --> J[Light Mode]
    I --> K[Dark Mode]
    style A fill:#00dc82,color:#fff

Installing Tailwind CSS

The easiest way to add Tailwind is with the Nuxt Tailwind module:

npm install @nuxtjs/tailwindcss
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/tailwindcss'],
  tailwindcss: {
    config: {
      content: [
        'components/**/*.vue',
        'pages/**/*.vue',
        'layouts/**/*.vue'
      ],
      theme: {
        extend: {
          colors: {
            primary: {
              50: '#f0fdf4',
              500: '#00dc82',
              900: '#14532d'
            }
          }
        }
      }
    }
  }
});

Create app/assets/css/tailwind.css:

@tailwind base;
@tailwind components;
@tailwind utilities;

Global Styles

Add global CSS that applies to every page:

// nuxt.config.ts
export default defineNuxtConfig({
  css: ['~/assets/css/global.css']
});
/* assets/css/global.css */
/* Base reset */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* Custom scrollbar */
::-webkit-scrollbar {
  width: 8px;
}
::-webkit-scrollbar-thumb {
  background: #00dc82;
  border-radius: 4px;
}

/* Focus styles */
:focus-visible {
  outline: 2px solid #00dc82;
  outline-offset: 2px;
}

Scoped Styles in Components

Vue's scoped styles apply only to the current component:

<template>
  <div class="card">
    <h3 class="card-title">{{ title }}</h3>
    <p class="card-body"><slot /></p>
  </div>
</template>

<style scoped>
.card {
  border: 1px solid #e2e8f0;
  border-radius: 8px;
  padding: 1.5rem;
  transition: box-shadow 0.2s;
}
.card:hover {
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.card-title {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.5rem;
}
.card-body {
  color: #64748b;
  line-height: 1.6;
}
</style>

Expected output: A styled card component whose styles don't leak to other components on the page.

Dark Mode with @nuxtjs/color-mode

Install the color-mode module for automatic dark mode support:

npm install @nuxtjs/color-mode
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/color-mode'],
  colorMode: {
    classSuffix: '',  // Add class without suffix
    preference: 'system',  // Follow OS preference
    fallback: 'light'
  }
});

Create a theme toggle component:

<template>
  <button @click="toggleColorMode" class="theme-toggle">
    {{ colorMode === 'dark' ? 'Light Mode' : 'Dark Mode' }}
  </button>
</template>

<script setup>
const colorMode = useColorMode();

function toggleColorMode() {
  colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark';
}
</script>

Expected output: A toggle button that switches between light and dark modes, respecting the user's OS preference by default.

CSS Custom Properties for Theming

Define theme-agnostic CSS variables:

/* assets/css/themes.css */
:root {
  --color-bg: #ffffff;
  --color-text: #1a202c;
  --color-primary: #00dc82;
  --color-border: #e2e8f0;
  --color-card-bg: #f8fafc;
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.dark {
  --color-bg: #0f172a;
  --color-text: #e2e8f0;
  --color-primary: #00dc82;
  --color-border: #334155;
  --color-card-bg: #1e293b;
  --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3);
}

Use them in components:

<template>
  <div class="themed-card">
    <h3>{{ title }}</h3>
    <p><slot /></p>
  </div>
</template>

<style scoped>
.themed-card {
  background: var(--color-card-bg);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
  padding: 1.5rem;
  border-radius: 8px;
}
</style>

Expected output: A themed component that automatically updates colors when the user switches between light and dark mode.

Using Tailwind for Dark Mode

Tailwind's dark mode variants work seamlessly with color-mode:

<template>
  <div class="bg-white dark:bg-slate-800 text-gray-900 dark:text-gray-100
              border border-gray-200 dark:border-gray-700
              shadow-sm dark:shadow-slate-900/50
              p-6 rounded-lg transition-colors duration-200">
    <h3 class="text-lg font-semibold">{{ title }}</h3>
    <p class="mt-2"><slot /></p>
  </div>
</template>

Expected output: A component that uses Tailwind's dark: prefix to swap styles, with a smooth color transition.

Common Mistakes

  1. Using @apply excessively in component styles: Tailwind's @apply directive in <style> blocks works but can bloat your CSS. Prefer utility classes in templates for smaller CSS output.

  2. Forgetting the scoped attribute: Without scoped, component styles become global and can conflict with other components. Always add scoped unless you intentionally need global styles.

  3. Mixing CSS variable naming conventions: Use --color-* naming consistently. Inconsistent variable names (some --color-, some --clr-) make theming confusing and error-prone.

  4. Not providing a fallback color scheme: Without a fallback, users with JavaScript disabled see no styles. Add an inline <script> in your app.html that sets the class before the page renders.

  5. Overriding Tailwind base styles incorrectly: Modifying Tailwind's base layer or preflight styles can break utility classes. Extend rather than override when possible.

Practice Questions

  1. How does the scoped attribute prevent style conflicts? Answer: Vue adds a unique data attribute to the component's elements and modifies the CSS selector to match only those elements, preventing styles from leaking.

  2. What does colorMode.preference = 'system' do? Answer: It tells the color-mode module to follow the user's operating system setting, automatically switching between light and dark based on the OS preference.

  3. How do you define a CSS custom property for theming? Answer: Use --name: value inside :root for the default (light) theme and :root.dark or .dark for the dark theme override.

  4. What is the purpose of the transition-colors utility? Answer: It applies a smooth color transition when theme-specific properties change, preventing jarring instant color swaps during theme switching.

Challenge

Build a complete design system with: custom color palette and typography scale as CSS custom properties, light and dark themes using color-mode, Tailwind configuration that maps custom properties to utility classes, reusable button, card, and input components with theme support, and a theme playground page demonstrating all components.

Mini Project

Create a themed portfolio site with: a hero section that adapts to light/dark mode, Tailwind utility classes for responsive layout, CSS variables for primary and accent colors, a theme toggle with system preference detection, and animated transitions between themes using CSS transitions.

FAQ

Does @nuxtjs/color-mode work with SSR?

: Yes. The module injects the color mode class before hydration, preventing the flash of unstyled content (FOUC) that other dark mode solutions cause.

Can I use SASS/SCSS with Nuxt?

: Yes. Install sass as a dev dependency. Use <style lang="scss" scoped> in components. Nuxt compiles SCSS automatically without additional configuration.

How do I add Google Fonts to Nuxt?

: Use the @nuxtjs/google-fonts module or add a link tag in the app.head configuration. The module provides automatic font loading and subset generation.

What is the best way to organize CSS in a large Nuxt project?

: Use Tailwind for utility classes, a global CSS file for base styles, scoped styles for component-specific rules, and CSS custom properties for theme values.

Does Nuxt support CSS modules?

: Yes. Use <style module> in Vue components. The styles are scoped and accessible via $style in the template.

What's Next

Learn about Nuxt Internationalization (i18n) to add multi-language support to your Nuxt application with the @nuxtjs/i18n module.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro