How to Increase Your Page Size by 1,500% with webpack and Vue

Avatar of Burke Holland
Burke Holland on (Updated on )

Disclaimer: This article is mostly satire. I do not think that I am better than you because I once wrote some TypeScript nor do I think that it’s a good thing for us to make web pages bigger. Feel free to misrepresent these views to maximize clicks.

You know, there are a lot of articles out there telling you how to make your page smaller: optimize your images, remove extraneous CSS rules, re-write the whole thing in Dreamweaver using framesets. Look,  Walmart just reduced their page size by some numbers, give or take.

What we don’t have are enough articles showing you how to increase your page size. In fact, the only article I could find was this one from the Geek Squad which ended up being about making the font size bigger. This is a good start, but I think we can do better.

Put on some weight

Now, why would you want to increase your page size? Isn’t that a not-very-nice thing for people on low bandwidth connections? Well, there are several excellent and in no-way-contrived reasons and here are three of them since things that come in threes are more satisfying.

  1. You have a gigabit connection and you live in Tennessee so surely everyone else is in better shape than you are.
  2. Browsers do caching, silly. That means that you only have to download the site once. Stop complaining. First world problems.
  3. You don’t care whether or not people ever visit your site because you, “work to live, not live to work.”

If any of those completely relatable reasons resonates with you, I’d like to show you how I increased the size of my CSS by 1,500% — and you can too, with one simple webpack trick.

One weird trick

It all started when I decided to refactor my retirement plan project called The Urlist over to the Bulma CSS framework.

The original incarnation of the site was all hand-rolled and my Sass looked like an episode of Hoarders.

“Burke, you don’t need 13 different .button styles. Why don’t you pick one and we can get rid of these other 12 so you have somewhere to sleep?”

Bulma also includes things like modals that I used third-party Vue components to make.

It also has a hamburger menu because it’s a well-known scientific fact that you cannot have a successful site without a hamburger.

Look, I don’t make the rules. This is just how business works.

I was quite happy with the result. Bulma styles are sharp, and the layout system is easy to learn. It’s almost as if someone somewhere understands CSS and also doesn’t hate me. That’s just a hard combination to find these days.

After a few weeks of refactoring (during which I would ask myself, “WHAT ARE YOU EVEN DOING MAN?!? THE SITE ALREADY WORKS!”), I finally finished. As a side note, the next time you think about refactoring something, don’t. Just leave it alone. If you don’t leave any technical debt for the next generation, they’re going to be extremely bored and that’s going to be on you.

When I built the project, I noticed something odd: the size of my CSS had gone up pretty significantly. My hand-crafted abomination was only 30KB gzipped and I was up to 260KB after the refactor.

And, to make matters worse, the Vue CLI was lecturing me about it…

Which, of course, I ignored. I don’t take instructions from robots.

What I did instead was deploy it. To production. On the internet. Because I did not spend all of this time refactoring to not deploy it. Yeah, sunk costs and all that, but excuse me if I’m more pragmatic than your poster of logical fallacies. All I’m saying is I came to party and I was not going home without a buzz.

Then I took to Twitter to announce my accomplishment to the ambivalent masses. As one does.

Shortly thereafter, Jeremy Thomas, who created Bulma (and clearly loves Dragon Ball) responded. It was quick, too. It’s like there is a bat signal that goes out whenever a moron tweets.

Duplicate styles? 13 times? What the heck is a namespace? Is that a π symbol or a custom Jeremy Thomas logo?

It’s at this moment that I realized that I have no idea what I’m doing.

Put the Sass down and back away slowly

I’ll be the first to admit that I don’t know a lot about CSS, and even Less about Sass. Get it? Less about Sass? Forget it. I don’t want your pity laugh.

When I setup my Vue CLI project to use Bulma, I created a src/styles folder and dropped in a bulma-but-not-all-of-bulma-only-some-of-it.scss file. They say naming things is hard, but I don’t see why.

That file imports the pieces of Bulma that I want to use. It’s Bulma, but not all of it. Only some of it.

@import "bulma/sass/utilities/_all.sass";
@import "bulma/sass/base/_all.sass";

@import "bulma/sass/form/shared.sass";
@import "bulma/sass/form/input-textarea.sass";

// etc...

Then I imported that file into a custom Sass file which I called… site.scss. I like to keep things simple.

@import "./bulma-but-not-all-of-bulma-only-some-of-it.scss";

html,
body {
  background-color: #f9fafc;
}

// etc...

I wanted to import these files into Vue globally so that I could use them in every component. And I wanted to do it the right way; the canonical way. I think it’s clear from my willingness to deploy 2+ MB of CSS into production that I like to do things the “right way”.

I read this excellent blog post from Sarah Drasner called, “How to import a Sass file into every component in your Vue app.” She shows how to do it by modifying the webpack build process via the vue.config.js file.

