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
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>
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>
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-borderRadius-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
767px, 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>
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--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.