Color Swatch Selector — Free HTML CSS JS Snippet

Color Swatch Selector · Forms · Plain HTML, CSS & JS · Live preview

Share & Support

What's included

Features

CSS custom property --c on each swatch: one declaration drives background + outline colour
Selected ring: border:3px solid #fff (gap) + outline:2px solid var(--c) (ring)
Out-of-stock: linear-gradient(45deg) diagonal white line via ::after
opacity: 0.35 + cursor:not-allowed + disabled on unavailable swatches
Size chips: same selected/unavailable pattern as colour swatches
getComputedStyle().getPropertyValue("--c") reads CSS custom property value in JS
Live preview: colour square + variant text update on every selection change
aria-pressed="true/false" on swatches for screen reader state announcements

About this UI Snippet

Color Swatch Selector — CSS Custom Property Colours, Selected Ring, Out-of-Stock & Live Preview

Screenshot of the Color Swatch Selector snippet rendered live

A colour swatch selector is the cornerstone of any e-commerce product page — it lets users choose a product variant by clicking a colour circle rather than selecting from a dropdown. For a free-form colour field, see the color picker input. This snippet provides a complete colour and size variant selector: circular swatches using CSS custom properties for colour, a selected ring that matches the swatch colour, an out-of-stock diagonal slash indicator, size chips with active/disabled states, and a live preview card that updates in real time.

CSS custom properties for swatch colours

Each swatch has a style="--c:#hexvalue" inline attribute. The CSS uses background: var(--c) to apply the colour. This means colours are declared once in HTML and used in both the background and the selected outline — outline: 2px solid var(--c) creates an accent ring that matches the swatch colour exactly, without needing a separate CSS rule per colour.

The selected ring pattern

The selected swatch uses two properties together: border: 3px solid #fff (white gap between swatch and ring) and outline: 2px solid var(--c) (the matching accent ring). This creates the standard e-commerce "selected colour" visual — a coloured ring separated from the swatch by a white gap. Both border and outline are required; one alone looks wrong.

Out-of-stock diagonal slash

