Product Art
BackgroundsA 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).
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). On a wide screen the picture holds while the details scroll past it. Zero JavaScript.
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.
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 20.6 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 105 · Firefox 121 · Safari 15.4; needs has. Elsewhere: the first colourway shows and the swatches do not recolour it.
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-detail---
import ProductDetail from '@/components/moonarc/pro/ProductDetail.astro';
---
<ProductDetail
name="Tall vase"
art={{ name: 'vase', block: 1 }}
ways={[
{ name: 'Sage', colour: 'var(--ma-block-2)', accent: 'var(--ma-block-3)' },
{ name: 'Ochre', colour: 'var(--ma-block-3)' },
]}
price="$86"
lede="A tall vase for branches and long stems."
trail={[{ href: '/pieces/', label: 'Pieces' }]}
batch={{ made: 24, left: 9 }}
primary={{ href: '#reserve', label: 'Reserve one' }}
specs={[{ label: 'Size', value: '32 cm tall' }, { label: 'Clay', value: 'Stoneware' }]}
notes={[{ title: 'Delivery', body: 'Sent the week after it comes out of the kiln.' }]}
transition="art-tall-vase"
/>Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: product-art.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | none | The product's name: the page's heading (level), and the picture's label. |
art | { name: string; block?: 1 | 2 | 3 | 4 | 5 } | none | The ProductArt drawing and the colour block of its plate. |
ways | { name: string; colour: string; accent?: string }[] | none | One to six colourways; the first is shown first. colour and accent are any CSS colour (var(--ma-block-2) follows the palette). |
price | string | none | The price, in the mono face. |
unit | string | none | After the price, smaller: per bag, each. |
lede | string | none | A line under the price. |
trail | { href: string; label: string }[] | none | The trail back: the shop, a kind. A nav named by trailLabel. |
batch | { made: number; left: number; text?: string } | none | How many the batch makes and how many are left; text is the sentence with {left} and {made}. |
primary | { href: string; label: string } | none | The main action, e.g. to the pre-order form. |
secondary | { href: string; label: string } | none | The second action, outlined. |
specs | { label: string; value: string }[] | none | The specifications, as a description list. |
notes | { title: string; body: string }[] | none | Short notes under the specifications, each a details element that opens on its title. |
transition | string | none | A view-transition-name for the picture, unique on the page: a card that names its picture the same way hands it over. |
waysLabel | string | 'Colour' | The colourways' legend. |
trailLabel | string | 'Breadcrumb' | Accessible name of the trail. |
priceLabel | string | 'Price' | The words a screen reader hears before the price. |
level | 1 | 2 | 1 | The name's heading level: 1 as the product page's own heading, 2 under another h1. |
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-art.ma-pdetail.ma-pdetail__actions.ma-pdetail__batch.ma-pdetail__btn.ma-pdetail__btn--primary.ma-pdetail__btn--secondary.ma-pdetail__count.ma-pdetail__dot.ma-pdetail__info.ma-pdetail__inner.ma-pdetail__lede.ma-pdetail__legend.ma-pdetail__media.ma-pdetail__meter.ma-pdetail__name.ma-pdetail__note.ma-pdetail__notes.ma-pdetail__plate.ma-pdetail__price.ma-pdetail__specs.ma-pdetail__swatches.ma-pdetail__trail.ma-pdetail__unit.ma-pdetail__way.ma-pdetail__way-name.ma-pdetail__ways
none
A new colourway paints at once instead of easing in, and the batch bar stands at its value.
ClientRouterCSS only; nothing to rebind.
Replaces: Product pages (Shopify themes) · Variant pickers. 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).
Contact · sample page
Sample channels on the left. The form on the right is real and sends nothing here: fill it in and watch the button.
Demo: nothing is sent.
A contact section in two columns: the ways to reach you (email, phone, office) on one side, a form that really sends on the other.
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).
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.
Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown