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>

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")
};

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>

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>

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

Top comments (0)