Layout Grids skill for Codex
One spacing scale, edges that line up. Codex audits the margins and arbitrary spacing values already in the code, defines a 4 px based scale with proximity rules, and builds a page shell with full-bleed and content tracks from CSS Grid named lines. It adds stack, cluster, sidebar and switcher primitives, aligns card internals with subgrid, and checks alignment with a debug overlay.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- layout-grids Installs to ~/.agents/skills/layout-grids/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install layout-grids The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project layout-grids Installs to .agents/skills/layout-grids/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/layout-grids.zipunzip -o layout-grids.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) layout-grids PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $layout-grids, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Sets up layout grids and spacing systems for web UI - a 4 px based spacing scale, column grids per breakpoint, content and full-bleed tracks with CSS Grid named lines, subgrid for aligned cards, stack, cluster and sidebar layout primitives, section rhythm and density modes, plus a grid overlay and an off-scale spacing audit. Use when spacing feels random, alignment drifts between sections, building a page shell or container system, defining gutters and max widths, or replacing magic-number margins with a scale.
Use it when
- Spacing values are inconsistent or full of magic numbers
- Building a page shell, container or column grid
- Section edges and card contents do not line up
- Adding compact and comfortable density modes
Use something else when
- Container queries, :has() and modern CSS layout features in depth: use modern-css-layout
- Breakpoint strategy for a whole responsive site: use responsive-design
How to check the result
A grid overlay at 360, 768, 1024 and 1440 px shows aligned edges, there is no horizontal scroll at 320 px, and the spacing audit finds no off-scale values outside the allowlist.
The mistake it prevents
Spacing siblings with margins on the children instead of gap on the parent, which collapses unpredictably and breaks when components are reused.
Layout is right when every gap comes from one scale, related things sit closer than
unrelated things, edges line up across sections, and the grid adapts by changing column
count and gutters rather than by squeezing content. Magic numbers (margin-top: 37px)
are the symptom; a missing system is the cause.
Before changing anything
- Read
AGENTS.mdand existing layout code: container components, Tailwind@themespacing, global CSS, page shells. - Audit spacing values in use:
rg -o --glob '*.{css,scss}' '(margin|padding|gap)[a-z-]*:\s*[^;]+' src | rg -o '[0-9.]+(px|rem)' | sort | uniq -c | sort -nr | head -30
rg -o --glob '*.{tsx,jsx,vue,svelte,html}' '\b(p|m|gap|space)-[xytrbl]?-?\[[^\]]+\]' src | sort | uniq -c | sort -nr
The second command finds Tailwind arbitrary values, which are usually off-scale escapes.
Spacing scale
Use a 4 px base with a geometric feel at the top end:
| Token | rem | px | Typical use |
|---|---|---|---|
space-1 | 0.25 | 4 | Icon to label, tight inline |
space-2 | 0.5 | 8 | Inside compact controls, between related lines |
space-3 | 0.75 | 12 | Control padding, list item gap |
space-4 | 1 | 16 | Default gap inside a group, card padding (compact) |
space-6 | 1.5 | 24 | Card padding, gap between groups |
space-8 | 2 | 32 | Between components in a section |
space-12 | 3 | 48 | Between sub-sections |
space-16 | 4 | 64 | Section padding on mobile |
space-24 | 6 | 96 | Section padding on desktop |
space-32 | 8 | 128 | Hero and major breaks |
Rules:
- Proximity: the gap between groups is at least 1.5–2x the gap inside a group. If a label is as far from its input as from the previous field, the form reads as noise.
- Padding inside a container is greater than or equal to the gap between its children.
- Section padding scales with viewport:
padding-block: clamp(4rem, 8vw, 8rem). - Tailwind v4 already uses a 0.25rem
--spacingbase (p-4= 1rem); restrict the team to the steps above by convention or lint, and treatp-[13px]as a bug.
Column grid
| Viewport | Columns | Gutter | Outer margin |
|---|---|---|---|
| < 640 px | 4 | 16 px | 16–20 px |
| 640–1023 px | 8 | 24 px | 32 px |
| ≥ 1024 px | 12 | 24–32 px | auto (centered container) |
Container max width: 1200–1280 px for apps and marketing, 680–760 px for reading columns. Text never spans the full 12 columns on desktop; 6–8 columns is a readable measure.
Page shell with named lines
One grid gives full-bleed, wide and content tracks without wrapper divs:
.shell {
--content: min(72rem, 100% - 2 * var(--margin));
--margin: clamp(1rem, 4vw, 2rem);
display: grid;
grid-template-columns:
[full-start] 1fr
[wide-start] minmax(0, 6rem)
[content-start] var(--content) [content-end]
minmax(0, 6rem) [wide-end]
1fr [full-end];
}
.shell > * { grid-column: content; }
.shell > .wide { grid-column: wide; }
.shell > .full { grid-column: full; }
Inner 12-column grid
.grid-12 {
display: grid;
grid-template-columns: repeat(12, minmax(0, 1fr));
gap: var(--space-6);
}
.span-7 { grid-column: span 7; }
.span-5 { grid-column: span 5; }
@media (width < 1024px) { .grid-12 > * { grid-column: 1 / -1; } }
Use minmax(0, 1fr), not 1fr: plain 1fr has an automatic minimum and long words or
wide tables blow the column out.
Card grids
.cards {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(18rem, 100%), 1fr));
gap: var(--space-6);
}
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* title, body, footer align across cards */
}
auto-fill keeps empty tracks so a single card does not stretch full width; use
auto-fit when stretching is wanted. The min(18rem, 100%) prevents overflow on narrow
screens. Subgrid is supported in all current evergreen browsers.
Layout primitives
Most layouts are compositions of four primitives; build these once:
| Primitive | CSS core | Use |
|---|---|---|
| Stack | display:flex; flex-direction:column; gap: var(--stack-gap, var(--space-4)) | Vertical flow of anything |
| Cluster | display:flex; flex-wrap:wrap; gap; align-items:center | Tags, buttons, nav items |
| Sidebar | display:flex; flex-wrap:wrap with sidebar flex-basis: 16rem and main flex: 999 1 30rem | Content with a side panel that drops below when narrow |
| Switcher | flex-wrap:wrap; > * { flex-grow:1; flex-basis: calc((40rem - 100%) * 999) } | Row of equal items that becomes a column below a threshold, no media query |
Spacing between siblings comes from the parent’s gap, never from margins on children.
Components own their internal padding, never their outer margin.
Vertical rhythm
- Keep line heights and spacing on the 4 px grid where practical (16 px text at 1.5 = 24 px line). Perfect baseline grids are rarely worth the cost on the web.
- Headings sit closer to the content they introduce than to the content above:
margin-block: var(--space-12) var(--space-3)for an h2 in prose. text-box: trim-both cap alphabetictrims the extra leading above caps and below baselines for exact optical spacing; support is recent, so treat it as a progressive enhancement.
Density modes
For data-heavy apps, define density via a multiplier rather than separate components:
:root { --density: 1; }
[data-density="compact"] { --density: 0.75; }
.cell { padding: calc(var(--space-2) * var(--density)) calc(var(--space-3) * var(--density)); }
Workflow
- [ ] 1. Audit current spacing values and containers
- [ ] 2. Define the scale and map existing values to it (table)
- [ ] 3. Build the page shell and primitives
- [ ] 4. Migrate sections; delete margins on children in favour of parent gap
- [ ] 5. Verify with the grid overlay at 360, 768, 1024, 1440 px
Debug overlay (dev only):
.debug-grid { position: relative; }
.debug-grid::before {
content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 9999;
background: repeating-linear-gradient(to right,
oklch(0.7 0.2 25 / .12) 0 calc((100% - 11 * var(--space-6)) / 12),
transparent 0 calc((100% - 11 * var(--space-6)) / 12 + var(--space-6)));
}
Gotchas
- Margin collapse between siblings and parent/child produces “random” gaps; flex and
grid gaps do not collapse, which is another reason to prefer
gap. 100vwincludes the scrollbar on desktop browsers and causes horizontal scroll; use the full-bleed grid track instead.- Fixed heights on cards cut content when copy or locale changes; use subgrid or
align-items: stretch. - Different containers per page (1140, 1200, 1280) make edges jump during navigation. One container token.
- Changing gap at breakpoints but not padding makes mobile layouts feel cramped inside and loose outside. Scale both.
Verification
- Re-run the audit: off-scale values and arbitrary Tailwind spacing reduced to an agreed allowlist (ideally zero).
- Overlay screenshots at each breakpoint: left edges of headings, text and cards align to columns; no horizontal scroll at 320 px.
- Check one long-content case (long title, translated German copy) for overflow.
Output contract
**Scale:** 4 px base, tokens space-1..space-32 (file: ...)
**Grid:** 4/8/12 columns, gutters 16/24/32, container 72rem, reading 65ch
**Primitives:** Stack, Cluster, Sidebar, Switcher (path)
**Migration:** <n> off-scale values replaced, <n> child margins converted to gap
**Exceptions:** <allowlisted values and why> What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Layout Grids"
short_description: "One spacing scale, edges that line up"
default_prompt: "$layout-grids The spacing on our pages is all over the place, set up a proper spacing scale" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.