Utility API

The utility API is a Sass-based tool to generate utility classes.

Bootstrap utilities are generated with our utility API and can be used to modify or extend our default set of utility classes via Sass. Our utility API is based on a series of Sass maps and functions for generating families of classes with various options. If you’re unfamiliar with Sass maps, read up on the official Sass docs to get started.

The $utilities map contains all our utilities and is later merged with your custom $utilities map, if present. The utility map contains a keyed list of utility groups which accept the following options:

OptionTypeDefault valueDescription
propertyRequiredName of the property. Can be a string, a space-separated list of strings (e.g., horizontal paddings or margins), or a map of property-to-default-value pairs (see Property-Value Mapping).
valuesRequiredList of values, or a map if you don’t want the class name to be the same as the value. If null is used as map key, class is not prepended to the class name.
selectorOptionalclassType of CSS selector in the generated CSS ruleset. Can be class, attr-starts, or attr-includes.
child-selectorOptionalnullA child/descendant selector appended to the utility’s selector, wrapped in :where() for zero specificity. Use to target children instead of the element itself.
classOptionalnullName of the generated class. If not provided and property is an array of strings, class will default to the first element of the property array. If not provided and property is a string, the values keys are used for the class names.
variablesOptionalnullList or map of CSS custom properties to generate within each utility class. When a list, each variable receives the utility value. When a map, the provided static values are used.
stateOptionalnullList of pseudo-class variants (e.g., :hover or :focus) to generate as prefix-style classes.
responsiveOptionalfalseBoolean indicating if responsive classes should be generated.
importantOptionalfalseBoolean indicating if !important should be added to the utility’s CSS rules.
printOptionalfalseBoolean indicating if print classes need to be generated.
darkOptionalfalseBoolean indicating if dark: prefixed classes should be generated, scoped to @media (prefers-color-scheme: dark).
enabledOptionaltrueSet to false to suppress output for a utility while keeping its definition in the $utilities map.

API explained

All utility variables are added to the $utilities variable within our _utilities.scss stylesheet, which is a giant, nested Sass map. Each group of utility classes are generated from a block that looks something like this:

SCSS
$utilities: (
  "opacity": (
    property: opacity,
    values: (
      0: 0,
      25: .25,
      50: .5,
      75: .75,
      100: 1,
    )
  )
);

Which outputs the following:

CSS
.opacity-0 { opacity: 0; }
.opacity-25 { opacity: .25; }
.opacity-50 { opacity: .5; }
.opacity-75 { opacity: .75; }
.opacity-100 { opacity: 1; }

property

Required

The property key must be set for any utility, and it must contain a valid CSS property. This property is used in the generated utility’s ruleset. When the class key is omitted, it also serves as the default class name. Consider the text-decoration utility:

SCSS
$utilities: (
  "text-decoration": (
    property: text-decoration,
    values: none underline line-through
  )
);

Output:

CSS
.text-decoration-none { text-decoration: none; }
.text-decoration-underline { text-decoration: underline; }
.text-decoration-line-through { text-decoration: line-through; }

Property value mapping

The property key also accepts a map of CSS property names to default values. This lets a single utility class set multiple properties at once, and lets values override individual properties when the value is itself a map.

When a property's default value in the map is null, the utility value is used directly for that property. When a default value is set, it is used unless the utility's value map contains a matching key.

A common pattern is pairing a CSS custom property (which receives the utility value) with a regular property that references it:

SCSS
$utilities: (
  "bg-color": (
    property: (
      "--bg": null,
      "background-color": var(--bg)
    ),
    class: bg,
    values: (
      primary: var(--blue-500),
      danger:  var(--red-500),
    )
  )
);

Output:

CSS
.bg-primary { --bs-bg: var(--bs-blue-500); background-color: var(--bs-bg); }
.bg-danger  { --bs-bg: var(--bs-red-500);  background-color: var(--bs-bg); }

Source CSS variables are used without a --bs- prefix, which is added in our compiled CSS with our PostCSS plugin.

You can also set per-value overrides. When values contains a nested map whose keys match properties in the property map, those values are used for the matching property:

SCSS
$utilities: (
  "text-size": (
    property: (
      "font-size":   1rem,
      "line-height": 1.5
    ),
    class: text,
    values: (
      "sm": ("font-size": .875rem, "line-height": 1.25),
      "lg": ("font-size": 1.25rem, "line-height": 1.75),
    )
  )
);

Output:

CSS
.text-sm { font-size: .875rem; line-height: 1.25; }
.text-lg { font-size: 1.25rem; line-height: 1.75; }

