Product detail
Preview Available in release assets
Compose server-owned product media, price, inventory, variants, quantity, and purchase submission without duplicating commerce truth in the component.
Product detail in context
<article class="thin-product-detail">
<section class="thin-product-gallery" aria-labelledby="product-gallery-title">
<h2 id="product-gallery-title" class="thin-visually-hidden">Canvas tote images</h2>
<div class="thin-reel">
<figure data-media-item>
<a href="/products/tote/front-large.jpg" data-lightbox-open="tote-lightbox" data-media-index="0">
<img src="/products/tote/front.jpg" alt="Blue canvas tote, front view">
</a>
</figure>
</div>
<p><a href="/products/tote/images/">View all product images</a></p>
</section>
<div class="thin-stack thin-product-info">
<nav aria-label="Breadcrumb">
<a href="/collections/bags/">Bags</a>
</nav>
<h1>Everyday canvas tote</h1>
<p class="thin-product-price">
<span aria-label="Sale price">$68.00</span>
<s aria-label="Original price">$82.00</s>
</p>
<p class="thin-inventory-status" data-state="low-stock">
<svg aria-hidden="true" data-icon>
<use href="/icons/sprite.svg#circle-alert">
</use>
</svg>Only 3 left</p>
<form class="thin-purchase-form" action="/cart/items" method="post">
<fieldset class="thin-fieldset">
<legend>Color</legend>
<div class="thin-product-variants">
<label>
<input type="radio" name="variant" value="blue" required checked> Blue</label>
<label>
<input type="radio" name="variant" value="sand"> Sand</label>
<label data-state="unavailable">
<input type="radio" name="variant" value="black" disabled> Black โ sold out</label>
</div>
</fieldset>
<div class="thin-field">
<label for="product-quantity">Quantity</label>
<input class="thin-input" id="product-quantity" type="number" name="quantity" min="1" max="3" value="1" required />
</div>
<button class="thin-button" type="submit">Add to cart</button>
</form>
</div>
</article>Show code
<article class="thin-product-detail">
<section class="thin-product-gallery" aria-labelledby="product-gallery-title">
<h2 id="product-gallery-title" class="thin-visually-hidden">Canvas tote images</h2>
<div class="thin-reel">
<figure data-media-item>
<a href="/products/tote/front-large.jpg" data-lightbox-open="tote-lightbox" data-media-index="0">
<img src="/products/tote/front.jpg" alt="Blue canvas tote, front view">
</a>
</figure>
</div>
<p><a href="/products/tote/images/">View all product images</a></p>
</section>
<div class="thin-stack thin-product-info">
<nav aria-label="Breadcrumb">
<a href="/collections/bags/">Bags</a>
</nav>
<h1>Everyday canvas tote</h1>
<p class="thin-product-price">
<span aria-label="Sale price">$68.00</span>
<s aria-label="Original price">$82.00</s>
</p>
<p class="thin-inventory-status" data-state="low-stock">
<svg aria-hidden="true" data-icon>
<use href="/icons/sprite.svg#circle-alert">
</use>
</svg>Only 3 left</p>
<form class="thin-purchase-form" action="/cart/items" method="post">
<fieldset class="thin-fieldset">
<legend>Color</legend>
<div class="thin-product-variants">
<label>
<input type="radio" name="variant" value="blue" required checked> Blue</label>
<label>
<input type="radio" name="variant" value="sand"> Sand</label>
<label data-state="unavailable">
<input type="radio" name="variant" value="black" disabled> Black โ sold out</label>
</div>
</fieldset>
<div class="thin-field">
<label for="product-quantity">Quantity</label>
<input class="thin-input" id="product-quantity" type="number" name="quantity" min="1" max="3" value="1" required />
</div>
<button class="thin-button" type="submit">Add to cart</button>
</form>
</div>
</article>Anatomy
- Linked product gallery
- Product heading and price
- Server-rendered inventory status
- Native variant fieldset
- Native quantity and purchase form
Variants and sizes
- Neutral theme
- Thin-Sky theme
- Intrinsic narrow and wide containers
Gallery coverage: No structured variant fixtures are published yet; the primary example above is the only verified production markup.
States and difficult content
Rendered fixtures exercise authored content and constrained layouts with the same production assets.
- Available
- Sale
- Sold out
- Low stock
- Variant error
- Pending purchase
- Missing media
- Long localized content
Structured state fixtures are not published for this item yet.
Interaction and keyboard behavior
Links and forms own navigation and submission. Optional dialog, media, toast, confirm, or Datastar enhancement may coordinate mechanics but must render returned server truth.
Accessibility
Preserve landmarks, headings, labels, native controls, status text, focus order, image alternatives, and non-color state. Announce only server-confirmed changes.
- Contract
COMPOSE- Basis
- Native links, headings, forms, lists, and status content; no product-specific APG widget
- Evidence
tests/unit/library/ecommerce-patterns.test.ts
API
Use semantic HTML with .thin-product-detail. Keep price, inventory, variants, cart lines, totals, and errors in server-rendered markup.
Behavior tier: dependency-free
Structured host, property, and example tables are not authored for this item yet. Use the summary above and the primary example.
Fallback
Products, prices, inventory messages, variant choices, and cart actions remain server-authored links, forms, and content without optional enhancement.
Assets and dependencies
/foundation.css/layouts.css/components.css/patterns/ecommerce.css/behaviors.js/icons/sprite.svg
Dependencies: quantity-input, rating, toast, confirm-dialog, drawer, product-gallery, lightbox
Source: packages/ui/src/style/ecommerce.ts
Composition
Derived from the examples and style specs, not authored.