Utilities

Small set of single-property classes for tweaking existing components.

Philosophy

A utility class changes one property on an existing shape. A button that needs a custom height. A card row that needs to be flex. A stack that needs more gap. A heading that needs muted color in one specific place. That’s what utilities are for.

What utilities are not for: assembling a new component from scratch. If you find yourself stacking .d-flex .gap-3 .p-4 .rounded .border on a <div> to build something that looks like a card, you’re using the wrong tool. Build the component in SCSS with its own BEM class, tokens, and color-mix states. The component file is small, scoped, deletable, and lets the next person reading your codebase see what the thing actually is.

Stisla is a design spec realized as components. Utilities are escape hatches. They aren’t Lego bricks. If composing utilities into components is your preferred workflow, reach for Tailwind instead.

The shipped set is small on purpose. Text (color, weight, size, line-height, alignment, transform, overflow). Layout (display, flex, gap, margin, padding, sizing, position, overflow). If a property isn’t in those two lists, the answer isn’t “add a new utility”. It’s “the component owns this”.

Text

Typography tweaks. Class names track the underlying property. Use .fw-* for weight, .fs-* for size, .lh-* for line height, and .text-* for color, alignment, transform, and overflow.

Semibold, fs-5, primary color.

Light weight, muted color.

A long sentence that will truncate with an ellipsis once it exceeds the container width.

Uppercased, centered, fs-1.

<div class="d-flex flex-column">
  <p class="fw-semibold fs-5 text-primary">Semibold, fs-5, primary color.</p>
  <p class="fw-light text-muted-foreground">Light weight, muted color.</p>
  <p class="text-truncate" style="max-width: 18rem;">A long sentence that will truncate with an ellipsis once it exceeds the container width.</p>
  <p class="text-uppercase text-center fs-1">Uppercased, centered, fs-1.</p>
</div>

Color

ClassReadsUse
.text-foreground--st-foregroundReset child color to body text.
.text-muted-foreground--st-muted-foregroundSecondary copy, helper text, metadata.
.text-primary--st-primaryBrand-colored text run.
.text-success--st-successPositive state text.
.text-warning--st-warningCaution state text.
.text-danger--st-dangerError state text.
.text-info--st-infoInformational text.

Weight

ClassValue
.fw-light300
.fw-normal400
.fw-medium500
.fw-semibold600
.fw-bold700

Size

Numeric scale, ascending small → large. Same direction as .gap-* and .m*-*. Use .fs-{breakpoint}-{step} for breakpoint-up variants.

ClassValuePixels (at 16 px root)
.fs-10.75rem12
.fs-20.875rem14
.fs-31rem16
.fs-41.125rem18
.fs-51.25rem20
.fs-61.5rem24
.fs-72rem32

Line height

ClassValue
.lh-11
.lh-sm1.25
.lh-base1.5
.lh-lg2

Alignment, transform, overflow

ClassEffect
.text-startAlign start of text container (LTR=left).
.text-endAlign end (LTR=right).
.text-centerCenter align.
.text-lowercaseForce lowercase.
.text-uppercaseForce uppercase.
.text-capitalizeCapitalize first letter of each word.
.text-nowrapPrevent line breaking.
.text-truncateSingle-line ellipsis on overflow.
.text-breakBreak long words to prevent overflow.

Accessibility

ClassEffect
.visually-hiddenRemoves the element from view while keeping it readable by assistive tech. Used for spinner labels, icon-only button labels, skip links.

Layout

Spacing uses a Tailwind-style 4 px linear scale (1=4 px, 2=8 px, 3=12 px, 4=16 px, 5=20 px, 6=24 px, 8=32 px). Every value multiplies by --st-density so the global density knob retunes spacing alongside component padding.

Margin and padding use CSS logical properties (margin-block-start, margin-inline-end, ...) so the same class works in LTR and RTL.

Edited 2 min ago
<div class="d-flex gap-3 align-items-center">
  <button type="button" class="btn btn--primary">Save</button>
  <button type="button" class="btn btn--neutral">Cancel</button>
  <span class="text-muted-foreground fs-2 ms-auto">Edited 2 min ago</span>
</div>

One flex row with a 12 px gap and an end-aligned timestamp pushed by .ms-auto. No new component, just three buttons and a span getting a layout tweak.

We'll never share your address.

<div class="d-flex flex-column gap-2" style="max-width: 18rem;">
  <label for="email" class="form-label">Email</label>
  <input type="email" id="email" class="input" placeholder="you@example.com" />
  <p class="text-muted-foreground fs-2 mb-0">We'll never share your address.</p>