values

Required

Use the values key to specify which values for the specified property should be used in the generated class names and rules. Can be a list or map (set in the utilities or in a Sass variable).

As a list, like with text-decoration utilities:

SCSS
values: none underline line-through

As a map, like with opacity utilities:

SCSS
values: (
  0: 0,
  25: .25,
  50: .5,
  75: .75,
  100: 1,
)

As a Sass variable that sets the list or map, as in our position utilities:

SCSS
values: $position-values

selector

OptionDescription
No selectorDefaults to "class"
"class"Matches elements with a class that matches the specified value
"attr-starts"Matches elements with a class that starts with a string
"attr-includes"Matches elements with a class that includes a string

Use the selector option to change the CSS selector used in the generated CSS ruleset. The default option is to generate a class selector. When using an attribute selector—either attr-starts or attr-includesthe class option is required. We use these internally to simplify the construction of other utilities.

For attribute selectors, you’ll most likely want the attr-includes as the starting attribute selector in CSS applies to the entire string of classes in an attribute’s value. For example, [class^="name"] would not match class="example name".

As an example, to change from .ratio-* to [class*="ratio-"]:

SCSS
$utilities: (
  // Create an attribute selector utility
  "aspect-ratio-attr": (
    selector: "attr-includes",
    class: "ratio-",
    property: aspect-ratio,
    values: var(--ratio),
  ),
  // Create a CSS variable utility that matches the attribute selector utility
  "aspect-ratio": (
    property: --ratio,
    class: ratio,
    values: $aspect-ratios
  ),
);

Note that the --ratio custom property will automatically be prefixed to --bs-ratio in the compiled CSS via PostCSS.

Which outputs the following:

CSS
[class*="ratio-"] { aspect-ratio: var(--bs-ratio); }
.ratio-auto { --bs-ratio: auto; }
.ratio-1x1 { --bs-ratio: 1 / 1; }
.ratio-4x3 { --bs-ratio: 4 / 3; }
.ratio-16x9 { --bs-ratio: 16 / 9; }
.ratio-21x9 { --bs-ratio: 21 / 9; }

child-selector

Use the child-selector option to apply a CSS property to children of the element with the utility class rather than the element itself. The generated selector is wrapped in :where() for zero specificity, making it easy to override. This powers our built-in space-x/y and divide-x/y utilities.

For example, our space-x utility uses child-selector to add horizontal spacing between direct children:

SCSS
$utilities: (
  "space-x": (
    property: margin-inline-end,
    class: space-x,
    child-selector: "> :not(:last-child)",
    values: (
      1: .25rem,
      2: .5rem,
      3: 1rem,
    )
  )
);

Output:

CSS
:where(.space-x-1 > :not(:last-child)) { margin-inline-end: .25rem; }
:where(.space-x-2 > :not(:last-child)) { margin-inline-end: .5rem; }
:where(.space-x-3 > :not(:last-child)) { margin-inline-end: 1rem; }

The child-selector value can be any valid CSS selector. For example, you could create a striped row utility using :nth-child():

SCSS
$utilities: (
  "striped-bg": (
    property: background-color,
    class: striped,
    child-selector: "> :nth-child(odd)",
    values: (
      null: var(--bs-bg-1),
    )
  )
);

Output:

CSS
:where(.striped > :nth-child(odd)) { background-color: var(--bs-bg-1); }

Or target all direct children with > *:

SCSS
$utilities: (
  "child-rounded": (
    property: border-radius,
    class: child-rounded,
    child-selector: "> *",
    values: (
      null: var(--bs-border-radius),
      0: 0,
    )
  )
);

Output:

CSS
:where(.child-rounded > *) { border-radius: var(--bs-border-radius); }
:where(.child-rounded-0 > *) { border-radius: 0; }

class

Use the class option to change the class prefix used in the compiled CSS. For example, to change from .opacity-* to .o-*:

SCSS
$utilities: (
  "opacity": (
    property: opacity,
    class: o,
    values: (
      0: 0,
      25: .25,
      50: .5,
      75: .75,
      100: 1,
    )
  )
);

Output:

CSS
.o-0 { opacity: 0; }
.o-25 { opacity: .25; }
.o-50 { opacity: .5; }
.o-75 { opacity: .75; }
.o-100 { opacity: 1; }

If class: null, generates classes for each of the values keys:

SCSS
$utilities: (
  "visibility": (
    property: visibility,
    class: null,
    values: (
      visible: visible,
      invisible: hidden,
    )
  )
);

Output:

CSS
.visible { visibility: visible; }
.invisible { visibility: hidden; }

