How to Use SCSS Variables to Theme a Bootstrap 5 Site

  • Canvas Team
  • 12 min read
How to Use SCSS Variables to Theme a Bootstrap 5 Site
12 min read
Share:

How to Use SCSS Variables to Theme a Bootstrap 5 Site

How to Use SCSS Variables to Theme a Bootstrap 5 Site

If you’ve ever tried to restyle a Bootstrap 5 project by hunting down hardcoded hex values across dozens of CSS files, you already understand the pain this article is here to solve. SCSS variables are the clean, scalable, and professional way to theme a Bootstrap 5 site — and once you understand how Bootstrap’s Sass architecture works, you’ll wonder how you ever managed without it.

In this guide, we’ll walk through exactly how Bootstrap 5 SCSS variables work, how to override them correctly, and how to build a consistent design system that scales from a single landing page to a full multi-section website. Whether you’re working with a custom build or a premium template like Canvas Template, these techniques apply universally.

Key Takeaways

Key Takeaways

  • Bootstrap 5 uses a comprehensive SCSS variable system that controls colours, typography, spacing, breakpoints, and more.
  • You should never edit Bootstrap’s source files directly — always override variables in a separate file before importing Bootstrap.
  • The !default flag in Bootstrap’s SCSS means your custom values take priority when declared first.
  • CSS custom properties (variables) are generated automatically from your SCSS overrides, giving you runtime flexibility too.
  • A well-structured SCSS architecture — with a single entry point — makes theming predictable, maintainable, and team-friendly.
  • Tools like CanvasBuilder can accelerate initial setup, but understanding the SCSS layer gives you full control over the output.

Why SCSS Variables Matter for Theming Bootstrap 5

Bootstrap 5 was rebuilt from the ground up with a Sass-first approach. Unlike Bootstrap 3 or 4 where customisation could feel like fighting the framework, Bootstrap 5’s entire design system — from colour palettes to border radii — is exposed as overrideable SCSS variables. This means theming isn’t an afterthought; it’s a first-class feature of the framework.

When you theme Bootstrap with SCSS, you’re not writing override rules that fight specificity wars in your CSS. You’re changing the design tokens at the source, so every component — buttons, navbars, cards, modals — inherits your changes automatically. Adjust $primary once and it propagates to hundreds of components across the entire framework.

The alternative — writing CSS overrides after the fact — creates technical debt. You end up with brittle rules that break when Bootstrap updates, specificity nightmares, and a codebase that’s impossible to hand off to another developer. SCSS variables solve all of that elegantly.

If you’re building out a full UI and want to see how components like cards and navbars respond to theme changes, check out our guides on Bootstrap 5 Card Components: All Variants With Live Examples and Bootstrap 5 Navbar: 8 Customisation Patterns You Need to Know — both demonstrate exactly why having a centralised SCSS variable system pays off at the component level.

Understanding the !default Flag — The Key to Non-Destructive Overrides

The single most important concept when working with Bootstrap’s SCSS is the !default flag. Every variable in Bootstrap’s _variables.scss file is declared with it:

$primary: #0d6efd !default;
$secondary: #6c757d !default;
$success: #198754 !default;

The !default flag tells Sass: “Use this value unless a value has already been assigned to this variable.” This is the mechanism that makes Bootstrap’s entire customisation system work. If you declare $primary: #e63946; in your own file before importing Bootstrap, Sass will use your value and ignore Bootstrap’s default.

This means the rule is simple: declare your overrides before you import Bootstrap. Never touch Bootstrap’s source files — doing so makes updates a nightmare and your changes will be overwritten.

Setting Up Your SCSS File Architecture

A clean SCSS architecture is the foundation of a maintainable Bootstrap 5 project. Here’s the structure we recommend, and the one used in Canvas Template’s SCSS build:

scss/
├── _variables.scss       ← Your Bootstrap overrides go here
├── _custom.scss          ← Your own component styles
├── _utilities.scss       ← Any utility extensions
└── main.scss             ← Entry point — imports everything

Your main.scss entry point should look like this:

// 1. Import your variable overrides FIRST
@import "variables";

// 2. Import Bootstrap source
@import "../node_modules/bootstrap/scss/bootstrap";

