DEV Community

Eric Brookfield
Eric Brookfield

Posted on

Add Dark Mode support to your Nuxt app

We're refactoring our in-house CRM in Vue as a Nuxt app, and since it's 2019, I wanted to support Dark Mode across the entire application with as little effort as possible.

Dark Mode is widely supported in Safari, Firefox, and Chrome by now, and a lot can be done on any web app with just CSS and CSS variables. 90%+ of my Dark Mode styling is just done in CSS, as it should be.

But if you're using a framework like Vue, you're probably controlling some styles of your components directly with props instead of just relying on CSS classes, like so:

/* Normal button for light mode */
<sui-button primary icon="plus">New entity</sui-button>

/* Inverted button for dark mode */
<sui-button primary inverted icon="plus">New entity</sui-button>
Enter fullscreen mode Exit fullscreen mode

I'm using Semantic UI for my buttons which is… fine for now. If I pass inverted="true" (shortened to simply inverted here), I get my Dark Mode-friendly button.

To turn that boolean into a globally accessible variable in my Nuxt app, a Vuex store seems like the right decision:

/* store/const.js */
export const state = () => ({
  darkMode: false
});
export const mutations = {
  setDarkMode: state => {
    state.darkMode = true;
  },
  unsetDarkMode: state => {
    state.darkMode = false;
  }
};
export const actions = {
  setDarkMode: ({ commit }) => commit("setDarkMode"),
  unsetDarkMode: ({ commit, state }) => state.darkMode && commit("unsetDarkMode")
};
Enter fullscreen mode Exit fullscreen mode

Assuming you have some Dark Mode media query styles set up, checking for Dark Mode and listening to changes is something we can do in our layout file:

/* layouts/default.vue */
<template>
 <nuxt />
</template>
<script>
export default {
  components: {
    AdminNav
  },
  data() {
    return {
      mql: window.matchMedia('(prefers-color-scheme: dark)')
    }
  },
  created() {
    this.darkMode(this.mql)
    this.mql.addListener(this.darkMode)
  },
  beforeDestroy() {
    this.mql.removeListener(this.darkMode)
  },
  methods: {
    darkMode: function(e) {
      if (e.matches) {
        return this.$store.dispatch('const/setDarkMode')
      }
      return this.$store.dispatch('const/unsetDarkMode')
    }
  }
}
</script>

Enter fullscreen mode Exit fullscreen mode

Finally, fetching the boolean in any component that needs it is just a computed property away:

/* components/myComponent.vue */
<template>
  <sui-button primary inverted="darkMode" icon="plus">New entity</sui-button>
</template>

<script>
export default {
  computed: {
    darkMode() {
      return this.$store.state.const.darkMode
    }
  }
};
</script>
Enter fullscreen mode Exit fullscreen mode

Now I get that nice inverted button flavor when using Dark Mode. Yum 😋.

Top comments (0)