4

I'm rolling out my own bootstrap-sass customization in a pretty big project. In my git repo I have my own application.scss file which mirrors the original bootstrap.scss, removing what I don't need and adding my own custom variables, mixins and styles.

I cannot understand the right way to do this while retaining a DRY approach. Should I @import 'application/variables'; before or after I @import 'path/to/bootstrap/variables';?

The before approach

This seems to be the preferred way for bootstrap developers, because all the variables declared in _variables.scss are followed by the !default flag, which takes action only if the variable has been previously declared.

Where does this fail? Take for example a declaration like this:

$brand-primary: $gray;

Compiling this SASS code will spit out an undeclared variable error, because $gray is defined in bootstrap's _variables.scss, which is imported later. If I want this to work, I'll have to re-declare $gray at the top of my file, even though it didn't change from the default.

This may not sound like a big deal, but over a certain level of complexity it starts to happen quite a lot, and your application/_variables.scss goes from being "the file where I define my own variables" to "the file where I define my variables and copy over some other bootstrap stuff without actually changing it".

The after approach

To overcome the problem of the before approach I tried to import my variables after bootstrap's ones. It turns out it doesn't really work either, and it's broken in subtler ways. Look at this example:

$padding-base-vertical: 8px;

Looks innocent, right? And in fact it will change the vertical padding as needed. How does this break? It breaks, for example, the $input-height-base variable, declared later in bootstrap's _variables.scss, which is not correctly recalculated based on $padding-base-vertical's changes.

And this is how $input-height-base is defined:

$input-height-base: ($line-height-computed + ($padding-base-vertical * 2) + 2) !default;

Thus $input-height-base is calculated with $padding-base-vertical's default value and then never again. As a side effect, this kind of problem is quite hard to debug (at least it was for me).

The "solution"? Redeclare $input-height-base and all the other dependent variables in application/_variables.scss just like they are in bootstrap. Yes, it's the same workaround of the after approach.


So, is there a DRY way to do this? I can't even split my variables in two, part to set before and part to set after, because I could bump in a variable which hits both problems.

The first approach is the less ugly, but it's far from being an ideal solution.

bardo
  • 363
  • 2
  • 10

1 Answers1

0

This problem took me some time to puzzle out as well. Here's the solution I came up with.

To keep your SASS code modular, keep distinct separation between the two types of code - declarative and imperative. By declarative code I mean definitions of variables, functions, and mixins. By imperative code I mean style definitions (any code that will actually produce some output when compiled). Keep those kinds of code in separate files. You can then transparently declare dependencies (@imports) between files containing declarative code, because including declarative code multiple times will have no effect on the output.

Using the approach outlined above, split your "customized Bootstrap" into two files - bootstrap-declarations and bootstrap-styles (I actually split it into more files to keep variables separate from functions/mixins). Then @import the bootstrap-declarations from your application's variables without any hassle and declare your variables both before and after the @import as needed. In your application's imperative code (styles), @import your application's variables as the very first thing, before @import of bootstrap-styles, and you're all set.

hon2a
  • 7,006
  • 5
  • 41
  • 55
  • So, to get this straight: you are suggesting to import my variables before bootstrap's ones, except the ones that depend on other bootstrap variables, right? This is the solution I hint at the end of the question, but it doesn't work in all case. For example: `$gray-dark: darken($gray, 20%);` when you didn't redefine `$gray` will not work in any of the files. I guess it's a very corner case, though. – bardo Nov 22 '14 at 22:41
  • Maybe I flew off the handle with my answer a bit and answered also some questions you didn't ask :) Anyway, yeah, split your variables. Of course, in some cases (like the one you provided), you might need to declare some few variables with original Bootstrap values. But there's nothing bad about it, really. Importing Bootstrap's `variables` file is just a shortcut. You might want to declare the important variables yourself anyway to have full control over the key aspects of your styling (through subsequent Bootstrap updates). – hon2a Nov 22 '14 at 22:52
  • 1
    Looks like this is the answer, then :) I wrote a small one-liner to get a list of the variables you should always import before bootstrap 'cause they are used to calculate other variables later. I'll leave it here for posterity: `sed 's://.*$::;s/^.*\$.\+://' _variables.scss | ack -o '\$\w+(-?(?<=-)\w+)*\b' | sort -u` – bardo Nov 22 '14 at 22:57