Eunomia v1.1.0
AllAngularReactNext.jsJavaScriptVue
EunomiaSalmonForestVioletOceanGoldFireCustom…
🇬🇧 English🇫🇷 Français

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 spacing scale. Most fixed-column variants collapse to a single column below the tablet breakpoint, 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.

1
2
<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.

1 part
2 parts
2 parts
1 part
<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.

1 part
3 parts
3 parts
1 part
<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.

2 parts
3 parts
3 parts
2 parts
<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.

1
2
3
<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.

1
2
3
4
<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.

Sidebar
Main
Secondary
<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.

01
Title
Description
<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.

Photo
Alice Martin
Product Designer
<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.

Header
Body
Footer
<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:

1
2
3
<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.