# Form-control sizing posture, fill by default, `[inline]` hugs

Source of truth: [ADR-0077](../../../../../../docs/ops/adr/adr-0077-form-control-sizing-posture.md)
(ratified 2026-08-19; amends [ADR-0037](../../../../../../docs/ops/adr/adr-0037-inline-display-mode-convention.md)
partial, `[inline]` semantics widen to own the sizing axis, the display-default
convention itself is unchanged). Companion SPEC:
`docs/ops/spec/spec-form-control-sizing-posture.md`. Read the ADR before
touching a form-control host's width/min-width rules: this file is the
routing pointer + the fix-now checklist, not a restatement of the ruling.

## The one-line rule

A form-control host, absent `[inline]`, is block-level (ADR-0037, unchanged)
**and fills its container's available inline space**, in block flow via
`width: auto` (already true), in flex/grid parents via `width: 100%` on the
host (the mechanism `field-ui`/`otp-input-ui` already shipped). `[inline]`
flips BOTH axes at once: inline-level display **and** shrink-wrap/hug sizing.
No separate `hug` attribute exists or should be minted, one token, one
posture flip.

## Where legibility floors live

**[retired 2026-08-28, ADR-0095]** The `--*-min-width: 20ch`-class floor
described below is no longer permitted anywhere, including on
`:scope[inline]`. A primitive never floors its own inline-size independent
of its own geometry: the only permitted floor is
`min-inline-size: var(--<component>-height)` (a square minimum). ADR-0095
Decision Class 1 removes the 20ch-class field-width floor for
`input-ui`/`select-ui`/`combobox-ui` (plus `--input-label-min-width`,
`--date-range-picker-trigger-min-width`) and replaces it with the square
floor, generalizing the `button-ui` keep-square pattern already named below
to the field-width components.

**[amended 2026-08-31, gh#2534, ADR-0095]** The square-minimum rule governs
a primitive's own OUTER inline-size floor against its own block-size only, it does not reach a component's INTERNAL grid-column sizing. A token like
`field-ui[inline]`'s `--field-label-inline-min: 10ch` floors the label
*column* inside the component's internal grid, to keep sibling rows
aligned, not to prevent the host itself from collapsing, so it is not
"a `--*-min-width: 20ch`-class floor" in ADR-0095's sense and is not
retired by the Decision above. Don't apply the square-minimum rule to an
internal grid-column token by analogy to the field-width class.

*(Historical, pre-ADR-0095 rule, superseded by the retirement above, kept
only for context on what the old convention was.)* A
`--*-min-width: 20ch`-class floor (the gh#781/gh#1633 pattern on
`input-ui`/`select-ui`/`combobox-ui`) used to belong on `:scope[inline]`
only, never on the plain `:scope`. In the fill state the container owns the
width, all the way down, no exception, no re-added floor. Two floor
classes are
explicitly NOT sizing-posture floors and stay in both states: inner
`min-width: 0` flex unclampers (the ellipsis-reset machinery) and
`min-width: var(--*-height)` squareness floors (`button-ui`'s keep-square
rule). Inner *segment* floors (`time-picker-ui`'s 2ch/2.5ch segments,
`tags-input-ui`'s 8em `[data-inline-input]`) are inner mechanics, not host
posture, and also stay in both states.

## Fix-now checklist for a form-control primitive

- Plain `:scope` (no `[inline]`): block-level, `width: auto` in block flow,
  `width: 100%` in flex/grid parents. No `min-width` legibility floor here.
- `:scope[inline]`: inline-level display, shrink-wrap sizing. Per ADR-0095,
  no `--*-min-width: 20ch`-class floor moves here any more, a floor-carrier
  component uses the square minimum (`min-inline-size:
  var(--<component>-height)`) instead, in both states.
- A component that wants to own its line but still hug has no attribute for
  that state (ADR-0077 Decision item 6, accepted consequence), tell the
  consumer to use explicit sizing (`width: fit-content` or an explicit
  width), don't invent a third posture token.
- `field-ui`/`fields-ui`'s `[inline]` carve-out (single-row layout meaning,
  ADR-0037 §5) is retained and stays behaviorally convergent with this
  decision, don't "fix" it to match the family's token meaning.
- The trip-wire gate is `scripts/release/check-display-default-convention.mjs`, it fails a 20ch-class host floor on the plain `:scope` and fails a
  missing fill mechanism in the default state, for roster components. One
  gate, no second check to satisfy.

## Roster and rollout

The exact component roster and the breaking-wave/golden-regeneration rollout
(one coordinated wave, MIGRATION GUIDE entry same cycle, deliberately
regenerated `evals/visual/` goldens, never additive-first, never shimmed)
are the companion PLAN's job (`docs/ops/plan/plan-form-control-sizing.md`),
not this reference, cite the ADR/PLAN rather than re-deriving scope here.
