Variant Swatches and Shade Pickers on Shopify
Definition
Color-accurate swatch and pill pickers on product pages and product cards, driven by a merchandiser-managed hex registry or metafield rather than a swatch app. Covers duo-tone shades, finish grouping, size pills with merchandiser-named labels, and swatch filters on collection pages. Replaces one of the most commonly rented app categories with native theme code.
The Evidence
- Evidence strength: 13 live, Deploi-attributed builds across 3 Shopify storefronts.
- Split by storefront: Nudestix 8, Three Ships 3, LUS Brands 2.
- Split by attribution: 8 Deploi-authored, 5 modifications of vendor theme code — named as such on each build page.
- Evidence quality: six carry code plus client working records. Seven are documented from the code; no client-side record of the decision survives.
- Confidence: 5 strong, 8 moderate.
- Surfaces: product pages, collection and search grids, home rails, cart-side upsell cards.
- Status: Live, verified 2026-09-06.
How It's Actually Built
Start with what a swatch is here, because it decides everything after it. In all 13 builds the swatch is Shopify's own variant radio input with a painted label on top of it. Nothing intercepts the product form and nothing holds a parallel selection state, so the picker inherits the theme's variant-change event, its price update and its media swap without being told any of them exist.
The color has to come from somewhere, and on a theme this old Shopify offers nowhere: Nudestix runs Dawn 6.0.2, which predates native swatch metaobjects on option values. So we made a place for it. settings.swatch_colors is one textarea holding roughly 250 Shade Name:#hex entries — one text field doing a database's job, and we know it.
A flat list doesn't survive a beauty catalog, so it carries two conventions. Name:#hex|Product Title scopes a hex to a single product, so the same shade word can be different pigment in two ranges. Name:#hexA&#hexB paints a duo shade as a hard-stop linear-gradient. Four surfaces read that one string — the PDP picker, the collection card, the kit modal and the cross-product selector — which is what keeps a single shade vocabulary everywhere a chip appears. Underneath sits a normalizer: product-get-options-strip-symbols.liquid strips 32 punctuation characters and downcases, so one key survives a CSS attribute selector, a data-value attribute and a JavaScript lookup.
Grouping is the second half of the pattern. Finish — Matte, Matte Lux, Bloom — is not an option dimension, because making it one would have multiplied the SKU matrix. It lives on variant.metafields.my_fields.stick-type, and the section pre-scans the variant list to decide whether to render one flat swatch row or one labelled fieldset per finish. Merchandising badges ride the same pass: custom.new_badge and custom.restocked render NEW and RESTOCKED pills, with an adjacency check on the neighbouring variants that fixes spacing when two badges land side by side.
The other two storefronts use the same primitive on a different problem. LUS Brands sells one formula in several sizes, so its picker is a pill rather than a chip: product-variant-options.liquid finds the first variant carrying an option value and reads variant.metafields.custom.option_custom_label, so a merchandiser writes "Travel" above "2.5 fl. oz" instead of leaving the shopper to infer it. Three Ships needs the choice on the grid, so one capture-phase click handler on document turns a card's size pills into a variant selection and posts the chosen id to /cart/add.js — capture phase specifically, because those cards are injected after load by carousels, upsell rails and section-rendered responses.
The Buy-vs-Build Position
The corpus's own verdict:
Built natively on all three storefronts.
That is a finding about three storefronts, not a ruling on the app category. Swatch apps are a reasonable purchase, and on a current theme Shopify's own option-value swatches cover the ordinary case for free — Three Ships' grid card reads option.swatch.image and option.swatch.color natively alongside the older approach. What pushed these three stores into theme code was narrower than "swatches": shade names reused across lines with different pigment, duo-tone shades needing two colors in one chip, finish grouping that is deliberately not an option, and a size pill that has to work inside a card that does not exist yet. Where that line sits is argued on the swatches and visual options decision page.
The Builds
Ten of the 13 have published pages. Two of the others — the cross-product shade and format selector and the four-mode filter UI — are documented and will follow. The third folds into a build page already published here.
Nudestix
- Color-accurate shade swatches driven by a merchandiser-editable hex registry — the registry itself: per-finish grouping, duo-tone chips, NEW/RESTOCKED badging. High complexity, product page and grid.
- Shade swatch rendering and per-variant content payload — the swatch layer plus a hidden per-variant mirror of copy, SEO and badge metafields, so a shade change is a DOM read. High complexity, product page.
- Grouped shade swatch picker with shade-finder dialog and GA4 shade-finder events — finish groups plus a native
<dialog>hosting a licensed shade quiz. High complexity, product page. - "Save With Sets": shade-selectable kit contents on the component product's page — the same swatch component inside a kit modal, one picker per component. High complexity, product page.
- On-model shade carousel bound two ways to the Dawn variant picker — a second visual treatment of the shade option, layered over the native picker instead of replacing it. Each cell carries the id of the Dawn radio it belongs to, so tapping a face clicks that radio and clicking a radio moves the carousel; the product form is never written to directly, which is why price, media and availability keep behaving the way Dawn wrote them. High complexity, product page.
Three Ships
- Size-pill variant picker and quick add-to-cart inside product grids — delegated selection on grid cards, add-to-cart blocked until a size is chosen. Medium complexity, collection, product and cart.
- Inline size picker on cart and PDP upsell cards — size pills inside the premium theme's own upsell card, with unavailable variants rendered as disabled pills. Medium complexity, cart and product.
- Collection grid card on a licensed theme: metafield merchandising, a badge precedence chain, a hover-swap swatch element and first-row loading hints — the swatch axis is matched from the locale file rather than from a hard-coded English option name, and each chip carries the first matching variant's media so hovering it swaps the card image; Shopify's native option swatches are supported alongside that. High complexity, collection, search, home and landing.
LUS Brands
- Pill variant picker with merchandiser-named size labels — Dawn's picker extended with two-line size pills labelled from a variant metafield. Medium complexity, product page.
- Vertical filter rail and an in-place mobile filter accordion replacing Dawn's drill-down drawer — the collection-side twin: swatch inputs inside Shopify's own storefront filters. High complexity, collection and search.
What This Rests On
- Liquid section schema and settings — 12 of the 13 builds; every merchandiser control here is a section setting or a block.
- Theme settings schema — where the shade registry lives, and why a new shade color is a merchandising action rather than a deploy.
- Metafields — 9 builds; finish, size label, badge state, marketing color text, per-product grouping.
- Custom elements — the variant-radios and selector components the swatch markup mounts inside.
- Cart AJAX API — the grid and upsell pickers add in place rather than routing to the product page.
- Storefront filtering and Section Rendering API — swatch facets, and the re-render they survive.
- Metaobjects — the badge definition read for per-variant social proof.
What Varies by Storefront
Theme base decides how much of this is ours. LUS Brands runs Dawn 15.4.1, so its picker is Dawn's picker with two snippets slotted into the existing product-variant-options contract — data-option-value-id, data-product-url and the visually-hidden unavailable label all stay. Nudestix runs Dawn 6.0.2, nine major versions behind and older than native swatch support; that gap is why a color registry had to be invented in theme settings rather than read off the option value. Three Ships runs Palo Alto 5.8.0 by Presidio Creative, a paid premium theme, so the grid card is vendor code we extended and the edits are narrow and named on the build page.
Catalogue shape decides the rest, and it is the bigger factor. A shade-led cosmetics catalogue is a different swatch problem from a haircare catalogue, not a bigger one. In cosmetics the shade is the decision, shade names collide across lines, finish is a grouping axis nobody modelled as an option, and one product carries dozens of chips — so the work is disambiguation and grouping. In haircare the same formula ships in three sizes and the question is which size, not which color, so the same primitive renders as a labelled pill and the whole problem is a per-variant label lookup. Skincare sits between: the choice is size and format, but it has to happen on the grid card, so the interesting part moves out of Liquid and into event delegation. Each storefront also has a different set of things already bound to the product form and the card — reviews, wishlist, upsell rails — so the picker coexists with a different neighbour list on each.
Where This Gets Hard
A color registry is a database with no schema. Roughly 250 entries in a textarea, with two hand-parsed conventions layered on top, is a merchandising asset that needs a named owner on the client side — someone whose job includes adding the chip the day a shade launches. It is also a contract, not just a list: four surfaces read that one string, and every new surface that shows a chip is a fifth reader of it. Handing the color list to a merchandiser hands them something the rest of the storefront depends on.
The parsing cost lands in Liquid, on every render. The registry string is split once per product render and then walked per option value, so the work grows with shades multiplied by registry size. That is fine at this catalogue size, and it is a real number that gets bigger rather than a free abstraction.
The conventions carry their own coordination cost. | for product scoping and & for duo-tone are a private format that several snippets have to agree on, and the people who edit the registry are not the people who read the parser.
So there are cases where we would tell you not to build this. On a current theme with unique shade names, Shopify's native option-value swatches already do it, and a registry buys you parity plus a maintenance job. For a handful of colorways, a swatch app is cheaper than the argument about who owns the hex list. And if nobody on the merchandising side will own that list after launch, an app that stores the colors for you fails more gracefully than a registry going quietly stale.
Related Capabilities
- Variant-aware content and media — what happens after the swatch is clicked.
- Metafield-driven PDP content blocks — where the finish, badge and label metafields are authored.
- Filters, faceted navigation and progressive grid loading — the collection side, where swatches become facets.
- Guided selling: quizzes, finders and routine builders — the shade-finder dialog next to the picker on one of these storefronts.
Answers to Help You Make Smart Decisions
Do I need an app to add color swatches to a Shopify product page? Not usually. Current Shopify themes read a swatch directly off the option value, and a theme that predates that can paint the chip in Liquid from a merchandiser-managed color list. All 13 builds here render the swatch as the theme's own variant radio, so the picker keeps the theme's price, availability and media behaviour without a second script on the page.
Why paint swatches server-side instead of with JavaScript? Because a swatch painted in Liquid is in the first frame. A client-side swatch layer re-renders the picker after paint, which is where the color flash comes from, and it puts the color data outside Shopify. Rendering in Liquid also means the correct chip is in the initial HTML, so it is there for a crawler and there before any script runs.
How do you handle two products that use the same shade name for different colors?
With a scoping convention in the color list. An entry written as Name:#hex|Product Title applies that hex only where the product title matches, so one flat registry can serve a catalogue that reuses shade words across ranges. It is the specific thing a name-to-hex map cannot do on its own, and the specific reason a bare map is not enough for beauty.
Can shoppers pick a variant without leaving the collection page? Yes. On one storefront here, size pills render inside the grid card and a delegated handler posts the chosen variant to the cart AJAX API. The rule that matters is in the code: add-to-cart is blocked until a size is selected, so an arbitrary default variant can never reach the cart.
Ready to Give Your Shades the Picker They Deserve?
If your product page hides half your range behind a dropdown, the problem isn't the range. Contact us today and we'll tell you whether your theme can paint the swatch natively — and what it would actually cost if it can't.