Skip to content

feat(examples): render variation options as swatches from the child product - #651

Merged
field123 merged 1 commit into
mainfrom
feat/531-colour-swatches
Oct 2, 2026
Merged

field123 merged 1 commit into
mainfrom
feat/531-colour-swatches

Conversation

@field123

@field123 field123 commented Oct 2, 2026 •

Copy link
Copy Markdown
Collaborator

Closes #531.

What changed

A variation option in examples/core can now render as a colour dot or a picture of the product. Otherwise it renders as text, as before. The product page and the search cards share one renderer, VariationOptions, so the two surfaces cannot drift.

Why it is built from the child product, not from a variation type

#531 assumed Elastic Path has a variation type. It does not:

  • Specs: in catalog_view.yaml and pim.yaml, a variation has only id, name, sort_order and options. An option has only id, name, sort_order and description.
  • PIM source: the model is Option{ID, Name, Description, SortOrder, Modifiers}, and no modifier sets a colour or an image.
  • Live store: the integration store's responses match the specs.

Branching on a variation called "Colour" would be a string heuristic. Elastic Path models variant-specific data on the child product instead. A child keeps its own main image, and its own shopper_attributes keys, through a rebuild of child products. So each option follows the parent's variation_matrix to its child product, holding the shopper's other choices fixed, and reads one of these, in order:

  1. shopper_attributes.color, if it is #rgb or #rrggbb: a colour dot, set with an inline style;
  2. otherwise the child's own main image: a thumbnail;
  3. otherwise the option name as text.

A kind applies to a variation only when it differs along that variation. Sizes that share one photo stay as text. A colour inherited from the parent shows nowhere. The README states exactly what the store must hold.

Both surfaces already load the child products with their main images, so this adds no new requests.

Behaviour

  • Radios on both surfaces. The product page moves from <button>s to radio inputs. The page gains arrow keys and a single tab stop per variation, and its variation heading becomes the group's <legend>. Text options look as they did.
  • Accessible names. A swatch keeps the option name for screen readers and as a tooltip. On the product page, the name of the selected option follows the variation name.
  • Selected state. The selected swatch has a dark ring, offset from the swatch, so it shows on light and dark colours alike.
  • Focus survives navigation. Choosing a variant on the product page navigates to the child URL, and the App Router remounts the page. The old buttons lost focus there too, but with arrow keys that broke every keypress. The page now refocuses the checked radio of the group the shopper changed. It holds that in a module-level variable, because React state does not survive the remount.
  • Selection works as before. The product page changes URL, price and image. The search card changes link and image, and a swatch choice still makes the card selectable for bulk add.

Verified live (integration store, next start)

  • Material: Plastic and Chrome have different images, so they render image swatches on the product page and on the search card.
  • Sandle and Green T-Shirt: the sizes share one image, so they stay as text.
  • Multi Variation: no images, so it falls back to text.
  • Chrome selected: URL and image change. Price is not shown, because the store has no price for the Material children. The price change is shown on Sandle's text options through the same control.
  • Keyboard: Tab reaches the group, and the arrows move between the options and navigate. Focus and :focus-visible persist across the navigation.
  • Phone width: no horizontal overflow on either surface.
  • Checks: vitest run (182 tests), tsc --noEmit and next build pass in examples/core.

Not covered, or left as is

  • Colour dots are unit-tested only. No child on the integration store has shopper_attributes.color.
  • Search cards show text first. A card shows text options until the variant lookup and image files resolve, then switches to swatches. This mirrors the existing "Pricing…" state.
  • Text options on the product page still mark the selection with the brand fill. That is today's rendering, kept as the issue asked.
  • Child page size (pre-existing). The product page fetches child products without a page limit, so a family larger than the default page would lose swatches, and prices, for the rest.

Screenshots

1. Product page: image swatches beside unchanged text

Material's children have different images, so they get thumbnails. Sandle's sizes share one photo, so they keep today's text buttons.

1a-product-image-swatches 1b-product-text-options-unchanged

2. Choosing an option changes the product

Chrome selected: the URL, main image and carousel move to the Chrome child, and the legend names the choice. The Material children have no price on this store, so Sandle shows the price change through the same control.

2a-swatch-selected-product-changed 2b-text-option-price-changes

3. Keyboard only

Tab reaches the group, and the arrow keys moved Chrome → Plastic → Chrome. The blue focus outline and the dark selected ring are both visible after each navigation.

3-keyboard-focus-and-selected

4. Search card

Before: thumbnails beside a text Size variation, and the card isn't selectable yet. After choosing Chrome: the card image and link move to the child, and the bulk-select checkbox is enabled.

4a-search-card-swatches 4b-search-card-swatch-selected

5. No usable swatch data falls back to text

Multi Variation's children have neither images nor colours.

5-no-swatch-data-falls-back-to-text

6. Phone width

Product page and search card at 375 px, with no horizontal overflow.

6a-phone-product-page 6b-phone-search-card

…roduct

A variation option on the product page and on a search card now shows as a
colour dot or as a picture of the product it leads to, read from the child
product through the parent's variation_matrix. Elastic Path has no variation
type, so the data decides: the child's shopper_attributes.color for a dot, its
own main image for a picture, and text otherwise. A kind applies to a
variation only when it differs along it, so sizes sharing one photo stay text.

Both surfaces share one radio-based renderer. The product page moves from
buttons to radios, gaining arrow keys and a single tab stop, and keeps focus
on the chosen option across the variant navigation.
@vercel

vercel Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
composable-frontend-core Ready Ready Preview Oct 2, 2026 3:24pm UTC
5 Skipped Deployments
Project Deployment Actions Updated
commerce-essentials Ignored Ignored Oct 2, 2026 3:24pm UTC
composable-frontend-algolia Ignored Ignored Oct 2, 2026 3:24pm UTC
composable-frontend-docs Ignored Ignored Oct 2, 2026 3:24pm UTC
composable-frontend-simple Ignored Ignored Preview Oct 2, 2026 3:24pm UTC
composable-frontend-subscriptions Ignored Ignored Oct 2, 2026 3:24pm UTC

Request Review

@changeset-bot

changeset-bot Bot commented Oct 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 8726b17

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@field123
field123 merged commit c2fbf83 into main Oct 2, 2026
9 checks passed
@field123
field123 deleted the feat/531-colour-swatches branch October 2, 2026 16:25

This branch was successfully deployed

1 active deployment
Preview – composable-frontend-core — 8726b17f Deployed Oct 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Render variation options as swatches from the child product

1 participant