module.exports = {
  css: {
    loaderOptions: {
      sass: {
        data: `@import "@/styles/site.scss";`
      }
    }
  }
}

What I did not understand is that this imports Sass into every component in a Vue app. You know, like the title of the blog post literally says. This is also how I ended up with a bunch of duplicate styles that had a data-v- attribute selector on them. I have scoped styles to thank for that.

How Vue handles `scoped`

Vue allows you to “scope” styles to a component. This means that a style only affects the component that it’s in, and not the rest of the page. There is no magic browser API that does this. Vue pulls it off by dynamically inserting a data- attribute in both the element and the selector. For example, this:

<template>
  <button class="submit">Submit</button>
<template>

<style lang="scss" scoped>
.submit {
  background-color: #20ae96;
}
</style>

…becomes this:

<button class="submit" data-v-2929>Submit</button>

<style>
.submit[data-v-2929] {
  background-color: #20ae96;
}
</style>

That dynamic data tag gets added to every child element in the component as well. So every element and every style for this component will have a data-v-2929 on them at runtime.

If you import a Sass file into your component that has actual styles in it, Vue (via webpack) will pull in those styles and “namespace” them with that dynamic data- attribute. The result is that is you include Bulma in your app 13 damn times with a bunch of data-v weirdness in front of it.

But this begs the question: if webpack renders the CSS in every single component, why would you ever want to use the vue.config.js approach? In a word: variables.

The variable sharing problem

You can’t define a Sass variable in one component and reference it from another. That would also be kind of hard to manage since you would be defining and using variables all over the place. Only I would write code like that.

You, on the other hand, would probably put all your variables in a variables.scss file. Each component would then reference that central store of variables. Importing a variables file into every single component is redundant. It’s also excessive. And unnecessary. And long-winded.

This is precisely the problem that Sarah’s article is solving: importing a Sass file into every component in your project.

It’s OK to import something like variables into every component because variables aren’t rendered. If you import 200 variables and only reference one of them, who cares? Those variables don’t exist in the rendered CSS anyway.

For example, this:

<style lang="scss" scoped>
$primary: #20ae96;
$secondary: #336699;

.submit {
  background-color: $primary
}
</style>

…becomes this:

<style>
.submit[data-v-2929] {
  background-color: #20ae96;
}
</style>

So, there are really two problems here:

  1. Bulma needs to be global.
  2. Bulma’s variables should be accessible from the components.

What we need is a clever combination of Sarah’s technique, along with a little proprietary knowledge about how Bulma is structured.

Using Bulma with the Vue

We’re going to accomplish this with the least amount of duplication by having three files in the src/styles directory:

variables.scss: This file will be where you pull in Bulma’s variables and override/define your own. Note that you have to include the following three files to get all of Bulma’s variables. And they have to be in this order…

// Your variables customizations go up here

// Include Bulma's variables
@import "bulma/sass/utilities/initial-variables.sass";
@import "bulma/sass/utilities/functions.sass";
@import "bulma/sass/utilities/derived-variables.sass";

bulma-custom.scss: This file is where you pull in the pieces of Bulma that you want. It should reference the variables.scss file.

@import "./variables.scss";

/* UTILTIES */
@import "bulma/sass/utilities/animations.sass";
@import "bulma/sass/utilities/controls.sass";
@import "bulma/sass/utilities/mixins.sass";

// etc...

site.scss: This pulls in the bulma-custom.scss file and is also where you define global styles that are used across the whole project.

@import url("https://use.fontawesome.com/releases/v5.6.3/css/all.css");
@import "./bulma-custom.scss";

html,
body {
  height: 100%;
  background-color: #f9fafc;
}

// etc...

Import the site.scss file into your main.js file. Or in my case, main.ts. Does it make me better than you that I use TypeScript? Yes. Yes it does.

import Vue from "vue";
import App from "./App.vue";
import router from "./router";

// import styles
import "@/styles/site.scss";

This makes all of the Bulma pieces we are using available in every component. They are global, but only included once.

Per Sarah’s article, add the variables.scss file to the vue.config.js file.

module.exports = {
  css: {
    loaderOptions: {
      sass: {
        data: `@import "@/styles/variables.scss";`
      }
    }
  }
}

This makes it so that you can reference any of the Bulma variables or your own from any .vue component.

Now you have the best of both worlds: Bulma is available globally and you still have access to all Bulma variables in every component.

Total size of CSS now? About 1,500% smaller…

Take that, Walmart.

Redemption via PR

In an effort to redeem myself, I’ve submitted a PR to the Bulma docs that walks through how to customize Bulma in a Vue CLI project. It’s an act of contrition for taking to Twitter and making Bulma seem like the problem when really Burke is the problem.

And you would think that by now I would have this figured out: Burke is always the problem.