Environmental Components in Vue.js

I recently spent some time trying to figure out the best way to include a ‘global reset’ into my Vue.js app. I didn’t find a satisfactory solution, so here is a proposal for a system which I think can help.

Let me start by saying that, I know using global variables in programming is controversial and I agree that a componentized system essentially strives to keep things isolated, and therein lies it’s strength.

There are still however, many projects out there using globally scoped inheritance based CSS that you or I may find ourselves working with. This article will attempt to outline a method for bringing more transparency and stability to those projects.

Global scope in a componentized system

Scouring Stack Overflow for clues about how to include a global reset into a Vue.js project is laborious because there are plenty of people trying to work out how to do this across various stacks and versions of Vue, Webpack, Nuxt and others.

If you are looking for guidance on how to do that with a specific setup, the following may help:

I’ve sieved my way through a few posts by now and tried to implement several solutions, some of which kinda worked.

Prepending global style data

The most common and solid solution I’ve found proposes including the global style as prepended data using the following configuration in your vue.config.js file:

vue.config.js
module.exports = {
  publicPath: '/',
  css: {
    loaderOptions: {
      sass: {
        prependData: `@import "@/styles/_global.scss";`
      }
    }
  }
};

This seems to work pretty well but as some have discovered, this method will provide the global style by including it in every component – rendering the CSS multple times, resulting in an ugly inspector and a weighty project.

A way to get around this is to include SASS variables only in the prepended data, and move anything that isn’t a variable into individual components or to a separate file that is included in the entry point to your app, like this:

main.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router'

import '@/styles/_global.scss'

...

This seems like a good enough solution and if you’re happy arranging the project this way, you’re good to go.

Is that the best we can do?

I can’t bring myself to separate the variables from the related styles. Perhaps it’s my neurosis, or perhaps it’s because it’s Christmas and I have too much time on my hands!

I want to keep variables and styles for specific things together. Global _typography.scss for example, should contain variables and styles relating to global line-height, leading and vertical rhythm. Then a more granular component such as _H1.scss would contain it’s own sandboxed styles whilst inheriting these global rules.

I also don’t want those global styles duplicated for every component. I think we can do better.

A compromise

I tried to find a way out of adding variables as prepended data. At one point I even moved away from SASS altogether and tried using pure CSS variables (custom properties). It just wasn’t working, there are currently too many limitations with using CSS variables to make it practicle for my project.

So I had to compromise. I moved the SASS variables into their own file and included them in prepended data as follows:

vue.config.js
module.exports = {
 publicPath: '/',
  css: {
   loaderOptions: {
    scss: {
     prependData: `
      @import "~@/components/00-environment/00-colours/_variables.scss"; 
      @import "~@/components/00-environment/01-fonts/_variables.scss"; 
      @import "~@/components/00-environment/03-layout/_variables.scss"; 
      @import "~@/components/00-environment/04-typography/_variables.scss"; 
      `
   }
  }
 }
};

Doing it this way means I can at least keep relative variables and styles together inside the same folder. These folders may give you a hint as to what happens next..

Surfacing the underlying environment

The excellent Atomic Design approach created by Brad Frost has served me very well over the years. It satisfies my desire to organise things and gives building pattern libraries a velocity that impresses clients.

Atomic Design structure is based on the principle of complexity. The complexity of a component or it’s tendency to contain simpler components, decides it’s place in the component hierarchy.

The following list defines the hierarchy as I use it.

Atoms
– HTML elements and variations
Molecules
– Containers of atoms that deal mostly with alignment and semantic grouping
Organisms
– Discrete design patterns that may stand alone or be included in a template
Templates
– A pattern with a definite purpose, usually used only once in a page such as a header
Pages
– Anything that needs to be adjusted at page level such as themed pages or campaigns

This works really well for me, but there have been times when I’ve wondered – Where so I put my reset? How about my font rules or brand colours? Surely I don’t repeat them in every component.

We’re back to our original problem. We end up needing to somehow shoehorn global styles into our system, often in a way where they are not visible to everybody involved in the project (such as our original pretendData method above).

Why can’t we just surface global requirements as components like everything else?

Global style as a component

In an interactive system as in chemistry, there are environmental factors which permeate everything. Whether we like it or not, they are relevant.

These factors are more general (read less complex) and so sit underneath even the simplest our components (atoms).

I’d like to propose an addition to the list

Environment
– Un-scoped components that deal with global inheritable rules

Adding global styles as a set of un-scoped environmental components in Vue means that we can surface them inside of our pattern library. We can then utilise them in the same way we would any other component.

Figure 1. shows how this method lends itself to the use of living style guide. Global properties such as fonts can be displayed in the pattern library, making them visible to all stakeholders

Figure 1. Environmental components in a project.

Conclusion

Global scope has been really useful in my experience. The ability to adjust a style, or completely rebrand a website by defining inheritable values has been a powerful tool.

Components are a great way to organise design patterns in such a way that they are very robust and Atomic Design structure makes this especially intuitive.

I love both of this things, but global CSS and inheritability is now at odds with the development of componentized systems.

Until we find ourselves consistently using CSS Modules, we can ensure any inherited values from resets and such are available for all to see by presenting them as Environmental Components.

These globally scoped components allow complex patterns to consume contextual styling where appropriate, whilst remaining robust and organised.

I’d like to get your feedback on this approach. Are you using a method like this?