Server truth, resilient presentation
Ecommerce compositions
Product discovery and purchase flows composed from native links, forms, dialogs, media, and intrinsic layouts. Price, inventory, variants, and cart totals always arrive from the server.
Product card
A durable product link with explicit sale pricing and text availability.
<article class="thin-product-card">
<img src="product.jpg" alt="Blue canvas tote">
<div class="thin-product-badges">
<span class="thin-badge">Sale</span>
</div>
<div class="thin-product-card-body">
<h2><a href="/products/tote">Canvas tote</a></h2>
<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">Only 3 left</p>
</div>
</article>Show code
<article class="thin-product-card">
<img src="product.jpg" alt="Blue canvas tote">
<div class="thin-product-badges">
<span class="thin-badge">Sale</span>
</div>
<div class="thin-product-card-body">
<h2><a href="/products/tote">Canvas tote</a></h2>
<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">Only 3 left</p>
</div>
</article>Product detail
Gallery navigation, variant selection, quantity, and purchase submission remain useful without JavaScript.
<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>Collection, filters, and sorting
One addressable GET composition owns filters and sort. Submit either form to refresh the durable collection URL; no client sorting theater.
Spring carry
Durable bags for daily routes and longer departures.
<section class="thin-collection" aria-labelledby="demo-collection-title">
<header class="thin-collection-header">
<h2 id="demo-collection-title">Spring carry</h2>
<p>Durable bags for daily routes and longer departures.</p>
</header>
<div class="thin-collection-layout">
<aside>
<form class="thin-product-filters" action="/collections/bags" method="get">
<input type="hidden" name="sort" value="featured">
<fieldset>
<legend>Availability</legend>
<div class="thin-choice-row">
<input class="thin-choice" id="availability-in-stock" type="checkbox" name="availability" value="in-stock">
<label for="availability-in-stock">In stock</label>
</div>
<div class="thin-choice-row">
<input class="thin-choice" id="availability-sale" type="checkbox" name="sale" value="true">
<label for="availability-sale">On sale</label>
</div>
</fieldset>
<button class="thin-button" type="submit">Apply filters</button>
<a href="/collections/bags">Clear filters</a>
</form>
</aside>
<div class="thin-collection-results">
<div class="thin-results-toolbar">
<p class="thin-results-summary">
<strong>2 products</strong>
<span aria-live="polite">Sorted by featured</span>
</p>
<form class="thin-product-sorting" action="/collections/bags" method="get" aria-label="Sort products">
<input type="hidden" name="availability" value="in-stock">
<div class="thin-field">
<label for="product-sort">Sort by</label>
<select class="thin-select" id="product-sort" name="sort">
<option value="featured" selected>Featured</option>
<option value="newest">Newest</option>
<option value="price-asc">Price, low to high</option>
<option value="price-desc">Price, high to low</option>
</select>
</div>
<button class="thin-button" type="submit">Update results</button>
</form>
</div>
<div class="thin-product-grid">
<!-- server-rendered product cards -->
</div>
</div>
</div>
</section>Show code
<section class="thin-collection" aria-labelledby="demo-collection-title">
<header class="thin-collection-header">
<h2 id="demo-collection-title">Spring carry</h2>
<p>Durable bags for daily routes and longer departures.</p>
</header>
<div class="thin-collection-layout">
<aside>
<form class="thin-product-filters" action="/collections/bags" method="get">
<input type="hidden" name="sort" value="featured">
<fieldset>
<legend>Availability</legend>
<div class="thin-choice-row">
<input class="thin-choice" id="availability-in-stock" type="checkbox" name="availability" value="in-stock">
<label for="availability-in-stock">In stock</label>
</div>
<div class="thin-choice-row">
<input class="thin-choice" id="availability-sale" type="checkbox" name="sale" value="true">
<label for="availability-sale">On sale</label>
</div>
</fieldset>
<button class="thin-button" type="submit">Apply filters</button>
<a href="/collections/bags">Clear filters</a>
</form>
</aside>
<div class="thin-collection-results">
<div class="thin-results-toolbar">
<p class="thin-results-summary">
<strong>2 products</strong>
<span aria-live="polite">Sorted by featured</span>
</p>
<form class="thin-product-sorting" action="/collections/bags" method="get" aria-label="Sort products">
<input type="hidden" name="availability" value="in-stock">
<div class="thin-field">
<label for="product-sort">Sort by</label>
<select class="thin-select" id="product-sort" name="sort">
<option value="featured" selected>Featured</option>
<option value="newest">Newest</option>
<option value="price-asc">Price, low to high</option>
<option value="price-desc">Price, high to low</option>
</select>
</div>
<button class="thin-button" type="submit">Update results</button>
</form>
</div>
<div class="thin-product-grid">
<!-- server-rendered product cards -->
</div>
</div>
</div>
</section>Empty results
The server-rendered zero-result state explains the outcome and offers ordinary recovery destinations.
No bags match these filters
Try removing a filter or return to all bags.
<section class="thin-empty-results" aria-labelledby="empty-results-title">
<svg aria-hidden="true" data-icon>
<use href="/icons/sprite.svg#search">
</use>
</svg>
<h2 id="empty-results-title">No bags match these filters</h2>
<p>Try removing a filter or return to all bags.</p>
<p class="thin-empty-results-actions">
<a href="/collections/bags" >Clear all filters</a>
<a href="/collections">Browse collections</a>
</p>
</section>Show code
<section class="thin-empty-results" aria-labelledby="empty-results-title">
<svg aria-hidden="true" data-icon>
<use href="/icons/sprite.svg#search">
</use>
</svg>
<h2 id="empty-results-title">No bags match these filters</h2>
<p>Try removing a filter or return to all bags.</p>
<p class="thin-empty-results-actions">
<a href="/collections/bags" >Clear all filters</a>
<a href="/collections">Browse collections</a>
</p>
</section>Related products
The server selects recommendations; the browser only presents durable product links.
Show code
<section class="thin-product-recommendations" aria-labelledby="related-title">
<header class="thin-cluster">
<h2 id="related-title">You may also like</h2>
<a href="/collections/bags">View all bags</a>
</header>
<div class="thin-product-grid">
<article class="thin-product-card">
<div class="thin-product-media-fallback" role="img" aria-label="Product image unavailable">
<svg aria-hidden="true" data-icon>
<use href="/icons/sprite.svg#image">
</use>
</svg>
</div>
<div class="thin-product-card-body">
<h3><a href="/products/field-pouch">Field pouch with an intentionally long localized product name</a></h3>
<p class="thin-product-price">CHF 1’234.50</p>
</div>
</article>
</div>
</section>Mini-cart drawer
The drawer is optional enhancement: Cart (2) still points at a durable cart destination when JavaScript is unavailable.
Full cart destination
Without the drawer behavior, the cart link lands here with the same server-owned lines, subtotal, and checkout path.
Subtotal$166.00
<p><a href="/cart" data-dialog-open="mini-cart">Cart (2)</a></p>
<dialog class="thin-drawer thin-mini-cart" id="mini-cart" aria-labelledby="mini-cart-title">
<header>
<h2 id="mini-cart-title">Your cart</h2>
<button class="thin-button" data-variant="quiet" data-icon-only type="button" data-dialog-close aria-label="Close cart">
<svg aria-hidden="true" data-icon>
<use href="/icons/sprite.svg#x">
</use>
</svg>
</button>
</header>
<div data-panel-body>
<ul class="thin-cart-items">
<li>
<img src="/products/tote-thumb.jpg" alt="">
<div>
<a href="/products/tote">Canvas tote</a>
<p>Blue · Quantity 1</p>
<p>$68.00</p>
</div>
<form action="/cart/items/tote/remove" method="post">
<button class="thin-button" data-variant="quiet">Remove <span class="thin-visually-hidden">Canvas tote</span>
</button>
</form>
</li>
</ul>
</div>
<footer class="thin-stack">
<p class="thin-cart-total">
<span>Subtotal</span>
<strong>$68.00</strong>
</p>
<p>Shipping and tax calculated at checkout.</p>
<a href="/checkout" >Checkout</a>
<a href="/cart">View full cart</a>
</footer>
</dialog>Show code
<p><a href="/cart" data-dialog-open="mini-cart">Cart (2)</a></p>
<dialog class="thin-drawer thin-mini-cart" id="mini-cart" aria-labelledby="mini-cart-title">
<header>
<h2 id="mini-cart-title">Your cart</h2>
<button class="thin-button" data-variant="quiet" data-icon-only type="button" data-dialog-close aria-label="Close cart">
<svg aria-hidden="true" data-icon>
<use href="/icons/sprite.svg#x">
</use>
</svg>
</button>
</header>
<div data-panel-body>
<ul class="thin-cart-items">
<li>
<img src="/products/tote-thumb.jpg" alt="">
<div>
<a href="/products/tote">Canvas tote</a>
<p>Blue · Quantity 1</p>
<p>$68.00</p>
</div>
<form action="/cart/items/tote/remove" method="post">
<button class="thin-button" data-variant="quiet">Remove <span class="thin-visually-hidden">Canvas tote</span>
</button>
</form>
</li>
</ul>
</div>
<footer class="thin-stack">
<p class="thin-cart-total">
<span>Subtotal</span>
<strong>$68.00</strong>
</p>
<p>Shipping and tax calculated at checkout.</p>
<a href="/checkout" >Checkout</a>
<a href="/cart">View full cart</a>
</footer>
</dialog>