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.

  • Frontend
  • $modern-css-layout
  • 197 lines
  • 2 files
  • ~2,000 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- modern-css-layout

Installs to ~/.agents/skills/modern-css-layout/ for every project on this machine.

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

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.

~/.agents/skills/modern-css-layout/SKILL.md

--- name: modern-css-layout description: Builds responsive layouts with modern CSS - grid with auto-fit and minmax, named areas, subgrid for aligned cards, container queries and cqi units… license: MIT ---

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

  1. Read AGENTS.md and the browser support policy (browserslist in package.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).
  2. Find the existing breakpoints and spacing tokens; reuse them.
  3. 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

LayoutUse
Items in one direction, sizes driven by content (toolbar, tag list, nav)Flexbox with gap and flex-wrap
Two-dimensional placement, aligned rows and columnsGrid
Repeating cards that wrap to fitGrid repeat(auto-fit, minmax(...))
Page shell (header, sidebar, main, footer)Grid with named areas
Children of separate cards must align to each otherSubgrid
Overlapping layers (text over image)Grid with both items in the same area
Centring one thingdisplay: 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%) inside minmax stops the grid overflowing on screens narrower than the minimum.
  • Use minmax(0, 1fr) instead of 1fr when a track contains long words, code blocks or wide tables; 1fr has an automatic minimum of the content’s min-content width.
  • auto-fit collapses empty tracks so few items stretch; auto-fill keeps 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-size applies 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 with width: auto that should hug content).
  • cqi units are a percentage of the container’s inline size; use them in clamp() 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

PhysicalLogical
margin-left, margin-rightmargin-inline-start, margin-inline-end
padding-top/bottompadding-block
width, height, min-heightinline-size, block-size, min-block-size
left: 0inset-inline-start: 0
text-align: lefttext-align: start
border-leftborder-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 a rem floor so user font size settings still apply.
  • Readable measure: max-inline-size: 65ch on text containers.
  • Mobile viewport height: 100dvh (dynamic) for full-screen shells, 100svh when the layout must not jump as browser UI hides. Avoid 100vh on mobile.
  • Media aspect: aspect-ratio: 16 / 9 with object-fit: cover on 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 to min-width: auto).
  • Grid track with 1fr: switch to minmax(0, 1fr).
  • Long URLs or tokens: overflow-wrap: anywhere on the text container.
  • Tables and code: wrap in a scroll container (overflow-x: auto) with a visible focus style and tabindex="0" so keyboard users can scroll.
  • Never “fix” overflow with overflow-x: hidden on body; it hides the bug and breaks position: sticky.

Gotchas

  • gap on flex is fine everywhere now; remove old negative-margin hacks when touching that CSS.
  • Percentages in grid-template-rows and height: 100% need a definite parent block size.
  • position: sticky fails when any ancestor has overflow: hidden|auto that is not the intended scroller, or when the sticky element’s parent is only as tall as it is.
  • order and 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 > clientWidth except 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.yamlgenerated from this page

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.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem