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.
| Class | Description |
|---|---|
.text-start | Align text to the inline-start edge |
.text-center | Center text |
.text-end | Align 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.)
| Class | Description |
|---|---|
.text-muted | Muted secondary text |
.text-subtle | Subtle de-emphasized text |
.text-inverse | Inverted text for dark surfaces |
.text-primary | Primary brand color |
.text-default | Reverts to the default --color-text |
.text-accent | Accent color |
.text-success | Success status color |
.text-warning | Warning status color |
.text-danger | Danger status color |
Family and case
| Class | Description |
|---|---|
.text-mono | Switch to the monospace family |
.text-uppercase | Transform to uppercase |
.text-lowercase | Transform to lowercase |
Size
Keyed to the fluid type scale. See the type scale on the Tokens page for the resolved sizes at each breakpoint.
| Class | Description |
|---|---|
.text--2 | Smallest caption size |
.text--1 | Step below body |
.text-0 | Body baseline |
.text-1 | One step above body |
.text-2 | Subheading |
.text-3 | Heading |
.text-4 | Large heading |
.text-5 | Display size |
Weight
| Class | Description |
|---|---|
.text-thin | Thin (100) |
.text-extralight | Extra light (200) |
.text-light | Light (300) |
.text-normal | Normal (400) |
.text-medium | Medium (500) |
.text-semibold | Semibold (600) |
.text-bold | Bold (700) |
.text-black | Black (900) |
Line height
| Class | Description |
|---|---|
.leading-tight | Tight leading for labels |
.leading-heading | Default heading leading |
.leading-body | Default body leading |
.leading-loose | Loose 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.
| Class | Description |
|---|---|
.text-balance | Even line lengths for short headings and titles |
.text-pretty | Avoids widows and orphans in wrapped paragraphs |
Link decorations
These utilities are for decorating links and changing default link styles.
| Class | Description |
|---|---|
.link-plain | Strips 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.
| Class | Description |
|---|---|
.gap-3xs | gap: var(--space-3xs) |
.gap-2xs | gap: var(--space-2xs) |
.gap-xs | gap: var(--space-xs) |
.gap-sm | gap: var(--space-sm) |
.gap-md | gap: var(--space-md) |
.gap-lg | gap: var(--space-lg) |
.gap-xl | gap: var(--space-xl) |
.gap-2xl | gap: 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.
| Class | Description |
|---|---|
.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.
| Class | Description |
|---|---|
.measure-slim | Reading width at 25ch |
.measure-narrow | Reading width at 45ch |
.measure-prose | Reading with at 65ch |
.measure-wide | Reading width at 75ch |
Accessibility
Brio tries to be accessible. So, a few helpers have been included to assist with creating more accessible interfaces.
-
.visually-hiddenremoves an element from sight without removing it from the accessibility tree: it clips to a zero-size box instead of usingdisplay: 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.
| Class | Description |
|---|---|
.visually-hidden | Removes 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.
| Class | Description |
|---|---|
.hide-below-sm | Hidden below the sm breakpoint |
.hide-below-md | Hidden below the md breakpoint |
.hide-below-lg | Hidden below the lg breakpoint |
.hide-above-sm | Hidden at the sm breakpoint and above |
.hide-above-md | Hidden at the md breakpoint and above |
.hide-above-lg | Hidden at the lg breakpoint and above |
.show-below-sm | Shown only below the sm breakpoint |
.show-below-md | Shown only below the md breakpoint |
.show-below-lg | Shown only below the lg breakpoint |
.show-above-sm | Shown only at the sm breakpoint and above |
.show-above-md | Shown only at the md breakpoint and above |
.show-above-lg | Shown 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.
| Class | Description |
|---|---|
.cq-inline | Opt into inline-size container queries |
.cq-hide-below-sm | Hidden when the container is below sm |
.cq-hide-below-md | Hidden when the container is below md |
.cq-hide-below-lg | Hidden when the container is below lg |
.cq-hide-above-sm | Hidden when the container is sm or wider |
.cq-hide-above-md | Hidden when the container is md or wider |
.cq-hide-above-lg | Hidden when the container is lg or wider |
.cq-show-below-sm | Shown only while the container is below sm |
.cq-show-below-md | Shown only while the container is below md |
.cq-show-below-lg | Shown only while the container is below lg |
.cq-show-above-sm | Shown only while the container is sm or wider |
.cq-show-above-md | Shown only while the container is md or wider |
.cq-show-above-lg | Shown only while the container is lg or wider |
Container queries demo
The box you are resizing is the container these two lines listen to.
<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-*.