Modern CSS Layout skill for Codex
Components that fit any slot, pages that never overflow. Codex picks grid or flex per layout, builds wrapping card grids that never overflow at 320px, aligns card internals across rows with subgrid, and makes components respond to their container rather than the viewport. It writes logical properties so RTL works without extra CSS, uses :has() anchored on components, and debugs overflow with a fixed checklist instead of hiding it with overflow-x hidden.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- modern-css-layout Installs to ~/.agents/skills/modern-css-layout/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install modern-css-layout 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 modern-css-layout Installs to .agents/skills/modern-css-layout/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/modern-css-layout.zipunzip -o modern-css-layout.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))) modern-css-layout 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 $modern-css-layout, 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
Builds responsive layouts with modern CSS - grid with auto-fit and minmax, named areas, subgrid for aligned cards, container queries and cqi units for components that adapt to their slot, :has() for parent and quantity styling, logical properties for RTL, intrinsic sizing with clamp, and dvh viewport units - with media queries only for page-level changes. Use when building or fixing a page or component layout, replacing breakpoint-heavy CSS, aligning card content across rows, making a component reusable in sidebars and main columns, or fixing overflow and RTL bugs.
Use it when
- Building or refactoring page and component layouts
- A component must work in containers of different widths
- Content overflows on small screens or at high zoom
- Preparing a layout for right-to-left languages
Use something else when
- Responsive images, srcset and art direction: use responsive-images
- Visual design decisions about grids and spacing scales: use layout-grids
How to check the result
Screenshots at 320 to 1440 px and 200% zoom show no horizontal scroll, the component renders correctly in narrow and wide containers, and switching dir to rtl mirrors the layout.
The mistake it prevents
Putting container-type on an element that should size to its content, which collapses it because inline-size containment ignores its children's width.
Done well means: components adapt to the space they are given (container queries), the page adapts to the viewport (a few media queries), content never overflows at 320px wide or 200% zoom, the layout works in RTL without separate CSS, and there are no magic pixel widths that break when copy changes.
Inspect first
- Read
AGENTS.mdand the browser support policy (browserslistinpackage.json, or a support statement). Everything below is Baseline widely available by 2026 except where noted; confirm before using newer features (anchor positioning, style queries). - Find the existing breakpoints and spacing tokens; reuse them.
- Reproduce the bug or design at three widths (320, 768, 1280) and note which element overflows or misaligns. DevTools grid and flex overlays show tracks and gaps.
Grid or flex
| Layout | Use |
|---|---|
| Items in one direction, sizes driven by content (toolbar, tag list, nav) | Flexbox with gap and flex-wrap |
| Two-dimensional placement, aligned rows and columns | Grid |
| Repeating cards that wrap to fit | Grid repeat(auto-fit, minmax(...)) |
| Page shell (header, sidebar, main, footer) | Grid with named areas |
| Children of separate cards must align to each other | Subgrid |
| Overlapping layers (text over image) | Grid with both items in the same area |
| Centring one thing | display: grid; place-items: center |
Core patterns
/* Responsive card grid with no media queries */
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
gap: var(--space-4, 1rem);
}
/* Page shell */
.shell {
display: grid;
grid-template:
"header" auto
"main" 1fr
"footer" auto / minmax(0, 1fr);
min-block-size: 100dvh;
}
@media (width >= 64rem) {
.shell {
grid-template:
"header header" auto
"nav main" 1fr
"footer footer" auto / 16rem minmax(0, 1fr);
}
}
/* Full-bleed sections inside a readable column */
.content {
display: grid;
grid-template-columns: [full-start] minmax(1rem, 1fr) [content-start] min(65ch, 100% - 2rem) [content-end] minmax(1rem, 1fr) [full-end];
}
.content > * { grid-column: content; }
.content > .bleed { grid-column: full; }
min(18rem, 100%)insideminmaxstops the grid overflowing on screens narrower than the minimum.- Use
minmax(0, 1fr)instead of1frwhen a track contains long words, code blocks or wide tables;1frhas an automatic minimum of the content’s min-content width. auto-fitcollapses empty tracks so few items stretch;auto-fillkeeps empty tracks so items keep their size. Choose deliberately.
Subgrid: aligned card internals
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(16rem, 100%), 1fr)); gap: 1rem; }
.card {
display: grid;
grid-row: span 3;
grid-template-rows: subgrid; /* title, body, footer line up across a row of cards */
gap: 0.5rem;
}
The parent needs enough implicit rows for the span; each card’s three children map to the three shared rows, so titles of different lengths no longer push buttons out of line.
Container queries
.product-card { container: product / inline-size; }
.product-card__inner { display: grid; gap: 0.75rem; }
@container product (width >= 28rem) {
.product-card__inner { grid-template-columns: 10rem 1fr; }
}
.product-card__title { font-size: clamp(1rem, 0.8rem + 2cqi, 1.5rem); }
- Query the container, style its descendants. An element cannot query itself, so add a wrapper when needed.
container-type: inline-sizeapplies size containment on the inline axis: the container no longer sizes from its children’s width. Do not put it on elements that must shrink-wrap their content (inline buttons, flex items withwidth: autothat should hug content).cqiunits are a percentage of the container’s inline size; use them inclamp()for fluid type inside components.- Style queries on custom properties (
@container style(--variant: compact)) are not supported in every engine yet; check support before relying on them. - Rule of thumb: media queries for page structure and user preferences
(
prefers-reduced-motion,prefers-color-scheme), container queries for components.
:has() for parent and quantity logic
.field:has(input:user-invalid) { --field-border: var(--color-danger); }
.card:has(> img) { grid-template-rows: auto 1fr; }
.grid:has(> :nth-child(5)) { grid-template-columns: repeat(3, 1fr); } /* 5+ items */
body:has(dialog[open]) { overflow: hidden; }
:has() makes the selector’s specificity that of its most specific argument and can be
costly when anchored on body or * with deep descendant arguments. Anchor it on the
nearest component and prefer child (>) arguments.
Logical properties and RTL
| Physical | Logical |
|---|---|
margin-left, margin-right | margin-inline-start, margin-inline-end |
padding-top/bottom | padding-block |
width, height, min-height | inline-size, block-size, min-block-size |
left: 0 | inset-inline-start: 0 |
text-align: left | text-align: start |
border-left | border-inline-start |
Use logical properties in all new component CSS. Flex and grid already follow writing
direction. Check icons that imply direction (arrows, chevrons) and flip them with
:dir(rtl) or scale: -1 1.
Sizing without magic numbers
- Fluid spacing and type:
clamp(min, preferred, max)with aremfloor so user font size settings still apply. - Readable measure:
max-inline-size: 65chon text containers. - Mobile viewport height:
100dvh(dynamic) for full-screen shells,100svhwhen the layout must not jump as browser UI hides. Avoid100vhon mobile. - Media aspect:
aspect-ratio: 16 / 9withobject-fit: coveron images. - Headlines:
text-wrap: balance; paragraphs:text-wrap: pretty(progressive enhancement).
Overflow debugging
- Find the offender: in DevTools console, list elements wider than the viewport:
[...document.querySelectorAll("*")].filter(e => e.scrollWidth > document.documentElement.clientWidth) - Flex child with long content: add
min-inline-size: 0(flex items default tomin-width: auto). - Grid track with
1fr: switch tominmax(0, 1fr). - Long URLs or tokens:
overflow-wrap: anywhereon the text container. - Tables and code: wrap in a scroll container (
overflow-x: auto) with a visible focus style andtabindex="0"so keyboard users can scroll. - Never “fix” overflow with
overflow-x: hiddenonbody; it hides the bug and breaksposition: sticky.
Gotchas
gapon flex is fine everywhere now; remove old negative-margin hacks when touching that CSS.- Percentages in
grid-template-rowsandheight: 100%need a definite parent block size. position: stickyfails when any ancestor hasoverflow: hidden|autothat is not the intended scroller, or when the sticky element’s parent is only as tall as it is.orderand grid placement change visual order but not tab or reading order. Keep DOM order meaningful (WCAG 1.3.2, 2.4.3).- Anchor positioning (
anchor-name,position-anchor) is the future for tooltips and popovers but check engine support and keep a fallback.
Verification
- Check widths 320, 375, 768, 1024, 1440 and a container in both narrow (sidebar) and wide (main) slots. Playwright screenshots at those viewports make this repeatable.
- Browser zoom 200% and text-only zoom: no horizontal scroll on the page (WCAG 1.4.10 reflow at 320 CSS px).
document.documentElement.dir = "rtl"in the console: layout mirrors correctly.- No element reports
scrollWidth > clientWidthexcept intended scroll containers.
Output
Report the layout technique per component (grid, flex, subgrid, container query), the breakpoints and container thresholds used, support caveats for any newer feature, and the widths and modes verified.
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: "Modern CSS Layout"
short_description: "Components that fit any slot, pages that never overflow"
default_prompt: "$modern-css-layout Make this product card adapt to the sidebar and the main column without media queries" 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.