Skip to content

Design Tokens → Bootstrap Variables

When to Use

Use this after identifying design tokens to map colors, typography, spacing, borders, shadows, and breakpoints to Bootstrap's SCSS variable system.

  • You've identified design tokens (colors, typography, spacing, etc.) using the Design System Recognition Guide
  • You need to map these tokens to Bootstrap's SCSS variable system
  • You're implementing a design system's foundation layer in Bootstrap

Color Tokens

Decision Table: Color Token Mapping

Design System Token Type Bootstrap Variable Bootstrap Map CSS Custom Property
Primary/secondary colors $primary, $secondary $theme-colors --bs-primary
Brand colors (additional) N/A (extend map) Add to $theme-colors --bs-brand-color
Gray/neutral scales $gray-100 through $gray-900 $grays --bs-gray-100
Semantic colors $success, $danger, $warning, $info $theme-colors --bs-success
Text colors $body-color, $headings-color N/A --bs-body-color
Background colors $body-bg, $secondary-bg N/A --bs-body-bg
Border colors $border-color, $border-color-translucent N/A --bs-border-color

Pattern: Color Token Implementation

// 1. Override Bootstrap color variables BEFORE importing Bootstrap
$primary: #194582;        // Design system primary
$secondary: #6c757d;      // Design system secondary

// 2. Extend theme colors map with brand colors
$theme-colors: map-merge($theme-colors, (
  "brand-blue": #0066cc,  // Additional brand color
  "brand-teal": #20c997   // Additional brand color
));

// 3. Import Bootstrap to apply changes
@import "bootstrap";

Common Mistakes (WHY Each Matters)

  • Setting colors after Bootstrap import - WHY: Bootstrap variables use !default flag, which means "only set if not already defined." If you import Bootstrap first, your colors are ignored because Bootstrap already defined them.
  • Using hex codes directly in components - WHY: Hardcoded colors break when design system changes. Using $primary means ONE update changes all instances; hardcoded #0066cc requires finding/replacing everywhere.
  • Ignoring RGB variants - WHY: Bootstrap auto-generates --bs-primary-rgb (e.g., 0, 102, 204) for rgba operations. You can use rgba(var(--bs-primary-rgb), 0.5) for opacity without manually extracting RGB values.
  • Not considering dark mode - WHY: Bootstrap 5.3+ supports [data-bs-theme=dark] attribute for automatic color inversion. If you hardcode colors, dark mode won't work without manual overrides.

See Also

Typography Tokens

Decision Table: Typography Mapping

Design System Token Bootstrap Variable Bootstrap Map Generated Utility
Base font size $font-size-base N/A N/A (affects rem calc)
Font family (body) $font-family-base N/A --bs-body-font-family
Font family (headings) $headings-font-family N/A N/A (inherits base)
Font family (mono) $font-family-monospace N/A --bs-font-monospace
Font size scale $font-size-sm, $font-size-lg N/A .fs-* utilities
Heading sizes $h1-font-size through $h6-font-size N/A <h1> through <h6>
Display sizes N/A $display-font-sizes .display-1 through .display-6
Line heights $line-height-base, $line-height-sm, $line-height-lg N/A N/A (applied in components)
Font weights $font-weight-lighter, $font-weight-normal, $font-weight-bold N/A .fw-light, .fw-normal, .fw-bold

Pattern: Typography Token Implementation

// 1. Set base typography before Bootstrap import
$font-family-base: system-ui, -apple-system, "Segoe UI", sans-serif;
$font-size-base: 1rem;      // 16px base
$line-height-base: 1.5;

// 2. Override heading sizes if design system differs by ≥6px
$h1-font-size: $font-size-base * 2.5;  // 40px
$h2-font-size: $font-size-base * 2;    // 32px

// 3. Extend display sizes map if needed
$display-font-sizes: map-merge($display-font-sizes, (
  7: 5rem,  // Additional large display size
));

@import "bootstrap";

