CommunityImagegithub.com

nkurunziza-saddy/layout

Structure and space in product UI on the web. It sets the spacing scale, grouping, alignment, density (compact or comfortable), hit areas, app shells (sidebar, toolbar, content, inspector), max widths, container-query responsiveness, dropdown and popover sizing, room for long and translated text, layout stability (no shift on load) and sticky elements that must not cover focus.

What is layout?

layout is a Claude Code agent skill that structure and space in product UI on the web. It sets the spacing scale, grouping, alignment, density (compact or comfortable), hit areas, app shells (sidebar, toolbar, content, inspector), max widths, container-query responsiveness, dropdown and popover sizing, room for long and translated text, layout stability (no shift on load) and sticky elements that must not cover focus.

Works with~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/nkurunziza-saddy/hmm-interfaces/tree/HEAD/layout

Installed? Explore more Image skills: steipete/songsee, affaan-m/frontend-design, mattpocock/improve-codebase-architecture · View all 6 →

Ask in your favorite AI

Open a new chat with this agent skill pre-loaded.

Documentation

What does layout do?

This skill decides where things go, how far apart they sit and how big they are. It makes the same decisions in the same order, and it checks the result against the rendered pixels.

Before any decision, apply product-ui §1 (the taste journal and precedence) and §5 (which skill owns what).

Decide in this order

  1. Shell. Name the regions of the screen: sidebar, toolbar, content, inspector. Each region is one scroll owner or none. Details and widths: shell-and-responsive.md. Why: the shell fixes every width the inner layout has to live in.
  2. Width and measure. Set the content column by what it holds: form 440px, reading 720px with prose capped at the type measure (66ch), data 1200px. A column never stretches because the screen is wide. Why: past 75 characters a line loses the reader, and a stretched form sends eyes across the screen.
  3. Space. Use the scale below and nothing else. Group first, with space; add a fill, then a line, only where space cannot carry it. spacing-and-density.md.
  4. Align. Pick the shared edges and put everything on them. A 1–4px miss is a bug. Correct optically where geometry looks wrong. spacing-and-density.md.
  5. Size. Choose the density for the region, then the hit areas. spacing-and-density.md.
  6. Worst-case data. Resize the box for the longest, shortest, missing, translated and still-loading content before you call it done. stability-and-text.md.
  7. Adapt. Make the structure respond to its container, not the device. shell-and-responsive.md.
  8. Measure. Render at 2×, measure gaps and edges, fix what the pixels show. measure.md.

The scale

One scale, 4px base. Never write a spacing value that isn't a step. Why: one-off values (10px, 14px, 18px) make rhythm impossible to see and impossible to keep.

TokenpxTypical use
--space-xs4label to its description; icon to label in a chip
--space-sm8items within a group; control padding block
--space-md12between bordered controls; control padding inline
--space-lg16between fields; compact card padding
--space-2020between blocks inside a card
--space-xl24between groups; comfortable card padding
--space-2xl32between sections
--space-3xl48above a page's first title
--space-4xl64below a page's last block

1–2px nudges for optical alignment are the only values off the scale. Name them (--play-nudge: 2px).

Rules that need no reference

  • Group with space. The break between groups is at least 2× the gap inside one: 8px inside means 16px or more between. Equal spacing everywhere makes grouping ambiguous, and ambiguity here is a functional bug.
  • Remove lines before adding them. Space first, a fill second, a divider last, and only for dense data. Why: every line costs attention for the same information space gives free.
  • Space above equals space below for anything between two things. Why: a thing nearer one neighbour reads as owned by it.
  • Controls must look like controls. A button, link or toggle has a shape, an underline or a toolbar position. Static text never shares those. Why: a control styled like text hides, and text styled like a control collects dead clicks.
  • Actions sit above the data they act on, never under it. A control that sets something sits next to it.
  • Primary content goes first and leading. Visual order equals DOM order: never order, row-reverse or grid placement to reorder content. Why: keyboard and screen-reader order follow the DOM.
  • Logical properties (margin-inline-start, padding-block, inset-inline-end, text-align: start) for anything that mirrors. Why: RTL then works without a second stylesheet.
  • Never put critical controls in the bottom edge of a window or sidebar. Why: people push windows partly off screen.
  • Scroll shows what is hidden. Content past an edge gets a visible cue: a peek of 16–32px or a fade.
  • Nothing shifts. Hover, selection and focus never change an element's size. Loaded data takes the box the skeleton held.