</div>

A vertical stack with an 8 px gap between rows. Note the helper text uses .mb-0 to kill its natural bottom margin so the gap owns the rhythm.

Display

ClassValue
.d-nonedisplay: none
.d-blockdisplay: block
.d-inlinedisplay: inline
.d-inline-blockdisplay: inline-block
.d-flexdisplay: flex
.d-inline-flexdisplay: inline-flex
.d-griddisplay: grid
.d-inline-griddisplay: inline-grid

Flex direction & wrap

ClassValue
.flex-rowflex-direction: row
.flex-row-reverserow-reverse
.flex-columncolumn
.flex-column-reversecolumn-reverse
.flex-wrapflex-wrap: wrap
.flex-nowrapnowrap
.flex-wrap-reversewrap-reverse

Flex grow & shrink

ClassValue
.flex-fillflex: 1 1 auto
.flex-grow-0flex-grow: 0
.flex-grow-1flex-grow: 1
.flex-shrink-0flex-shrink: 0
.flex-shrink-1flex-shrink: 1

Justify & align

ClassValue
.justify-content-startflex-start
.justify-content-endflex-end
.justify-content-centercenter
.justify-content-betweenspace-between
.justify-content-aroundspace-around
.justify-content-evenlyspace-evenly
.align-items-{start,end,center,baseline,stretch}Cross-axis alignment for flex children.
.align-self-{start,end,center,baseline,stretch,auto}Per-child override of cross-axis alignment.

Gap

.gap-{n} sets both row and column gap. .row-gap-{n} and .column-gap-{n} target one axis. Values: 0 / 1 / 2 / 3 / 4 / 5 / 6 / 8.

StepValueDefault density (1)
000 px
10.25rem × density4 px
20.5rem × density8 px
30.75rem × density12 px
41rem × density16 px
51.25rem × density20 px
61.5rem × density24 px
82rem × density32 px

Margin & padding

Direction abbreviations follow Bootstrap convention: top, bottom, start, end, x for both inline sides, y for both block sides. Start and end use logical properties so RTL flips for free. Same step scale as gap.

PatternMaps to
.m-{0…8}margin (all sides)
.mt-{0…8}margin-block-start
.mb-{0…8}margin-block-end
.ms-{0…8}margin-inline-start (LTR=left)
.me-{0…8}margin-inline-end (LTR=right)
.mx-{0…8}margin-inline (both)
.my-{0…8}margin-block (both)
.m-auto, .mx-auto, .ms-auto, .me-auto, .mt-auto, .mb-auto, .my-autoAuto margin variants for centering and flex push-to-end.
.p-{0…8}, .pt-, .pb-, .ps-, .pe-, .px-, .py-Same shape, padding properties.

Sizing

Percentage widths and heights resolve against the containing block. .h-* needs a sized parent to be meaningful.

ClassValue
.w-25width: 25%
.w-50width: 50%
.w-75width: 75%
.w-100width: 100%
.w-autowidth: auto
.h-{25,50,75,100,auto}Same shape, height.
.mw-100max-width: 100%
.mh-100max-height: 100%

Position

ClassValue
.position-staticstatic
.position-relativerelative
.position-absoluteabsolute
.position-fixedfixed
.position-stickysticky

Overflow

ClassValue
.overflow-{hidden,auto,scroll,visible}Both axes.
.overflow-x-{hidden,auto,scroll,visible}Horizontal axis.
.overflow-y-{hidden,auto,scroll,visible}Vertical axis.

Responsive

Insert a breakpoint name into any responsive-enabled class to make it apply from that width up. The mobile-first base lays the default. Each breakpoint cascades on top via min-width.

Stacks on mobile.
Sits side‑by‑side from md up.
<div class="d-flex flex-column flex-md-row gap-3 align-items-md-center">
  <div class="w-100 w-md-50">
    <div class="card">
      <div class="card__body">Stacks on mobile.</div>
    </div>
  </div>
  <div class="w-100 w-md-50">
    <div class="card">
      <div class="card__body">Sits side&#8209;by&#8209;side from <code>md</code> up.</div>
    </div>
  </div>
</div>

Resize the preview frame. Below 768 px the row stacks (.flex-column) with each card at .w-100; at 768 px and above .flex-md-row + .w-md-50 kick in and the cards split the row.

Breakpoints

Same five breakpoints as the grid system.

