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.
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 (
@import
s) 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
andbootstrap-styles
(I actually split it into more files to keep variables separate from functions/mixins). Then@import
thebootstrap-declarations
from your application'svariables
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'svariables
as the very first thing, before@import
ofbootstrap-styles
, and you're all set.