Constraints — per-axis resize anchors
How an absolutely-positioned child responds when its parent box resizes. Horizontal: Left · Right · Left and right · Center · Scale; vertical: Top · Bottom · Top and bottom · Center · Scale. Anchors are CSS-native: the choice changes what is stored and emitted — real right: insets, calc(50% ± o) + translate: -50%, percentages — and the browser reflows children on any parent resize, including indirect ones (a fill-width parent resized by its grandparent, a fit box re-hugging, a grid track change). No anchor = Left/Top = the default behavior, unchanged.
Try it. Draw a box, draw a child near its right edge, select the child, click the right pin in the Display section's diagram, select the box, drag its east handle. The child holds its distance to the right edge; hold ⌘ mid-drag and it stops following ("raw resize").
Which nodes can be constrained
Children of an absolute-mode box, and flex/grid children opted out of the flow via the Absolute checkbox. Any node type. Top-level boxes on the canvas have no parent edge to pin to; children participating in a flex or grid flow belong to that flow instead.
| Selection | Row treatment | Hint |
|---|---|---|
| Absolute child of a box | Enabled | value tooltips |
| Flow child of flex/grid | Greyed (never hidden) | "Constraints need absolute position — … Check Absolute above to pin it." (non-box selections are pointed at dragging out of the flow instead) |
| Top-level box on the canvas | Greyed | "Constraints need a parent box — a box on the canvas has no edges to pin to." |
| Mixed contexts | Greyed | combined hint |
The feature surface
Inspector
A constraints row in the Display section — the outer half of the positioning story the Absolute checkbox starts: two icon-fronted dropdowns (the leading pin glyph tracks the value) beside a pin diagram. Position fields morph to the stored fact — X/Y (left/top/both), R/B (inset), C (center offset), % (scale share) — so the inspector never shows a stale number. A "both" axis reads Auto in W/H (the extent is browser-derived from the two insets); a Scale axis owns its dimension as a percentage and parks the Fit/Fill toggles.
Canvas
Dashed accent pin lines from a selected child to its anchored parent edges. Non-default anchors draw at rest (a deliberate fact deserves standing ink); every axis draws while the constraints row is being edited; a hovered diagram pin previews its would-be line at half strength. Lines stop 3px shy of the child (annotation ink never strikes the subject's outline) and 1px inside the parent's padding edge. Center is an interior tick composing to a +; Scale is both lines at half strength with the share riding the leading line.
While resizing
Resizing the parent needs no extra step — the browser reflows anchored children per their pins. Holding ⌘/Ctrl while resizing is raw resize: the long-standing snap bypass (canvas guides, and the spacing-token size magnet) also suspends constraint solving — children hold their rects live, with a "Constraints off" badge when the box has anchored children. Resizing an anchored child works in its anchored terms: a west drag on a right-anchored child grows it leftward while its right inset holds still.
When anchors clear
Anchors clear automatically when the context that gave them meaning ends: detaching a child to the canvas root, joining a flex/grid flow, toggling Absolute off, wrap/unwrap, reparenting via an agent, copying a node as a clipboard root. Wrapping a selection in a hugging box transfers the selection's anchors to the new box.
Interaction reference
Inspector
| Affordance | Trigger | Effect | History / cancel |
|---|---|---|---|
| H/V dropdown | pick a mode | axis re-anchors, visually still (the value converts so nothing moves); dims normalize (percent↔fixed) | one entry "Constraints" |
| Edge pin (diagram) | click | that edge becomes the axis anchor; clicking the sole active pin is a no-op | one entry |
| Edge pin | shift-click | combines with the opposite pin into Left-and-right / Top-and-bottom; shift-clicking a lit member removes it | one entry |
Center + | click | H and V center, one batched write | one entry |
| Scale | dropdown only | position and size become percentages (diagram reflects it as a dashed tie — no fake pin affordance) | one entry |
| R/B/C/% field | type/scrub | writes the stored value directly | "Move", scrub-coalesced |
| W/H on a Scale axis | type/scrub | writes the percent share | "Resize", scrub-coalesced |
Canvas / resize
| Affordance | Trigger | Effect | History / cancel |
|---|---|---|---|
| Parent resize | drag any handle | browser reflows anchored children per their pins | one coalesced step; Esc rolls back box + children |
| ⌘ held during parent resize | live, per frame | children hold their rects; release re-solves against the current size | same step; Esc rolls back everything |
| Shift+corner scale | during resize | subtree scales uniformly; anchors scale their px values, percent shares are invariant | one step |
| Move / nudge an anchored child | drag / arrows | the stored field moves (inset shrinks, offset shifts, percentages convert through the parent size) | one coalesced step |
| Reparent into an absolute box | drop / layers drag | modes preserved, values recomputed in the new parent | one step |
Agents (MCP)
update_styles accepts layout.constraints: {h?, v?} — {mode, inset?|offset?|pct?} per axis, null clears; omit the value to anchor at the current spot (keep-still conversion), pass it to place the anchor. x/y on an anchored axis convert to the anchored field automatically; percent dims validate against a Scale axis; move_nodes converts positions the same way and strips anchors on reparent. Reads ride the whole layout object in get_node_info.
Known limits (by design)
- Rotated children convert through their bounding-box measurements — the approximation every pin path accepts.
- Duplicating a scale-axis child skips the usual +10px paste offset on that axis (percent geometry has no parent size at clone time); the copy lands atop the source on that axis.
- Wrap/unwrap strip anchors from children (hug-wrap transfers them to the box); preserving them through union-wrap is a possible later polish.
- Website import parks
right/bottom/percent geometry in Other styles (raw CSS) rather than mapping it onto constraints — mapping it is the natural fidelity follow-up.