Didii
Didii

Reputation: 1363

Including partials in components results in duplicate css

I'm trying to make use of the @extend of sass so that I don't mix markup and html together. As explained in this article.

In short, instead of writing

<div class="alert alert-primary>This is an alert!</div>

You'd instead write something like

<div class="banner">This is an alert!</div>
.banner {
    @extend .alert;
    @extend .alert-primary;
}

Such that styling and content stay nicely separated.

The problem: When using this with webpack (sass-loader) and components (e.g. Vue.js or Angular), I run into a problem where including a bootstrap partial will now result in the complete compilation of the entire bootstrap file into css. This results into a class .btn[data-v-3614b62c] and another .btn[data-v-45ac961c] etc. for every component that uses the partial bootstrap/scss/_buttons.scss and that for all classes defined in that partial. Even if I don't use it.

In the long run, this will be detrimental for the application since its size will increase rapidly and I image the browser will slow down with that many css classes to parse.

The question(s): How do I make sure sass doesn't duplicate the entire imported partial? Can I enable some kind of tree shaking where it only includes the classes I use? Do I have to change my file structure so that sass understands I only need certain classes inside the partial rather than everything?


Code example

This is a vue component using bootstrap

<template>
    <form class="form">
        <input type="text" class="input">
        <button class="button-submit">Send</button>
        <button class="button-cancel">Cancel</button>
    </form>
</template>

<style lang="scss" scoped>
@import "node_modules/bootstrap/scss/functions";
@import "node_modules/bootstrap/scss/variables";
@import "node_modules/bootstrap/scss/mixins";
@import "node_modules/bootstrap/scss/root";
@import "node_modules/bootstrap/scss/buttons";

.form {
    .button-submit {
        @extend .btn;
        @extend .btn-primary;
    }
    .button-cancel {
        @extend .btn;
        @extend .btn-danger;
    }
}
</style>

This will result in the entire partial _buttons.scss to be compiled into css instead of only .form .button-submit and .form .button-cancel.


Live example

https://codesandbox.io/embed/musing-feynman-8w2kx.

To see the problem I have:

  1. Right click on the example to the right and click Inspect
  2. In the Elements tab, navigate to #document > html > head
  3. At the bottom you'll have several style elements
  4. Two of them will contain all the button css where only the [data-v-######] attribute is different and at the end are my couple of lines code.

Note that the same happens for production builds. The css is then simply bundled up in a single file, but duplicates are still around.

Upvotes: 5

Views: 3654

Answers (1)

Decade Moon
Decade Moon

Reputation: 34286

If you are @importing the same CSS rules into different components, then you will get the same rules duplicated across all modules. That's just how it works.

You should only be @importing modules that define abstract declarations like variables, mixins, functions, etc, not actual styles.

The only way you can de-duplicate the styles globally is if you use something like mini-css-extract-plugin to extract and combine all the CSS into a single file and then run it through something like cssnano which will discard duplicate rules (although with scoped CSS, this probably won't work).

Modules are typically built independently of other modules and there isn't a simple way to know if a rule has been declared already by a previous module. In development you may be using style-loader which operates on a per-module basis and injects styles into the webpage on demand; there's just no way it can work out which styles should be injected in case some particular style has already been injected by another component.

It just gets messy; keep it simple by not duplicating styles in the first place.

If you really want to use @extend, then make a separate .scss file which is the only module that @imports the bootstrap styles, and define all your extensions in there.

Upvotes: 5

Related Questions