SuffixApplies atWidth ≥
(none)Always (mobile default)0
smSmall tablet up576 px
mdTablet up768 px
lgSmall laptop up992 px
xlDesktop up1200 px
xxlWide desktop up1400 px

What gets a responsive variant

These are the defaults. Every group is toggleable through $utilities-config, so this table shows what ships when the bundle is imported untouched.

GroupPatternExample
Display.d-{bp}-{value}.d-md-flex, .d-lg-none
Flex direction & wrap.flex-{bp}-{value}.flex-md-row, .flex-lg-wrap
Justify content.justify-content-{bp}-{value}.justify-content-md-between
Align items.align-items-{bp}-{value}.align-items-md-center
Gap.gap-{bp}-{step}, .row-gap-{bp}-{step}, .column-gap-{bp}-{step}.gap-md-4
Margin.m{,t,b,s,e,x,y}-{bp}-{step}.mt-md-3, .mx-lg-auto
Padding.p{,t,b,s,e,x,y}-{bp}-{step}.p-md-4, .py-lg-2
Width.w-{bp}-{25,50,75,100,auto}.w-md-50
Text align.text-{bp}-{start,end,center}.text-md-center
Font size.fs-{bp}-{step}.fs-md-4

Base-only by default: flex grow / shrink, align self, height, max-width / max-height, position, overflow, text color, font weight, line height, text transform, visually-hidden. Flip any of them on in $utilities-config.

Customizing the set

$utilities-config is the single override surface. Every group has an enabled flag and a responsive flag. Override the map before importing the bundle.

Toggling a group

Set responsive: true to add breakpoint variants to a group that ships base-only, or enabled: false to drop a group entirely.

your-stisla.scss
// Override before the bundle import. map-merge preserves all other defaults.
$utilities-config: map-merge($utilities-config, (
  overflow:        (enabled: true,  responsive: true),
  position:        (enabled: true,  responsive: true),
  visually-hidden: (enabled: false, responsive: false),
));

@import '@stisla/css/bundles/stisla';

Emits .overflow-md-hidden, .position-lg-sticky, etc., and drops every .visually-hidden* class from the output. Disabled groups cost zero bytes.

Adding a new utility

You need two pieces. An entry in $utilities-config, and a matching @include utility-group(…) block. The helper handles base and breakpoint emission. $i is the empty string at base, and -sm / -md / … inside each breakpoint media query.

your-utilities.scss
@import '@stisla/css/utilities/utilities';

$utilities-config: map-merge($utilities-config, (
  cursor: (enabled: true, responsive: false),
));

@include utility-group('cursor') using ($i) {
  .cursor#{$i}-pointer     { cursor: pointer     !important; }
  .cursor#{$i}-grab        { cursor: grab        !important; }
  .cursor#{$i}-not-allowed { cursor: not-allowed !important; }
}

Emits .cursor-pointer, .cursor-grab, .cursor-not-allowed. Flip responsive: true and the same block also emits .cursor-md-pointer etc. at every breakpoint.

All groups

Group keyClassesResponsive by default
text-color.text-foreground, .text-primary, …no
font-weight.fw-*no
font-size.fs-*yes
line-height.lh-*no
text-transform.text-lowercase, .text-uppercase, .text-capitalizeno
text-wrap.text-nowrap, .text-truncate, .text-breakno
text-align.text-start, .text-end, .text-centeryes
visually-hidden.visually-hiddenno
display.d-*yes
position.position-*no
overflow.overflow-*, .overflow-x-*, .overflow-y-*no
flex-fill.flex-fill, .flex-grow-*, .flex-shrink-*no
flex-direction.flex-row, .flex-column, …yes
flex-wrap.flex-wrap, .flex-nowrap, .flex-wrap-reverseyes
justify-content.justify-content-*yes
align-items.align-items-*yes
align-self.align-self-*no
gap.gap-*, .row-gap-*, .column-gap-*yes
margin.m{,t,b,s,e,x,y}-*yes
padding.p{,t,b,s,e,x,y}-*yes
width.w-*yes
height.h-*no
max-size.mw-100, .mh-100no

Customization

Two knobs, no per-class vars.

Spacing values resolve through --st-density, the global lever from Customization that retunes every .gap-*, .m*-*, and .p*-* alongside component padding. Compact density (0.875) shrinks every utility step proportionally. Comfortable density (1.125) expands them.

Which groups ship, and which carry breakpoint variants, is set in $utilities-config. Disable groups you don’t use to drop them from the bundle. Flip responsive: true on a group to add breakpoint variants.