The Utility API

Every utility class TrailMap uses — .mt-3, .text-primary, .d-flex, .rounded — isn't written out by hand in Bootstrap's source as hundreds of individual CSS rules. It's generated from a single Sass map called $utilities, where each entry describes one utility group: what the class prefix is, which CSS property it sets, and what values it accepts. This generation system is what Bootstrap calls the Utility API, and once you can read that map, you can add your own utilities or delete ones TrailMap never uses — all without writing a single hand-rolled CSS rule.

How the Utility API Works

Simplified, the entry that produces every .text-* color class looks like this inside Bootstrap's own _utilities.scss:

"color": (
  property: color,
  class: text,
  values: map-merge(
    $theme-colors,
    ("body": $body-color)
  )
)

Read as: "for the utility group named color, set the CSS property color, using the class prefix text-, for every value in $theme-colors." Bootstrap loops over that one entry and emits .text-primary, .text-secondary, .text-success, and so on — one map entry, dozens of generated classes.

Adding a Custom Utility

To add a class Bootstrap doesn't ship, TrailMap merges its own entry into the map using Sass's map-merge, in the same trailmap-custom.scss entry file from the previous lesson — variables and utility additions live before the same @import "bootstrap/scss/bootstrap" line:

// trailmap-custom.scss
$primary: #2c7a4b;

// add a letter-spacing utility Bootstrap doesn't ship
$utilities: map-merge(
  $utilities,
  (
    "letter-spacing": (
      property: letter-spacing,
      class: ls,
      values: (
        0: 0,
        1: .05em,
        2: .1em
      )
    )
  )
);

@import "bootstrap/scss/bootstrap";

That produces three new classes — .ls-0, .ls-1, .ls-2 — usable exactly like any built-in utility:

<span class="badge bg-primary ls-2">NEW TRAIL</span>

Removing Utilities You Never Use

The full utility set compiles into a lot of CSS. If TrailMap never uses Bootstrap's float utilities (.float-start, .float-end) because everything is laid out with flex and grid, they can be deleted from the map entirely before compiling, shrinking the final CSS file:

// remove utility groups TrailMap never uses
$utilities: map-remove($utilities, "float");

@import "bootstrap/scss/bootstrap";
Note: Removing a utility group means those classes no longer exist in the compiled CSS at all. Double-check the group isn't used somewhere in TrailMap's markup before deleting it, or those elements will silently lose their styling.

Worked Example: A .rounded-xl Utility for TrailMap Cards

Bootstrap's built-in .rounded utilities top out at a fairly modest radius. TrailMap's trail-photo cards want a noticeably rounder corner, so instead of writing one-off CSS, the border-radius group gets an extra value merged in:

// extend the existing "border-radius" utility group
$utilities: map-merge(
  $utilities,
  (
    "rounded": map-merge(
      map-get($utilities, "rounded"),
      (
        values: map-merge(
          map-get(map-get($utilities, "rounded"), "values"),
          ("xl": 1rem)
        )
      )
    )
  )
);

@import "bootstrap/scss/bootstrap";
<div class="card rounded-xl">
  <img src="eagle-ridge.jpg" class="card-img-top rounded-xl" alt="Eagle Ridge Trail">
  <div class="card-body">
    <h5 class="card-title">Eagle Ridge Trail</h5>
  </div>
</div>

.rounded-xl now works anywhere .rounded or .rounded-lg would — it's a real, ordinary utility class, generated the exact same way Bootstrap generates its own.

Next: The Utility API changes what classes exist. Color Modes (Dark Mode) covers a runtime switch that re-themes every existing class at once.