// 3. Import your custom styles AFTER
@import "custom";
@import "utilities";

This order is critical. Variables first, Bootstrap second, custom styles third. Deviating from this order is the number one cause of theming issues for developers new to the Bootstrap SCSS workflow.

If you’re using a package manager, install Bootstrap via npm:

npm install bootstrap@5

If you’re working from a premium template like Canvas, the SCSS architecture is already scaffolded for you — you just need to locate the variables file and start overriding.

Core Bootstrap 5 SCSS Variables You Need to Know

Bootstrap exposes hundreds of variables, but a focused set covers the vast majority of real-world theming needs. Here’s a breakdown of the most impactful categories:

Colour System

// Brand colours
$primary:   #6c63ff;
$secondary: #f72585;
$success:   #2dc653;
$info:      #17a2b8;
$warning:   #ffc107;
$danger:    #e5383b;
$light:     #f8f9fa;
$dark:      #212529;

// Body
$body-bg:    #ffffff;
$body-color: #212529;

Typography

$font-family-sans-serif: 'Inter', system-ui, -apple-system, sans-serif;
$font-size-base:         1rem;      // 16px
$line-height-base:       1.6;
$font-weight-normal:     400;
$font-weight-bold:       700;
$headings-font-weight:   600;
$headings-color:         #1a1a2e;

Spacing

$spacer: 1rem; // Base unit — all spacing utilities derive from this

$spacers: (
  0: 0,
  1: $spacer * .25,
  2: $spacer * .5,
  3: $spacer,
  4: $spacer * 1.5,
  5: $spacer * 3,
  6: $spacer * 4,    // Custom addition
  7: $spacer * 5,    // Custom addition
);

Border Radius

$border-radius:    0.375rem;
$border-radius-sm: 0.25rem;
$border-radius-lg: 0.5rem;
$border-radius-xl: 1rem;    // Bootstrap 5.1+
$border-radius-pill: 50rem;

Shadows

$box-shadow:    0 0.5rem 1rem rgba(0, 0, 0, 0.15);
$box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
$box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);

For a deeper dive into how typography variables interact with Bootstrap’s type scale, see our article on Bootstrap 5 Typography: Fonts, Sizes, and Hierarchy Explained.

Building a Complete Custom Theme — A Practical Example

Let’s build a real dark-mode-friendly theme with a purple primary brand colour. Here’s a complete _variables.scss file you could use as a starting point:

// ============================================================
// CANVAS CUSTOM THEME — _variables.scss
// Override Bootstrap 5 defaults before @import "bootstrap"
// ============================================================

// Colour palette
$primary:        #7c3aed;  // Violet
$secondary:      #06b6d4;  // Cyan
$success:        #10b981;
$warning:        #f59e0b;
$danger:         #ef4444;
$light:          #f1f5f9;
$dark:           #0f172a;

// Body
$body-bg:        #0f172a;
$body-color:     #e2e8f0;

// Typography
$font-family-sans-serif: 'Inter', system-ui, sans-serif;
$font-size-base:          1rem;
$line-height-base:        1.7;
$headings-font-weight:    700;
$headings-color:          #f8fafc;
$lead-font-size:          1.2rem;

// Links
$link-color:              $primary;
$link-hover-color:        lighten($primary, 15%);
$link-decoration:         none;

// Buttons
$btn-border-radius:       0.5rem;
$btn-font-weight:         600;
$btn-padding-y:           0.6rem;
$btn-padding-x:           1.5rem;

// Cards
$card-bg:                 #1e293b;
$card-border-color:       rgba(255, 255, 255, 0.08);
$card-border-radius:      1rem;
$card-box-shadow:         0 4px 24px rgba(0, 0, 0, 0.3);

// Navbar
$navbar-dark-color:       rgba(255, 255, 255, 0.8);
$navbar-dark-hover-color: #ffffff;

// Inputs
$input-bg:                #1e293b;
$input-color:             #e2e8f0;
$input-border-color:      rgba(255, 255, 255, 0.15);
$input-focus-border-color: $primary;
$input-border-radius:     0.5rem;

// Spacing extensions
$spacers: (
  0: 0,
  1: 0.25rem,
  2: 0.5rem,
  3: 1rem,
  4: 1.5rem,
  5: 3rem,
  6: 5rem,
  7: 7rem,
);

This single file controls the entire visual language of your site. Every Bootstrap component will inherit these values when you compile. No specificity fights. No scattered overrides. One source of truth.

SCSS Variables vs CSS Custom Properties — What’s the Difference?

A common point of confusion: Bootstrap 5 generates CSS custom properties (CSS variables) from your SCSS values. So what’s the difference, and when should you use each?

Feature SCSS Variables CSS Custom Properties
Resolved at Compile time Runtime (in the browser)
Can be changed after load No Yes (via JS or media queries)
Works without a build tool No Yes
Supports Sass functions (lighten, darken) Yes No
Best for Full theme control, design tokens Dark mode toggling, runtime theming
Bootstrap 5 generates these from your SCSS? Yes (source) Yes (output)

The practical answer: use SCSS variables for all your design decisions at build time, and use the generated CSS custom properties when you need runtime flexibility — like toggling between a light and dark theme via a JavaScript class swap on <body>.

For example, Bootstrap 5 outputs --bs-primary based on your $primary SCSS variable. You can then override it at runtime:

/* Toggle dark theme at runtime */
body.theme-dark {
  --bs-primary: #7c3aed;
  --bs-body-bg: #0f172a;
  --bs-body-color: #e2e8f0;
}

This gives you the best of both worlds: compile-time consistency and runtime flexibility.

Extending the Theme With Custom Utilities and Maps

Bootstrap 5’s utility API means you can extend the spacing, colour, and display utilities using SCSS maps without writing a single line of manual CSS. This is especially powerful when you’ve added custom colour or spacing values.

To add your custom colours to utility classes like bg-brand or text-brand, extend Bootstrap’s colour map:

// In _variables.scss — BEFORE importing Bootstrap

// Add custom colours to Bootstrap's theme colour map
$custom-colors: (
  "brand":   #7c3aed,
  "accent":  #06b6d4,
  "surface": #1e293b,
);

// Merge with Bootstrap's default theme colours
$theme-colors: map-merge($theme-colors, $custom-colors);

Wait — $theme-colors is defined in Bootstrap’s source, so how can you reference it before importing Bootstrap? You need to import just the functions and variables first:

// main.scss

// Step 1: Import Bootstrap functions first (required for map-merge etc.)
@import "../node_modules/bootstrap/scss/functions";

// Step 2: Import your overrides
@import "variables";

// Step 3: Import Bootstrap variables (so $theme-colors exists)
@import "../node_modules/bootstrap/scss/variables";

// Step 4: Merge your custom map additions
$theme-colors: map-merge($theme-colors, $custom-colors);

// Step 5: Import the rest of Bootstrap
@import "../node_modules/bootstrap/scss/bootstrap";

// Step 6: Your custom styles
@import "custom";

This is the advanced pattern used in production-grade builds. It’s slightly more verbose but gives you full access to Bootstrap’s internal maps while still injecting your own values cleanly. Canvas Template’s SCSS build follows exactly this pattern, which is why theming it is as simple as editing a single variables file.

For those building complete sites from scratch and wanting a head start, How to Customise a Bootstrap 5 HTML Template Without Breaking It covers the broader workflow of working within an existing template structure.

Common SCSS Theming Mistakes and How to Avoid Them

Even experienced developers trip up in predictable ways when theming Bootstrap with SCSS. Here are the most common pitfalls and how to avoid them:

1. Editing Bootstrap’s source files directly. This is the most destructive mistake. When you run npm update bootstrap, all your changes vanish. Always work in your own files.

2. Declaring overrides after importing Bootstrap. The !default flag means Bootstrap won’t pick up variables declared after the import. Order matters absolutely.

3. Using CSS hex values where SCSS colour functions would work. Instead of guessing a lighter shade of your primary, use lighten($primary, 10%) or Bootstrap’s own tint-color() and shade-color() functions for perceptually consistent results.

// Bootstrap 5's built-in colour utilities
$primary-light: tint-color($primary, 40%);   // Mix with white
$primary-dark:  shade-color($primary, 20%);  // Mix with black

4. Not testing across all components. A colour change to $primary affects buttons, badges, alerts, links, focus rings, and more. Always do a full UI review after theme changes, not just a spot check.

