Episode 1 — Fundamentals / 1.11 — CSS Frameworks TailwindCSS and Bootstrap

1.11.g — Customizing Bootstrap

In one sentence: Real-world Bootstrap projects require customization — SASS variable overrides, cherry-picking components, and the utilities API let you match your brand, reduce bundle size, and extend the framework without fighting it.

Navigation: ← 1.11.f — Bootstrap Grid & Components · 1.11.h — TailwindCSS vs Bootstrap →


1. Why customize?

Out-of-the-box Bootstrap is fast to prototype with, but shipping a production site with default styles has problems:

  • Generic look — users and designers notice "the Bootstrap look"
  • Bundle size — the full CSS is ~230 KB uncompressed; you likely use < 30% of it
  • Brand mismatch — your brand colors, fonts, and spacing differ from Bootstrap defaults
  • Specificity conflicts — overriding deeply nested selectors with custom CSS leads to brittle code

Bootstrap was designed to be customized via SASS. Working with the system is easier than fighting it.


2. SASS variable overrides

The most powerful customization technique: override variables before the import.

// custom.scss

// 1. Override variables
$primary:          #6366f1;      // Indigo instead of blue
$secondary:        #64748b;
$success:          #22c55e;
$danger:           #ef4444;
$font-family-base: "Inter", system-ui, sans-serif;
$font-size-base:   1rem;
$border-radius:    0.75rem;
$body-bg:          #f8fafc;
$body-color:       #1e293b;

// 2. Import Bootstrap (uses your overrides)
@import "bootstrap/scss/bootstrap";

Why before? Bootstrap uses !default on all variables — this means "use this value unless the variable is already defined." By defining yours first, Bootstrap picks up your values.

Common variables to override

VariableDefaultControls
$primary#0d6efdPrimary brand color (buttons, links, active states)
$body-bg#fffPage background
$body-color#212529Default text color
$font-family-basesystem stackBase font
$font-size-base1remRoot font size
$border-radius0.375remDefault rounding
$spacer1remBase spacing unit (generates margin/padding utilities)
$grid-gutter-width1.5remGutter between grid columns
$enable-roundedtrueToggle border-radius globally
$enable-shadowsfalseToggle box shadows on components

3. Custom color themes

Bootstrap's color map generates variants for every component:

$theme-colors: (
  "primary":   #6366f1,
  "secondary": #64748b,
  "success":   #22c55e,
  "info":      #06b6d4,
  "warning":   #f59e0b,
  "danger":    #ef4444,
  "light":     #f1f5f9,
  "dark":      #0f172a,
);

@import "bootstrap/scss/bootstrap";

This automatically generates .btn-primary, .bg-primary, .text-primary, .alert-primary, .badge-bg-primary, etc. — all using your custom color.

Adding new theme colors

$custom-colors: (
  "brand": #8b5cf6,
  "accent": #f97316,
);

$theme-colors: map-merge($theme-colors, $custom-colors);

Now btn-brand, bg-accent, text-brand all work automatically.


4. Cherry-picking components

Don't need carousels, toasts, or popovers? Import only what you use:

// custom.scss — selective imports

// Required core
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
@import "bootstrap/scss/maps";
@import "bootstrap/scss/mixins";
@import "bootstrap/scss/root";

// Reset and base
@import "bootstrap/scss/reboot";
@import "bootstrap/scss/type";

// Layout
@import "bootstrap/scss/containers";
@import "bootstrap/scss/grid";

// Components you actually use
@import "bootstrap/scss/buttons";
@import "bootstrap/scss/card";
@import "bootstrap/scss/navbar";
@import "bootstrap/scss/nav";
@import "bootstrap/scss/forms";
@import "bootstrap/scss/modal";
@import "bootstrap/scss/alert";

// Utilities
@import "bootstrap/scss/utilities";
@import "bootstrap/scss/utilities/api";

Result: A CSS bundle with only the components your project needs — often 50–70% smaller than the full library.


5. Adding custom components alongside Bootstrap

Write custom components that use Bootstrap's variables and mixins for consistency:

// _custom-components.scss

.feature-card {
  background: $white;
  border-radius: $border-radius-lg;
  padding: $spacer * 2;
  box-shadow: $box-shadow;
  transition: transform 0.2s ease;

  &:hover {
    transform: translateY(-4px);
    box-shadow: $box-shadow-lg;
  }

  &__title {
    font-size: $h5-font-size;
    font-weight: $font-weight-bold;
    color: $primary;
    margin-bottom: $spacer * 0.5;
  }

  &__text {
    color: $body-color;
    font-size: $font-size-base;
  }
}

By using Bootstrap's variables ($spacer, $primary, $border-radius-lg), your custom components stay in sync with the theme.


6. The utilities API

Bootstrap 5 lets you generate custom utility classes or modify existing ones:

$utilities: map-merge(
  $utilities,
  (
    "cursor": (
      property: cursor,
      class: cursor,
      values: auto pointer grab not-allowed,
    ),
    "opacity": map-merge(
      map-get($utilities, "opacity"),
      (
        values: map-merge(
          map-get(map-get($utilities, "opacity"), "values"),
          (10: .1, 90: .9),
        ),
      ),
    ),
  )
);

This generates .cursor-pointer, .cursor-grab, .opacity-10, .opacity-90 — utility classes that follow Bootstrap's naming conventions.

Utilities API options

OptionWhat it does
propertyThe CSS property (cursor, background-color, etc.)
classThe class prefix (.cursor-*)
valuesMap or list of values to generate
responsivetrue → generate responsive variants (cursor-md-pointer)
stateGenerate state variants (e.g., hover)

7. Using CSS custom properties

Bootstrap 5 exposes many values as CSS custom properties (aka CSS variables):

:root {
  --bs-primary: #0d6efd;
  --bs-body-bg: #fff;
  --bs-body-color: #212529;
  --bs-border-radius: 0.375rem;
}

You can override these at runtime without recompiling SASS:

[data-theme="dark"] {
  --bs-body-bg: #0f172a;
  --bs-body-color: #e2e8f0;
}

Limitation: Not all Bootstrap internals use CSS variables yet — SASS overrides remain the most complete approach.


8. Reducing bundle size — checklist

TechniqueSavings
Cherry-pick imports50–70% smaller CSS
PurgeCSS (via PostCSS)Removes unused classes from final CSS
Skip JS componentsDon't include bootstrap.bundle.js if you only need styles
Minify + gzipStandard compression; reduces ~230 KB → ~25 KB gzipped
Use CDNLeverages browser cache from other sites using the same CDN

9. Key takeaways

  1. Override SASS variables before importing Bootstrap — !default makes this work.
  2. Use the $theme-colors map to set your brand colors; Bootstrap generates all variant classes automatically.
  3. Cherry-pick imports to ship only the components you use — dramatically reduces bundle size.
  4. Custom components should use Bootstrap's variables and mixins for theme consistency.
  5. The utilities API lets you generate custom utility classes that follow Bootstrap conventions.
  6. CSS custom properties allow runtime theme changes without recompilation.

Explain-It Challenge

Explain without notes:

  1. Why Bootstrap variables use !default and why your overrides must come before the import.
  2. How cherry-picking SASS imports reduces the production CSS bundle size.
  3. One use case for Bootstrap's utilities API — what can you create with it?

Navigation: ← 1.11.f — Bootstrap Grid & Components · 1.11.h — TailwindCSS vs Bootstrap →