Common Mistakes (WHY Each Matters)

  • Hardcoding font families in components - WHY: font-family: "Helvetica Neue", sans-serif scattered throughout code means changing the typeface requires global find/replace. Using $font-family-base means ONE variable change updates entire site.
  • Using numeric font weights - WHY: font-weight: 700 breaks when font changes (new font's bold might be 600 or 800). Using $font-weight-bold ensures consistency and allows global weight adjustments.
  • Not accounting for rem-based scaling - WHY: $font-size-base: 1rem affects ALL rem calculations. If you change it to 0.875rem, all spacing/sizing based on rem scales proportionally. Hardcoded rem values (padding: 1rem) don't respect this relationship.
  • Ignoring responsive fluid typography - WHY: Bootstrap's RFS (Responsive Font Size) system automatically scales typography based on viewport width. Hardcoded sizes (font-size: 24px) stay static on mobile, while $h1-font-size scales down for better mobile UX.

See Also

Spacing Tokens

Decision Table: Spacing System Mapping

Design System Scale Bootstrap Default Bootstrap Variable Bootstrap Map Key Generated Utilities
0px 0px N/A 0: 0 .m-0, .p-0
4px 4px (0.25rem) $spacer * 0.25 1: $spacer * 0.25 .m-1, .p-1
8px 8px (0.5rem) $spacer * 0.5 2: $spacer * 0.5 .m-2, .p-2
16px 16px (1rem) $spacer 3: $spacer .m-3, .p-3
24px 24px (1.5rem) $spacer * 1.5 4: $spacer * 1.5 .m-4, .p-4
48px 48px (3rem) $spacer * 3 5: $spacer * 3 .m-5, .p-5
Custom values N/A N/A Add to $spacers .m-custom, .p-custom

Pattern: Spacing Token Implementation

ACCOMMODATE Strategy (≤6px difference from Bootstrap defaults):

// Use Bootstrap defaults directly - no override needed
@import "bootstrap";

EXTEND Strategy (add missing micro-spacing):

$spacers: map-merge($spacers, (
  "3xs": 2px,   // Add 2px micro-spacing
  "2xs": 6px,   // Add 6px spacing
));

@import "bootstrap";

CUSTOMIZE Strategy (≥6px systematic differences):

// Replace Bootstrap's spacing scale entirely
$spacers: (
  0: 0,
  xs: 8px,
  sm: 24px,    // 8px different from Bootstrap's 24px
  md: 32px,
  lg: 40px,
  xl: 64px,
);

@import "bootstrap";

Common Mistakes (WHY Each Matters)

  • Mixing rem and px inconsistently - WHY: Bootstrap uses rem for accessibility (respects user font size preferences). Mixing padding: 16px with margin: 1rem breaks proportional scaling when users change browser font size.
  • Not using spacing variables in components - WHY: padding: 24px is inflexible; padding: $spacer * 1.5 or padding: map-get($spacers, 4) ties to design system. When spacing scale changes, variable-based spacing updates automatically.
  • Overriding when accommodation works - WHY: Overriding 16px with custom 14px (2px difference) creates maintenance burden for minimal visual gain. The 6px threshold (see Section 1.1: The 6-Pixel Rule) balances design precision vs. system compatibility.
  • Forgetting negative margins - WHY: Bootstrap auto-generates .m-n1, .m-n2, etc. for negative margins from $spacers map. Using custom margin: -8px misses these utilities and breaks spacing consistency.

See Also

Surface Tokens

Decision Table: Border, Radius, Shadow Mapping

Design System Token Bootstrap Variable Bootstrap Map Generated Utility
Border width $border-width N/A .border, .border-2
Border color $border-color N/A --bs-border-color
Border radius (base) $border-radius N/A .rounded
Border radius (small) $border-radius-sm N/A .rounded-sm
Border radius (large) $border-radius-lg N/A .rounded-lg
Border radius (pill) $border-radius-pill N/A .rounded-pill
Box shadow (base) $box-shadow N/A .shadow
Box shadow (small) $box-shadow-sm N/A .shadow-sm
Box shadow (large) $box-shadow-lg N/A .shadow-lg
Elevation system N/A (CREATE) Custom map Custom utilities

Pattern: Surface Token Implementation

// 1. Override border and radius tokens
$border-width: 1px;
$border-radius: 0.375rem;   // 6px
$border-radius-sm: 0.25rem; // 4px
$border-radius-lg: 0.5rem;  // 8px

// 2. Override shadow tokens
$box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
$box-shadow-sm: 0 0.0625rem 0.125rem rgba(0, 0, 0, 0.075);
$box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175);

@import "bootstrap";

Common Mistakes

  • Not considering component-specific overrides - Some components have their own radius variables (e.g., $btn-border-radius)
  • Hardcoding shadow values - Use Bootstrap's shadow variables for consistency
  • Ignoring elevation systems - If design system has numbered elevation (1-5), use CREATE category (see Section 1.2: CREATE Category)
  • Mixing border utilities without considering responsive needs - Border utilities support responsive variants

See Also

Breakpoint Tokens

Decision Table: Responsive Breakpoint Mapping

Design System Breakpoint Bootstrap Default Bootstrap Map Key Media Query Container Max Width
Mobile (small) 576px sm: 576px @media (min-width: 576px) 540px
Tablet 768px md: 768px @media (min-width: 768px) 720px
Desktop (small) 992px lg: 992px @media (min-width: 992px) 960px
Desktop (large) 1200px xl: 1200px @media (min-width: 1200px) 1140px
Desktop (extra large) 1400px xxl: 1400px @media (min-width: 1400px) 1320px
Custom breakpoint N/A Add to $grid-breakpoints Custom mixin Add to $container-max-widths

Pattern: Breakpoint Implementation

// Override Bootstrap breakpoints if design system differs significantly
$grid-breakpoints: (
  xs: 0,
  sm: 576px,
  md: 768px,
  lg: 1024px,   // Custom breakpoint (vs Bootstrap 992px)
  xl: 1280px,   // Custom breakpoint (vs Bootstrap 1200px)
  xxl: 1440px   // Custom breakpoint (vs Bootstrap 1400px)
);

// Update container max widths to match
$container-max-widths: (
  sm: 540px,
  md: 720px,
  lg: 960px,
  xl: 1200px,
  xxl: 1320px
);

@import "bootstrap";

Common Mistakes

  • Using CSS variables in media queries - Media queries cannot use CSS variables (CSS spec limitation)
  • Not updating container widths - When changing breakpoints, update $container-max-widths too
  • Breaking Bootstrap grid - Changing breakpoints affects ALL responsive utilities (.col-md-*, .d-lg-block, etc.)
  • Forgetting mobile-first approach - Bootstrap uses min-width media queries (mobile-first)

See Also

See Also