Staaarter

Advanced Variant Selector

A configurable product-options panel: native radio color swatches shown in each color's real value, size buttons that disable per the currently selected color's stock, and a material dropdown with its own price delta. Changing any option updates the displayed price and the 'in stock' message from a fixed stock lookup table.

By Staaarter Team
Ecommerce
Docs
Live preview

Docs

Installation

A real shadcn registry command, not a copy-paste stand-in: it fetches this block plus its button dependencies and any missing npm packages, and installs them straight into your project.

npx shadcn add https://staaarter.com/r/advanced-variant-selector.json

Prefer not to use the CLI? Copy the source from the Code toggle above into src/components/blocks/ecommerce/ecommerce16.tsx instead.

Usage

The file also exports ecommerce16Demo, the exact props behind the preview above. Spread it to get a working section in one line, then replace it with your own data.

import { Ecommerce16, ecommerce16Demo } from "@/components/blocks/ecommerce/ecommerce16";

export default function Page() {
  return <Ecommerce16 {...ecommerce16Demo} />;
}

Props

PropTypeDefaultDescription
productNamestring"Heavyweight Crewneck Sweatshirt"Product name heading.
basePricenumber4800Base price in cents, before any material price delta.
image{ src: string; alt: string }undefinedProduct photo; falls back to a placeholder when unset.
colorsColorOption[][]Color swatches, each rendered with its own real value as the swatch background.
sizesstring[][]Size labels, in display order.
materialsMaterialOption[][]Material choices for the select, each with its own price delta.
stockByVariantRecord<string, number>{}Stock count keyed by `${colorName}-${sizeLabel}`; a missing key is treated as 0 (out of stock).
classNamestringnoneExtra classes for the outer section element.

Types

interface ColorOption {
  name: string;
  value: string; // real CSS color, e.g. a hex code
}

interface MaterialOption {
  label: string;
  priceDelta: number; // cents, relative to basePrice
}

Behavior notes

  • Color, size, and material are all real useState, and every one of them feeds the displayed price and stock message live, not just for show.
  • Color swatches are real native `<input type="radio">` elements grouped by name, visually hidden with sr-only and styled entirely through the sibling label (a `has-[:checked]` ring shows the active swatch, `has-[:focus-visible]` shows a keyboard focus ring); each swatch's background is the color's own real hex value, the one documented exception to this repo's semantic-token-only rule since the swatch's whole point is showing the actual product color.
  • Size buttons are the same radio-as-button pattern, but a size becomes a real disabled input (not just muted styling) when stockByVariant has 0 (or no entry) for `${selectedColor}-${size}`, so it cannot be selected by mouse or keyboard while that color is active.
  • Switching color re-checks the currently selected size against the new color's stock; if that combination is out of stock, selection jumps to the first in-stock size for the new color instead of leaving a disabled size selected.
  • The material `<select>` adds its own priceDelta on top of basePrice; the displayed price recomputes immediately on change, it is not restricted to whole-dollar deltas.
  • The stock message has three states driven by the current color+size combination's count: 'Out of stock in this combination' at 0, an amber 'Only N left in stock' at 5 or fewer, and a plain 'N in stock' otherwise; it's wrapped in aria-live="polite" so screen reader users hear updates as they change options.
  • Add to cart is disabled whenever the current combination has 0 stock, but otherwise has no real cart wiring.