5. Forgetting to update the colour contrast. Bootstrap 5 includes a contrast-checking mechanism. If your $primary is too dark or light, white text on a primary button may fail WCAG contrast requirements. Use Bootstrap’s color-contrast() function or test with a contrast checker.

If you’re building a landing page and want to see all of this in practice on a real layout, our guide on How to Build a Landing Page with Bootstrap 5 in Under 1 Hour walks through component assembly with theming in mind from the start.


Frequently Asked Questions

Do I need Node.js and npm to use Bootstrap 5 SCSS variables?

Yes, in most practical workflows you’ll need a Sass compiler, which is typically set up via Node.js using a build tool like Vite, Webpack, or Parcel. You install Bootstrap via npm and import its SCSS source. That said, some tools like CodeKit or Scout-App provide a GUI-based Sass compiler if you prefer to avoid the command line. You cannot use SCSS variable overrides with Bootstrap’s pre-compiled CSS CDN version — that only supports CSS custom property overrides at runtime.

What’s the difference between $primary and --bs-primary in Bootstrap 5?

$primary is a Sass variable resolved at compile time — it’s used to generate all the component styles during your build process. --bs-primary is a CSS custom property that Bootstrap automatically outputs based on your $primary value. The CSS variable can be changed at runtime (for example, for dark mode toggling), but it won’t retroactively update compiled styles. Use SCSS variables for reliable theming; use the CSS variables when you need JavaScript-driven or media-query-driven runtime changes.

Can I add completely new colour variants to Bootstrap’s theme system?

Absolutely. By using map-merge() to extend Bootstrap’s $theme-colors map, you can add custom named colours like brand, accent, or surface. Once added, Bootstrap will automatically generate utility classes like .bg-brand, .text-brand, .btn-brand, and .badge bg-brand for your new colour — exactly as it does for the built-in theme colours. This is one of the most powerful features of Bootstrap 5’s Sass architecture.

How do SCSS variable overrides work in a premium Bootstrap template like Canvas?

Canvas Template ships with a dedicated _variables.scss file where all Bootstrap overrides are pre-organised. To retheme the template, you simply edit that file — changing $primary, $font-family-sans-serif, or any other variable — recompile, and the entire template updates. Because Canvas follows the correct import order (your variables before Bootstrap’s source), changes propagate instantly to all components. You don’t need to hunt through component files or write CSS overrides.

Should I use SCSS maps or individual variable overrides for custom spacing?

Both approaches work, but extending Bootstrap’s $spacers map is cleaner for spacing because Bootstrap’s utility generation is map-driven. If you add entries to $spacers, Bootstrap automatically generates .p-6, .mt-7, .gap-6, and all the corresponding utility classes. Individual variable overrides (like changing $spacer from 1rem to 1.125rem) affect the base unit and scale everything proportionally, which is useful for tighter or more airy designs. In practice, most projects do both: adjust the base $spacer and extend the map with additional steps.


Start Building Smarter With SCSS and Bootstrap 5

Mastering Bootstrap 5 SCSS variables transforms how you build websites. Instead of wrestling with CSS specificity and scattered overrides, you’re working with a coherent design system that scales from a single component to an entire product. Every minute invested in setting up your SCSS architecture correctly saves hours of debugging and refactoring later.

If you want to skip the scaffolding and start with a professionally structured Bootstrap 5 SCSS codebase out of the box, Canvas Template is built exactly this way. The SCSS architecture follows everything covered in this guide — correct import order, a pre-populated variables file, and component styles that inherit theme changes automatically. It’s available on ThemeForest and used by thousands of developers and agencies worldwide.

If you’d rather generate a themed Bootstrap 5 site quickly without touching code at all, CanvasBuilder — the AI-powered website builder — handles layout and initial styling through a visual interface, giving you a clean starting point you can then take further with your own SCSS customisations.

Explore Canvas Template on ThemeForest →
Build with CanvasBuilder AI →

Skip the setup — build it free

Spin up a complete Bootstrap 5 site, blog included, with Canvas Builder. No coding, no cost.

Share:
Canvas Team
Canvas Team

Tutorials and tips for building beautiful Bootstrap 5 websites with the Canvas HTML Template and Canvas Builder.

More from the Canvas Blog