The .unavailable swatch has a ::after pseudo-element with background: linear-gradient(45deg, transparent 43%, #fff 43%, #fff 57%, transparent 57%) — a diagonal white line across the circle. This communicates "out of stock" without removing the swatch from the UI (users can still see the colour exists). The swatch also gets opacity: 0.35 and cursor: not-allowed.

Size chips

Size buttons use a similar selected/unavailable pattern but as rectangular chips rather than circles. The .selected chip fills with indigo. The .unavailable chip has text-decoration: line-through and is disabled. The active state is managed by JavaScript removing .selected from all chips and adding it to the clicked one.

The live preview card

A preview card below the selectors shows the selected colour (as a coloured square), product name, the current variant combination as text ("Midnight Indigo · M"), and the price. This gives users an instant confirmation of their selection before adding to cart.

Persisting the selection

Store the selected variant in localStorage or a URL parameter for returning users. On page load: const saved = new URLSearchParams(location.search).get("colour"); if (saved) { const btn = document.querySelector(".swatch[data-name='" + saved + "']"); if (btn) btn.click(); }. On selection: history.replaceState(null, "", "?colour=" + encodeURIComponent(selectedColour)). This preserves the selection across page reloads and allows sharing a specific colour variant via URL.

Build with AI

Build, Understand, Optimize, and Extend It With AI

You don't have to puzzle out why the selected ring looks right just by staring at the CSS. Paste this snippet's HTML, CSS, and JS into an AI coding assistant like Claude and ask it to explain exactly why the selected swatch needs both a solid white border and a separate outline sharing the same --c custom property, and what would go wrong visually if either one were removed. The same assistant can help you optimize it — for instance asking whether reading the color back with getComputedStyle on every click is necessary versus reading the --c value straight from the button's inline style. It is also useful for extending the component: ask it to add keyboard arrow-key navigation between swatches within the radiogroup, support fabric or texture swatches using background-image instead of a flat color, or wire the selected variant into the URL so a specific color/size combination is shareable as a link. Treat the code less like a finished artifact and more like a starting point for a conversation.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a product color and size variant selector in plain HTML, CSS, and JavaScript — no frameworks, no component libraries.

Requirements:
- A row of circular color swatch buttons inside a role="radiogroup" container, where each swatch's color is set once via an inline CSS custom property (e.g. style="--c:#hex") and referenced by CSS for both its background and, when selected, its outline color — not two separate hardcoded color declarations per swatch.
- The selected swatch must show a solid white border creating a gap plus a separate outline in the swatch's own color, so the ring reads as a distinct halo rather than a plain border, and toggle aria-pressed appropriately on all swatches when selection changes.
- At least one swatch must be marked out of stock: disabled, dimmed with reduced opacity, and visually crossed out using a CSS pseudo-element diagonal gradient line rather than removing the swatch from the layout entirely.
- A separate row of rectangular size chips following the same selected/disabled visual pattern (fill color on selection, strikethrough text and disabled cursor when unavailable), toggled independently from the color swatches.
- A live preview card that updates immediately on any swatch or chip click to show a color swatch, the combined variant text ("ColorName - Size"), and a price, reading the actual applied color back from the DOM via getComputedStyle rather than a hardcoded lookup table.
- An "Add to cart" button that gives temporary visual confirmation (changed text and background color) for about two seconds after being clicked, then reverts.

Step by step

How to Use

  1. 1
    Click a colour swatch or size chip to selectThe selected colour gets an accent ring that matches its colour. The selected size chip fills with indigo. The preview card updates instantly to show the chosen combination.
  2. 2
    Update swatch colours using data attributesChange the style="--c:#hexvalue" on each .swatch button and update the data-name attribute with the colour name. Add or remove .swatch elements to match your product variants.
  3. 3
    Mark out-of-stock variantsAdd class="swatch unavailable" and disabled to any colour or size that is out of stock. The diagonal slash and disabled cursor appear automatically from CSS.
  4. 4
    Update the product name and priceEdit the .preview-name and .preview-price text in the HTML. The variant text (colour + size combination) updates automatically from JavaScript. Add logic to onUpdate() to change price based on the selected variant.
  5. 5
    Wire Add to Cart to your APIIn the add() function, replace the feedback with a fetch POST: fetch("/api/cart", { method:"POST", body: JSON.stringify({ sku: selectedColour + "-" + selectedSize, qty: 1 }) }). Show success state on resolve.
  6. 6
    Export in your formatClick "HTML" for a standalone file, "JSX" for a React component with selectedColour and selectedSize in useState, or "Tailwind" for a React + Tailwind CSS version.

Real-world uses

Common Use Cases

E-commerce product page colour and size variant selector
The standard product variant selection pattern on Shopify, WooCommerce, and custom e-commerce sites. Users click colour swatches and size chips before adding to cart. The selected combination feeds into the cart API as a variant SKU.
Interior design product colour options
Furniture, paint, and home decor products often have 20+ colour options. The swatch grid handles many colours in less space than a dropdown. Out-of-stock swatches remain visible with a diagonal line so users know the colour exists for future restocking.
Theme and personalisation colour picker
Let users pick an accent colour for their profile, dashboard theme, or notification colour. The swatch grid is faster than a colour picker for predefined brand colours. The selected ring gives clear confirmation of the active choice.
Car configurator and product customiser
Vehicle colour selectors, custom print colours, and configurable product pages all use the swatch pattern. Show the selected configuration in a live preview alongside the swatches for immediate visual feedback.
Study CSS custom properties for dynamic component theming
The --c custom property pattern shows how to use inline CSS variables to drive multiple CSS rules from a single HTML attribute. This technique applies to any component where a colour needs to drive multiple related styles (background, border, shadow, text).
Fabric and material selector for fashion e-commerce
Fabric swatches can use background-image: url("fabric-texture.jpg") instead of a flat colour. Use the same CSS structure but swap background: var(--c) for background: var(--swatch-img). The selected ring, out-of-stock indicator, and JavaScript selection logic work identically.

Got questions?

Frequently Asked Questions

Each swatch button has a CSS custom property --c set as an inline style attribute: style="--c:#6366f1". The CSS rule .swatch.selected uses outline: 2px solid var(--c) — this reads the custom property value from the element itself. Since each swatch has its own --c value, the outline colour matches the swatch background automatically. No JavaScript needed to set the ring colour — just add the .selected class and CSS does the rest.

Use getComputedStyle: const hex = getComputedStyle(btn).getPropertyValue("--c").trim(). This returns the CSS custom property value as a string. Note that getComputedStyle reads the computed value (after any CSS inheritance), while element.style.getPropertyValue("--c") reads only inline styles. For inline-set custom properties, both work. The .trim() removes any whitespace the browser adds around the value.

The title attribute on each swatch button provides a native browser tooltip: <button class="swatch" title="Midnight Indigo">. For a styled custom tooltip, use the CSS Tooltip snippet from the Navigation category: add data-tip="Midnight Indigo" to each swatch and apply the tooltip CSS. The tooltip text matches the data-name attribute, so you can generate it: btn.dataset.tip = btn.dataset.name.

Click "JSX" to download. Manage selectedColour and selectedSize with useState. The swatch onClick calls setSelectedColour(btn.dataset.name). Derive the outline colour from the selected swatch: the active ring uses the --c custom property which is set via inline style on each swatch element — this works identically in React. For the preview colour square, store the hex separately: setSelectedHex(btn.style.getPropertyValue("--c")).