Setting up Dark Mode for Nuxt and Storybook via Tailwind CSS

Setting up Dark Mode for Nuxt and Storybook via Tailwind CSS

Enable multiple color modes for component design in Storybook, and use color mode-aware components in Nuxt

Published on December 29, 2020

This article reviews configuring dark mode via Tailwind CSS for Nuxt and Storybook. The following resources are used:

The companion repository includes the complete configuration of all moving parts, demonstrated via the example of a simple Button component:

Nuxt Color Mode – Enabling Dark Mode

Color Mode module adds boilerplate-free and effortless color mode switching – including auto detection based on the system's color-mode preferences – to any Nuxt app. See this excellent walkthrough for a more thorough introduction.

Nuxt Color Mode and Tailwind CSS – Dark Mode

Nuxt Color Mode and Tailwind CSS – Dark Mode

Different color modes require different styling. Thus, every component needs to be configured with conditional styling for all supported color modes, which will be applied based on the user's selection.

Nuxt Color Mode and Tailwind CSS – Light Mode

Nuxt Color Mode and Tailwind CSS – Light Mode

In order to define global styles for dark mode, add the respective CSS declarations. For instance, specify white text on dark background for when dark mode is activated by the user:

  assets › css ›tailwind.css
1@import "tailwindcss/base";
2@import "tailwindcss/components";
3@import "tailwindcss/utilities";

4.dark {
5  @apply bg-dark;
6  @apply text-white;
7}

Tailwind CSS utility classes based on props such as the component's variant can be dynamically computed within buttonClass() – for both light and dark mode. A base class provides fundamental styling scoped to the button component via @apply.

  components ›Button.vue
1<template>
2  <button class="base" :class="buttonClass">
3    <slot name="text">{{ text }}</slot>
4  </button>
5</template>

6<script>
7export default {
8  props: {
9    variant: {
10      type: String,
11      default: "primary",
12    },
13    square: {
14      type: Boolean,
15      default: false,
16    },
17    text: {
18      type: String,
19      default: "Button",
20    },
21  },
22  computed: {
23    buttonClass() {
24      const c = []
25      // Square
26      if (!this.square) {
27        c.push("rounded-md")
28      }
29      // Variant
30      if (this.variant === "primary") {
31        c.push("bg-primary-500 hover:bg-primary-600")
32        c.push("dark:bg-primary-900 dark-hover:bg-primary-800")
33      } else if (this.variant === "secondary") {
34        c.push("bg-gray-500 hover:bg-gray-600")
35        c.push("dark:bg-gray-700 dark-hover:bg-gray-600")
36      }
37      return c.join(" ")
38    },
39  },
40}
41</script>

42<style lang="postcss" scoped>
43.base {
44  @apply font-bold py-2 px-4 text-white;
45}
46</style>

Storybook Dark Mode – Component Library

A third-party Dark Mode addon for Storybook allows switching both UI and component view between light and dark mode. Optional: Add argTypes to interact with a component's arguments dynamically via a graphical UI. See Storybook's control addon docs for more information. For instance, switch between rectangular and default (round) buttons by modifying component props via Storybook's UI.

  components ›Button.stories.js

1import Button from "./Button"

2export default {
3  title: "Button",
4  component: Button,
5  argTypes: {
6    variant: {
7      control: {
8        type: "select",
9        options: ["primary", "secondary"],
10      },
11      defaultValue: "primary",
12    },
13    square: { control: "boolean" },
14    text: {
15      control: "text",
16    },
17  },
18}

19export const Template = (arg, { argTypes }) => ({
20  components: { Button },
21  props: Object.keys(argTypes),
22  template: '<Button v-bind="$props" />',
23})

24export const Primary = Template.bind({})
25Primary.args = {
26  variant: "primary",
27}

28export const Secondary = Template.bind({})
29Secondary.args = {
30  variant: "secondary",
31}

32export const Square = Template.bind({})
33Square.args = {
34  square: true,
35}

Adding dark mode support directly within Storybook enables effortless display of stories and components in both color modes – in the same way users would switch between modes – i.e., based on CSS class inheritance: A dark class is added to Dark Mode Plugin for Tailwind CSS to the <head> tag, while Color Mode module for Nuxt adds the dark class to the <html> tag.

Storybook – Dark Mode for both UI and component view

Storybook – Dark Mode for both UI and component view

The top right corner of the center toolbar enables switching between light and dark mode. This will switch the UI between color modes, and, as configured in the companion repository, at the same time modify the component view with a specified dark class, effectively simulating an user's color mode preference.

Storybook – Light Mode for both UI and component view

Storybook – Light Mode for both UI and component view

Optional – Tailwind Config Viewer

A useful tool for previewing your Tailwind CSS configuration is Tailwind Config Viewer, which is integrated into Nuxt's Tailwind CSS module since v3.4.0. Simply launch Nuxt in dev mode and access /_tailwind/ in your browser.

Tailwind Config Viewer

Tailwind Config Viewer

Conclusion

I hope this article proves insightful to anyone wanting to set up Nuxt with Tailwind CSS and Storybook. As shown here, supporting dark mode for component prototyping and design, and subsequent integration in your frontend app requires some additional tooling.

See the companion repository for specific configuration of all moving parts. Let me know if you have suggestions – I'm curious to learn of alternative approaches!

Let's stay in touch!

If you'd like to get notified about updates, feel free to

Enter your email address below to receive an email whenever I write a new post.

Note: You can unsubscribe by clicking on the unsubscribe link included at the bottom of every email.