layout

A form grid in two classes and three custom properties — md-grid, md-grid-auto, --md-span, --md-grid-columns, --md-grid-gap. Sizing is a container query, so the same form stacks on its own inside a narrow column with no viewport breakpoints involved.

Form grid

Drag the width. The grid measures itself, not the window — below 30rem every field takes its own row regardless of how wide the browser is, and above it each field takes the --md-span columns it asks for.

Fewer columns

--md-grid-columns changes the track count on the container. A span left over from a 12-column layout can't overflow a 4-column one — it clamps to the tracks that exist, so the third field below asks for 8 and gets 4.

Auto grid

For tiles, where the count doesn't matter: md-grid-auto fits as many tracks as will hold --md-grid-min (16rem by default) and reflows by itself. No spans, no breakpoints.

Reflows

No column count to maintain.

Equal tracks

Each at least 16rem wide.

Tune it

Set --md-grid-min.

Or don't

The default is usually right.

Sample usage
<!-- 12 tracks; below 30rem of container width every field gets its own row -->
<form class="md-grid">
  <material-textfield label="First name" style="--md-span: 3"></material-textfield>
  <material-textfield label="Last name"  style="--md-span: 3"></material-textfield>
  <material-textfield label="Address"    style="--md-span: 12"></material-textfield>
</form>

<!-- Tiles, no counting -->
<div class="md-grid-auto" style="--md-grid-min: 20rem"> … </div>

<!-- React: same classes, span goes on style -->
<div className="md-grid">
  <MaterialTextfield label="Address" style={{ '--md-span': 12 } as CSSProperties} />
</div>

<!-- Angular -->
<div class="md-grid">
  <material-textfield label="Address" [style.--md-span]="12"></material-textfield>
</div>

<!-- Already on Tailwind? You don't need any of this: -->
<form class="@container grid grid-cols-12 gap-4">
  <material-textfield class="col-span-full @md:col-span-3"></material-textfield>
</form>