Pairs

BrokenFixedWhy
gap: 12px in the form and gap: 12px between its fieldsetsgap: 8px inside, gap: 24px betweenGroups need a break of at least 2×
padding: 10px 14pxpadding: var(--space-sm) var(--space-md)Steps only
Title at x=24, description at x=26Both at x=24A 1–4px miss reads as a mistake
Icon button 16×16 with padding: 016px glyph, ::before { inset: -12px } for a 40px targetMouse target is 40px
.row + .row { margin-top: 8px } with hover and click on rows.row { padding-block: 4px }, no marginA gap between rows is a dead zone and flickers hover
.menu { width: 200px }width: max-content; min-width: 160px; max-width: 320pxThe menu hugs its longest label
@media (max-width: 768px) on a card@container (width < 400px) on the card's parentA card in a narrow sidebar needs its own width, not the screen's
<img src> with no size, then a card that jumpswidth, height or aspect-ratio setThe box exists before the bytes
Sticky header, anchor lands under itscroll-padding-block-start: 56px on the scrollerFocus and anchors never sit under chrome

Before you finish

Search the diff for these, then verify.

PatternFix
margin, padding or gap with a value not in the scaleNearest step
One gap value used inside and between groupsInside ≤ half of between
Icon-only button under 24px, or under 40px with no ::beforeExpand with a pseudo-element
gap or margin between clickable rows or menu itemsPadding inside the item
Fixed width on a menu, button or text boxmax-content, min-width, max-width; min-height for text
@media (max-width inside a reusable component@container on its parent
height: 100vh for a shell100dvh
1fr track or flex child holding long textminmax(0, 1fr) or min-width: 0
margin-left, padding-right, left: 0, text-align: left in mirrored UILogical form
Sticky header or footer with no scroll-padding-blockSet it to the chrome's height
Skeleton row height or column widths differ from loadedMatch them
<img> with no dimensions or aspect-ratioAdd one

Verify with measure.md: render at 2× at 390px and the shell's widest width, run the script, crop each candidate and count pixels. Then Tab through the screen with sticky chrome in place, and load the longest strings. Report what you couldn't check as not verified.

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

nkurunziza-saddy/break

Stress-tests a piece of UI with worst-case data and every reachable state, then reports what broke with a numbered fix for each. Use it when the user types /break, or asks to "break this", "stress-test", "try the worst case", "what happens with long names", "check the empty and error states" or "find the edge cases" on a component or screen.

nkurunziza-saddy/motion

Builds motion for product UI on the web. It decides whether something should move at all, then why, which tool (CSS transition, WAAPI, Motion, spring), which properties, curve, duration, interruption, exit and reduced-motion path, and it writes the code. Also covers stagger and choreography, gestures (drag, flick, sheets), motion tokens and animation performance.

nkurunziza-saddy/pick-library

Picks the right library for a frontend task from a curated, status-checked list, instead of hand-rolling it or installing an abandoned package. Use it when the user asks "what should I use for…", "is there a library for…", or names a package, and when you are about to build a toast, command menu, drawer, date picker, table, virtual list, drag and drop, chart, OTP input, animated number, theme switch, form validation…

nkurunziza-saddy/surfaces

What product UI is made of on the web. It sets elevation levels with exact shadow recipes for light and dark mode, when to use a shadow and when a border, the transparent border for forced-colours mode, a small radius scale with concentric nesting, image outlines, every interactive state as a surface change (hover, pressed, selected, disabled, focus-visible), icons (one set, currentColor, outline and fill, stroke tha…

Related Skills