Product grid
Stable Available in release assets
Arrange server-authored product summaries in an intrinsic collection grid while keeping every product link and commerce fact independent of layout.
Product grid primary example
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Show code
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Variant gallery
Each authored example uses the production assets and includes copyable markup.
Collection list
The collection remains a semantic list.
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Show code
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Related products
A named related-product list uses ordinary product links.
Show code
<ul class="thin-product-grid" aria-label="Related products">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Local card minimum
Customize intrinsic card size and spacing.
<ul class="thin-product-grid" style="--thin-product-grid-min: 12rem; --thin-product-grid-space: 2rem" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Show code
<ul class="thin-product-grid" style="--thin-product-grid-min: 12rem; --thin-product-grid-space: 2rem" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Anatomy
- Semantic collection list or wrapper
- Product summary children
- Durable product links
Variants and sizes
- Collection list — shown above
- Related products — shown above
- Local card minimum override — shown above
States and difficult content
Rendered fixtures exercise authored content and constrained layouts with the same production assets.
Long content and missing media
Long localized text and broken image resources retain text alternatives and durable links.
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="missing-image.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Show code
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="missing-image.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>Narrow RTL container
Reflow is exercised without changing reading or link order.
<div dir="rtl">
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>
</div>Show code
<div dir="rtl">
<ul class="thin-product-grid" aria-label="Field collection">
<li>
<article class="thin-product-card">
<a href="/products/field-pack">
<img src="field-pack.jpg" alt="Blue field pack">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/field-pack">Field pack</a></h2>
<p>$128.00</p>
</div>
</article>
</li>
<li>
<article class="thin-product-card">
<a href="/products/utility-pouch">
<img src="utility-pouch.jpg" alt="Sand utility pouch">
</a>
<div class="thin-product-card-body">
<h2><a href="/products/utility-pouch">Utility pouch with an intentionally long localized name</a></h2>
<p>CHF 1’234.50</p>
</div>
</article>
</li>
</ul>
</div>- One product — documented, example pending
- Many products — documented, example pending
- Narrow container — shown above
- Long localized product names — shown above
- Missing media — shown above
Interaction and keyboard behavior
This composition owns no JavaScript behavior. Links, images, lists, reading order, scrolling, and focus retain native behavior.
Accessibility
Choose semantic wrappers for the content, preserve logical source order, provide meaningful image alternatives and captions, and keep every destination available as an ordinary link.
- Contract
COMPOSE- Basis
- Composition of native landmarks, headings, lists, links, and forms; no APG widget
API
Use .thin-product-grid around product summaries. A ul remains appropriate when the products form a list. Override --thin-product-grid-min or --thin-product-grid-space locally.
Behavior tier: css-only
Custom properties
--thin-product-grid-min15remMinimum intrinsic product-card track size.--thin-product-grid-spacevar(--thin-space-m)Gap between product summaries.Fallback
Semantic content, media, product links, and captions remain in ordinary document order when layout styles are unavailable.
Assets and dependencies
/layouts.css
Dependencies: None
Source: packages/ui/src/style/layouts.ts
Stability evidence
- Item-specific API, state and promotion contract:
test:tests/unit/library/layout-promotion.test.ts - No-JS/no-CSS fallback, semantic accessibility and asset-path contract:
test:tests/browser/layout-promotion.spec.ts - Cross-browser difficult-content geometry, print and forced-colors screenshots:
test:tests/browser/layout-promotion-stress.spec.ts
Composition
Derived from the examples and style specs, not authored.