Specs must state constraints, not frozen values
Aliases: constraint specs · hug vs fill · min-max sizing · frozen pixels
What it is
What a handoff spec must encode is constraint-based annotation: relative to whom, minimum, maximum, hug contents or fill the container. The “13px” measured on one artboard width is a single sample. Writing the sample into the spec freezes a still as if it were the contract; implementation can match only at that width, and must guess at every other.
The point is not finer measurement. It is a different compilable object: layout engines consume relations, not pixel snapshots. A logo with a locked size, a hard safe-area floor, belong as frozen values because they do not flex. Spacing and sizes that recompute with container, copy, and density do not.
Why it happens
A box on screen is not a photocopy of the artboard. Parent width, copy length, type size, and density all re-solve the same element’s margin and extent. If engineering reads “left 16, width 343”, it has one solution from a 375-wide frame; at 320, or when a column narrows, that tuple is no longer legal. Relational specs hand the engine what may change and what must hold: hug contents, fill container, min 8 / max 24, relative to parent, relative to sibling. The engine re-solves at the new width and the spec still holds.
Frozen numbers are tempting because acceptance is done with a ruler between implementation and mock. The ruler measures distance between stills; it cannot see whether a constraint was encoded. Absolute positioning then nails that one frame, and flex was never handed off.
Where it stops holding
Print, icon grids, and panels that must register to physical keys have a fixed-pixel target; there is nothing to constrain. On a legacy canvas with no constraint engine, Hug/Fill is a note to a runtime that does not exist. A min/max written as a decorative range (“anywhere from 8 to 24”) still does not say where to stop. Brand color and radius are named values that should not follow the container; rewriting them as hug/fill mixes two kinds of decision.
Applying it
- For every flexing box, state one of: hug contents, fill container, or fixed — and if fixed, why it must not flex.
- Write spacing relative to an edge or a sibling, with a floor and a ceiling; do not write “it looks like 13 here”.
- Stretch the artboard to the narrowest and widest stops and check the spec still suffices; a stop that can only be recovered by eyeballing numbers is a missing constraint.
- How to check: give only the spec to an implementer who has not seen the mock, and have them lay out the component at both extremes. If they go back to measure 13px, or one extreme misses the intent, the constraint was not delivered.