Utilities

Single-purpose override classes that can be used to override text, spacing, and responsive helpers.

What they are for

Brio is not a utility-first library, but that doesn't mean they aren't useful. To me, utilities are super useful when they encapsulate common patterns or can alter the behavior of something, such as a composition. It should be considered a one-off override that is so useful it would be a crime not to have it readily available, such as altering the --flow-space of the .flow composition. Utilities are higher in the layer stack, above components, so they win by layer order without !important.

So, there are a few utilities added with restraint. When a utility starts appearing in the same shape in several places, that shape wants to be a component or a scoped custom property, not a utility you paste everywhere. The tables below are generated straight from src/css/utilities.css.

Text

Alignment, color, size, weight, family, and line height. The size and leading classes key to the same fluid scales the type tokens define, so they move with the viewport the way the rest of the system does. The wrapping classes set text-wrap: balance evens out line lengths in short text, pretty keeps widows and orphans out of paragraphs.

Alignment

Logical-property values so RTL writing modes flip correctly.

ClassDescription
.text-startAlign text to the inline-start edge
.text-centerCenter text
.text-endAlign text to the inline-end edge

Color

Maps to semantic color tokens. Keep these minimal; component-scoped tokens are usually the right override point instead. (The color reset is .text-default, not .text-normal -- that name belongs to the weight group below and the pair once collided.)

ClassDescription
.text-mutedMuted secondary text
.text-subtleSubtle de-emphasized text
.text-inverseInverted text for dark surfaces
.text-primaryPrimary brand color
.text-defaultReverts to the default --color-text
.text-accentAccent color
.text-successSuccess status color
.text-warningWarning status color
.text-dangerDanger status color

Family and case

ClassDescription
.text-monoSwitch to the monospace family
.text-uppercaseTransform to uppercase
.text-lowercaseTransform to lowercase

Size

Keyed to the fluid type scale. See the type scale on the Tokens page for the resolved sizes at each breakpoint.

ClassDescription
.text--2Smallest caption size
.text--1Step below body
.text-0Body baseline
.text-1One step above body
.text-2Subheading
.text-3Heading
.text-4Large heading
.text-5Display size

Weight

ClassDescription
.text-thinThin (100)
.text-extralightExtra light (200)
.text-lightLight (300)
.text-normalNormal (400)
.text-mediumMedium (500)
.text-semiboldSemibold (600)
.text-boldBold (700)
.text-blackBlack (900)

Line height

ClassDescription
.leading-tightTight leading for labels
.leading-headingDefault heading leading
.leading-bodyDefault body leading
.leading-looseLoose leading for emphasis contexts

Text wrapping

CSS provides two modes with different jobs. balance evens out line lengths in short text; pretty pushes widows and orphans out of paragraphs. The degrade gracefully, as unsupported browsers will simply ignore the declarations.

ClassDescription
.text-balanceEven line lengths for short headings and titles
.text-prettyAvoids widows and orphans in wrapped paragraphs

These utilities are for decorating links and changing default link styles.

ClassDescription
.link-plainStrips the underline and resets the color to ambient text

Spacing

The .gap-* classes set the gap property directly, keyed to the raw --space-* scale. That reaches any gap-driven container, the compositions included: gap does not inherit, and the utilities cascade layer wins over the composition layer, so .stack.gap-xs or .cluster.gap-2xl restyles that one element at any step. The .is-snug and .is-tight variants are the tier-named shortcuts for the common density shifts; remapping the primitive's own gap custom property to a --gutter-* alias keeps the tier vocabulary when you want it.

The .flow-space-* classes do the same for flow rhythm through --flow-space, which can be set on the container or on an individual child.

The .measure-* classes set a reading width in ch for text that isn't prose, such as a paragraph inside a sidebar or a table's intro. The mechanics match .gap-*: element-scoped, and the @utilities layer override the width tokens that prose and center already support.

Gap

Set the gap property directly, keyed to the raw --space-* scale. Works on any gap-driven container, the gap compositions included: the utilities layer wins over the composition layer, and gap does not inherit.

ClassDescription
.gap-3xsgap: var(--space-3xs)
.gap-2xsgap: var(--space-2xs)
.gap-xsgap: var(--space-xs)
.gap-smgap: var(--space-sm)
.gap-mdgap: var(--space-md)
.gap-lggap: var(--space-lg)
.gap-xlgap: var(--space-xl)
.gap-2xlgap: var(--space-2xl)

Flow space

Set --flow-space at the --space-* scale. Apply to a .flow container to set its rhythm, or to an individual child to tune the gap above just that element.

