Centinadocs

Grid & subgrid — UI guide

What every part of the grid editing surface does and how the pieces fit together. This is the user-facing tour; the deeper behavior reference — drag math, degradation, exact undo/cancel semantics — is the grid reference.

The editor with a grid box selected: canvas chrome on the left, the grid inspector on the right

Draw a box (B) and switch Display to Grid. From that moment the same grid is editable in two places at once:

  • the inspector (right) shows the grid as structured controls — track lists, gap, alignment;
  • the canvas dresses the selected grid box in direct-manipulation chrome — size chips, draggable boundaries, gap pills.

Both surfaces edit the same underlying CSS (grid-template-columns/rows, gap, …), so a drag on the canvas moves the inspector numbers live and vice versa. The chrome also appears when a child of a grid is selected — you never have to deselect your item to adjust the grid it sits in.

The canvas chrome

A selected 3×2 grid: size chips above and left, gap pills in the gap bands, + chips past the last track

Everything here keeps a constant size on screen no matter the zoom level.

PartWhereWhat it does
Track chips (180px, 1fr, auto)above each column, left of each rowShow each track's authored size (mm = minmax, fit = fit-content, = other CSS; full value in the tooltip). Hover reveals a × that removes the track. Chips hide on tracks too narrow to fit one.
**+ chips**past the last column / below the last rowAdd a track within the box — adding never resizes it: an fr track absorbs the new one, rigid px/%/auto templates re-fit proportionally, and only a hug axis grows (by content, as any added content would). Removal via × redistributes the same way.
Boundary stripscentered in each gap (hatched bands)Invisible until hover, then an accent line and a resize cursor. Drag to resize the adjacent track pair — see below.
Gap pillssmall pills at the top/left of each gap bandDrag to scrub the axis gap. The model has one gap per axis, so all bands of that axis move together. (To create a gap from zero, use the inspector's Gap fields — at gap 0 the boundary strip owns the space.)

Dragging a boundary

Mid-drag on a column boundary: accent line plus a dark badge reading 220px · 1fr

While you drag, a badge rides the boundary showing the live values of the two tracks being resized. The math is unit-aware:

  • fr / fr — the pair's total fr is conserved and redistributed; other tracks stay pinned.
  • px / fr (either order) — only the px side changes; the fr side reflows through free space.
  • auto — converts to px at its current size the moment you drag it (the track stays px afterwards).

One drag is exactly one undo step. Escape mid-drag cancels just the drag, restoring the template to the exact string it was before — selection kept. Boundaries whose neighbors aren't plainly numeric (minmax, fit-content, calc…) show a not-allowed cursor; edit those in the inspector instead.

Hover is synchronized across surfaces

Hovering the 1fr chip tints that column's full extent on canvas
The matching inspector track row lights up

Hovering a chip tints the track's full extent on canvas and lights the matching row in the inspector; hovering an inspector row tints the canvas track. Useful for confirming which 1fr is which before you edit it.

Inspector: Columns & Rows

Columns and Rows track lists: count field, code toggle, +, and one row per track with value and unit

One list per axis. In the header:

  • Count field — type, step (↑/↓, Shift ×10), or drag to scrub the number of tracks (1–100). Growing appends copies of the last track; shrinking drops from the end.
  • **<> code toggle** — swaps the rows for the raw grid-template-* text, the full-syntax escape hatch (repeat(), auto-fill, named lines…):
Columns list in raw text mode showing 180px 1fr auto

Templates the structured UI can't represent (auto-fill/auto-fit, mixed multi-selections, >100 repeats) fall back to this text form automatically — nothing becomes uneditable, it just degrades to CSS.

  • **+** — appends a duplicate of the last track (same space-sharing behavior as the canvas + chip when a single box is selected).

Each track row is a scrubbable number plus a unit menu:

  • fr / px / % — switching unit converts through the track's current rendered size, so the layout doesn't jump; only the notation changes.
  • auto — the field grays out and shows the resolved pixel size (row 3 above).
  • — flips the row to free CSS text; this is how you type minmax(120px, 1fr) into a single track.
  • The at the row's end (revealed on hover) removes the track; it's disabled — not hidden — when only one track remains.

Gap, Auto, Alignment

Gap fields for both axes; Auto group with flow direction and implicit track sizes
Alignment 3×3 marker with per-axis stretch toggles and the Distribute row
  • Gap — column and row gap; the same values the canvas pills scrub.
  • Autogrid-auto-flow (which direction unplaced items fill) and the size of implicit tracks the browser creates when items overflow the explicit ones (grid-auto-columns/rows).
  • Alignment — the 3×3 marker writes justify-items + align-items together: how every item sits inside its cell. The two toggles next to it stretch one axis each (stretch is the CSS default — items fill their cells); picking a dot on the marker exits stretch.
  • Distributejustify-content / align-content: how the track grid as a whole distributes inside the box when the tracks don't fill it (start, center, end, space-between…).

Why did my canvas chrome disappear?

Distribute set to center with the note: Distributed tracks resize here, not on canvas
The same grid with column chips and pills gone; row chrome still present

Canvas editing is enabled per axis, and an axis switches off when its Distribute is anything other than start/stretch (the browser only exposes track sizes, not positions, so the chrome would drift). The passive guides stay, and the inspector says so right under the setting that caused it. The chrome also stays off for an axis whose template doesn't parse or that has implicit tracks.

Placement — when a grid child is selected

Placement section: area picker with a two-cell span painted, Align X/Y rows, and raw Col/Row inputs

Select an item inside a grid and the Placement section appears (the parent's canvas chrome stays up too).

  • Area picker — the parent's tracks in miniature. Click a cell to place the item there; drag to span a rectangle (above: columns 1–2, written as 1 / 3 — CSS line numbers, end-exclusive). The in the header clears placement back to auto flow.
  • Col / Row fields — raw grid-line syntax for everything the picker doesn't draw: span 2, 1 / -1, named lines. Spans and negative lines don't highlight in the picker but work fine.
  • Align X / Align Y — by default these edit the parent grid's items alignment (the same justify-items/align-items as the grid's own Alignment group, reachable without switching selection — grid is how plain CSS aligns things: on the container).

Aligning one item differently

The same rows switched to Self X/Self Y with a dashed auto segment and the scope toggle lit

The toggle at the row's end (item-in-cell icon) switches the rows to Self X / Self Y — per-item justify-self/align-self overrides, with a dashed auto segment meaning "inherit the grid's default". Items that already carry overrides open in this mode, so the state is never hidden. Clicking the lit toggle clears the overrides and the item follows the grid again.

CSS grid has no per-row or per-column alignment property, so "align the second column's items to the bottom" is: multi-select that column's items, flip the toggle, set Self Y once — the write fans out as one undo step.

Subgrid

When a grid box itself sits in a grid, each axis header grows a subgrid toggle (the leftmost button below). Switching it on sets that axis's template to subgrid: the box adopts the parent's tracks for perfectly aligned nesting. Switching it off gives the box one 1fr per track it was rendering, so the layout doesn't jump.

Columns list while subgridded: the toggle is lit and a card reads Columns come from the parent grid, with an Edit parent button

While an axis is subgridded its tracks aren't this box's to edit, so the track rows are replaced by a card that says where they live — Edit parent jumps the selection to the grid that owns them. Count and + disappear; the code toggle stays as the raw-text escape hatch (that's where line-name lists like subgrid [a] [b] go).

The Layers panel marks subgridded boxes with their own icon:

Layers panel: parent row with the grid icon, child row with the subgrid icon

Subgrid on canvas

A selected subgrid spanning three parent columns: dashed adopted lines, line numbers 1–4, faint parent guides below

A selected subgrid renders the parent tracks it spans as its own chrome:

  • dashed lines at the adopted interior boundaries;
  • line numbers renumbered from 1 — these are what placement inside the subgrid counts (the area picker for its children uses the same adopted tracks);
  • the parent's own guides soften to faint while the adopted chrome shows.

The adopted boundaries are still draggable — the drag is forwarded to the parent's template, with the same badge, undo, and Escape behavior as dragging on the parent itself:

Dragging an adopted boundary inside the subgrid: the badge shows the parent's tracks, 1.227fr · 0.773fr

There are no chips, +, or gap pills on a subgridded axis — track structure belongs to the parent. If the subgrid is shrink-wrapped off the parent's track edges (self-alignment other than stretch), the adopted chrome stays away rather than drawing in the wrong place.

Undo, cancel, commit

The same rules everywhere:

ActionRule
Any drag (boundary, gap pill, scrub, area-picker paint)Exactly one undo step for the whole gesture
Escape during a dragCancels just the drag — document restored to the exact pre-drag state, selection kept
Typed fieldsCommit on Enter/blur, revert on Escape
Multi-select writes (e.g. Self alignment on a column's items)Fan out as one undo step