| name | layout-grid |
| description | Design-focused grid layout system with fluid scaling, responsive columns, and resolution-independent patterns. Use when creating page layouts, card grids, or multi-column designs. |
| allowed-tools | Read, Write, Edit |
Layout Grid Skill
This skill covers design grid systems for page layout—fluid, resolution-independent grids that scale proportionally across all viewport sizes without fixed breakpoints.
Philosophy
Layout grids should:
- Scale fluidly - No fixed pixel values; use relative units throughout
- Work at any resolution - Single grid system, not breakpoint-specific grids
- Maintain proportions - Gutters and margins scale with content
- Support composition - Enable both rigid alignment and flexible content areas
Fluid Grid Token System
Define grid properties as fluid custom properties that scale between viewport bounds.
Core Grid Tokens
@layer tokens {
:root {
/* ==================== GRID FOUNDATION ==================== */
/* Viewport bounds for fluid calculations */
--grid-min-width: 20rem; /* 320px - mobile */
--grid-max-width: 90rem; /* 1440px - large desktop */
/* Content max-width (readable area) */
--content-max-width: 75rem; /* 1200px */
/* ==================== FLUID GUTTER ==================== */
/* Gutter scales from 1rem to 2rem based on viewport */
--grid-gutter: clamp(1rem, 0.5rem + 2vw, 2rem);
/* ==================== FLUID MARGIN ==================== */
/* Page margin scales from 1rem to 4rem */
--grid-margin: clamp(1rem, -0.5rem + 6vw, 4rem);
/* ==================== COLUMN SYSTEM ==================== */
--grid-columns: 12;
/* Single column width (fluid) */
--grid-column-width: calc(
(100% - (var(--grid-columns) - 1) * var(--grid-gutter)) / var(--grid-columns)
);
}
}
The Fluid Scaling Formula
Fluid values use clamp() with a calculated preferred value:
clamp(min, preferred, max)
preferred = min + (max - min) × viewport-factor
viewport-factor = (100vw - min-viewport) / (max-viewport - min-viewport)
Simplified pattern:
/* Scale from 1rem (320px) to 2rem (1440px) */
--value: clamp(1rem, calc(0.5rem + 1.5vw), 2rem);
Page Layout Container
The Fluid Container
@layer layout {
/* Main content container */
body > * {
--_container-width: min(
var(--content-max-width),
100% - var(--grid-margin) * 2
);
width: var(--_container-width);
margin-inline: auto;
}
/* Full-bleed sections */
[data-layout="full"] {
width: 100%;
padding-inline: var(--grid-margin);
}
/* Wide sections (larger than content, not full) */
[data-layout="wide"] {
--_container-width: min(
var(--grid-max-width),
100% - var(--grid-margin) * 2
);
}
}
Named Grid Areas for Page Layout
@layer layout {
body {
display: grid;
grid-template-columns:
[full-start] var(--grid-margin)
[wide-start] minmax(0, 1fr)
[content-start] min(var(--content-max-width), 100%)
[content-end] minmax(0, 1fr)
[wide-end] var(--grid-margin)
[full-end];
grid-template-rows: auto 1fr auto;
}
/* Content aligns to content area */
body > * {
grid-column: content;
}
/* Full-bleed elements span full width */
body > [data-layout="full"] {
grid-column: full;
}
/* Wide elements extend past content */
body > [data-layout="wide"] {
grid-column: wide;
}
}
Responsive Card Grids
Auto-Fit Pattern (Recommended)
Cards flow into available columns automatically:
@layer components {
card-grid {
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(min(100%, 18rem), 1fr)
);
gap: var(--grid-gutter);
}
}
How it works:
auto-fitcreates as many columns as fitminmax(min(100%, 18rem), 1fr)ensures cards are at least 18rem but stretch to fill spacemin(100%, 18rem)prevents overflow on narrow viewports
Auto-Fill vs Auto-Fit
| Property | Behavior | Use When |
|---|---|---|
auto-fit |
Collapses empty tracks | Cards should stretch to fill row |
auto-fill |
Keeps empty tracks | Maintain consistent column widths |
/* auto-fit: cards stretch */
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
/* auto-fill: consistent widths, may leave gaps */
grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
Constrained Column Count
Limit maximum columns while staying fluid:
card-grid {
--_min-card-width: 18rem;
--_max-columns: 4;
display: grid;
grid-template-columns: repeat(
auto-fit,
minmax(
max(
var(--_min-card-width),
calc((100% - var(--grid-gutter) * (var(--_max-columns) - 1)) / var(--_max-columns))
),
1fr
)
);
gap: var(--grid-gutter);
}
Explicit Column Grids
12-Column Grid
@layer layout {
[data-grid="12"] {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--grid-gutter);
}
/* Span utilities */
[data-span="1"] { grid-column: span 1; }
[data-span="2"] { grid-column: span 2; }
[data-span="3"] { grid-column: span 3; }
[data-span="4"] { grid-column: span 4; }
[data-span="5"] { grid-column: span 5; }
[data-span="6"] { grid-column: span 6; }
[data-span="7"] { grid-column: span 7; }
[data-span="8"] { grid-column: span 8; }
[data-span="9"] { grid-column: span 9; }
[data-span="10"] { grid-column: span 10; }
[data-span="11"] { grid-column: span 11; }
[data-span="12"] { grid-column: span 12; }
}
Responsive Column Spans
Use container queries for component-level responsiveness:
@layer components {
feature-grid {
container-type: inline-size;
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--grid-gutter);
}
feature-grid > * {
grid-column: span 12; /* Full width by default */
}
@container (min-width: 30rem) {
feature-grid > * {
grid-column: span 6; /* Half width */
}
}
@container (min-width: 50rem) {
feature-grid > * {
grid-column: span 4; /* Third width */
}
feature-grid > :first-child {
grid-column: span 8; /* Featured item wider */
}
}
}
Subgrid for Alignment
Subgrid enables nested elements to align with parent grid tracks.
Card Grid with Aligned Content
@layer components {
/* Parent defines the column structure */
product-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
/* Define row tracks for card internals */
grid-auto-rows: auto 1fr auto; /* image, content, actions */
gap: var(--grid-gutter);
}
/* Cards span 3 rows and inherit row tracks */
product-card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid;
gap: var(--spacing-md);
}
product-card img { grid-row: 1; }
product-card .content { grid-row: 2; }
product-card .actions { grid-row: 3; }
}
Form Alignment with Subgrid
@layer components {
form {
display: grid;
grid-template-columns: max-content 1fr;
gap: var(--spacing-md);
}
form-field {
display: grid;
grid-column: span 2;
grid-template-columns: subgrid;
}
form-field label {
grid-column: 1;
}
form-field input,
form-field select {
grid-column: 2;
}
}
Asymmetric Layouts
Content + Sidebar
@layer layout {
[data-layout="sidebar"] {
display: grid;
grid-template-columns: 1fr min(20rem, 30%);
gap: var(--grid-gutter);
}
/* Responsive: stack on narrow */
@container (max-width: 50rem) {
[data-layout="sidebar"] {
grid-template-columns: 1fr;
}
}
}
Golden Ratio Split
@layer layout {
[data-layout="golden"] {
display: grid;
/* 1.618:1 ratio */
grid-template-columns: 1.618fr 1fr;
gap: var(--grid-gutter);
}
}
Feature + Gallery
@layer layout {
[data-layout="feature-gallery"] {
display: grid;
grid-template-columns: 2fr 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: var(--grid-gutter);
}
[data-layout="feature-gallery"] > :first-child {
grid-row: span 2;
}
}
Fluid Gap Scaling
Gap Token Scale
:root {
/* Gaps scale with viewport */
--gap-xs: clamp(0.25rem, 0.125rem + 0.5vw, 0.5rem);
--gap-sm: clamp(0.5rem, 0.25rem + 1vw, 1rem);
--gap-md: clamp(1rem, 0.5rem + 2vw, 2rem);
--gap-lg: clamp(1.5rem, 0.75rem + 3vw, 3rem);
--gap-xl: clamp(2rem, 1rem + 4vw, 4rem);
}
Using Gap Tokens
card-grid {
gap: var(--gap-md);
}
section-grid {
gap: var(--gap-lg);
}
icon-grid {
gap: var(--gap-sm);
}
Resolution-Independent Patterns
Using Proportional Units
| Unit | Use For | Example |
|---|---|---|
fr |
Flexible column widths | 1fr 2fr 1fr |
% |
Proportional within container | max-width: 80% |
vw/vh |
Viewport-relative | width: 50vw |
cqi/cqw |
Container-relative | width: 50cqi |
rem |
Root-relative sizing | gap: 1.5rem |
ch |
Character-based width | max-width: 65ch |
Never Use Fixed Pixels For:
- Column widths
- Gutters and gaps
- Margins and padding
- Container max-widths (use
reminstead)
Pixel-Safe Uses:
- Border widths (
1px,2px) - Box shadows
- Fine visual details
Common Layout Patterns
Holy Grail Layout
body {
display: grid;
grid-template:
"header header header" auto
"nav main aside" 1fr
"footer footer footer" auto
/ minmax(10rem, 15rem) 1fr minmax(10rem, 20rem);
min-height: 100vh;
gap: var(--grid-gutter);
}
header { grid-area: header; }
nav { grid-area: nav; }
main { grid-area: main; }
aside { grid-area: aside; }
footer { grid-area: footer; }
/* Stack on mobile */
@media (max-width: 60rem) {
body {
grid-template:
"header" auto
"nav" auto
"main" 1fr
"aside" auto
"footer" auto
/ 1fr;
}
}
Magazine Layout
article-layout {
display: grid;
grid-template-columns:
[full-start] 1fr
[main-start] minmax(0, 65ch)
[main-end] 1fr
[full-end];
gap: var(--grid-gutter);
}
article-layout > * {
grid-column: main;
}
article-layout > figure,
article-layout > blockquote {
grid-column: full;
}
Masonry-Style (CSS Grid Approximation)
masonry-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
grid-auto-rows: 1rem;
gap: var(--grid-gutter);
}
masonry-grid > * {
/* Items span variable rows based on content */
grid-row: span var(--rows, 10);
}
Note: True masonry requires JavaScript to calculate --rows or wait for CSS masonry property support.
Integration with Container Queries
Component-Level Grid Responsiveness
@layer components {
dashboard-widget {
container-type: inline-size;
display: grid;
gap: var(--spacing-md);
}
/* Single column by default */
dashboard-widget {
grid-template-columns: 1fr;
}
/* Two columns when widget is wide enough */
@container (min-width: 25rem) {
dashboard-widget {
grid-template-columns: 1fr 1fr;
}
}
/* Three columns with sidebar */
@container (min-width: 45rem) {
dashboard-widget {
grid-template-columns: 2fr 1fr 1fr;
}
}
}
Debug Grid Overlay
Visualize grid during development:
/* Add to :root for development */
:root {
--debug-grid: 0;
}
[data-grid],
[data-layout] {
position: relative;
}
[data-grid]::before,
[data-layout]::before {
content: "";
position: absolute;
inset: 0;
pointer-events: none;
background: repeating-linear-gradient(
90deg,
oklch(60% 0.15 250 / 0.1) 0,
oklch(60% 0.15 250 / 0.1) var(--grid-column-width),
transparent var(--grid-column-width),
transparent calc(var(--grid-column-width) + var(--grid-gutter))
);
opacity: var(--debug-grid);
z-index: 9999;
}
Toggle with: document.documentElement.style.setProperty('--debug-grid', '1')
Checklist
When implementing grid layouts:
Tokens
- Grid gutter uses
clamp()for fluid scaling - Page margin scales with viewport
- No fixed pixel values for layout dimensions
- Content max-width defined in
rem
Grid Structure
- Use
auto-fit/auto-fillfor card grids -
minmax()includesmin(100%, value)to prevent overflow - Named grid areas for page layout
-
frunits for flexible column widths
Responsiveness
- Container queries for component-level changes
- Media queries only for page-level layout shifts
- Single grid system works across all viewports
- Test at arbitrary widths, not just standard breakpoints
Alignment
- Subgrid used for nested element alignment
- Consistent gap tokens throughout
- Items align to baseline grid where appropriate
Related Skills
- css-author - @layer organization, container queries, @scope
- typography - Baseline grid, vertical rhythm
- responsive-images - Images in grid contexts
- progressive-enhancement - CSS-only responsive patterns