ClassDescription
.flow-space-none--flow-space: 0
.flow-space-3xs--flow-space: var(--space-3xs)
.flow-space-2xs--flow-space: var(--space-2xs)
.flow-space-xs--flow-space: var(--space-xs)
.flow-space-sm--flow-space: var(--space-sm)
.flow-space-md--flow-space: var(--space-md)
.flow-space-lg--flow-space: var(--space-lg)
.flow-space-xl--flow-space: var(--space-xl)
.flow-space-2xl--flow-space: var(--space-2xl)
.flow-space-3xl--flow-space: var(--space-3xl)

Measure

Set max-inline-size to a reading width in ch so the measure tracks the font rather than the viewport. Same mechanics as .gap-*: the utilities layer wins over prose and center, and max-inline-size does not inherit, so the override is scoped to its element. Two steps bracket the 65ch default that .prose and .center already carry; anything narrower or wider is a project call.

ClassDescription
.measure-slimReading width at 25ch
.measure-narrowReading width at 45ch
.measure-proseReading with at 65ch
.measure-wideReading width at 75ch

Accessibility

Brio tries to be accessible. So, a few helpers have been included to assist with creating more accessible interfaces.

  • .visually-hidden removes an element from sight without removing it from the accessibility tree: it clips to a zero-size box instead of using display: none, so screen readers still announce it. Use it for skip links, table captions, and labels that exist for assistive technology only.

Accessibility

Helpers for assistive technology. The comparative prose -- why .visually-hidden over display: none, when to reach for .link-plain -- lives on the utilities page; these rows carry the one-line contract.

ClassDescription
.visually-hiddenRemoves an element from sight without removing it from the accessibility tree

Responsive helpers

Container-query opt-in and viewport-based hiding. The container classes keep the response tied to the box that actually changed size, which is usually what a component-level layout wants, while the viewport classes hide chrome at breakpoints. Each .hide-* class also has a .show-* counterpart that names the same behavior from the other side: .show-below-md is visible only below md, the same rule as .hide-above-md. I included both, as some names just seem more intuitive when reading the element's role. For example, an element that appears on small screens is a might be better named as .show-below-md, but an element that leaves might use .hide-below-md. It depends on how you're reading the element's role.

Viewport

Show and hide elements based on viewport width. Breakpoints: sm 40rem, md 48rem, lg 64rem.

ClassDescription
.hide-below-smHidden below the sm breakpoint
.hide-below-mdHidden below the md breakpoint
.hide-below-lgHidden below the lg breakpoint
.hide-above-smHidden at the sm breakpoint and above
.hide-above-mdHidden at the md breakpoint and above
.hide-above-lgHidden at the lg breakpoint and above
.show-below-smShown only below the sm breakpoint
.show-below-mdShown only below the md breakpoint
.show-below-lgShown only below the lg breakpoint
.show-above-smShown only at the sm breakpoint and above
.show-above-mdShown only at the md breakpoint and above
.show-above-lgShown only at the lg breakpoint and above

Container queries

Opt a parent into container queries with .cq-inline, then use .cq-hide-* and .cq-show-* on its descendants to respond to the container's inline size instead of the viewport.

ClassDescription
.cq-inlineOpt into inline-size container queries
.cq-hide-below-smHidden when the container is below sm
.cq-hide-below-mdHidden when the container is below md
.cq-hide-below-lgHidden when the container is below lg
.cq-hide-above-smHidden when the container is sm or wider
.cq-hide-above-mdHidden when the container is md or wider
.cq-hide-above-lgHidden when the container is lg or wider
.cq-show-below-smShown only while the container is below sm
.cq-show-below-mdShown only while the container is below md
.cq-show-below-lgShown only while the container is below lg
.cq-show-above-smShown only while the container is sm or wider
.cq-show-above-mdShown only while the container is md or wider
.cq-show-above-lgShown only while the container is lg or wider

Container queries demo

The box you are resizing is the container these two lines listen to.

The container is narrower than md (48rem).

The container is md (48rem) or wider.

<div class="cq-inline flow">
            <p>The box you are resizing is the container these two lines listen to.</p>
            <p class="cq-hide-below-md text-success">The container is narrower than md (48rem).</p>
            <p class="cq-hide-above-md text-danger">The container is md (48rem) or wider.</p>
        </div>

Drag the handle past 48rem and the lines trade places, with no media query and no viewport involved. The breakpoints are the same sm, md, and lg the viewport classes use, matched on purpose; the only difference is what the width resolves against, the container's inline size for .cq-hide-* and the viewport for .hide-*.