Nuxt.js app from UI kit to deployment. Part 2: Dark theme

Hello, Habr!



We are publishing the second part of a series of articles on creating a modern blog with Nuxt.js. Today we will implement the dark theme in the application that we wrote together with you in the first part .



Please note that the code for each part can be found in its own thread on Github , and the master



version of the application from the last published article is available.







What is dark theme?



A dark theme is a color scheme for any interface that displays light text and interface elements against a dark background, making it easier to view the screen on mobile phones, tablets, and computers in low light conditions. The dark theme reduces the light emitted from the screen while maintaining the minimum color contrast ratio needed for legibility.



The dark theme improves visual ergonomics by reducing eye strain by adjusting the screen to suit current lighting conditions and providing ease of use at night or in the dark.



Also, keep in mind that using the dark theme in web and mobile applications can extend the battery life of your device. Google has confirmed that the dark theme on OLED screens is very helpful in extending battery life.



@ nuxtjs / color-mode



To implement the dark theme, we will use the @ nuxtjs / color-mode module , which provides the following capabilities:



  • Adds a class .${color}-mode



    to the tag <html>



    to make it easier to manage CSS themes
  • works in any mode Nuxt



    ( static



    , ssr



    or spa



    );
  • automatically detects the color mode of the system on the user's device and can set the appropriate theme based on this data;
  • allows you to synchronize the selected theme between tabs and windows;
  • allows you to use the implemented themes for individual pages rather than for the entire application (ideal for incremental development);
  • the module also supports IE9 + (I'm not sure if this is still relevant in modern development, but it might be useful to someone).


First, let's install the module:



npm i --save-dev @nuxtjs/color-mode
      
      





And then add information about this module to the section buildModules



in the file nuxt.config.js



:



{
  buildModules: [
    '@nuxtjs/color-mode'
  ]
}
      
      





Fine! Now, if we run our application and open a tab Elements



in the developer console, we will see that html



a class has been added to the tag that corresponds to the operating system theme, for example, in our case class="light-mode"



.



Theme switcher



In the next step, let's implement a switch that will change the dark theme to the light theme and vice versa.



If we look at the design of our application in Figma, we can see that next to the theme switcher is also a language switcher, which we will implement in one of the next articles in this series.



Let's immediately write a wrapper component that will encapsulate these switches and be responsible for margins before other components.



To do this, create a component AppOptions



with the following content:



<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 .



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



, let's implement it.



Let's take a look at the section of script



this component:



<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 are implementing a method changeColorMode



that changes theme in the object provided by the module @nuxtjs/color-mode



.



When the value is changed $colorMode.preference



, the corresponding class of the tag will also be set html



: class="light-mode"



or class="dark-mode"



.



In addition, there is a computed property icon



that returns the icon we need, depending on the selected theme. Please note that in order to work correctly you need to add icons sun.svg



and moon.svg



directory assets/icons



.



The component template will look like this:



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





Everything is quite simple here! We have a button, when we click on which we call a method changeColorMode



and change our theme. Inside the button, we show an image of the selected theme.



Component on Github .



It remains only to add this component to the main page of our application. After that, the page template should look like this:



<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>
      
      





Variable management



As you may remember from the first part, we used scss



variables to define all the colors in the application , and now all we have to do is change the values ​​of these variables depending on the chosen theme.



But the problem is that the scss



variables are set once when building the application, and in the future we cannot redefine them when changing the theme.



This limitation can be circumvented using js



, but there is a much simpler solution: we can use native css



variables.



Now in our file with variables, the assets/styles/variables.scss



section with colors looks like this:



// 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 first define two color schemes in the same file - light and dark - using css



variables:



: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 the css



variables in the selector :root



. By standard, a css



variable is specified and used with a prefix --



. Read



about css



pseudo- class:root



on MDN and W3Schools . Quote from MDN



: The



css



pseudo class :root



finds the root element of the document tree. Applies to HTML, :root



finds a tag html



and is identical to the html tag selector, but its specificity is higher.



As we can see, those colors that were previously written directly in scss



variables are now specified in css



variables as default values, and if a class is present, .dark-mode



these values ​​are overridden.



Now our scss



color variables will look like this:



$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);
      
      





When switching the theme, the color scheme will change according to the given values ​​and we do not need to change anything in the already implemented components.



Conclusion



Thanks to this article, we learned how to implement a dark theme for a Nuxt.js application.



Did you manage to complete all the steps? Do you think the dark theme is just a hype or is it a necessity? Share your thoughts in the comments.



Links to required materials:






All Articles