CSS Grid, Flexbox, and Subgrid using Utopia.fyi fluid spacing for gaps and gutters...
CSS Grid, Flexbox, and Subgrid with Utopia.fyi fluid spacing system
Utopia's layout approach follows the same declarative design principle as its type and space scales: establish rules at @min and @max viewports, let the browser interpret contextually. Grid and Flexbox become systematic layout primitives powered by fluid space tokens.
Key Insight: "Get the gutters right, and the rest will follow." — Utopia focuses on fluid gutters/gaps that maintain proportional relationships across all viewports.
Need a layout system?
├─ One-dimensional (row OR column)?
│ └─ Use Flexbox
│ Examples: Navigation bars, button groups, card content, centered items
│ Gap: Use Utopia space tokens (var(--space-s), var(--space-m))
│
└─ Two-dimensional (rows AND columns)?
└─ Use CSS Grid
Examples: Page layouts, card grids, dashboards, galleries
Gap: Use Utopia space tokens for consistent spacing
Best Practice: Combine both — Grid for overall structure, Flexbox for components within grid cells.
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--space-m); /* Fluid gap from Utopia space scale */
}
/* Gap scales: 24px → 30px automatically */
Why This Works:
gap uses Utopia space token.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: var(--space-s-m); /* Dramatic scaling: 16px → 30px */
}
auto-fit vs auto-fill:
auto-fit: Collapses empty tracks, stretches itemsauto-fill: Maintains empty tracks, items don't stretchUse Cases:
auto-fit: Card grids where items should fill spaceauto-fill: Galleries maintaining consistent item sizes.page-layout {
display: grid;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
grid-template-columns: 250px 1fr 200px;
grid-template-rows: auto 1fr auto;
gap: var(--space-m); /* Fluid gap between all areas */
min-height: 100vh;
}
.header { grid-area: header; }
.sidebar { grid-area: sidebar; }
.main { grid-area: main; }
.aside { grid-area: aside; }
.footer { grid-area: footer; }
.asymmetric-grid {
display: grid;
grid-template-columns: 2fr 1fr;
column-gap: var(--space-l); /* Horizontal: 32px → 40px */
row-gap: var(--space-m); /* Vertical: 24px → 30px */
}
/* Or combined */
.asymmetric-grid {
gap: var(--space-m) var(--space-l); /* row-gap column-gap */
}
.outer-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-l);
}
.nested-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-s); /* Smaller gap for nested content */
}
Pattern: Outer grids use larger space tokens (L, XL), inner grids use smaller tokens (S, M) for visual hierarchy.
Rather than fixed columns, the Utopian grid uses fluid gutters derived from the space scale and lets columns fill remaining space.
"Get the gutters right, and the rest will follow."
:root {
/* Grid configuration */
--grid-max-width: 77.5rem; /* Content max-width */
--grid-gutter: var(--space-s-l); /* Fluid gutter: 16px → 40px */
--grid-columns: 12;
}
.grid {
display: grid;
grid-template-columns:
/* Outer gutter (fluid) */
minmax(var(--grid-gutter), 1fr)
/* 12 content columns */
repeat(
var(--grid-columns),
minmax(0, calc(var(--grid-max-width) / var(--grid-columns)))
)
/* Outer gutter (fluid) */
minmax(var(--grid-gutter), 1fr);
column-gap: var(--grid-gutter);
}
/* Default: full width within gutters */
.grid > * {
grid-column: 2 / -2;
}
/* Narrower content */
.grid-item--narrow {
grid-column: 4 / -4;
}
/* Specific column spans */
.grid-item--span-6 {
grid-column: auto / span 6;
}
URL: https://utopia.fyi/grid/calculator/
Inputs:
Output: CSS grid template using fluid gutters
Why 12 Columns?
.flex-row {
display: flex;
gap: var(--space-m); /* Fluid gap between flex items */
}
.flex-column {
display: flex;
flex-direction: column;
gap: var(--space-s); /* Vertical spacing */
}
.nav {
display: flex;
gap: var(--space-s-m); /* 16px → 30px between nav items */
align-items: center;
}
.nav__item {
padding: var(--space-2xs) var(--space-s);
}
.button-group {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs); /* Consistent, subtle scaling */
}
.button {
padding: var(--space-2xs) var(--space-s);
font-size: var(--step-0);
}
.card-row {
display: flex;
gap: var(--space-m-l); /* Dramatic gap scaling: 24px → 40px */
flex-wrap: wrap;
}
.card {
flex: 1 1 min(300px, 100%);
padding: var(--space-s-m);
display: flex;
flex-direction: column;
gap: var(--space-xs); /* Inner card spacing */
}
.center-flex {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
padding: var(--space-l); /* Outer spacing */
}
.center-flex__content {
max-width: 70ch;
display: flex;
flex-direction: column;
gap: var(--space-m); /* Inner spacing */
}
Subgrid allows nested grids to inherit track sizing and gap values from parent grids, enabling perfectly aligned nested layouts.
Browser Support (2025): Chrome 118+, Edge 118+, Firefox 71+, Safari 16+ (Baseline ✅)
.parent-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-l); /* Parent gap */
}
.subgrid-item {
display: grid;
grid-template-columns: subgrid; /* Inherits parent's columns */
grid-column: span 2; /* Spans 2 parent columns */
gap: inherit; /* Inherits parent's gap */
}
.card-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-m-l); /* Fluid gap: 24px → 40px */
}
.card {
display: grid;
grid-template-rows: subgrid; /* Align card sections across all cards */
grid-row: span 3; /* Image, content, footer */
padding: var(--space-s);
gap: var(--space-xs); /* Inner card spacing */
}
/* All images align horizontally across cards */
.card__image {
grid-row: 1;
aspect-ratio: 16 / 9;
}
/* All content sections align */
.card__content {
grid-row: 2;
}
/* All footers align at bottom */
.card__footer {
grid-row: 3;
margin-block-start: auto;
}
Why This Works:
.card__footer elements align across cards regardless of content heightgap: var(--space-xs) inside cards.form {
display: grid;
grid-template-columns: [labels] 150px [inputs] 1fr [help] 200px;
gap: var(--space-s); /* Fluid gap throughout form */
}
.form-group {
display: grid;
grid-template-columns: subgrid; /* Inherits 3-column structure */
grid-column: 1 / -1; /* Spans all columns */
gap: inherit; /* Inherits parent gap */
}
.form-group label {
grid-column: labels;
padding-block: var(--space-3xs); /* Vertical alignment */
}
.form-group input {
grid-column: inputs;
}
.form-group .help-text {
grid-column: help;
font-size: var(--step--1);
}
.parent {
display: grid;
grid-template-columns:
[sidebar-start] 250px
[sidebar-end main-start] 1fr
[main-end];
gap: var(--space-l);
}
.child {
display: grid;
grid-template-columns: subgrid; /* Inherits named lines */
grid-column: sidebar-start / main-end;
gap: inherit;
}
/* Child can reference parent's named lines */
.nested-element {
grid-column: main-start / main-end;
padding: var(--space-m);
}
.card {
display: grid;
grid-template-rows: auto 1fr auto; /* Fallback */
gap: var(--space-xs);
}
@supports (grid-template-rows: subgrid) {
.card-grid {
display: grid;
grid-template-rows: repeat(3, auto);
gap: var(--space-m);
}
.card {
grid-template-rows: subgrid;
grid-row: span 3;
}
}
/* Outer structure: Grid */
.page-layout {
display: grid;
grid-template-areas:
"header"
"main"
"footer";
grid-template-rows: auto 1fr auto;
gap: var(--space-l); /* Page section spacing */
min-height: 100vh;
}
/* Header: Flex */
.header {
grid-area: header;
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-s-m);
gap: var(--space-m);
}
/* Main: Grid for content */
.main {
grid-area: main;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--space-m-l); /* Card grid spacing */
padding: var(--space-l);
}
/* Card: Flex for internal layout */
.card {
display: flex;
flex-direction: column;
gap: var(--space-s); /* Internal card spacing */
padding: var(--space-m);
}
/* Footer: Flex */
.footer {
grid-area: footer;
display: flex;
justify-content: center;
padding: var(--space-m);
}
Pattern Hierarchy:
.article-layout {
display: grid;
grid-template-columns: 1fr min(65ch, 100%) 1fr; /* Center column */
gap: var(--space-m);
}
.article-layout > * {
grid-column: 2; /* All content in center column */
}
.article__hero {
grid-column: 1 / -1; /* Full width */
min-height: 50vh;
display: flex;
align-items: center;
justify-content: center;
padding: var(--space-xl-2xl);
}
.article__content {
display: flex;
flex-direction: column;
gap: var(--space-m); /* Paragraph spacing */
}
.article__content h2 {
font-size: var(--step-3);
margin-block-start: var(--space-l);
margin-block-end: var(--space-s);
}
.article__content p {
font-size: var(--step-0);
line-height: 1.6;
}
gap valuesgap: inherit in subgrids to maintain parent spacinggrid-template-areas for readable, semantic layouts@supports.hero {
display: grid;
place-items: center;
min-height: 80vh;
padding: var(--space-xl-2xl); /* Dramatic scaling */
text-align: center;
}
.hero__content {
display: flex;
flex-direction: column;
gap: var(--space-m);
max-width: 60ch;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: var(--space-m-l); /* 24px → 40px */
}
.card {
display: flex;
flex-direction: column;
gap: var(--space-s);
padding: var(--space-m);
border: 1px solid var(--color-border);
}
.dashboard {
display: grid;
grid-template-areas:
"header header header"
"sidebar main aside"
"footer footer footer";
grid-template-columns: 250px 1fr 300px;
grid-template-rows: auto 1fr auto;
gap: var(--space-m);
min-height: 100vh;
}
.widget-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--space-s-m);
}
.gallery {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
grid-auto-flow: dense; /* Fill gaps with smaller items */
gap: var(--space-s);
}
.gallery__item--large {
grid-column: span 2;
grid-row: span 2;
}
Prerequisites:
utopia-fluid-scales skill first to generate space tokens:rootComplementary:
utopia-container-queries skill for component-level responsivenessWorkflow:
utopia-fluid-scales)utopia-grid-layout)utopia-container-queries)After implementing grid layouts:
utopia-container-queries for component-level fluid behavior