Product Card
The card a product grid is made of. It composes image, price, rating, badge and button — so the pieces you already know keep behaving the way you already know.
Brass desk lamp
Warm, dimmable, and heavy enough to stay where you put it.
Prices and sales
Pass price, and compare-at for what it used to cost:
<x-wirekit::product-card name="Brass desk lamp" :price="3900" :compare-at="5900" :minor-units="true" currency="EUR" />
The old price renders struck through in a real <del>, and the whole story is
announced: "€39.00, was €59.00, 34% off". That comes from
price, which already does it properly — so the "Sale" badge on
the corner is pure decoration and carries nothing a screen-reader user would miss.
A compare-at that is equal to or lower than price is not treated as a sale.
It is not a discount, and striking it through would be a claim the numbers do not
support.
Stock
availability |
What happens |
|---|---|
in-stock |
Nothing (the default) |
low-stock |
A "Low stock" badge. The CTA stays live — it is a nudge, not a wall. |
out-of-stock |
An "Out of stock" badge, and the CTA is disabled and says why |
Both states are words, not a wash over the image. A reader who cannot resolve a gray tint sees a perfectly normal product.
Images
image-alt defaults to empty on purpose. The product name is already right there
in text, so an alt that repeats it makes a screen reader read the product twice.
Pass one when the image shows something the name does not:
<x-wirekit::product-card
name="Brass desk lamp"
image="/img/lamp.jpg"
image-alt="Lit and angled over a dark desk"
:price="3900"
/>
The link
href makes the whole card clickable — but the link is the product name, not
the card.
That is deliberate. Wrapping the card in one anchor makes the link's accessible name the entire card (image, price, rating, button text, all read as one breathless sentence), and it swallows the add-to-cart button, which cannot nest inside an anchor at all. The pointer gets the big target; the screen reader gets a short, honest name.
A different action
The action slot replaces the default CTA:
<x-wirekit::product-card name="Studio chair" :price="24900" availability="out-of-stock">
<x-slot:action>
<x-wirekit::button intent="neutral" class="w-full">Notify me</x-wirekit::button>
</x-slot:action>
</x-wirekit::product-card>
Props
| Prop | Type | Default | Description |
|---|---|---|---|
name |
string |
'' |
The product. Rendered as the heading. |
href |
string|null |
null |
Product page. Makes the card clickable; the name is the link. |
image |
string|null |
null |
Cover image. |
imageAlt |
string|null |
null |
Accessible name for the image. Empty by default — the name is already in text. |
price |
int|float|null |
null |
Current price. |
compareAt |
int|float|null |
null |
What it used to cost. Only treated as a sale when it is higher. |
currency |
string |
config | Currency code. |
minorUnits |
bool |
false |
Treat the amounts as cents. |
rating |
int|float|null |
null |
Average out of 5. |
reviewCount |
int|null |
null |
How many people rated it. |
description |
string|null |
null |
Short line under the name. Clamped to two lines. |
availability |
string |
'in-stock' |
in-stock, low-stock, out-of-stock. |
scope |
string|null |
null |
Class-scope override. |
Slots
| Slot | Description |
|---|---|
action |
Replaces the default add-to-cart button. |
Accessibility
- An
<article>, so a screen reader can move between products as units. - The name is the link, never the whole card — see above.
- The sale is announced by price ("was €59.00, 34% off"), so it never depends on seeing a strike-through or a red badge.
- Stock states are words in a badge, not a tint.
- A disabled CTA says why in its accessible name ("Out of stock: Brass desk lamp"). A disabled button with no explanation is a dead end.
- The review count sits next to the stars: "4.5 stars" from two people and from two thousand are different claims.
- A product nobody has rated renders no star row at all — an empty row implies zero stars, and unrated is not zero.
Keyboard Interaction
| Key | Action |
|---|---|
| Tab | Move to the product name, then to the CTA. |
| Enter | Follow the name to the product page. |
| Enter / Space | Fire the CTA when it has focus. |
Two stops per card, not one per element — the image, badges and rating are not focusable, because none of them do anything.