Variables

Use the variables option to generate CSS custom properties within each utility class’s ruleset. The value can be either a list or a map.

When variables is a list, each variable receives the current utility value:

SCSS
$utilities: (
  "link-opacity": (
    property: color,
    class: link,
    variables: link-color,
    values: (
      10: 10%,
      50: 50%,
      100: 100%,
    )
  ),
);

Output:

CSS
.link-10 { --bs-link-color: 10%; color: 10%; }
.link-50 { --bs-link-color: 50%; color: 50%; }
.link-100 { --bs-link-color: 100%; color: 100%; }

When variables is a map, the provided static values are used on every generated class:

SCSS
$utilities: (
  "link-underline": (
    property: text-decoration-color,
    class: link-underline,
    variables: (
      "link-underline-opacity": 1
    ),
    values: (...)
  )
);

Output:

CSS
.link-underline-primary {
  --bs-link-underline-opacity: 1;
  text-decoration-color: ...;
}

States

Use the state option to generate pseudo-class variations. Example pseudo-classes are :hover and :focus. When a list of states are provided, classnames are created for that pseudo-class with a prefix-style syntax matching the responsive prefix pattern. For example, to change opacity on hover, add state: hover and you’ll get .hover\:opacity:hover in your compiled CSS.

Need multiple pseudo-classes? Use a space-separated list of states: state: hover focus.

SCSS
$utilities: (
  "opacity": (
    property: opacity,
    class: opacity,
    state: hover,
    values: (
      0: 0,
      25: .25,
      50: .5,
      75: .75,
      100: 1,
    )
  )
);

Output:

CSS
.hover\:opacity-0:hover { opacity: 0; }
.hover\:opacity-25:hover { opacity: .25; }
.hover\:opacity-50:hover { opacity: .5; }
.hover\:opacity-75:hover { opacity: .75; }
.hover\:opacity-100:hover { opacity: 1; }

Responsive

Add the responsive boolean to generate responsive utilities (e.g., .md\:opacity-25) across all breakpoints.

SCSS
$utilities: (
  "opacity": (
    property: opacity,
    responsive: true,
    values: (
      0: 0,
      25: .25,
      50: .5,
      75: .75,
      100: 1,
    )
  )
);

Output:

CSS
.opacity-0 { opacity: 0; }
.opacity-25 { opacity: .25; }
.opacity-50 { opacity: .5; }
.opacity-75 { opacity: .75; }
.opacity-100 { opacity: 1; }

@media (min-width: 576px) {
  .sm\:opacity-0 { opacity: 0; }
  .sm\:opacity-25 { opacity: .25; }
  .sm\:opacity-50 { opacity: .5; }
  .sm\:opacity-75 { opacity: .75; }
  .sm\:opacity-100 { opacity: 1; }
}

@media (min-width: 768px) {
  .md\:opacity-0 { opacity: 0; }
  .md\:opacity-25 { opacity: .25; }
  .md\:opacity-50 { opacity: .5; }
  .md\:opacity-75 { opacity: .75; }
  .md\:opacity-100 { opacity: 1; }
}

@media (min-width: 1024px) {
  .lg\:opacity-0 { opacity: 0; }
  .lg\:opacity-25 { opacity: .25; }
  .lg\:opacity-50 { opacity: .5; }
  .lg\:opacity-75 { opacity: .75; }
  .lg\:opacity-100 { opacity: 1; }
}

@media (min-width: 1280px) {
  .xl\:opacity-0 { opacity: 0; }
  .xl\:opacity-25 { opacity: .25; }
  .xl\:opacity-50 { opacity: .5; }
  .xl\:opacity-75 { opacity: .75; }
  .xl\:opacity-100 { opacity: 1; }
}

@media (min-width: 1536px) {
  .\32 xl\:opacity-0 { opacity: 0; }
  .\32 xl\:opacity-25 { opacity: .25; }
  .\32 xl\:opacity-50 { opacity: .5; }
  .\32 xl\:opacity-75 { opacity: .75; }
  .\32 xl\:opacity-100 { opacity: 1; }
}

Print

Enabling the print option will also generate utility classes for print, which are only applied within the @media print { ... } media query.

SCSS
$utilities: (
  "opacity": (
    property: opacity,
    print: true,
    values: (
      0: 0,
      25: .25,
      50: .5,
      75: .75,
      100: 1,
    )
  )
);

Output:

CSS
.opacity-0 { opacity: 0; }
.opacity-25 { opacity: .25; }
.opacity-50 { opacity: .5; }
.opacity-75 { opacity: .75; }
.opacity-100 { opacity: 1; }

