Sass Customization
Once TrailMap needs a brand color that isn't Bootstrap's default blue, most
people reach for a CSS override sheet loaded after Bootstrap's own stylesheet, sprinkled with
!important to make sure it wins. It works for about a week, and then it doesn't:
a component you haven't touched yet still renders in the old blue, a Bootstrap update changes
selector specificity, and now you're playing whack-a-mole across a dozen files. Bootstrap 5
ships its entire source as Sass, and the officially recommended fix is to override its
variables before that source ever gets compiled — so every component that
reads $primary picks up TrailMap's trail-green automatically, with zero overrides
to maintain.
The Problem with !important Overrides
Say TrailMap's stylesheet, loaded after the Bootstrap CDN link, tries to recolor every primary button:
/* trailmap-overrides.css — loaded AFTER bootstrap.min.css */
.btn-primary {
background-color: #2c7a4b !important;
border-color: #2c7a4b !important;
}
This "works," but only for .btn-primary. TrailMap's .text-primary
headings, .border-primary cards, .bg-primary badges, active nav
links, focus rings, and form validation states are all generated from the same
$primary Sass value — and none of them get the memo. You'd need a matching
!important override for every single one, forever fighting a framework that
already has a mechanism built in for exactly this.
!important overrides aren't "wrong" for a one-off exception. The problem is using them as your primary customization strategy — that's a sign you're fighting Bootstrap instead of configuring it.The Right Way: Override Variables Before Importing
Bootstrap 5's compiled CSS is just the end result of compiling its own .scss
source files. Every color, spacing value, and border-radius in that source is a Sass
variable with a !default flag, which means: "use this value, unless something
already set it earlier." So instead of importing the compiled CSS, TrailMap installs
Bootstrap's source and writes its own entry file that sets variables first:
// trailmap-custom.scss
// 1. Your variable overrides go FIRST
$primary: #2c7a4b;
// 2. THEN import Bootstrap's actual source
@import "bootstrap/scss/bootstrap";
Order matters completely here. Because Bootstrap's own $primary: #0d6efd !default;
line only assigns a value if nothing already claimed the variable, TrailMap's line —
compiled first — wins, and Bootstrap's own default is silently skipped. Flip the two lines
and this stops working entirely, since by the time your override runs, $primary
is already locked in.
Worked Example: Recoloring TrailMap to Trail-Green
With $primary overridden, every component that derives from it changes at once —
no per-component CSS required:
// trailmap-custom.scss
$primary: #2c7a4b;
$link-color: $primary;
@import "bootstrap/scss/bootstrap";
# compile it (via the sass CLI, or your bundler's sass loader)
npx sass trailmap-custom.scss trailmap-custom.css
<link href="trailmap-custom.css" rel="stylesheet">
<button class="btn btn-primary">Save Trail</button>
<a class="link-primary" href="#">View all trails</a>
The button, the link, the navbar's active state, form focus rings, badges using
bg-primary-subtle — every one of them now renders in trail-green, compiled
straight from a single line change. No selector ever had to be found and overridden by hand.
A Few Variables Worth Knowing
| Variable | Controls |
|---|---|
$primary | The main brand color used across buttons, links, active states, focus rings |
$body-bg / $body-color | Default page background and text color |
$font-family-base | The default font stack for the whole page |
$border-radius | Default corner rounding for buttons, cards, inputs, alerts |
$enable-shadows | Turns on subtle box-shadows across components (off by default) |
Why This Beats the CDN Build
The CDN link you used in the Setup: CDN vs npm lesson points to a
file that has already been compiled — its variables were resolved once, at Bootstrap's own
build time, and baked into plain CSS. There is no way to hand a compiled CSS file a new value
for $primary; by the time you can see it, the Sass step has already happened.
Sass-variable overrides only work if you have Bootstrap's .scss source
available and control the compile step yourself — which means npm (or another package
manager) plus a Sass compiler, not a CDN link. It's the direct tradeoff for the customization
power: a build step now, in exchange for never writing an override sheet again.
.mt-3.