Dark mode. How to create your first Nuxt.js app (Part 2)
Tags: webdev, vue, darkmode, javascript
Published: 2020-12-17 17:12:00

This is the second post in our series of articles about creating a modern blog with [Nuxt.js](https://nuxtjs.org/). In [the previous post](https://dev.to/hostman_com/how-to-create-your-first-nuxt-js-app-lch) we have created our Nuxt.js application and then deployed it to [Hostman](https://bit.ly/3mhvSyv).

Here we will implement a dark mode for this application.

> Please note you can find code for each post in its own branch on [Github](https://github.com/mikhail-shpakov/nuxt-blog/tree/part-2). The app version from the latest published article is available in `master`.

# Dark mode. What is it? #

Dark mode is a color scheme for any interface that displays light text and interface elements against a dark background. This makes it easier to look at the screen of phones, tablets, and computers under low light conditions. The dark theme reduces the light emitted from the screen but maintains the minimum color contrast ratio needed for legibility.

The dark theme improves visual ergonomics and reduces eyestrain by adjusting the screen with current lighting conditions and providing ease of use at night or in darkness.

Furthermore, keep in mind that using the dark theme in web and mobile applications can extend the lifespan of your device battery. `Google` [has confirmed](https://www.howtogeek.com/fyi/google-wants-more-battery-saving-dark-mode-options-in-its-apps/) the dark theme on `OLED` displays is very helpful in prolonging battery life.

# @nuxtjs/color-mode #

To implement the dark theme, we will use the [@nuxtjs/color-mode](https://color-mode.nuxtjs.org/) module, which provides the following possibilities:

* Add `.${color}-mode` class to the `<html>` tag to simplify `CSS` theme management,

* Works in any `Nuxt` mode (`static`, `ssr` or `spa`),

* Automatically detects the system color mode on the user's device and can set the appropriate theme based on this data,

* Allows to synchronize the selected theme between tabs and windows,

* Allows to use the implemented themes for individual pages, not for the whole application (perfect for incremental development),

* It also supports IE9 + (I'm not sure if this is still relevant in modern development, but it might be useful for someone).

Firstly, let's install the module:
```bash
npm i --save-dev @nuxtjs/color-mode`
```
Then add information about this module to the `buildModules` section in the `nuxt.config.js` file:
```javascript
{
  buildModules: [
    '@nuxtjs/color-mode'
  ]
}
```
Great! Now if we run our application and open the `Elements` tab in the developer's console, we will see that a class that matches the operating system theme has been added to the `html` tag.

For example, in my case, `class="light-mode"`.

# Dark and Light Theme switcher #

Let's now implement a switcher that will change the dark theme to the light theme and vice versa.

According to the [design](https://www.figma.com/file/s1B9t0XOmRhtNY8daezIs7/Nuxt.js-blog?node-id=1529%3A3303) of our application, there is a language switcher next to the theme switcher. We will cover it in our next posts here.

Let's start with creating a wrapper component that will encapsulate these switchers and will be responsible for margin from other components.

To do this, let's create an `AppOptions` component with the following content:

```bash
<template lang="pug">
section.section
  .content
    .app-options
      switcher-color-mode
</template>

<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'AppOptions',
})
</script>

<style lang="scss" scoped>
.app-options {
  display: flex;
  margin-top: 24px;
}
</style>
```
Component on [Github](https://github.com/mikhail-shpakov/nuxt-blog/blob/part-2/components/AppOptions/index.vue).

As we can see, there is no logic in this component and it just sets margins for nested components. Now we have only one nested `switcher-color-mode` component, let's implement it.

Let's take a look at the `script` section of this component:

```typescript
<script lang="ts">
import Vue from 'vue'

export default Vue.extend({
  name: 'SwitcherColorMode',

  computed: {
    icon() {
      return (this as any).$colorMode.value === 'light'
        ? 'assets/icons/sun.svg'
        : 'assets/icons/moon.svg'
    },
  },

  methods: {
    changeColorMode() {
      ;(this as any).$colorMode.preference =
        (this as any).$colorMode.value === 'light' ? 'dark' : 'light'
    },
  },
})
</script>
```

Here we implement a `changeColorMode` method that changes the theme in the object provided by the `@nuxtjs/color-mode` module.

When the value of `$colorMode.preference` is changed, the corresponding class of the `html` tag will also be set: `class="light-mode"` or `class="dark-mode"`.

There is also a computed property `icon` that returns the icon we need, depending on the selected theme. Please note that to work correctly, you need to add the `sun.svg` and `moon.svg` icons to the `assets/icons` directory.

The component template looks like this:

```bash
<template lang="pug">
button(@click="changeColorMode")
  img(
    alt="theme-icon"
    :src="getDynamicFile(icon)"
  )
</template>
```

This is quite easy now! There is a button. Clicking on it we call the `changeColorMode` method and change our theme. Inside the button, we show an image of the selected theme.

Component on [Github](https://github.com/mikhail-shpakov/nuxt-blog/blob/part-2/components/AppOptions/SwitcherColorMode/index.vue).

What we have to do is add this component to the main page of our application. After this step the page template should look like this:

```bash
<template lang="pug">
.page
  section-header(
    title="Nuxt blog"
    subtitle="The best blog you can find on the global internet"
  )

  app-options

  post-list
</template>
```
# Variables management #

As you know from the first part, we used `scss` variables to define all the colors in the application. Now we should change the values ​​of these variables depending on the selected theme.

But the real problem is that `scss` variables are set once when building the application and later we cannot override them when changing the theme.

This limitation can be bypassed using `js`, but it would be much easier if we use native `css` variables.

Now in our file with variables `assets/styles/variables.scss`, the section with colors looks like this:

```scss=
// colors  
$text-primary:                      rgb(22, 22, 23);  
$text-secondary:                    rgb(110, 109, 122);  
$line-color:                        rgb(231, 231, 233);  
$background-color:                  rgb(243, 243, 244);  
$html-background-color:             rgb(255, 255, 255);
```

Let's define two color schemes in this file - light and dark - using `css` variables:

```scss=
:root {
  // light theme
  --text-primary:                   rgb(22, 22, 23);  
  --text-secondary:                 rgb(110, 109, 122);  
  --line-color:                     rgb(231, 231, 233);  
  --background-color:               rgb(243, 243, 244);  
  --html-background-color:          rgb(255, 255, 255);  
  
  // dark theme  
  &.dark-mode {
    --text-primary:                 rgb(250, 250, 250);  
    --text-secondary:               rgb(188, 187, 201);  
    --line-color:                   rgb(45, 55, 72);  
    --background-color:             rgb(45, 55, 72);  
    --html-background-color:        rgb(26, 32, 44);  
  }  
}
```

We have defined `CSS` variables in the `:root` selector. According to the `CSS` standard, a variable is specified and used with the prefix `--`.

Read about the `CSS` pseudo-class `:root` on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/:root) and [W3Schools](https://www.w3schools.com/cssref/sel_root.asp). 

> “The CSS [pseudo-class](https://developer.mozilla.org/ru/docs/Web/CSS/%D0%9F%D1%81%D0%B5%D0%B2%D0%B4%D0%BE-%D0%BA%D0%BB%D0%B0%D1%81%D1%81%D1%8B) `:root` finds the root element of the document tree. Applies to HTML, `:root` finds the `html` tag and is identical to the selector html, but its [specificity](https://developer.mozilla.org/ru/docs/Web/CSS/Specificity) is higher”.
Source: [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/:root).

Those colors that were previously defined directly into `SCSS` variables are now specified in `css` variables as default values, and if the `.dark-mode` class is present, these values ​​are overridden.

Now our `SCSS` variables with colors will look like this:

```scss=
$text-primary:                      var(--text-primary);  
$text-secondary:                    var(--text-secondary);  
$line-color:                        var(--line-color);  
$background-color:                  var(--background-color);  
$html-background-color:             var(--html-background-color);
```

Now, when switching a theme, our color scheme will change according to the specified values ​​and we do not need to change anything in the already implemented components. The file with styles on [Github](https://github.com/mikhail-shpakov/nuxt-blog/blob/part-2/assets/styles/variables.scss).

# Conclusion #

Here we learned how to implement dark mode for Nuxt.js application. Are you going to follow our steps? Is dark mode just overrated and overhyped or necessity and benefit? Please share your thoughts below. What do you think?

* [Design](https://www.figma.com/file/s1B9t0XOmRhtNY8daezIs7/MVP-on-a-modern-stack-in-1-day?node-id=34%3A0)
* [Github](https://github.com/mikhail-shpakov/nuxt-blog/tree/part-2)
* [Demo of the second part](https://nuxt-blog.hostman.site/)

Later in our blog here we will discuss such topics as:
 
* multilingual applications,
* PWA and SEO, including Sitemap auto-generation and robots.txt,
* setting up Analytics (Google and Yandex) and bug tracker (Sentry),
* application optimization for passing tests Lighthouse /PageSpeed.