Spruce CSS has no classical grid system. Instead, it gives you a set of intrinsic layout primitives: they adapt to the available space, mostly without media queries.
Every primitive exists as a mixin for your components, and (new in v3) as an opt-in utility class for quick prototyping and content pages.
@use 'sprucecss/scss/spruce' with (
$settings: (
'utilities': (
'layout': true,
),
),
);Each class is configurable with custom properties, inline or in your CSS.
Vertical rhythm: equal space between the direct children.
<div class="l-stack" style="--stack-gap: 0.5rem;">…</div>.article { @include layout-stack('m'); }Inline items that wrap, like tags, buttons or navigation links.
<div class="l-cluster" style="--cluster-gap: 0.5rem; --cluster-justify: center;">…</div>.tags { @include layout-cluster('xs', $justify: flex-start, $align: center); }Responsive columns with a minimum width, without media queries.
<div class="l-grid" style="--grid-min: 15rem; --grid-gap: 1rem;">…</div>.cards { @include layout-grid('m', 15rem); }A fixed-width sidebar next to a fluid main area; they stack when the main area would get narrower than 50%.
<div class="l-sidebar" style="--sidebar-inline-size: 15rem;">
<aside>…</aside>
<main>…</main>
</div>.page { @include layout-sidebar('l', 15rem); }A row of equal columns that switches to a column when the container is narrower than the threshold.
<div class="l-switcher" style="--switcher-threshold: 40rem;">…</div>.features { @include layout-switcher(40rem, 'm', $limit: 4); }A horizontally scrollable row with scroll snapping, for carousels and card rows.
<div class="l-reel" style="--reel-item-inline-size: 16rem;">…</div>.gallery { @include layout-reel(16rem, 's'); }A centered container with a maximum width and inline padding.
<div class="l-center" style="--center-inline-size: 60rem;">…</div>.container { @include layout-center('m', 70rem); }| Class | Custom properties (defaults) |
|---|---|
.l-stack | --stack-gap (spacer('m')) |
.l-cluster | --cluster-gap (spacer('s')), --cluster-justify (flex-start) |
.l-grid | --grid-gap (spacer('m')), --grid-min (12.5rem) |
.l-sidebar | --sidebar-gap (spacer('m')), --sidebar-inline-size (18.75rem) |
.l-switcher | --switcher-gap (spacer('m')), --switcher-threshold (30rem) |
.l-reel | --reel-gap (spacer('m')), --reel-item-inline-size (18rem) |
.l-center | --center-gap (spacer('m')), --center-inline-size (container-inline-size) |
When you need a breakpoint, use the breakpoint mixins: breakpoint('md') (min-width), breakpoint-down('md'), breakpoint-between('md', 'xl'), or the container-query() mixin for component-level responsiveness.


