Morning cup
300 ml · Two glazes
Price: $24
A row of products that runs off the page's edge: each drawn on its plate (ProductArt) with a hang tag for the price, the name as the card's link and a spec line in the mono face. The row scrolls sideways and snaps to a card, and a thin bar under it follows the scroll. A hover or the keyboard focus on a card picks its product up and swings its tag. Each picture can carry a view-transition-name, so a product page that names its picture the same way takes it over from the card. Zero JavaScript.
On the shelf
Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.
Variants · each row writes the attribute the prop writes; the section's own CSS answers
JavaScript of its own
0 B
CSS 18.5 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.
Browser support
Chrome 116 · not Firefox · Safari 26; needs scroll-timeline, timeline-scope. Elsewhere: the row scrolls and snaps; the bar under it is not drawn.
The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:
npx shadcn@latest add @moonarc-pro/product-rail---
import ProductRail from '@/components/moonarc/pro/ProductRail.astro';
---
<ProductRail
eyebrow="On the shelf"
more={{ href: '/pieces/', label: 'All pieces' }}
products={[
{ href: '/pieces/morning-cup/', name: 'Morning cup', spec: '300 ml', price: '$24', art: { name: 'cup', colour: 'var(--ma-block-1)', block: 2 } },
{ href: '/pieces/low-vase/', name: 'Low vase', spec: '18 cm', price: '$64', art: { name: 'vase', colour: 'var(--ma-block-4)', block: 2 } },
{ href: '/pieces/dinner-plate/', name: 'Dinner plate', spec: '27 cm', price: '$38', art: { name: 'plate', colour: 'var(--ma-block-3)', block: 4 } },
]}
>
<span slot="headline">This firing, <em>piece by piece</em></span>
</ProductRail>Without a key the registry answers 401 with an upgrade message.
| Prop | Type | Default | Description |
|---|---|---|---|
products | { href, name, spec?, price?, art: { name, colour?, accent?, block? }, transition? }[] | none | The products, in order. art.name is a ProductArt drawing; block the plate (the first four in turn when unset); transition a view-transition-name for the picture, unique on the page. |
eyebrow | string | none | A short line above the heading. |
headline | string | none | The heading. Slot "headline" wins and may hold an <em>, set in the emphasis face and colour. |
more | { href: string; label: string } | none | A link beside the heading, e.g. to the whole collection. |
label | string | 'Products' | Accessible name of the scrolling row. |
priceLabel | string | 'Price' | The words a screen reader hears before each price. |
level | 2 | 3 | 2 | Heading level of the section's heading; the products' names are one below. |
tone | 'surface' | 'muted' | 'ink' | 'brand' | unset (paints nothing) | The section's ground; the plates keep their blocks on every tone. |
density | 'compact' | 'cozy' | 'roomy' | 'cozy' | Block padding ×0.8 / ×1 / ×1.25 through --ma-density. |
The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.
.ma-prail.ma-prail__eyebrow.ma-prail__head.ma-prail__headline.ma-prail__item.ma-prail__more.ma-prail__progress.ma-prail__titles.ma-prail__track.ma-prail__viewport
slot="eyebrow"slot="headline"
The products and the tags stay where they are drawn; the row still scrolls.
ClientRouterCSS only; nothing to rebind.
Replaces: Product sliders (Swiper, Splide) · Featured product carousels (Shopify themes). See the migration table.
A product drawn flat, for a shop slot that has no photograph yet: 29 drawings in six sets (clothes, ceramics, coffee, skincare, furniture and objects, bakery).
Firing 08 is on the shelf
Cups, bowls and vases thrown by hand and fired a few dozen at a time.
Tall vase32 cm · StonewarePrice: $86
Morning cup300 ml · Two glazesPrice: $24
Breakfast bowl16 cm · Speckled clayPrice: $32
The first screen of a maker's site: the heading, a line, two actions and three facts on one side; on the other, the products themselves, each drawn on its plate (ProductArt) with its name, a spec line in the mono face and its price.
Price: $86
A tall vase for branches and long stems, thrown in two parts and joined at the shoulder.
9 of 24 left in this batch
Wrapped in paper and card, and sent the week after it comes out of the kiln.
The top of a product's page: the product drawn large on its plate (ProductArt), with its colourways under it as small swatches (radio buttons) that recolour the drawing in place; beside it the trail back to the shop, the name, the price, a line, how many of the batch are left, two actions, the specifications in the mono face and a few notes that open (care, delivery).
The shop
32 cm
Price: $86
300 ml
Price: $24
16 cm
Price: $32
18 cm
Price: $64
90 ml
Price: $18
28 cm
Price: $72
A whole collection on one page, filtered by kind: the products as cards (each drawn on its plate with ProductArt, a hang tag for the price, the name as the card's link and a spec line in the mono face) in two, three or four columns by the box's width, and above them a row of filters with their counts (core FilterGrid: the cards that stay slide into place, and the URL keeps the filter).
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown