Grid utilities
Plain CSS grid utility classes for laying out form fields and content
blocks without hand-writing display: grid each time: .eun-grid
plus one of eleven layout modifiers, all built on the
tablet
width < 768px, the same
condition as the eunomia-below-tablet mixin; the lead layouts
keep their column at every width instead, and the gallery and panel
layouts don't need to collapse in the first place, see their own
sections below for why.
Column splits
Halves
Two equal columns, using the --halves modifier.
<div class="eun-grid eun-grid--halves">
<eun-button>First</eun-button>
<eun-button>Second</eun-button>
</div>
Uneven thirds
A narrow column plus a wide one, in either order, using the
--thirds-1-2 or --thirds-2-1 modifier.
<div class="eun-grid eun-grid--thirds-1-2">
<div>1 part</div>
<div>2 parts</div>
</div>
Uneven quarters
A wide column taking three quarters of the width plus a narrow one, in
either order, using the --quarters-1-3 or --quarters-3-1 modifier.
<div class="eun-grid eun-grid--quarters-3-1">
<div>3 parts</div>
<div>1 part</div>
</div>
Uneven fifths
Two columns closer in width, two fifths and three fifths, in either
order, using the --fifths-2-3 or --fifths-3-2 modifier.
<div class="eun-grid eun-grid--fifths-2-3">
<div>2 parts</div>
<div>3 parts</div>
</div>
Equal thirds
Three equal columns, using the --thirds-equal modifier.
<div class="eun-grid eun-grid--thirds-equal">
<div>1</div>
<div>2</div>
<div>3</div>
</div>
2×2 grid
A 2×2 grid, using the --2x2 modifier, for a compact block of four
related fields.
<div class="eun-grid eun-grid--2x2">
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
</div>
Page layouts
Sidebar layouts
Two equal columns, using the --sidebar-left or --sidebar-right
modifier. The first element spans both rows in its own column,
letting a single nav block run the full height alongside two
stacked blocks in the other column.
<div class="eun-grid eun-grid--sidebar-left">
<nav>Sidebar</nav>
<main>Main content</main>
<aside>Secondary content</aside>
</div>
Lead layouts
Two columns, using the --lead-left or --lead-right modifier. Like
the sidebar layouts above, the first element spans both rows in its
own column, but that column sizes itself to its content
(grid-template-columns: auto 1fr) instead of taking an equal share,
built for a leading icon, number, or avatar next to a stacked title
and description, rather than a full-width sidebar.
<div class="eun-grid eun-grid--lead-left">
<eun-icon name="bolt" size="24"></eun-icon>
<span class="title">Instant delivery</span>
<p class="description">Ships within the hour.</p>
</div>
The auto column sizes itself to the lead element's own natural
content size, which suits a small icon or a couple of digits. Give
that element width: 100%; height: 100% instead (object-fit: cover
too, for an image) to have it fill the column completely, edge to
edge across both rows, rather than sitting at its own content size:
useful for a photo or a color swatch running the full height next to
a stacked title and description.
<div class="eun-grid eun-grid--lead-left">
<img class="avatar" src="/images/alice.jpg" alt="" />
<span class="title">Alice Martin</span>
<p class="description">Product Designer</p>
</div>
.avatar {
width: 100%;
height: 100%;
object-fit: cover;
border-radius: var(--eun-border-radius-m);
}
Gallery grid
An unknown, variable number of equal tiles, using the --gallery
modifier: as many columns as fit at --eun-grid-min-width (220px by
default) each, wrapping to further rows on its own. Unlike every
variant above, this one is never forced to a single column below
the tablet breakpoint, since repeat(auto-fill, minmax(...)) already reflows
continuously at any width on its own, a fixed breakpoint would only
fight that.
<div class="eun-grid eun-grid--gallery" style="--eun-grid-min-width: 220px;">
<eun-card>1</eun-card>
<eun-card>2</eun-card>
<eun-card>3</eun-card>
<!-- however many more -->
</div>
Panel layout
A header, a body that takes whatever room is left, and a footer,
using the --panel modifier (grid-template-rows: auto 1fr auto),
a dialog's or a card's own internal structure, rather than a
multi-column layout. The 1fr body row only actually has "room left"
to give once the grid itself has a height (the viewport, a fixed
height, or a flex/grid ancestor that stretches it) ; a body that can
overflow still needs its own overflow-y: auto, the grid track alone
doesn't add scrolling.
<div class="eun-grid eun-grid--panel" style="height: 100%;">
<header>Header</header>
<div style="overflow-y: auto;">Body</div>
<footer>Footer</footer>
</div>
Responsive grids
Columns per breakpoint
.eun-grid sets its column count from variables, mobile first.
--eun-grid-columns applies at every width, and
--eun-grid-columns-mobile, -tablet, -laptop and -desktop apply
from that breakpoint up. A level left unset takes the one below it, so
"3 columns on desktop, 2 on tablet, 1 on mobile" needs no media query:
<div
class="eun-grid"
style="--eun-grid-columns-mobile: 2; --eun-grid-columns-laptop: 3"
>
…
</div>
--halves, --thirds-equal and --2x2 are defaults of these
variables, so any of them you set wins over the variant.
Template per breakpoint
When equal columns aren't enough, --eun-grid-template sets the whole
column template, with the same -mobile, -tablet, -laptop and
-desktop levels. A 2/1 split that only starts at laptop needs no
media query either:
<div class="eun-grid" style="--eun-grid-template-laptop: 2fr 1fr">…</div>
The uneven splits (--thirds-*, --quarters-* and --fifths-*) are
tablet defaults of these variables, so any level you set wins over
the variant. The sidebar and lead variants keep a fixed template,
which the variables don't change.
Content-sized columns
Equal columns can shrink below their content, so a long word or a
nowrap line overflows its column. Set --eun-grid-track-min to
auto to let each column grow to fit its content instead (default
0):
<div class="eun-grid" style="--eun-grid-columns: 3; --eun-grid-track-min: auto">
…
</div>
Nested grids
The column, template and --eun-grid-track-min variables only apply to
the grid that sets them, they aren't inherited. A grid placed inside
another one keeps its own columns, and the variables have to be set on
the .eun-grid element itself rather than on an ancestor.
Overriding a grid
The grid classes carry no specificity, since they're written with
:where(), and they're emitted once, outside of any theme selector. A
single class of your own overrides any of their properties, a CSS
Modules class included:
.products {
grid-template-columns: 2fr 1fr;
}
Reference
| Class | Layout |
|---|---|
eun-grid eun-grid--halves |
1fr 1fr |
eun-grid eun-grid--thirds-1-2 |
1fr 2fr |
eun-grid eun-grid--thirds-2-1 |
2fr 1fr |
eun-grid eun-grid--quarters-1-3 |
1fr 3fr |
eun-grid eun-grid--quarters-3-1 |
3fr 1fr |
eun-grid eun-grid--fifths-2-3 |
2fr 3fr |
eun-grid eun-grid--fifths-3-2 |
3fr 2fr |
eun-grid eun-grid--thirds-equal |
1fr 1fr 1fr |
eun-grid eun-grid--2x2 |
repeat(2, 1fr), 2 rows |
eun-grid eun-grid--sidebar-left |
1fr 1fr, first child spans both rows on the left |
eun-grid eun-grid--sidebar-right |
1fr 1fr, first child spans both rows on the right |
eun-grid eun-grid--lead-left |
auto 1fr, first child spans both rows on the left |
eun-grid eun-grid--lead-right |
1fr auto, first child spans both rows on the right |
eun-grid eun-grid--gallery |
repeat(auto-fill, minmax(220px, 1fr)) |
eun-grid eun-grid--panel |
a single column, auto 1fr auto rows |
Every variant shares the same gap (from the spacing scale):
--eun-grid-column-gap (default --eun-space-m) between columns,
--eun-grid-row-gap (default --eun-space-s, tighter, since a wrapped
row is usually closer content like a title stacked over its
description) between rows. Both are plain CSS custom properties, so
either can be overridden per instance. Most fixed-column variants
collapse to a single column on small screens, with no separate mobile
markup needed; the lead layouts are the deliberate exception, since a
leading icon, number, or avatar reads as attached to the title next to
it rather than as a section of its own, so it keeps its column instead
of stacking above the title/description. The gallery already reflows
on its own, and the panel is already a single column.