2025-06-12
Grids
In many projects we use two separate grids, one for structure (basically defining the width of the content) and one for the actual content layout (usually made up of 12 columns or similar).
The HTML structure for a page with blocks
<main>
<div class="block-name" data-grid-structure="true">
<div class="grid-content" data-grid="text|**popout|**content|layout|full">
<div class="text">…</div>
<div class="image">…</div>
</div>
</div>
<div class="block-name" data-grid-structure="true">
<div class="grid-content" data-grid="text|**popout|**content|layout|full">
<div class="text">…</div>
<div class="image">…</div>
</div>
</div>
<div class="block-name" data-grid-structure="true">
<div class="grid-content" data-grid="text|**popout|**content|layout|full">
<div class="text">…</div>
<div class="image">…</div>
</div>
</div>
</main>
The structural grid
The structural grid defines the width
// ============================================================================
// Structural grid
// ============================================================================
// https://ryanmulligan.dev/blog/layout-breakouts/
[data-grid-structure='true'] {
// Gutter for small screens
--gutter: 7px;
// Text column for articles. The min() function sets the max-width of the
// text column and on smaller screens it will be 100% minus gutters on both side.
--text: min(300px, 100% - var(--gutter) * 2);
// Used for images and other content on article pages.
// Extends up to 50 px outside text column.
--popout: minmax(0, 40px);
// Content area for most blocks.
// Extends up to 20px outside popout column.
--content: minmax(0, 20px);
// The main layout for the site. This is the default choice for content.
// Extends up to 50px outside content column.
--layout: minmax(0, 50px);
// Full width container. The min value makes it also take on the role of
// visible page gutters for smaller screens.
--full: minmax(var(--gutter), 1fr);
// Tweaks for larger screens
// …
display: grid;
// Explanation of named lines and areas at
// https://www.smashingmagazine.com/2017/10/naming-things-css-grid-layout/#from-named-lines-come-areas
grid-template-columns:
[full-start] var(--full) [layout-start] var(--layout) [content-start] var(--content)
[popout-start] var(--popout) [text-start] var(--text) [text-end] var(--popout) [popout-end] var(--content)
[content-end] var(--layout) [layout-end] var(--full) [full-end];
[data-grid='text'] {
grid-column: text;
}
[data-grid='popout'] {
grid-column: popout;
}
[data-grid='content'] {
grid-column: content;
}
[data-grid='layout'] {
grid-column: layout;
}
[data-grid='full'] {
grid-column: full;
}
}
The content grid
The .grid-content can extend one of the following content grids from the placeholders:
// ============================================================================
// Placeholders
// ============================================================================
// Base 12 column content grid
%grid {
display: grid;
grid-gap: 20px;
grid-template-columns: repeat(12, 1fr);
grid-template-rows: auto;
align-items: start;
justify-content: center;
max-width: $layout-max-width;
height: 100%;
@include respond-to($tablet) {
grid-gap: $grid-tablet-gutter + px;
}
@include respond-to($desktop) {
grid-gap: $grid-desktop-gutter + px;
}
@include respond-to($widescreen) {
grid-gap: $grid-widescreen-gutter + px;
}
}
// 50 / 50 grid
%grid-50-50 {
display: grid;
grid-gap: 2 * 20px;
grid-template-columns: 1fr;
grid-template-rows: auto;
align-items: start;
justify-content: center;
@include respond-to($tablet) {
grid-template-columns: 1fr 1fr;
grid-gap: 2 * $grid-tablet-gutter + px;
}
@include respond-to($desktop) {
grid-gap: 2 * $grid-desktop-gutter + px;
}
@include respond-to($widescreen) {
grid-gap: 2 * $grid-widescreen-gutter + px;
}
}
// 33 / 33 / 33 grid
%grid-33-33-33 {
display: grid;
grid-gap: 20px;
grid-template-columns: 1fr;
grid-template-rows: auto;
align-items: start;
justify-content: center;
@include respond-to($tablet) {
grid-template-columns: 1fr 1fr 1fr;
grid-gap: $grid-tablet-gutter + px;
}
@include respond-to($desktop) {
grid-gap: $grid-desktop-gutter + px;
}
@include respond-to($widescreen) {
grid-gap: $grid-widescreen-gutter + px;
}
}
// 70 / 30 grid
%grid-70-30 {
display: grid;
grid-gap: 20px;
grid-template-columns: 1fr;
grid-template-rows: auto;
align-items: start;
justify-content: center;
@include respond-to($desktop) {
display: grid;
grid-template-columns: 5fr 2fr;
grid-template-rows: auto;
}
}
Blocks
This way, the block’s CSS could look something like this:
.block-name {
@extend %grid;
.text {
grid-column: 1 / span 4;
// …
}
.image {
grid-column: 4 / span 8;
// …
}
// …
}