@media print {
  .print\:opacity-0 { opacity: 0; }
  .print\:opacity-25 { opacity: .25; }
  .print\:opacity-50 { opacity: .5; }
  .print\:opacity-75 { opacity: .75; }
  .print\:opacity-100 { opacity: 1; }
}

Dark

Enabling the dark option generates dark: prefixed classes scoped to @media (prefers-color-scheme: dark). This follows the same prefix convention as responsive and print utilities.

SCSS
$utilities: (
  "bg-color": (
    property: background-color,
    class: bg,
    dark: true,
    values: (
      white: #fff,
      black: #000,
    )
  )
);

Output:

CSS
.bg-white { background-color: #fff; }
.bg-black { background-color: #000; }

@media (prefers-color-scheme: dark) {
  .dark\:bg-white { background-color: #fff; }
  .dark\:bg-black { background-color: #000; }
}

Enabled

Set enabled: false to suppress output for a utility without removing it from the $utilities map. This is useful when you want to keep the utility's definition available for reference or downstream map.get() calls, but don't want it to emit any CSS. See Using the API for the load order this example needs.

SCSS
// _my-utilities.scss
@use "sass:map";
@use "bootstrap/scss/config" as *;
@use "bootstrap/scss/utilities";

$utilities: map.merge(
  $utilities,
  (
    "float": map.merge(
      map.get($utilities, "float"),
      (enabled: false),
    ),
  )
);

Importance

Utilities generated by the API no longer include !important by default in v6. This is because we now use CSS layers to ensure utilities override components and modifier classes as intended. You can enable !important on a per-utility basis by setting the important option to true.

SCSS
$utilities: (
  "opacity": (
    property: opacity,
    important: true,
    values: (
      0: 0,
      25: .25,
      50: .5,
      75: .75,
      100: 1,
    )
  )
);

This will generate utilities with !important:

CSS
.opacity-0 { opacity: 0 !important; }
.opacity-25 { opacity: .25 !important; }
.opacity-50 { opacity: .5 !important; }
.opacity-75 { opacity: .75 !important; }
.opacity-100 { opacity: 1 !important; }

Using the API

Now that you’re familiar with how the utilities API works, learn how to add your own custom classes and modify our default utilities.

The examples below use two patterns. Pick the pattern that matches your change.

  • One file—Configure $utilities when you load bootstrap/scss/bootstrap. Use this pattern to add a utility, to replace a utility, or to remove a utility.
  • Two files—Put a map.merge() call in a partial. Then load the partial before bootstrap/scss/bootstrap. Use this pattern when your change must read the default definition. Examples are a new value, a new class prefix, or new responsive variants on a group you keep.

The one-file pattern needs one @use rule:

SCSS
// my-bootstrap.scss
@use "bootstrap/scss/bootstrap" with (
  $utilities: (
    // Your utility groups go here
  )
);

Bootstrap merges your map with map.merge(). This merge is shallow. Your utility group replaces the whole default group with the same key. Supply the property and values keys in every group that emits CSS.

Sass cannot read a module and configure the same module in one file. Some changes therefore need the two-file pattern. The bootstrap/scss/config module declares $utilities. The bootstrap/scss/utilities module fills it with the default definitions. Your partial loads both modules. Then it writes the new map back to the config module.

The partial loads the config module with as *. This avoids a namespace prefix, so you can reference $utilities directly. An assignment to $utilities then writes back to the config module.

For a utility-only build, load bootstrap/scss/utilities and then bootstrap/scss/utilities/api. The API module emits the utility classes and nothing else.

SCSS
// my-bootstrap-utilities.scss
@use "bootstrap/scss/utilities" with (
  $utilities: (
    // Your utility groups go here
  )
);
@use "bootstrap/scss/utilities/api";

Override utilities

Override an existing utility with the same key. Pass the new definition in the $utilities configuration when you load Bootstrap. For example, take our existing overflow utility class definition:

"overflow": (
  property: overflow,
  values: auto hidden visible scroll,
),
"overflow-x": (
  property: overflow-x,
  values: auto hidden visible scroll,
),
"overflow-y": (
  property: overflow-y,
  values: auto hidden visible scroll,
),

If you want to make those responsive, and adjust the values used, you can do this:

SCSS
// my-bootstrap.scss
@use "bootstrap/scss/bootstrap" with (
  $utilities: (
    "overflow": (
      property: overflow,
      responsive: true,
      values: visible hidden scroll auto,
    ),
  )
);

Your group replaces the default group. Keep the property key, or the build stops with an error.

Add utilities

Add a new utility with the $utilities configuration. Bootstrap merges your map on top of its defaults, so supply only the new utilities.

SCSS
// my-bootstrap.scss
@use "bootstrap/scss/bootstrap" with (
  $utilities: (
    "cursor": (
      property: cursor,
      class: cursor,
      responsive: true,
      values: auto pointer grab,
    ),
  )
);

Modify utilities

Modify an existing utility with map.get() and map.merge(). This change reads the default definition, so it needs the two-file pattern. In the example below, we’re adding an additional value to the width utilities:

SCSS
// _my-utilities.scss
@use "sass:map";
@use "bootstrap/scss/config" as *;
// Fills `$utilities` with the default definitions
@use "bootstrap/scss/utilities";

$utilities: map.merge(
  $utilities,
  (
    "width": map.merge(
      map.get($utilities, "width"),
      (
        values: map.merge(
          map.get(map.get($utilities, "width"), "values"),
          (20: 20%),
        ),
      ),
    ),
  )
);

Load your partial first. Then load Bootstrap:

SCSS
// my-bootstrap.scss
@use "my-utilities";
@use "bootstrap/scss/bootstrap";

Enable responsive

You can enable responsive classes for an existing set of utilities that are not currently responsive by default. This change keeps the default definition, so it needs the two-file pattern. For example, to make the border classes responsive:

SCSS
// _my-utilities.scss
@use "sass:map";
@use "bootstrap/scss/config" as *;
@use "bootstrap/scss/utilities";

$utilities: map.merge(
  $utilities,
  (
    "border": map.merge(
      map.get($utilities, "border"),
      (responsive: true),
    ),
  )
);

Load this partial before bootstrap/scss/bootstrap, as shown above. You can also restate the whole group with the one-file pattern. See Override utilities.

This will now generate responsive variations of .border and .border-0 for each breakpoint. Your generated CSS will look like this:

CSS
.border { ... }
.border-0 { ... }

@media (min-width: 576px) {
  .sm\:border { ... }
  .sm\:border-0 { ... }
}

@media (min-width: 768px) {
  .md\:border { ... }
  .md\:border-0 { ... }
}

@media (min-width: 1024px) {
  .lg\:border { ... }
  .lg\:border-0 { ... }
}

@media (min-width: 1280px) {
  .xl\:border { ... }
  .xl\:border-0 { ... }
}

@media (min-width: 1536px) {
  .2xl\:border { ... }
  .2xl\:border-0 { ... }
}

Rename utilities

Missing v4 utilities, or used to another naming convention? The utilities API can be used to override the resulting class of a given utility. This change keeps the default definition, so it needs the two-file pattern. For example, to rename .ms-* utilities to oldish .ml-*:

SCSS
// _my-utilities.scss
@use "sass:map";
@use "bootstrap/scss/config" as *;
@use "bootstrap/scss/utilities";

$utilities: map.merge(
  $utilities,
  (
    "margin-start": map.merge(
      map.get($utilities, "margin-start"),
      (class: ml),
    ),
  )
);

Load this partial before bootstrap/scss/bootstrap, as shown above.

Remove utilities

Remove a default utility with the $utilities configuration. Set the group key to null.

SCSS
// my-bootstrap.scss
@use "bootstrap/scss/bootstrap" with (
  $utilities: (
    "width": null,
    "float": null,
  )
);

You can also remove utilities with the map.remove() Sass function. This function reads the default map, so it needs the two-file pattern.

SCSS
// _my-utilities.scss
@use "sass:map";
@use "bootstrap/scss/config" as *;
@use "bootstrap/scss/utilities";

$utilities: map.remove($utilities, "width", "float");

Load this partial before bootstrap/scss/bootstrap, as shown above.

Add, remove, modify

You can add, remove, and modify many utilities all at once with the map.merge() Sass function. Here’s how you can combine the previous examples into one larger map.

SCSS
// _my-utilities.scss
@use "sass:map";
@use "bootstrap/scss/config" as *;
@use "bootstrap/scss/utilities";

$utilities: map.merge(
  $utilities,
  (
    // Remove the `width` utility
    "width": null,
    // Make an existing utility responsive
    "border": map.merge(
      map.get($utilities, "border"),
      (responsive: true),
    ),
    // Add a new utility
    "cursor": (
      property: cursor,
      class: cursor,
      responsive: true,
      values: auto pointer grab,
    ),
  )
);

Then load the partial before Bootstrap:

SCSS
// my-bootstrap.scss
@use "my-utilities";
@use "bootstrap/scss/bootstrap";