registry:ui · Layout · alpha
prototype-grid — the standardized grid
The layout family, alpha track: the design studio's property panel edits these exact props. Three laws carry over from prototype-flex — single root + rest spread, zero translation, inline style only — plus the track coercion: cols/rows number → repeat(N, minmax(0, 1fr)) (the no-max-content-blowout form), strings stay verbatim, and grid-template-areas passes through as one template string.
$npx jixoai-ui add prototype-grid
https://ui.jixoai.com/r/prototype-grid.json
overview
Overview
The flex contract carries over, plus one real coercion: track forms. Everything else — stamps, forwarder axes, host-agnosticism — is the alpha lane verbatim.
The three alpha laws hold unchanged from prototype-flex: SINGLE ROOT + REST SPREAD (one
div; consumer attributes first, family stamps after — replace, never merge), ZERO
TRANSLATION (the props are CSS: cols/rows/gap/areas pass through as
grid-template-columns / -rows / gap / grid-template-areas verbatim), and INLINE STYLE
ONLY (no utilities, no theme tokens, no css file — the item renders in any host).
The one real coercion is the TRACK FORM: a number N becomes repeat(N, minmax(0, 1fr)) — the
no-max-content-blowout form (min(0, 1fr) floors each track at zero so a wide chip can
never stretch the column past its fr share); a string stays verbatim for named and
hybrid tracks. Measured in the rig below, against the container's RESOLVED track
geometry (px per track from computed grid-template-columns), not the style string:
three equal tracks at cols={3}, the auto-fit form reflowing its track count
with the viewport, and a verbatim 1fr 2fr 1fr ratio resolving to a 1:2:1 px split.
The eight axes are forwarders exactly as on prototype-flex — all no-own, carriers in the consumer style attr, density stamping its rung for descendants, theme riding the .dark bridge, size echoing on the root. The grid-specific accessibility truth: auto-placement follows the DOM in BOTH dimensions, so plain grids keep source order — but the AREAS form lets you place items anywhere, and a template that visually reorders meaningful content breaks the source-order contract the same way row-reverse does on flex.
prototype-grid · the rig
The property panel, live: cols form, rows and gap are real props — measured end-to-end in task 36 against the container's RESOLVED grid-template-columns (px per track), not the style string.
- cols form
- rows
- gap (px)
the cols select demonstrates BOTH coercion forms and the verbatim ratio: watch the resolved tracks in the readout below the container — auto-fit re-counts its tracks as the viewport changes. rows=2 floors the container at two explicit minmax(0, 1fr) rows.
<PrototypeGrid cols={3} gap={12}>
<span>one</span><span>two</span><span>three</span>
</PrototypeGrid>
<!-- number → repeat(N, minmax(0, 1fr)); string stays verbatim -->
<PrototypeGrid cols="repeat(auto-fit, minmax(140px, 1fr))" gap="0.75rem">
<span>cards</span><span>reflow</span><span>by</span><span>width</span>
</PrototypeGrid>resolved tracks, measured live against this served container (probe, task 36; px receipts name their seat — re-measures match to the rounding only at the named widths): cols=3 → three equal tracks (240px each at 1400px viewport, style declaration repeat(3, minmax(0px, 1fr))); the auto-fit form shrinks its tracks to the 140px floor (measured 145px at a 760px viewport) and re-counts past it; "1fr 2fr 1fr" resolves a 1:2:1 px ratio (180/361/180 at 1400px); rows=2 pins two equal row tracks; gap 30 → 30px column-gap (the tracks absorb it, 240 → 228px at 1400px).
law
The alpha contract + the track coercion
The three flex laws carry over verbatim; the one grid addition is the track form — number → repeat(N, minmax(0, 1fr)), string verbatim, areas as one template string.
1 · SINGLE ROOT + REST SPREAD — one div, consumer attributes first, family stamps
after. 2 · ZERO TRANSLATION — cols/rows/gap/areas are the CSS grid properties;
the sole vocabulary event is the NUMBER form of cols/rows, a coercion to repeat(N, minmax(0, 1fr)) chosen for the
no-max-content-blowout guarantee (a track can never grow past its fr share because a
chip is wide — the css-architecture grid law's vocabulary). 3 · INLINE STYLE ONLY —
zero tokens: a grid track you pass is a track that renders, in any host. The areas
form is deliberately a single verbatim string in v0 (an array-join convenience is a
recorded future enhancement, not guessed).
The layout trio, cross-referenced: prototype-flex — the row prototype-grid — the tracks prototype-waterfall — the columns — one contract, three engines.
types
Types — the track forms, rendered
The coercion inventory: the number form (repeat(N, minmax(0, 1fr))), the verbatim string form (named/hybrid tracks), and the areas form with placed children — one element, three ways to say where things go.
prototype-grid · track forms
<PrototypeGrid cols={3} gap={12}>
<span>one</span><span>two</span><span>three</span>
</PrototypeGrid>
<!-- number → repeat(N, minmax(0, 1fr)); string stays verbatim -->
<PrototypeGrid cols="repeat(auto-fit, minmax(140px, 1fr))" gap="0.75rem">
<span>cards</span><span>reflow</span><span>by</span><span>width</span>
</PrototypeGrid>usage
Usage
Pass a number for the even-lanes form, a string for named/hybrid tracks — or omit and let the implicit grid serve.
<PrototypeGrid cols={3} gap={12}>
<span>one</span><span>two</span><span>three</span>
</PrototypeGrid>
<!-- number → repeat(N, minmax(0, 1fr)); string stays verbatim -->
<PrototypeGrid cols="repeat(auto-fit, minmax(140px, 1fr))" gap="0.75rem">
<span>cards</span><span>reflow</span><span>by</span><span>width</span>
</PrototypeGrid>api
API
The one vocabulary coercion: number → repeat(N, minmax(0, 1fr)) — strings stay verbatim. Everything else rides through as native div attributes.
Properties
| Property | Type | Default | Description |
|---|---|---|---|
| cols | number | string | — | grid-template-columns: number → repeat(N, minmax(0, 1fr)) (the no-max-content-blowout form); string verbatim (named/hybrid tracks). |
| rows | number | string | — | grid-template-rows, same two forms as cols. Omitted → implicit content-sized rows. |
| gap | number | string | — | gap: number → px, string verbatim. |
| areas | string | — | grid-template-areas, verbatim — a single template string. A template that visually reorders meaningful content breaks source-order reading (the a11y note). |
| class | string | — | Passed through verbatim — the component owns no class of its own. |
| ...rest | HTMLAttributes<HTMLDivElement> | spread | Every other attribute lands on the single root (stamped data-jx-prototype-grid after the spread — replace, never merge). |
Universal props
| Property | Type | Default | Description |
|---|---|---|---|
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | the base scale — root font-size; parts size in em |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' · ambient scope | corner geometry (CSS corner-shape; §14 degrade table) |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | corner size; auto = the concentric broadcast (§3) |
| density | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' · ambient scope | spacing/leading scale over the kernel channels (§4) |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' · ambient scope | the hue axis of the fixed oklch primary system (§5) |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' · ambient scope | light/dark profile; system = the JS-mutable global (§6) |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' · ambient scope | official M3 levels over the surface ladder (§7) |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' · ambient scope | intensity across the motion kernels (§8) |
axes
The eight axes on prototype-grid
The zero-translation posture extends to paint: the first-time contract is ALL NO-OWN, and the family paints nothing for any axis — carriers join the consumer style attr, density stamps its rung for descendants, theme rides the .dark bridge, size echoes on the root (fr and em tracks reflow with it). Every row below is a forwarder, measured by stamp receipts rather than paint (there is no family paint to measure).
| Property | Type | Default | Description |
|---|---|---|---|
| density | '2xs' | 'xs' | 'sm' | 'default' | 'lg' | 'auto' | number (+ the five legacy spellings) | 'auto' | FORWARDER — the resolved rung stamps data-density on the grid root (measured data-density=lg with an explicit lane; null ambient), opening the kernel channels for DESCENDANTS; the track geometry consumes none. Number unit: coefficient. |
| size | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | FORWARDER, ECHO MEASURED — the §11 stamp lands inline as font-size: var(--jx-size-effective, 1rem) (the carrier chain, measured in the style attr; computed 18px at size={18}), so fr/em-inheriting tracks and children reflow. Number unit: px. |
| shape | 'round' | 'scoop' | 'bevel' | 'notch' | 'square' | 'squircle' | 'auto' | 'auto' | FORWARDER — carrier stamp only; zero family readers (an inline-style-only family: no css file, no tokens). Number unit: none. |
| radius | 'small' | 'medium' | 'large' | 'auto' | number | 'auto' | FORWARDER — carrier stamp only; zero family readers. Number unit: px. |
| color | 'primary' | 'secondary' | 'error' | 'warn' | 'success' | 'info' | 'auto' | number | string | 'auto' | FORWARDER — carrier stamp only; children re-tint through the ambient chain if they read it. Zero family readers. Number unit: hue degrees. |
| theme | 'light' | 'dark' | 'system' | 'auto' | 'auto' | FORWARDER — dark rides the .dark class bridge on the grid root (measured with theme="dark"), re-scoping token chains for the subtree; the track geometry is theme-blind. No number lane. |
| elevation | 'level-1' | 'level0' | 'level1' | 'level2' | 'level3' | 'level4' | 'level5' | 'auto' | number | 'auto' | FORWARDER — carrier stamp only; zero family readers. Number unit: dp. |
| motion | 'reduced' | 'subtle' | 'normal' | 'expressive' | 'auto' | number | 'auto' | FORWARDER — carrier stamp only; the layout has no transitions of its own (grep receipt: zero transition declarations in the family). Number unit: coefficient. |
Receipts: the rig's three controls measured end-to-end against the container's RESOLVED track geometry (computed grid-template-columns in px per track: three equal 240px tracks at cols=3; the auto-fit form shrinking to its 140px floor (145px measured) before re-counting; the verbatim 1fr 2fr 1fr ratio resolving 180/361/180; rows=2 pinning two equal row tracks; gap 30 → 30px column-gap with the tracks absorbing it) — task 36; the density rung stamp (data-density=lg explicit, null ambient), the size carrier (font-size: var(--jx-size-effective, 1rem), computed 18px at size={18}) and the dark class bridge were measured on this page's served DOM; the zero-reader rows carry grep receipts over ui/prototype-grid/ (an inline-style-only family). The query() seat below rides the md viewport key (48rem) on the size LANE — the lanes-vs- passthroughs boundary (task 35) applies: cols/rows/gap/areas reject query().
<script lang="ts">
import PrototypeGrid from '@ui/prototype-grid.svelte';
import { query } from '@lib/universal-props-query.svelte';
</script>
<!-- below 48rem the base (13px root) applies; at 48rem+ the md case
(18px) wins — the §11 stamp scales the root, fr tracks reflow -->
<PrototypeGrid cols={3} size={query({ md: 18 }, 13)} gap={12}>…</PrototypeGrid>prototype-grid · query()
Media keys are min-width: below 48rem the base (13px root) applies; at 48rem and wider the md case wins (18px) — the §11 stamp scales the root and the fr tracks reflow around it. The number lane goes bare. Resize across 48rem.
<script lang="ts">
import PrototypeGrid from '@ui/prototype-grid.svelte';
import { query } from '@lib/universal-props-query.svelte';
</script>
<!-- below 48rem the base (13px root) applies; at 48rem+ the md case
(18px) wins — the §11 stamp scales the root, fr tracks reflow -->
<PrototypeGrid cols={3} size={query({ md: 18 }, 13)} gap={12}>…</PrototypeGrid>PrototypeGrid · universal props
<PrototypeGrid cols={3} size={18} density="small">…</PrototypeGrid>a11y
Accessibility
A layout primitive: no role, no keys, no announcements of its own — its a11y story is PLACEMENT: auto-placement keeps source order, the areas form can break it.
Keyboard
| Key | Action |
|---|---|
| — | Not a focus stop and no keys of its own — a plain div wrapper |
ARIA
| Attribute | Value | Description |
|---|---|---|
| auto-placement | source order | Without areas/explicit placement the grid places children in DOM order across the tracks — reading order and visual order agree (WCAG 1.3.2). |
| the areas trade | placement vs sequence | grid-template-areas can place any item anywhere; a template that visually reorders meaningful content breaks source-order reading — keep the template a rearrangement of regions, not of reading order. |
| rest-spread ARIA | verbatim | aria-label, role, data-* — anything in the rest lane lands on the single root after the spread, so consumer semantics compose with the layout stamps. |
| role | none authored | The container is a plain div; give it a role only when its children need grouping semantics. |
| theme / density stamps | subtree-scoped | The .dark bridge and data-density rung re-scope token chains for descendants — visible only to children that read them. |