Horizon Works
Changelog
Straight from the thatworkagency/horizonWorks repo — every merge to main shows up here.
Added
Group Card Object (HW)new block that renders one Group (HW) per entry picked from a
card_objectmetaobject list, so the repeated card is designed once and bound to each entry's fields with the dynamic source picker instead of building every card by hand. Manually added Group blocks render first, ahead of the dynamic ones. Standalone alternative to Loop – Card Object (HW) for when the cards need to sit inside an existing section/Group/Grid rather than get their own carousel/grid section.Layout-transparent (
display: contents) — it adds no wrapper of its own, so the row/column direction of the repeated cards comes from whatever Group/Grid/section the block is placed inside, not from a setting on this block.
Fixed
Hero Card (HW)content children placed directly on the card now respect their own Width setting (Fit content / Custom / percent) instead of always filling the card, and stay inside the card instead of being cut off when Max width is wider than the card itself.
The card was forcing a full width on every content child, overriding the child's own width. The card now only caps width, and that cap is clamped to the card so a Fit content, Custom or content-width child can no longer overflow a card narrower than Max width and get clipped.
Header menu (HW)removed the
Uncaught (in promise) Error: Invalid element types in <OverflowList />console error that appeared on every page load.The block's inline
<overflow-list>shadow template contained only the list and default slot, butassets/overflow-list.jsrequires themoreandoverflowslots, the placeholder and the overflow container during initialisation, even when the list isdisabled. The template now includes them, hidden by its existing inline style, so the menu looks and behaves exactly as before.
v4.1.0#
13 added3 changed1 fixed1 removed
Added
Quick Add Popup (HW)new product popup that merchants compose with ordinary blocks, fetched for the clicked product rather than baked into every card. Independent of Theme settings → Quick add (the overlay on the card). Edit the contents on the quick-add product template; Theme settings → Quick Add Popup (HW) owns the shell (size, overlay, close button).
Ships with a default layout: product image, title, variant picker, quantity, line price and add button. The section accepts every public theme block, so the tree is free to rearrange.
The first open learns the section id and later opens are a cheap section fetch; Preload popup on hover on the trigger (on by default) means a second click is instant.
Adding this section to an ordinary page from Add section will not open as a popup — it only works through the quick-add template and the trigger block.
Quick Add Button (HW)new trigger for that popup. Drop it on Product card (HW), or over the image via Absolute (HW) inside Product card media (HW). The block's presence is the opt-in; the older overlay quick add is unchanged.
Label / icon / icon-only, button presets, Show on card hover only, and a separate label for single-variant products (for example "Add" rather than "Choose").
Add areanew product-form block that holds quantity, line price and the add button in any order or nesting — they share the form rather than being wired parent-to-child. Do not nest it inside another buy-buttons form.
Quantitynew styleable quantity selector for use inside Add area (width, button and icon size, colours). Behaviour and quantity rules are the same as the core selector.
Line pricenew price that shows variant × quantity and updates as either changes. Can sit inside or outside the add area.
Variant picker (popup)the picker to use in this popup — updates without a network request and works on collection, search and home pages, which the PDP picker does not.
Add buttonnew add-to-cart control using the same style surface as Button (HW), plus an icon-only mode. Place it inside Add area; Add to cart (HW) remains the PDP buy-buttons child.
Product image (fixed)new product-sourced image (same size tier as Image md), with Use selected variant image so the popup photo follows the picker. Falls back to the featured image when a variant has none; inside a product card it stays a static featured image because the card already owns its gallery.
Product card (HW)now accepts Quick Add Button (HW) as a child, so the trigger can sit in the card flow beneath the media.
Product card media (HW)now accepts Quick Add Button (HW) and Absolute (HW), so the trigger can be placed at custom insets over the image.
Group (HW, max)new widest Group variant for the cases
hw-groupcannot express — top/bottom margin (with its own desktop override), per-side border switches over one shared thickness/colour/opacity, a custom border colour, and the shadow-blur / backdrop-blur pair already onhw-absolute. Everything else is identical to Group (HW), which stays the default: max is the step-up for one block, not a replacement, because every setting a merchant touches is stored in the template.Renders through the existing
snippets/hw-group.liquidwith a newis_maxparam, sohw-groupand the-basic/-card/-link/-hovervariants are byte-identical to before. New files:blocks/hw-group-max.liquid,snippets/hw-group-max-style.liquid.No schema changes to register it:
@themealready admits every public block, so every host offering@themepicks it up automatically. The few older schemas that enumerate children by hand instead of using@themedo not offer it yet, which resolves when those gain@theme.Additive and backward compatible: no existing block, section, snippet or template changes behaviour.
HW Video / HW Card (HW)the custom video play icon can now be rounded — a Play icon corner radius slider (0–60 px) with an optional Custom desktop corner radius override, so the icon can be squared on mobile and circular on desktop (or vice versa). Defaults to
0, so existing videos and card background videos are unchanged.HW Carousel Navigationan Arrow border sides group — Top / Bottom / Left / Right switches over the existing thickness and the enabled/hover/disabled colour pickers, the same shape Group (HW, max) uses. Sits under the arrow styling group and only appears once Border thickness is above 0. These apply to the border drawn on each prev/next button itself, not to the navigation block's wrapper, which has no border of its own.
The prev and next arrows can take different edges: a Set next arrow sides separately switch reveals a second Next arrow border sides group with its own four. Off — the default — the next arrow mirrors the four above, so the pair stays in step unless a merchant deliberately splits them. The case this exists for is a pair that meets in the middle: prev without its right edge, next without its left.
All switches default to on, and an unset value reads as on, so every existing nav renders an unchanged all-round ring.
The arrow border is an inset
box-shadow, not a real CSS border, so there is no per-side form of it: newsnippets/hw-carousel-nav-border-ring.liquidbuilds the shadow list for one arrow and is rendered once per arrow. It keeps emitting the single spread ring (inset 0 0 0 Npx) byte-for-byte while every edge is on, and only swaps to one directional inset shadow per enabled edge once an edge is switched off. A real border was rejected because it would eat into the icon box on a fixed-size control.Left and Right mean the edges the merchant sees, on both arrows.
slideshow-arrowputsflip-x(scale: -1 1) on the previous control, which mirrors itsbox-shadowalong with everything else, so the previous arrow's ring is built with left and right swapped. The custom-icon control is not flipped and takes them unswapped, which is why the emitted CSS addressesslideshow-control--previousandhw-carousel-navigation__control--previousin separate rules.Consequence of that technique, called out in the setting's help text: with an edge off, the remaining edges are drawn independently, so on heavily rounded controls the ring can thin out where two edges meet.
Changed
Collection Tabs (HW)the pill settings are reorganised into one deliberate order — Content, Layout (small screens / desktop), Spacing, Pill style, Size override — and a single Style preset now decides which controls the merchant sees, instead of typography, button and colour settings all being live at once.
Pill style → Button exposes an Active and an Inactive button preset + style, so the selected tab and the rest are two genuinely different buttons rather than one button dimmed by a hardcoded
color-mix. The swap happens on activation inassets/hw-featured-collections.js.Pill style → Custom exposes a typography preset (full Horizon list, including Custom with the usual type fields), a border (None/Solid + thickness + opacity, the same shape Group (HW) uses), an active and an inactive background/text/border colour, and a corner radius. Colours swap in CSS off
aria-selected, no JS involved.New
snippets/util-color-with-opacity.liquidfolds a percentage opacity into a picked colour and echoes nothing when the colour is blank, so a palette-token fallback survives — the channel mathhw-group-max-stylehad inline.Inactive pill opacity is shared by both presets and sits at the end of the group.
Size override is now its own group and applies to every pill style: minimum height (mobile/desktop) and pill padding on all four sides, each with a desktop value.
Fixes two long-standing pill bugs: a chosen typography preset was out-specified by the block’s own label-font fallback and never applied, and a non-button pill inherited
.text-block { width: 100% }and stretched to fill the row.Corner radius no longer applies to button-style pills — a button takes its radius from its button preset, as it does everywhere else in the theme.
Breaking:
button_preset,button_style_class, the button font override group (override_button_font,button_font_preset,btn_font_*) and the x/y padding pair (pill_padding_x_mobileand siblings) are removed. The section presets inhw-featured-collectionsare migrated; saved instances (template JSON, client stores) fall back to the new defaults for those controls and are re-saved from the theme editor.Migration: re-pick Active/Inactive button style on existing instances that used a non-default
button_style_class; re-enter pill padding where the x/y override was on; atype_presetsaved as a typography value (h3,paragraph_2, …) reads as Button and needs switching to Custom with the matching typography preset.Carousel sectionsthe footer-like wrapper that holds the carousel navigation and progress bar is now Group (HW, max) instead of Group (HW), across 19 sections — the
carousel-control/carousel-controlsgroup inhw-carousel-grid(3 presets),hw-col-carousel-grid,hw-col-carousel-grid-group,hw-collection-tabs-cards,hw-featured-blog,hw-featured-collections(2 presets) andhw-product-carousel-grid, and thestatic-footerslot inhw-collection,hw-fade-sliderand the tenhw-loop*sections. The control row is exactly where per-side borders, a custom border colour and block margin are wanted, which is the case Group (HW) cannot express.Static footers are keyed on type *and* id, so each section's
{% content_for 'block', type: 'hw-group', id: 'static-footer' %}moved tohw-group-maxalongside its preset entry;hw-group-maxwas added to each section's acceptedblockslist, withhw-groupkept for headers and the groups nested inside cards.Safe as a swap because
hw-group-maxaccepts an identical child-block list and its settings are a strict superset of Group (HW)'s, so preset values likecontent_directionandpadding-block-endcarry over untouched.Scope is deliberately the footer/control wrapper only:
static-headerand every group nested inside a card stay on Group (HW), since each setting a merchant touches on max is stored in the template.Migration: presets only. Sections already placed in a template keep the
hw-groupfooter stored in their JSON and are not migrated here — re-add or re-save those instances from the theme editor to pick up the max footer.Schema locales
locales/en.default.schema.jsonis deduplicated to buy back room against Shopify's 3,400-translation cap on a locale file — the file sat at 3,262 (96%), leaving 138 slots for everything still to be built. It is now 3,132 (92%), so headroom roughly doubles to 268.The cap that bites is the number of translations, not file size: at ~228 KB the file uses 15% of the 1.5 MB budget, and no value comes near the 1,000-character limit. Compacting the JSON or trimming the long
info:help text would therefore have freed nothing that matters, so the merchant-facing documentation is left exactly as written. Only removing or merging keys returns slots.77 keys merged into 68, wherever two or more keys in the same namespace held a byte-identical value. The clearest case: five separate "Icon size" labels (
settings.hw_quick_add_icon_size,settings.hw_header_utilities_icon_size,settings.hw_search_field_icon_size,settings.hw_qa_popup_close_icon_size,settings.hw_quantity_icon_size) collapse to onet:settings.icon_size. Likewise "Border color" ×4 →t:settings.border_color, "Corner radius" ×3 →t:settings.border_radius, "Button text color" ×3, "Icon color" ×3.Only
settings:,content:,options:,names:andhtml_defaults:labels merge. Theinfo:help texts are left untouched — each documents one specific setting, so none were duplicates in the first place, and their wording is what makes the theme editor usable.Canonicals were chosen to be generically named, not merely first-found, so a shared label is not parked on a feature-specific key where a later edit would silently change it everywhere. Nine were renamed to earn that:
hw_quantity_icon_color→settings.icon_color,hw_quantity_icon_size→settings.icon_size,hw_menu_overlay_blur→settings.overlay_blur,hw_mobile_menu_drawer_text_color→settings.text_color,hw_qa_popup_close_size→settings.button_size,hw_search_icon_image→settings.search_icon_image,hw_showcase_title_metafield→settings.title_metafield,hw_text_price_product→settings.live_price_product, andcontent.hw_mobile_drawer_nav_item_chevron→content.chevron.187 `t:` references were remapped across 57 block, section and config files. No schema setting
id, default, or behaviour changes — this is label wiring only, so nothing stored in a template JSON is affected and no merchant-visible string changes.hw_quick_add_offset_y("Vertical offset") was deliberately not folded into the existingsettings.vertical_offset: despite the identical English, that key means *vertical shadow offset* in the translated locales.Scope: only
en.default.schema.jsonis touched. The 19 translated*.schema.jsonfiles are left alone, since they are managed by the Shopify admin language editor and upstream Horizon sync. They keep the now-unreferenced keys as harmless dead entries (each sits at 3,026, well inside the cap) and lack the new canonicals, so those few labels fall back to the English value for non-English admins until a sync pass.Migration: brand themes and client forks that reference any removed key from their own sections will render the raw
t:string. The merged keys are listed above; the safe fix is to point at the canonical key rather than re-adding the old one, which would spend a slot again.
Fixed
Add to cart (HW)Full width now fills the parent instead of shrinking to the label, and a button used outside Buy buttons is no longer stuck disabled — it reads availability from the selected variant.
Removed
Schema locales53 orphan keys in
en.default.schema.jsonthat no"t:…"reference insections/,blocks/,snippets/,templates/,layout/orconfig/still used — leftovers from the removedhw-marqueesection schema (marquee_layout,marquee_speed,repeat_items,split_static_justifyand siblings), the retiredhw_header_menu_2/hw_header_actions_2header pair, thecontent.button_preset_1/2/3size labels, thehw_drawer_nav_*andhw_button_font_size*typography group, and thehw_collection_list/hw_collections_bento/hw_carousel_navigationblock names. Every remaining key in the file is now referenced, and all 21,071"t:…"references in the theme resolve.Note for tooling: schema keys in this theme use both
_and-(settings.show-placeholder,settings.media-sidebar-gap,settings.wrap-horizontal-row-large-screens). Any script that scans for"t:…"references must allow hyphens in the key pattern — a[A-Za-z0-9_.]character class silently misses those references and reports live keys as orphans.
v4.0.0#
38 added6 changed19 fixed
Added
FAQ Split (HW)the FAQ item template's toggle icon can now be set to a custom SVG or image on the "Icon" setting, alongside the existing caret/plus choices; display size is controlled by each row's existing Toggle icon size setting.
FAQ Split (HW)the filter block gained a "Show All pill" toggle — off hides the catch-all pill so only one pill per category shows. Defaults to on, so every existing instance (product, collection and page templates) is unaffected.
Bundle Promo Tabs (HW): new metaobject-driven pill-toggle promo section for bundle products.
sections/hw-bundle-promo-tabs.liquidrenders a two-column image + content layout with a pill toggle (e.g. "Luxe Premium" / "Basic Premium") that cross-fades between an image panel and a content panel per entry - heading, colour swatches, sold count, USP chips, a real-photo/placeholder thumbnail stack, and a CTA linking straight to the tab's linked product.Content is driven entirely by a
bundle_promometaobject list setting (Bundle entries) - one entry per pill/tab, no bundle-building logic in the section itself.Layout: image always fills the section's full height (padding no longer carves a gap out of it, matching the split used by
hw-feature-row.liquid- padding insets the content column only, and a separate, off-by-default margin setting owns real space between this section and its neighbours).Minimum height (mobile/desktop, free-text px/rem/% fields) lets the section be forced taller than its content, with the image stretching to match.
Typography: heading and body (colour label + USPs) text each get a preset select (Paragraph / Paragraph 2-12 / Label / H1-H6, matching the scale used by
blocks/_heading.liquidandblocks/text.liquidelsewhere in the theme), defaulting to Custom size so the section's own pixel sliders keep control until a preset is chosen.All colours (content background, pill states, colour swatches, sold badge, USP border, thumbnail placeholder, CTA) and remaining spacing are schema settings rather than hardcoded values, each defaulting to the section's original design so no visual change happens until a merchant overrides one.
New metaobject type expected:
bundle_promo(tab_label,hero_image,bundle_name,item_count,colors_label,sold_label,uspslist,thumbnail_imageslist,productreference).Additive and backward compatible: this is a new section with no changes to any existing section, block, or snippet (only a new
<script>tag added tohw-scripts.liquidto load its custom element).Collection Hero (HW): new per-collection hero banner section, metaobject-driven, with editor-exposed layout/typography/color controls.
sections/hw-collection-hero.liquidreads acollection.metafields.custom.heroreference to a Collection Hero metaobject entry (heading, subheading, image_desktop/image_mobile, button_label/button_link, optional badge text + style) and renders nothing when the current collection has no entry assigned - safe to include on any shared collection template.Content (heading/subheading/image/button/badge) is deliberately metaobject-driven rather than block-based: a shared collection template (e.g. a "cards" view used by every collection) has exactly one section instance, so per-collection content can't come from blocks, which are fixed to one instance per template file.
Design (alignment, spacing, typography size, text color) is ordinary section settings instead, since it doesn't need to vary per collection: Layout (horizontal/vertical alignment, independently mobile and desktop, matching the
hw-hero-cardconvention), Spacing (content max-width, gap, vertical padding, mobile/desktop pairs), Typography & color (heading size H1-H6, subheading size paragraph/paragraph_2-7, optional text color override), and Minimum height (mobile/desktop text inputs, in px) as an opt-in floor on top of the section's aspect-ratio frame - all default to values matching the section's original hardcoded styling, so an instance with nothing touched renders identically.Settings are wired through CSS custom properties on the rendered elements rather than static
{% stylesheet %}rules, because{% stylesheet %}blocks are deduplicated globally per section *type*, not per instance - a static rule can't express "this instance's gap is 20px, that one's is 8px."Optional Trust bar: a full-bleed strip along the bottom edge with up to two icon+text items (checkbox to show/hide, icon picker, text field each) - section-level rather than metaobject-driven, since it's store-wide messaging ("14 day returns"), not per-collection content.
Reuses
assets/hw-hero.css's.hw-hero__*frame/media-fill/content-inner classes so it matches the visual rhythm ofsections/hw-hero.liquidwithout pulling in that section's full block system.Collection Tabs & Cards (HW): new tabbed collection browsing section with a right-pinned filter/sort drawer.
sections/hw-collection-tabs-cards.liquidrenders a row of collection tabs (each backed by ahw-collection-tab-cardblock sourcing its own collection), each showing a carousel of that collection's products via_hw-col-tab-carousel-content, withsnippets/hw-tab-filter-drawer.liquidproviding a slide-in-from-right drawer for Sort and per-tab Filter (color swatches, price range slider) gated behind explicit Apply/Clear rather than live-updating on every change.Each tab lazily fetches its own real filter list on tab select (not on drawer open), so the filter options shown always match the active tab's actual collection rather than a stale/shared list.
Known gap, left unresolved on purpose:
hw-collection-tab-card.liquid's swatch slot still targetsll-swatch, a block from the source client project that shows *sibling products* (metafield-driven:products_metafield+swatch_color_metafield) as colour swatches on a card - not the same thing as core'shw-swatches, which shows a single product's own variant options. Core has no equivalent for "metafield-sourced sibling-product swatches in a shared card/grid instance" yet (hw-product-siblings.liquidis the closest relative, but it's a PDP block with a staticcollection/product_listdata source, which can't vary per-card in a grid the way a metafield can). See thePORT NOTEcomment at the block's swatchcontent_forslot.Ported from
thatworkagency/legend-london(feat/collections-cards-tab-page);templates/collection.cards.jsonis included as a starting preset with the source store's real collection handles stripped to"".Sort uses
snippets/hw-tab-sorting.liquid(Clone & Own fork of core'ssorting.liquid) rather than the core snippet directly, so the drawer's curated Sort list and Apply-gated (not live-updating) behavior actually work.Known gap: Apply-gated behavior currently only covers Sort - the color-swatch and price-range filters in this drawer still apply live on change rather than waiting for Apply.
Product Feature Row (HW): new section for a full-bleed image with an icon+label+description feature row, resolved by product/collection metaobject.
sections/hw-feature-row.liquidrenders a large product photo with a heading/subheading and a grid of feature tabs (icon, label, description), for the "Tailored Fit / All-Day Comfort / Wrinkle Resistant" style row near the top of a product page.Resolution order matches Compare (HW) and Loop – Card Object (HW)'s metaobject mode: a
pdp_feature_rowmetafield override directly on the product, then whichever of the product's collections has that metafield set with the highest Priority, then the section's own Global (fallback) row setting, then a manual block-based fallback (enable with Enable block fallback, using_hw-feature-row-itemblocks), then the section doesn't render at all.Strict all-or-nothing completeness check at every tier: a row only counts as resolved if heading, subheading and image are all present and it has at least one complete feature (icon, label and description all set); an incomplete row or item is treated as unresolved and falls through to the next tier rather than rendering with gaps. A "no data resolved" diagnostic only ever shows in the theme editor or on localhost, never on the live storefront.
New metaobject types:
pdp_feature_row(heading, subheading, image,featureslist ofpdp_feature_itemreferences, priority) andpdp_feature_item(icon, label, description).New restricted blocks:
_hw-feature-row-item(one feature tab, dual-sourced fromclosest.metaobject.pdp_feature_itemorblock.settingsin blocks-fallback mode) and_hw-feature-row-header(the heading/subheading, same dual-sourcing). Both delegate their rendering to the sharedhw-groupsnippet, so they inherit its full layout/typography/color settings rather than a fixed, hand-rolled style.Content placement: heading/subheading and the feature grid can each be set to sit above the image (in normal document flow) or overlaid on it, independently per breakpoint. Desktop overlay is a fixed horizontal split (heading left, feature tabs right, not configurable); mobile overlay is a vertical stack (heading above the tabs). Both grow to fit their content rather than clipping or overlapping when copy runs long.
Layout: always a CSS grid for the feature tabs (no separate grid/stack mode) — column count and gap are independently set per breakpoint, plus an optional max-width for the tab grid. All three vertical-spacing values (frame min-height, item gap, item max-width) are free-text px/rem fields rather than range sliders, for exact values a slider's step size can't reach.
Width: the background image always spans the full viewport edge to edge; the section's Width setting ("Page"/"Full") controls the heading/feature content only, inset to the theme's normal page-width column or left to run edge to edge with the image.
Padding vs. margin: section padding insets the heading/feature content within the image rather than adding a gap around the image itself; a new, separate Add section margin setting (mobile/desktop, off by default) adds real space between this section and its neighbours.
Additive and backward compatible: this is a new section with no changes to any existing section, block, or snippet.
Drawer nav item (HW)new block for Mobile menu drawer content (HW) that builds one mobile drawer nav row per block, instead of deriving every row from a linklist — the drawer's counterpart to Nav item (HW) on the desktop nav.
A row with a Submenu renders as an accordion; a row without one renders as a plain link with the forward chevron. Markup mirrors
hw-header-drawer-accordion-mode.liquidexactly, so the drawer's existing rules for rows, dividers, animation and colour apply with no new layout CSS.Per row: Badge (text, filled/outline, background, text colour, corner radius) and a Chevron override. Badge colours travel as inline custom properties on the block's own root — the reverse of Nav item (HW), where they live on the parent Menu (HW) block, because there the parent draws the badge and here the block draws its own. Nothing reads a parent block's settings, which is the lookup
.agent/docs/blocks/loop/FINDINGS.md§2.2 documents as unreliable.Outline reuses the text colour for the ring rather than adding a fifth colour that would almost always be set to the same value, matching the Figma's red-on-red pill.
Per row Divider: on/off, width and colour. It reuses the theme's
.menu-drawer__list-item--dividerclass rather than drawing a private border, because that class also drives the childlist spacing rules inhw-header-drawer.liquid— a separate border would have missed them. Blank colour keeps--color-border. Turn it off on the last row so the list does not end on a line.Mobile menu drawer content (HW)new Navigation rows and Nav typography groups.
Nav rows come from — Menu (default, unchanged) or Nav item blocks. In block mode the linklist nav is skipped entirely rather than hidden with CSS: rendering both would duplicate every row for screen readers and double the drawer's height before the CSS loaded.
Nav link size / color and Child link size / color are set once here rather than on each row block. Left at 0 / blank the property is not emitted at all, so each row keeps falling back to the drawer's own
--menu-*pair and a block row renders identically to a linklist row out of the box.Menu (HW)new Padding group — top and bottom, on the nav block itself.
.header-menuisheight: 100%inside the header row and everything in it centres, so a symmetric pair tightens the nav's hover area and an uneven pair nudges the links up or down against the logo. That is the reason to reach for this rather than the row padding on the header section, which moves every column at once.box-sizingisborder-boxtheme-wide, so the padding eats into the 100% rather than adding to it: the nav shrinks inside the row instead of growing the header.Menu (HW)new Show submenu link icon beside Show nav chevron, turning off the chevron on links inside dropdown and mega menu panels (
.hw-dropdown-menu__link-icon-wrap).Those icons had no switch of their own: Show nav chevron drove both the top-level nav chevron and the panel link icons, so losing the panel icons meant losing the top-level chevron with them. The new setting hides the panel icons alone.
The old coupling is left in place — Show nav chevron off still hides both — so existing headers render exactly as before. The new setting only ever subtracts.
Header (HW)new Overlay coverage beside the menu overlay colour/blur — Page below header (default, unchanged) or Header and page.
.hw-menu-backdropis pinned below the nav row, so with a transparent header the hero kept showing through it at full brightness while everything below was dimmed — a bright band across the header. Header and page starts the scrim at the top of<header-component>instead, so what shows through the header is covered too.topis host-relative because#header-componenthascontain: layoutand is therefore the containing block for the fixed backdrop. The height cannot use the same origin — it is a viewport measurement, and the header is not always flush with the viewport top. It comes from the difference between the two anchor variablesheader-menu.jsalready publishes off one shared rect, so the two can never drift apart.z-indexis unchanged at 0: above the underlays so what the header lets through is dimmed, below the row (raised to--layer-heightenedwhile a submenu is open) so the logo, links, icons and the open panel all stay legible on top of the scrim.Header (HW)new Selector style under the country/region settings — Flag + currency code (default, unchanged) or Currency symbol + square flag.
The Figma desktop header reads
($)then an uncropped rectangular flag, with no ISO code. One switch rather than three toggles, because the three parts only make sense together: a symbol with no flag is an orphaned bracket, and the ISO code next to the symbol states the same fact twice.The symbol is emitted before the flag in the markup rather than reordered in CSS, so what a screen reader announces matches what is painted. The square flag rules carry both the modifier and the button class so they outrank the theme's
clip-path: circle(50%)on specificity rather than on source order.Search field (HW)new header block that renders a full search pill — icon plus placeholder copy — instead of the icon-only search, for headers where search is a visible field rather than a hidden control.
It is a trigger, not a second search form: it opens the same overlay the search icon opens, and reads the icon block's own Search style so the two controls cannot disagree about their target. No duplicate
qinput and no second search landmark for screen readers.Width, padding, corner radius, icon size, background (with blur), text and border colours are settings; text colour falls back to
inheritso the field tracks a transparent header over a hero.Hidden on mobile by default — mobile search belongs in the menu drawer, and the header row has no column for a field at that width.
Off by default on Header (HW); turn on Show search field and pick its column.
Header (HW)the country and language selector can now appear in the header row, and the one in the mobile menu drawer finally renders.
The drawer's localization row had always been gated on section settings this section never had, so it was dead markup — adding Country/region, Language, Flag, font and size switches it on for both surfaces at once.
Desktop placement is its own column setting, so the right bookend reads search field, then region, then account and cart.
New Space between header items overrides the gap between the items in one bookend; left at 0 the header renders exactly as before.
Nav item (HW)a nav label can now carry a small pill badge, so a promotional top-level link reads as "Outlet · Up To 60% Off" in one item instead of needing a second link.
Badge text and Filled / Outline style are per nav item; every colour, size, radius, padding and typography value is shared on the Menu (HW) block so all badges in a nav match.
Works on plain links as well as dropdowns — a badge on a link with no chevron lays the item out horizontally the same way a submenu link does.
Menu (HW)mega menu panels can be a card sized to their own content, not just full-bleed or page-width, with a corner radius to match.
New Hug content width plus Corner radius. Hug shrinks the panel background itself and anchors it to the inline start of the nav row; Full and Page are unchanged, and still keep the background full width so no page shows through at the sides.
New Promo width (px) sets the promo rail as a fixed length. Required with Hug — a percentage rail cannot resolve against a panel that is sized by its own content.
Menu (HW)mega menu promo tiles take a description under the label, with its own shared typography group.
New Promo 1 / 2 description on Nav item (HW) and a Promo descriptions typography group on Menu (HW), plus a Promo text gap between the two lines.
Setting a Promo arrow background turns the arrow into a fixed, rounded, optionally blurred chip pinned to the bottom of the tile instead of a full-height divider cell; size, radius and blur are settings.
Mobile menu drawer content (HW)blocks added to this block now render inside the drawer, above the navigation list.
The schema had always accepted children here but nothing output them, so anything a merchant added silently disappeared. Search fields, promo tiles and any
@themeblock now appear in the drawer with their own gap and padding settings.Menu (HW)the plain dropdown panel is now styleable from the editor — corner radius, padding, link gap, link inset, and resting/hover link colour.
Every one of those was hard-coded in CSS, so a simple "Assistance"-style dropdown could not be matched to a design without a code change. Defaults are the exact former values, so an untouched header renders unchanged.
The third level of a dropdown reads the same radius, padding and gap, so a nested panel matches its parent card. Compact dropdowns deliberately keep their own tighter 16px inset.
Menu (HW)new Space between nav items overrides the theme gap between top-level nav items. Left at 0 the nav keeps the theme gap exactly as before.
Mobile menu drawernav label badges now show in the drawer, matched to a drawer row from the Nav item (HW) badge with the same label — one badge, typed once, in both navs.
Appearance comes from the shared badge settings on Menu (HW), plus drawer-only style / background / text / border / border width / letter case overrides, because the same pill usually reads filled on a transparent header and outlined on the white drawer.
A badge is matched by handleized label, so renaming a nav item's label without renaming the menu link stops the badge appearing — that coupling is deliberate.
Mobile menu drawer content (HW)new Chevron on links without a submenu gives a plain top-level link the same chevron as the rows that open something, so every row in the drawer reads as tappable. Off by default.
Button (HW)new Appearance group — background, text colour, corner radius and font size (mobile + desktop) per button.
A button's look came only from the global style presets, so matching one button to a design meant restyling every button in the theme. The overrides are off by default and land in the block's existing
0,2,0scope rule, so they beat.buttonand the size preset without!important; background also setsborder-colorso an outline preset does not keep its old ring around a new fill.First user is the drawer's Shop All button, now a black pill at the Figma's 14px.
Mobile menu drawer (HW)new Country/language selector position under Drawer shell — Below menu links (default, unchanged) or Below footer.
The Figma puts the flag row last in the drawer, under Sign In/Register, but the selector's markup is hard-coded between the nav and the footer blocks, so there was no way to get it there from the editor.
The markup is captured once and echoed into whichever slot the setting names, rather than duplicated — the two positions render byte-identical markup apart from a modifier class.
Below footer also re-aligns it: the default rule pins the block with an
autotop margin and its own--padding-xlindent, neither of which lines up with footer content that pads by--drawer-padding. It hides with the footer when a sliding submenu is open, which the default position does not need to do.Mobile menu drawer (HW)new Text color under Drawer shell, alongside the background.
Set on the drawer panel rather than per link, so nav items, footer links, the close button and every chevron follow it — the icons are
stroke="currentColor"already. Blank keeps the theme foreground.Mobile menu drawer (HW)new Background color under Drawer shell, so the drawer can be dark or branded without overriding the theme's page background.
Left blank the drawer keeps
--color-background, so an untouched header renders exactly as before.It also covers the sliding submenu panels, which sit over the drawer at full height — colouring only the drawer would have flashed back to white the moment a submenu opened.
Account link (HW)new block whose label and destination follow the customer — "Sign in / Register" to a visitor, "My account" once they are signed in.
Nothing else in the theme could do this: a menu link's label is fixed, and Group (Conditional) keys off a dynamic source, not the customer. The mobile drawer's account row is the first user.
Optional custom link overrides the destination; leave it blank and signed-out visitors go to the login page, signed-in customers to their account.
Search field (HW) can now be placed in the mobile menu drawer, not just the header row, so the drawer's search pill opens the same overlay instead of navigating to the search page.
New Full width setting for that use: the header row's width lives in
flex-basis, which reads as a *height* inside the drawer's column stack.Renamed from
_hw-header-search-fieldtohw-search-field(dropping the_is what makes it placeable). Not yet released, so nothing to migrate.Menu (HW)mega menu promo tiles can sit side by side as well as stacked, and each takes an editable badge.
New Promo layout (Stacked / Side by side). Side by side is the Figma variation with two tiles; set Promo width to the pair's total, not one tile's.
New Promo 1 / 2 badge text and per-tile badge background on Nav item (HW), with shared colour, size, case, radius, padding and blur on Menu (HW) so every badge in the nav matches.
New Promo arrow background / colour (hover) turns the arrow chip white on hover to signal it is clickable. Blank keeps the resting colours, so an existing menu is unchanged.
Live price in a badgeText (HW, background) and the mega menu promo badge take a Live price product; write
{price}(or{compare_at_price}on the text block) and it renders that product's price in the visitor's market currency.An offer pill typed as "3 For £65" is wrong the moment someone shops another market. This keeps one badge correct everywhere.
Compare (HW): new "Content source" setting — Blocks or Metaobject (category table). Metaobject mode drives the whole table (heading, subheading, rows, columns) from a
comparison_tablemetaobject entry, resolved per product so the same table swaps content by category with no per-product theme-editor work.Resolution order: a one-off
comparison_tablemetafield override directly on the product, then whichever of the product's collections has that metafield set with the highest Priority, then the section's own Global fallback table setting. Collection-level is the intended primary path — tag a category's collection with its table once, every product in it picks it up automatically.New metaobject types:
comparison_table(heading, subheading, rows, columns, priority),comparison_row(label, tooltip — same shape as the manual Compare Row block),comparison_column(title, logo, floating product image, badge text, highlight, background color, order-matched values list, CTA text/link).New dynamic blocks render the resolved entry in place of the section's normal manual/nested blocks:
_hw-compare-row-dynamic,_hw-compare-column-dynamic(one per list entry), and_hw-compare-header-dynamic/_hw-compare-header-subheading-dynamicfor the heading and subheading. The last two are full-settings text blocks (width, alignment, size preset, case override, color — same schema as Text (HW, basic)) rather than fixed styling, and are two separate block instances specifically so heading and subheading can each carry independent settings.The floating product image (metaobject mode only) fixes two real layout bugs found while building this: a circular shrink-to-fit/percentage-width dependency that silently rendered the image at roughly half its correct width, and a missing top-clearance case where the image could sit on top of whatever text precedes the table below the 990px side-layout breakpoint.
New Bleed table to screen edge on mobile setting (
mobile_edge_bleed, on by default, matches prior behavior): off keeps a table that already fits a phone screen inside the normal page margin instead of bleeding edge-to-edge, and lets the configured item gap apply on mobile instead of a small fixed default.Blocks mode (the section's original, fully manual behavior) is unchanged.
Product card quick add (HW): border, corner radius and an exact height per breakpoint. The button's outline (
2px solid hsl(0 0% 0% / 0.15)), its 50px pill radius and its height all came from base Horizon CSS with nothing to change them from. All three are settings now, in Theme settings → Product cards → Quick add (HW) and mirrored on the Product card media (HW) block override.Height — an exact px value per breakpoint (1–100), mobile and desktop set separately. The icon cell scales with it, staying the height minus both border edges the way base Horizon does against
--button-size-md, so the button stays balanced at any size. Full button stretches to its label, so the same number is a floor there rather than a fixed box.Border and corner radius — thickness 0–20px, colour with alpha (blank keeps the theme's hairline), radius 0–100px. The radius also lands on the inner
.add-to-cart-textlayer, whose own 50px corners would otherwise show through a less-rounded button. Both hide themselves for Full button, which spans the image edge to edge, and for Size picker, whose tile grid has a look of its own.Every one of these values travels as a CSS custom property rather than a variant class, so
assets/hw-quick-add.cssholds no merchant numbers and eachvar()fallback repeats the base-theme value — an unset setting renders exactly as base Horizon does.Product card quick add (HW): icon and placement controls. The quick-add button's icon was hard-coded to the theme cart SVG and its position was fixed to the bottom-right corner. Both are now settings, in Theme settings → Product cards → Quick add (HW) and mirrored on the Product card media (HW) block override.
Icon — Default (theme cart icon), Custom image, or Custom SVG, with its own size (8–48px). Picking a custom source and leaving it empty falls back to the default icon rather than rendering nothing. Icon position (left or right of the label) is available for every style except Icon only, which has no label.
Placement — Any of the four corners plus bottom-centre, with horizontal and vertical offsets (0–48px) on top of the card's existing padding. Each edge reads the card padding on its own side: base Horizon defines only the bottom-right pair, so the top and left insets are built from the same parts rather than borrowing the opposite edge's value. The corner-radius compensation that pushes a pill clear of a rounded corner is dropped for bottom-centre, which sits nowhere near one, and the horizontal offset rides on the centring translate there so it still moves the control. Full button spans the image width, so it takes only the top/bottom half of the value; Size picker is a bottom strip and ignores placement entirely — those controls hide themselves for it.
Both quick-add buttons ("Add" and "Choose") render one captured icon, so they cannot drift apart.
Div (HW): new block that renders a plain coloured box — no text, no nested blocks — for small square/dot accents and long horizontal or vertical bars.
Width and height are set independently for mobile and desktop, each with its own unit: 0–100% or 0–100px, in steps of 1.
100%covers the fill-the-parent case, so there is no separate fill option. A percentage *height* only resolves once the parent has a height of its own (e.g. Group (HW) with a set height) — use px when the parent grows with its content.Background is a colour-or-gradient picker rather than a flat colour, so a bar can carry a gradient; left empty it falls back to the palette's primary colour instead of a hard-coded hex. No overlay and no text colour — there is nothing inside the block to tint.
Corner radius takes px or percent, so 50% gives a pill or a circle at any size. Optional border (0–20px + colour), opacity, and −180°/180° rotation round out the shape controls.
One Alignment setting maps to
align-self/justify-self, which reads as left/centre/right inside a vertical Group and top/middle/bottom inside a horizontal one — the axis belongs to the parent, so it is one control rather than two.Available anywhere
@themeblocks are allowed. Use Inset (HW) for spacing around it — Div (HW) deliberately ships no padding or margin settings.Price (HW default): new price block that always follows Theme settings → Prices, with no per-instance typography or colour controls — use it when every price should stay on the store default, or when saved layout JSON is getting large.
Same sale-first, “From”, tax and installments toggles as Price (HW); the two blocks render identically until you override styling on Price (HW).
Available anywhere
@themeblocks are allowed, and listed explicitly on Product card (HW) and the product media overlay blocks.Hero Cards (HW)new split hero section — one or more hero columns side by side, each with its own background image, copy stack and frame height.
Composed from existing blocks rather than new markup: an Image (HW) child acts as the card background and a Group (HW, basic) child carries the heading/paragraph/CTA as Text (HW, basic) + Button (HW). Gap, section width and palette colours are section settings.
Columns are derived from the number of cards rather than set by hand: desktop always fits every card into one row of equal columns, so three cards means three thirds and five means five fifths, and every card in a row stretches to the same height.
Two cards per row on mobile (off by default) puts small screens two across instead of stacked; with an odd number of cards the last one spans the full width. Off keeps the stacked layout, each card at its own height.
Section width honours the theme's page width: Page constrains the row to the same centre column as any other page-width section, Full runs it edge to edge.
Section preset ships two cards;
templates/index.jsonnow leads with a configured two-card instance matching the Legend London hero design — two full-bleed cards with centred copy, a per-column utility bar on desktop, and a single shared utility bar under the stack on mobile.Hero Card (HW)the column block for Hero Cards (HW) — sets card height per breakpoint, content max width, content alignment, an optional scrim, corner radius and a whole-card link with its own Link label for screen readers (falling back to the URL's last path segment, then to the theme's generic link label).
Content placement chooses whether the copy floats over the background image or sits below it in normal flow. Editor block order decides which comes first. Either way the background image runs the full width of the column; the content max width only caps the copy.
Content alignment is set per breakpoint: Layout (small screens) and Layout (desktop) each carry their own horizontal (left / center / right) and vertical (top / center / bottom) position, so a card can sit bottom-left on mobile and centred on desktop. Desktop falls back to the mobile value when left alone.
Accepts Image (HW), the Group (HW) family, the Text (HW) family, Button (HW), Inset (HW), Flex spacer (HW) and Group Overlay (HW).
Group Overlay (HW)a strip that pins to the top or bottom edge of whatever contains it — nested in a Hero Card it covers that one column, added straight to the section it spans every column. Blur, background colour and opacity, strip width, padding and horizontal alignment are all settings.
Options to distribute items evenly and show dividers between them, for utility bars like "14 Day Returns | Fast Priority Shipping".
Accepts Text (HW), Text (HW, custom), Icon (HW) and Group (HW, basic) — the group variant is there so an icon and its label can travel as one item.
Changed
Icon with text (HW): the label field is rich text instead of a single line, so you can bold, italicize, or link parts of the label in the editor.
Menu (HW): the mega menu promo rail sizes itself to the number of promo tiles. Promo width (px) is gone.
That setting was one shared number on Menu (HW) while the promo images are per Nav item (HW), so a nav item with one promo got the two-tile width (530px) and rendered half an empty rail. There was no value that could be right for both.
The rail is now
265pxfor one tile and530pxfor two side-by-side tiles. Two *stacked* tiles stay265px, because stacked tiles sit on top of each other and the pair is still only one tile wide — which is why this is a CSS rule combining a per-nav-item count attribute with the parent's layout attribute, rather than a single length computed in Liquid: the count and the layout live on different blocks.The percentage Promo width setting remains only as the CSS fallback. It no longer applies when a rail renders, since a rail only exists when there is at least one tile. Stored
dropdown-grid-promo-width-pxvalues are ignored.Quick add (HW) size is a px range instead of three fixed options.
hw_quick_add_size(Compact / Standard / Large) and thedata-hw-quick-add-sizeCSS variants it drove are gone, replaced byhw_quick_add_height_mobileandhw_quick_add_height_desktop(andquick_add_height_mobile/quick_add_height_desktopon the block override). Standard was 36px and that is the new default, so a store on Standard is unchanged; one that had picked Compact or Large needs to enter a height once.Product card quick add (HW): one place decides, instead of three. The gate lived in
blocks/hw-product-card-gallery.liquid, the visibility variables insnippets/hw-product-card.liquid, and the appearance insnippets/hw-quick-add-wrapper.liquid, with the three conditions disagreeing. Quick add is now resolved entirely inhw-quick-add-wrapper.liquid:Theme settings own whether quick add exists (
quick_add,mobile_quick_add); the block override owns only how it looks, and applies as a complete set rather than a mix of block and theme values. The editor docs previously claimed a block could switch quick add back on when the theme had it off — it never could, and now says so.The gallery block renders the wrapper unconditionally and lets it decide, so there is no second gate to fall out of sync.
Sold-out products no longer render quick-add markup or a pre-rendered modal template. The card already hid it; now it isn't emitted.
Schema copy on the gallery block moved from hard-coded English to locale keys, matching the rest of the theme.
Prices (theme settings): Price typography presets (three named pixel scales) is replaced by Default price styling — one typography preset and one colour for the current price, compare-at price, save label, and “From” prefix. Prices now track the theme’s typography scale, including fluid sizing, instead of independent px values. Leaving a preset on Default still means inherit from the surrounding text.
Save-label background, text colour and corner radius stay in the Savings label group and feed the same store-wide defaults.
Migration: the eighteen Price 1 / Price 2 / Price 3 size ranges are removed, so stored pixel sizes are dropped on sync. Re-apply the look under Theme settings → Prices → Default price styling by picking matching paragraph or heading presets. Price color and Compare price color keep their existing ids.
Price (HW): each of the four price parts can override the theme default independently (preset + colour), plus save-label background/radius and a gap between elements. Colour and preset controls start blank, so an untouched block looks like the store default. Save-label radius and gap sit behind Override checkboxes — a range slider always has a value, and without the gate it would pin 50px / 8px on every instance. The old Price typography preset (None / Price 1 / 2 / 3) and the wrapper Preset (which only ever styled the tax note and installments) are gone.
Compare-at colour is no longer dimmed to 40% when a colour is picked; the dim only applies when the picker is left blank, matching the previous unset look.
Styling is applied on the price container so it survives a variant change (the script that swaps the inner HTML no longer wipes preset classes off the amounts).
Migration: stored Price 1 / 2 / 3 and wrapper Preset / custom typography on Price (HW) are dropped. Re-set look on the block or in Default price styling. Product card, Featured Collections, Product Carousel/Grid and Product List presets no longer pin prices to a heading size. Compare (HW) product columns no longer force Price 2 — they inherit the theme default.
Fixed
FAQ Split (HW)filter pills with a custom background color silently lost their border — the border color defaulted to match the background instead of the text color, making an outline-style pill's border invisible. New Border color settings (inactive and active) default to the text color instead.
FAQ Split (HW)the default-active filter pill's category filter only applied after a click — a fresh page load showed every row regardless of which pill was marked active. The same filter now runs once on init.
Product Carousel/Grid (HW): recommendations mode broke the carousel's edge-to-edge bleed. The visibility wrapper added around the header + carousel to hide empty complementary results sat between
.sectionand the carousel's own.force-full-widthdiv —assets/base.css's.section > .force-full-width { grid-column: 1 / -1; }rule only matches a *direct* child, so the carousel silently lost its bleed and picked up normal page-gutter padding instead whenever recommendations mode was on. The wrapper is removed; the empty-state hide rule now targets.section.hw-product-carousel-griddirectly instead of introducing a new element between it and the bleed div.Loop – Card Object (HW): dividers broke both the header's gutter and the carousel's bleed. A single shared divider wrapper around the header, carousel and footer put all three one level deeper than
.section, so the same.section > .force-full-widthdirect-child rule above misfired for both: the header inheritedforce-full-widthit was never meant to have (losing its normal page-margin inset), and the carousel's own inner.force-full-widthdiv was nested one level too deep (losing its bleed). Split into three separate divider wrappers — header, carousel, footer — matching the pattern already used bysections/hw-carousel-grid.liquidandsections/hw-product-carousel-grid.liquid.Menu (HW): the desktop nav sat above the centre line of the header. The v1 Menu block (
blocks/_header-menu.liquid) setsflex-direction: columnon.menu-list__list-item, and Shopify compiles every block's stylesheet into the theme's global CSS whether or not the block renders — so the rule reached the HW nav even though_header-menuis not inheader-group.json. Column direction top-packs the link inside aheight: 100%list item and leaves the row's spare height below it, knocking the nav out of line with the centred logo and search.Undone in
sections/hw-header.liquid's inline style block, next to the reset that already neutralises the same block's::afterbridge, rather than by editing the v1 block: base Horizon blocks must stay intact (.cursor/rules/horizon-works-layer.mdc), and_header-menuis still used by the basesections/header.liquid, so deleting the rule there would change the base header for every build. The override only renders when the HW header section does.Mega items are excluded, because those deliberately keep column layout for their own bridge.
Mobile menu drawer: the whole panel inherited the transparent header's colour pair. The drawer is nested inside the header row, which paints its own background/text inline — over a hero that is a transparent background and white text — and those land on
--color-background/--color-foreground. The panel resolved transparent, and every nav link (--menu-top-level-font-color: var(--color-foreground)), the close button and the footer links came out white on white. The drawer now re-establishes its own colour pair from the block's Background/Text settings, falling back to the theme's page palette rather than whatever the header row happens to be using.Mobile menu drawer: the nav font-size settings had no effect on the main nav. The settings live on Drawer menu links (HW), a *static* child block, and the drawer nav was reading them by looping
block.blocksand pullingchild.settings— the exact pattern.agent/docs/blocks/loop/FINDINGS.md§2.2 documents as unreliable: static children are not reliably listed, and their settings come back unreadable on the storefront. The lookup returned nil, so no typography classes were emitted on the main rows and every one of them fell back to the theme's--menu-font-2xl--size(1.75rem / 28px). The block now renders its own<style>, from its ownblock.settings, mapping the stored values onto the--menu-top-level-*/--menu-parent-*/--menu-child-*variables the base drawer rules already read — no cross-block lookup and no class plumbing in the path.Those variables are scoped to the
<ul class="menu-drawer__menu">rather than tonav.menu-drawer__navigation, because the nav rendersmenu-font-stylesinto an inlinestyleattribute and an inline declaration beats any stylesheet rule on that same element regardless of selector. Declaring one level down wins on inheritance instead of needing!important.Menu (HW): "Space between nav items" had no effect.
--menu-horizontal-gapwas set on.menu-list, but the flex container that actually lays the items out is[part='list']inside<overflow-list>'s shadow root, whereassets/overflow-list.csshard-codescolumn-gap: 1rem. Nothing read the variable. The gap only ever looked right because the default (16px) happens to equal 1rem. The::part(list)rule now consumes it.Header (HW): flooring the nav's grid track was not enough to stop it being squeezed.
1frisminmax(auto, 1fr), and thatautominimum comes from the grid item's own automatic minimum — its min-content, i.e. the widest single nav item — not from the track'smax-content. The menu's column now carriesmin-width: max-content, which sets that minimum directly instead of relying on intrinsic sizing propagating out of the overflow list's shadow root.Header (HW): the nav column was capped at roughly half the header width, wrapping the menu onto a second row. With the menu in a side column and the logo in the centre, neither of
hw-header.css's track carve-outs applied — one requiresheader-menuinside the centre column, the other requires no centre column at all — so the menu track stayed a bare1frand split space evenly with the actions column instead of sizing to the nav. Both side tracks are now floored atmax-contentwhen the menu is in a bookend, matching what the centred-menu layout already did.Nav item (HW): "Hug content" mega menu width had no effect — the panel was always full width. The
[data-mega-target]carve-out that makes full and page-width panels fill the fixed submenu pins the same wrapper towidth: 100% !importantat a higher specificity than the hug rule, so hug lost every time. Hug is now re-stated at a specificity that wins. The drop shadow moved with it: it is painted on the fixed submenu, which is always the full nav-row width, so a hugged card kept a full-width slab of shadow around it and still read as edge-to-edge.Nav item (HW): the mega menu panel was far taller than its links. The promo tile's image was the only in-flow child of the tile, and
height: 100%against an auto-height rail falls back to the asset's intrinsic height — around 1300px for a portrait crop — so the promo rail, not the link column, set the whole panel's height. The image is now out of flow, so the panel measures the links and the tiles crop to that height.Nav item (HW): the block failed to upload to the theme entirely.
visible_ifwas set on three resource-picker settings (menu, and both promo badge product pickers), which Shopify rejects — and a schema error rejects the whole file, so every nav item, mega menu grid, promo rail and badge silently stayed at the previously uploaded version. The attribute is not supported on those setting types and has been removed.Nav item (HW): mega menu columns could not be set to a single column. The Columns range started at 2, but under Hug the value is applied literally (
repeat(N, max-content)) rather than reflowed byauto-fit, and the design is one column. Minimum is now 1.Docs: the Group (HW) developer guide documented an overflow / edge-bleed setting the block does not have. Sections 3 and 4 described
overflow_xandedge_bleed_*as living onhw-group; both belong to Overflow (HW) (hw-overflow), withhw-gridcarrying its own copy of the overflow pair. Corrected in place, because reading the guide as written leads straight to re-implementing a scroll toggle that already exists one block over.Nav item (HW): the two mega menu promo description settings showed on every nav type. They were missing the
visible_iftheir sibling promo settings all carry, so they appeared under a plain link or a product showcase where they do nothing.Quick add (HW): the Size picker style ignored the theme's mobile toggle. The overlay size picker renders its own element and never saw the card's
--quick-add-mobile-*variables, so Mobile quick add had no effect on it. It now sits inside the same.hw-quick-add-wrapperas the button and is gated from there. With On hover visibility it also stays reachable on touch screens, where there is no hover to trigger it.Quick add (HW): Size picker on a product with no size option showed nothing at all. The size option is now resolved once in the wrapper, which falls back to the Icon + label button when the product has no matching option instead of leaving the card with no quick add.
Quick add (HW): the hover-visibility rules never matched. They targeted
quick-add-component, but the HW card rendershw-quick-add-component; the transition they were meant to add was silently dead. Corrected to the element that is actually rendered.
v3.3.0#
7 added2 changed5 fixed
Added
Hotspot (HW): new block that pins an image anywhere over a positioned parent and shows or hides the blocks nested inside it when the image is clicked.
Hotspot placement uses the same free-text top/right/bottom/left insets as Absolute (HW) — px, %, or blank for auto — with an independent desktop override.
Nested content is placed relative to the *centre* of the hotspot: a 9-way direction (top/center/bottom × left/center/right) picks which corner of the panel meets that point, then horizontal and vertical insets (−100 to +100px) nudge it. Separate direction and insets per breakpoint.
Trigger size is a 1–100px width/height per breakpoint; the image fits inside that box without distortion, and leaving the image empty falls back to a simple round pin.
Hover animation composes scale, opacity, a directional move, and rotation with a shared duration; an optional idle animation (pulse / float / breathe) draws the eye before anyone interacts. All motion is skipped under
prefers-reduced-motion.Behaviour toggles for open-on-hover (desktop pointers only), close-on-outside-click, and closing sibling hotspots; shoppers also close a panel with Escape, which returns focus to the trigger.
With open-on-hover on, hovering reveals the panel transiently and clicking the pin keeps it open until dismissed, so a click never reads as “dismiss the thing I was about to read”.
No padding, background or appearance settings on the block itself — nest a Group (HW) for those, so the controls live in one place rather than being split across two blocks.
Image Small / Medium / Large (HW): three new blocks that size an image by exact pixel width and height, set separately for mobile and desktop — Small 1–100px in steps of 1, Medium 4–400px in steps of 4, Large 10–1000px in steps of 10.
hw-imagesizes its box by intent (fit-content / fill / custom %) and then caps it, so it can’t reliably land on a specific pixel size; these take literal px instead and drop the max-width, max-height, and aspect-ratio controls they no longer need. Corner radius, crop/fit, link, overlay, placeholder, and device visibility all carry over. Image (HW) stays the right choice when the image should scale with its column, needs an aspect-ratio crop, or is larger than 1000px.Each tier keeps its slider at 100 discrete values, so every value in its range is reachable — which is why none of them need Image (HW)’s parallel text inputs. Setting IDs are shared across tiers, so swapping one block for another preserves the values (clamped to the new tier’s range).
Optional Auto height keeps the image’s own ratio at the set width. All three render through
snippets/hw-image-fixed.liquid, so the family ships one copy of its CSS. Available anywhere@themeblocks are allowed, plus every slot that already listed Image (HW) explicitly.Absolute (HW): new Backdrop blur setting frosts whatever sits behind the panel, separate from the existing shadow blur that softens the panel’s own contents.
Default stays 0, so existing overlays are unchanged; a transparent or semi-transparent background is needed for the frost to show.
Product card (HW): new Height auto setting sizes that block’s cards to their content instead of always stretching them to fill the row.
Default stays fill, so existing lists are unchanged; only cards from the block with the setting on are affected.
Header (HW): new Desktop menu breakpoint setting controls the width at which the header switches from hamburger/drawer to the full inline nav (750–1400px, default 990px).
Previously hardcoded to 990px across four places (two
{% stylesheet %}blocks, a shared JS breakpoint constant, and the pre-paint FOUC guard intheme.liquid); all four now read the section setting. Default behaviour is unchanged for existing stores.Loop sections (HW): new Laptop layout band (990–1439px) with its own carousel/grid, slides per view, swipe, and gap controls, plus optional fixed card width (px) per breakpoint when in carousel mode — leave empty to keep sizing by slides per view.
Same layout model on Carousel Grid, Product Carousel/Grid, Collections Carousel/Grid, Featured Collections, Featured Blog, and Collection (HW).
Collection Scroll (HW): collection titles can be placed independently on mobile and desktop — horizontal alignment, vertical position, gap, and separate horizontal/vertical inset from the frame edges.
Previous shared Alignment / Position / Gap apply to mobile; matching desktop controls default to the same values. Uniform Mobile/Desktop inset is replaced by horizontal and vertical inset per breakpoint (defaults match the old 24px / 48px).
Optional collection metafield paths can replace each slide’s title and mobile/desktop background images, with independent fallback to the standard collection title and image.
Changed
Buttons theme settings: Horizontal padding, Vertical padding and Minimum height (mobile + desktop) are now set per button size preset (Extra Small … Extra Large) instead of one global padding pair plus a minimum height on each of the twelve Fill/Outline colour presets.
The split is now clean: a size preset owns the whole box (typography, padding, minimum height), a colour preset owns colour, border width and radius. Picking Fill 2 over Fill 1 no longer silently changes a button's height, and the five size presets are no longer forced to share one padding value.
Section headers renamed Button size presets / Button shape to match. Minimum-height ranges are 0–100px step 1 throughout (Button (HW)'s own override was 0–120px step 2).
A button with no size preset — base Horizon buttons, add-to-cart, and anything set to
customtypography — follows the Small preset, matching how its typography already resolved.Migration:
button_padding_inline/button_padding_blockand all twenty-four colour-preset*_min_height_mobile/_desktopsettings are removed, so their stored values are dropped on sync. New per-size padding defaults match the old global (24 / 16); new per-size minimum heights default to 0. Stores that had set a colour-preset minimum height must re-enter it on the size preset(s) those buttons use.config/settings_data.jsoncarries the shipped 20 / 12 padding across to all five presets, so the reference theme is unchanged.Loop sections (HW): Desktop layout settings now apply from 1440px up instead of 990px, so mid-width screens use the new Laptop controls rather than sharing Desktop.
Migration: stores that tuned Desktop for laptop-width viewports should re-check Laptop settings after sync; schema defaults usually mirror the previous Desktop values, so most sections keep a sensible look without edits.
Same breakpoint split on Carousel Grid, Product Carousel/Grid, Collections Carousel/Grid, Featured Collections, Featured Blog, and Collection (HW).
Fixed
Button (HW): the Padding sliders now actually inset the button when Override theme button padding is on, on every kind of button the block renders — including modal triggers, which were silently unaffected before.
The override wrote
paddingonto a hand-listed set of elements (aand.hw-button__submit) and the modal trigger is neither, so its sliders did nothing. Padding is now resolved through--hw-button-pad-block-start/-end/-inline-start/-endon the control, consumed by one rule per shape in the block's stylesheet.Icon cell width and padding were also each written from two places at different specificities and breakpoints, so turning on padding override *and* Different icon width on desktop dropped the desktop icon width. Both now resolve from a single variable.
Buttons theme settings: Text case gained Capitalize, and Font gained Subheading and Heading — the button size presets previously offered only Default/UPPERCASE and Body/Accent while every other typography preset in the theme offered the full set.
Product Media Gallery Carousel (HW): fixed an iOS/WebKit-only bug where the rounded carousel corners showed black triangles instead of clipping cleanly, caused by WebKit failing to clip composited descendant layers via border-radius + overflow:hidden. Forces clipping via
-webkit-mask-imageinstead. Sync-back from Glowwa.Carousel/Grid (HW): fixed the intermittent "loads on last slide" race condition on mobile, wired via a
<slideshow-component>-scopedMutationObserveron[in-viewport]that forcesscrollLeftback to 0 once overflow actually becomes scrollable. Sync-back from Glowwa, revised after review: corrects viascrollLeftdirectly rather thanslideshow.select(0), which guards onthis.currentalready being0and so no-ops in exactly this scenario.Collection Scroll (HW): Active title color now paints the current collection name — type presets (e.g. h3) were winning the cascade, so the setting had no effect. Control moved under Appearance next to Text color.
v3.2.0#
3 added2 changed2 fixed
Added
Collection Scroll (HW): new full-bleed section that shows one collection background at a time — the stage parks in the screen under the header while scrolling steps through images and titles, then lets the page carry on after the last collection.
Frame height is the visible stage (
px/vh), capped to the space under the header so it always sits fully in view; the header height is measured automatically and Extra top offset only adds to it.Scroll distance per collection sets the pace, and Snap to one collection (on by default) glides to the nearest whole image once scrolling stops.
Mouse wheel, trackpad, touch, arrow keys, Page Down and scrollbar drags all step through it — it rides ordinary page scroll rather than intercepting gestures.
Transitions: crossfade, cross-zoom, push, or wipe. Optional progress bar, custom active-title icon, and Absolute (HW) overlay slot.
Header menu (HW): new Mega menu nav type — a full-width, column-grid dropdown (link columns + an optional promo tile) that sits alongside the existing Product showcase (renamed from the old "Mega menu" — collection grid + optional highlight) and plain/compact Dropdown types.
Width: independent Full width / Page toggle (
Mega menu → Width) lets the grid's columns and promo rail stop at the theme's normal page width and centre, matching Product showcase's page-width option — the panel's background, border, and shadow always stay full-bleed regardless of this setting, so only the content is ever constrained.Promo tile: up to two promo images with label, link, and position (left/right of the columns); configurable background/blur, label padding, arrow (default or a custom SVG), and divider colour.
Grid heading typography: column headings get their own size preset (paragraph/heading/label scale or a fully custom font, size, line height, letter spacing, weight, and case) instead of inheriting the nav link style.
Header menu (HW): new unified Menu open style setting (
hw-headersection) — Page overlay (default, dims/blurs the page behind an open menu), Bottom border, or Drop shadow — applies consistently across Product showcase, Mega menu, and plain/compact Dropdown, replacing the old per-block "shadow on/off" toggle.Plain and compact dropdowns no longer carry an automatic subtle shadow; pick Drop shadow if that look is wanted.
Migration: the old
dropdown_show_shadowblock setting is removed. Any store that had it switched on needs Menu open style set to Drop shadow after syncing, or it will silently fall back to the new default (Page overlay).
Changed
Icon with text (HW): Color is now always available for the label, so you can recolour the line without switching Text style to Custom.
Blank still inherits the parent or theme; theme icons and custom SVGs that use current color follow the same value.
Sync Bot (CI): client-repo PR titles now carry a short description instead of just a commit sha and target branch —
Flagship Sync (CORE): <description> (<branch>). Manualworkflow_dispatchruns require a newdescriptioninput; automatic merge-triggered syncs fall back to the merged core PR's own title (full sha/range detail still lives in the PR body). See.agent/docs/workflows/client-sync-target-branch.md§6 andMAINTAINER_GUIDE.md§4.
Fixed
Header menu (HW): an open menu's shadow no longer gets clipped by the panel's own scroll container (Chromium clips an element's box-shadow when it's also the scrollport) — shadow now paints on the outer, non-scrolling submenu box.
Header menu (HW): the page-overlay dim/blur now reliably appears for Product showcase panels whose content (e.g. a collection grid) hydrates asynchronously, instead of sometimes never activating.
v3.1.0#
6 added4 fixed
Added
Announcement Bar (HW): new Display mode setting switches the bar between a rotating carousel, a static row showing every message at once, and a continuously scrolling marquee ticker, using the same slides either way.
Switching modes needs no content rebuild and no saved-JSON migration; carousel remains the default, so existing stores are unchanged.
Announcement Bar (HW): Navigation style can now show a dot progress indicator instead of arrows, so autoplaying bars can hint at position without prev/next controls.
Announcement Bar (HW): dots can sit below, left, or right of the message, with alignment and gap controls for below and an edge inset for left/right.
Announcement Bar (HW): new Hug content width option for dots shrinks the message box to its real text so dots sit beside the words instead of out at the edge of a wide, mostly-empty bar.
Announcement Bar (HW): new Arrow layout option places arrows tight against the widest message rather than stretched to the bar edges, with a configurable gap, matching the BPerfect reference design.
Hugging measures the real rendered text, not the Slides max width setting, so long messages are never forced to wrap.
Announcement Bar (HW): marquee mode's pause control can be placed left or right and sized independently, so it fits a thin announcement row rather than borrowing the Marquee section's larger default.
Fixed
Announcement Bar (HW): the dot corner radius slider now applies its value as a percentage as its label describes, instead of pixels — previously every setting above the lowest few rendered a fully round dot, making a 51-step slider behave as an on/off toggle.
Announcement Bar (HW): Arrow layout is now offered only with the Fade transition, since Slide gives every message an equal share of a wider track and leaves no width for arrows to hug.
Announcement Bar (HW): turning off Show arrows now hides dot navigation too, instead of leaving dots visible when the navigation style had previously been set to dots.
Announcement Bar (HW): two marquee schema labels referenced translation keys that did not exist, causing theme-check errors on the edge fade settings.
Show 9 earlier releases
v3.0.0#
7 added4 changed1 fixed
Added
Typography: Theme settings → Typography now offers twelve paragraph presets (was six), so two different font weights at the same size are possible without duplicating text blocks or overriding weight per-instance.
Typography: Letter spacing and line height on every type preset (paragraphs, headings, label, link, and button sizes) now accept an optional exact override in px, %, em, or rem alongside the existing slider, for finer control than whole-percent steps allow.
Col Carousel Card (HW) / Col Carousel Group (HW): split into two sections so Content offers only Card or only Group — the theme editor no longer shows both layouts under one tree.
Collections Carousel/Grid (HW): sibling of Carousel Grid that loops a section collection list into slides with Closest collection; gutters match Carousel Grid for correct peek/edge width. Card overlay works for all background media; background image uses the block setting only.
Card (HW): optional Collection setting so nested blocks can connect to Closest collection (falls back to an inherited closest when blank).
Button (HW): Link style gains an optional colour picker for the link text; blank inherits the surrounding colour.
Button (HW): Theme settings → Buttons now include five size presets (Extra Small through Extra Large). Default is Middle; custom sizing still works where it already existed.
Changed
Icon with text (HW): block type renamed to
hw-icon-text(editor name unchanged).Migration: update saved theme JSON from
"type": "hw-card-icon-text"to"type": "hw-icon-text".Button (HW): size presets renamed to Small / Middle / Large, with Extra Small and Extra Large added in theme settings. Default is Middle.
Migration: saved
1/2/3still map; re-check theme typography button sizes after sync (setting ids renamed).Button (HW): Fill and Outline presets now use Fill 1–6 / Outline 1–6 naming. HW blocks and templates were migrated; base Horizon blocks keep the old option values; legacy values still accepted.
Styleguide: button showcase updated for the colour palette and twelve Fill / Outline presets (full style × size matrix).
Fixed
Carousel slides-per-view width: whole-number Desktop/Tablet/Mobile slides per view (e.g.
3) no longer reserves peek space for the next card, so N cards match the page-width controls row. Use a fraction (e.g.3.2) when you want a next-card peek.
v2.2.0#
2 added1 changed1 fixed1 removed
Added
Group (HW) / Group Hover (HW) / Group (Conditional): new Centre while scrolling (desktop) option keeps a column's contents vertically centred in the viewport as the shopper scrolls past, without sliding under the sticky header.
Needs a taller sibling column to have room to travel; the sticky-centre shell preserves the group's gap, direction, and alignment.
Carousel navigation: Theme settings now set brand-wide defaults for prev/next arrows (style, size, gap, corner radius, border, and disabled opacity), so carousels look consistent without per-block tweaks.
Carousel navigation (HW), Cart upsells (HW), and Product linked cards (HW) each gain an Override theme carousel navigation toggle for one-off instances; arrow colours stay block-level either way.
Changed
FAQ Split (HW)the default preset's heading/filters and sticky-image columns now use Centre while scrolling (desktop), matching the new Group (HW) behaviour.
Fixed
Loop ingredient tabbed (HW)the "Ingredient card" preset no longer ships a leftover metaobject image binding, matching the unbound default of every other preset.
Removed
Group (HW) / Group Hover (HW) / Group (Conditional): the advanced Sticky on desktop setting is replaced by Centre while scrolling (desktop).
hw-faq-sticky-group(a separate block used elsewhere in FAQ Split) is unaffected and still offers plain fixed-offset sticky.
v2.1.0#
1 added3 changed4 fixed
Added
Expanding cards (HW): new footer slot below the card track (same nested text/group/button model as the header) so merchants can add CTAs or copy under the carousel without changing the section width.
Changed
Expanding cards (HW): section and card colours move onto the colour palette — an optional background/text pair (and divider pair) replaces the old colour schemes. Card content stays transparent so media shows through; white text remains the photo default until a text colour is set.
Expanding cards (HW): the card content panel now exposes the same mobile/desktop direction, alignment, gap, and padding controls as HW Group. Media, stretch link, and progress stay card-owned.
Expanding cards (HW): the pause control gains a corner position and a show-on breakpoint (both / desktop-only / mobile-only) alongside the existing size toggle.
Fixed
Expanding cards (HW): an empty or unconfigured card loop no longer renders a stray placeholder card on the live storefront (the editor still shows scaffolding).
Expanding cards (HW): empty header/footer no longer leave a blank gap above or below the card track.
Expanding cards (HW): fixed a white flash when clicking rapidly between cards; desktop cards now always fill the track and progress restarts cleanly.
Expanding cards (HW): on full-width sections, header and footer copy now sit within the page gutters instead of flush to the viewport edge while the card track keeps bleeding edge-to-edge.
v2.0.0#
3 added4 changed4 fixed3 removed2 docs
Added
Colour palette: a single store-wide set of colours in *Theme settings → Colors* replaces the old colour schemes. Any section or block can override its own colours, and those colours cascade only to that subtree.
Whatever the merchant picks is applied verbatim — nothing is derived and no contrast is computed, so a dark background needs an explicit text colour set alongside it.
Docs:
.agent/docs/architecture/COLOR_PALETTE.md,.agent/docs/theme-settings/color-palette/.Buttons: twelve palette-backed presets — six Fill and six Outline — each with its own base and hover colours plus border width, corner radius, and mobile/desktop min-height. Every button-bearing block now picks a preset instead of carrying its own colour fields.
Carousel & linked-card arrows: own base, hover, and disabled colours (background / icon / border) plus a border width, instead of borrowing from a button preset. Colours now hold on hover.
Changed
Colour palette migration: every section, block, and shared snippet moved off colour-scheme classes onto palette colours. Sections expose a background/text pair (plus a divider pair where relevant); blocks inherit by leaving their pickers blank.
Breaking: saved
color_schemevalues no longer resolve — a section previously set to a non-default (e.g. dark) scheme falls back to inheriting the page palette until its colours are re-set. It renders light, not an error.Migration: all 15 shipped JSON templates were converted with equivalent colours. Client stores holding their own saved data need a pass through the editor after syncing.
Buttons: the
tertiaryfamily is now presented as Outline 2; its underlying class is unchanged so existing markup and saved data are unaffected.Marquee, Compare, and Hero slideshow (which landed from
mainmid-migration) were brought onto the palette alongside everything else.Colour-scheme vocabulary retired from merchant-facing copy, setting labels, code comments, and docs (this changelog's historical entries excepted).
Fixed
Colour palette: an inverted section no longer renders dark-on-dark muted copy — the muted foreground is re-derived from the section's own text colour.
Media hotspots: the popup background no longer paints across the whole gallery image; it lands on the popup itself.
Carousel & linked-card arrows: colours hold through hover instead of being taken over by the old button preset.
Hero + templates: an orphaned colour key on saved text/heading blocks was renamed, clearing the invalid-preset error it raised.
Removed
Legacy colour-scheme system: the
color_schemesconfig, the scheme snippet and its class emitters, and the old scheme docs are gone, replaced by the palette equivalents.Colour-only wrapper elements, inert colour-class guards, the dead
hw-textcolour wrapper, and other now-unused scheme-era scaffolding.Locale keys and preset/template data orphaned by the migration.
Docs
New:
COLOR_PALETTE.md(the model, the fullcolor-styleparameter table, the token reference, and a troubleshooting section) plus thecolor-palettetheme-settings guides (overview, editor, developer).Rewritten for the palette: the colour docs, the architecture cheat sheet and section-building template, the cart and product family guides, and the merchant-facing editor guides.
v1.3.0#
1 added
Added
Hero slideshow (HW) (
sections/hw-hero-slideshow.liquid,blocks/_hw-hero-slide.liquid): Multi-slide full-bleed banner sibling to static Hero (HW). Per-slide image/video (optional mobile media), overlay, and nested HW content (Group / Text / Button). Section-level minimum frame height (vhorpx, content can grow taller), autoplay with pause control, and hero-native arrows/pagination with icon style/shape, colour schemes (shaped arrows use primary button colours), inset, and pagination position/dot size/gap. Transparent-header compatible viahw-hero-wrapper. Prefer over bare Horizonslideshowfor new HW work. Docs:.agent/docs/sections/hw-hero-slideshow/.
v1.2.0#
5 added2 changed4 fixed
Added
hw-product-card-gallery: optional
image_metafieldsetting to source the card image from a product metafield (falls back to featured image).Carousel navigation & hw-product-linked-cards: arrows can be sized via explicit
nav-button-width/nav-button-height(falls back to legacy padding sizing when unset).hw-star-rating:
underline_textoption to underline the rating text.Marquee (HW) section (
sections/hw-marquee.liquid,snippets/hw-marquee.liquid,assets/hw-marquee-section.js): Standalone scrolling strip with trimmed settings (gap, optional leading label, direction left/right, slow/medium/fast speed, pause on hover/focus, pause control, optional edge fade). Dual-track CSS loop withprefers-reduced-motionsupport; script registered once viahw-scripts.liquid. Presets: scrolling text + logo strip. Core templates (index,collection,product,page.general) migrated off nestedhw-section-starter+ block wrappers. Docs:.agent/docs/sections/hw-marquee/.`hw-compare` sectionNew comparison-table section family — no prior HW equivalent. N-column, horizontal scroll with a sticky feature-label column, optional per-column product binding, column-level background/typography/icon overrides, custom typography support, and stacked or side-by-side header layouts. Blocks:
hw-compare-header,hw-compare-footer,hw-compare-column-list→hw-compare-column→hw-compare-cell,hw-compare-row-list→hw-compare-row. Two presets: "Us vs. them" and "Pricing tiers".
Changed
hw-faq-split: 'Show top border' now governs both outer borders (relabelled 'Show outer borders (top & bottom)').
Legacy marquee block deprecated for new use:
blocks/hw-marquee.liquidpreset removed; Marquee preset removed fromhw-section-starter. Block file kept for sync safety until client instances migrate.
Fixed
media: native play/pause now broadcasts so only one deferred video plays at a time (base Horizon file).
slideshow: next arrow no longer stays disabled after swiping back to the first slide (base Horizon file).
hw-search: 'No results for X' now updates on each keystroke instead of freezing on the first term.
hw-text: text alignment now applies when a block renders a bare text node (e.g. truncated text) at full width.
v1.1.0#
2 added1 changed5 fixed
Added
Design-to-Template Playbook (
.agent/docs/architecture/DESIGN_TO_TEMPLATE_PLAYBOOK.md): New process guide for turning a Figma design into a composed template/section/block tree from existing HW components. Covers decompose → match components → colours → typography → spacing → assemble → validate → flag-gaps, with a delivery checklist. Hardened after two real process failures: (1) a build that hand-wrote new markup instead of composing from existing components, and (2) a build that conflated a block'stype_preset(visual size only) with its semantic HTML tag. Step 4 now states explicitly that the semantic tag andtype_presetare independent decisions; Step 7 and the delivery checklist now require writing down real Figma numbers as a spec table and diffing livegetComputedStyle/getBoundingClientRectvalues against it, on every breakpoint, before calling composition work done.`.cursor/rules/design-to-template.mdc`New
alwaysApply: trueCursor rule (scoped totemplates/*.json,sections/*.liquid,sections/*-group.json) that forces a read of the playbook,COMPOSITION_RULES.md, andCAPABILITY_MATRIX.mdbefore any design-to-template work, and summarizes the two failure modes above so they're caught even on a rushed read.
Changed
`.agent/catalog/catalog.json` guidance: Removed all playbook references that pointed at the full ~58k-line component catalog for single-component lookups. The playbook now directs readers to the component's own
.liquidfile directly; the catalog is reserved for genuine cross-component surveys only.
Fixed
`.size-style` height inheritance: Scoped the
heightrule inassets/base.cssto.group-block.size-styleso nestedhw-button/hw-textelements no longer inherit an ancestor Group (HW)'s custom height via the shared CSS class (was causing buttons to balloon to the full height of their parent card).Template JSON — no Liquid: Replaced
{{ article/blog/collection/page.* }}intemplates/*.jsonwith static placeholders so CI (check-template-json-no-liquid.sh) passes and storefronts do not print literal tokens.PDP/client price fallback:
product-price.jshonorsdata-show-sale-price-firstwhen morphing from embedded variant JSON misses the livedata-block-id.Variant ATC labels:
hw-modal-variant-pickerupdates everyproduct-form-componentlabel in the section/dialog (main + sticky). Product cards distinguish sold-out vs unavailable label copy.PDP enrichment:
hw-pdp-variant-pickersection fetch usesasync/await.hw-add-to-cart-buttontranslates locale-keyadd_to_cart_textvalues when unavailable.
v1.0.0#
62 added59 changed68 fixed1 removed17 docs
Added
Hero Shape (HW) (
blocks/hw-hero-shape.liquid,snippets/hw-hero-shape.liquid): New decorative shape block for use inside HW Hero. Absolutely positioned relative to the section frame (not the page-width wrapper —section-content-wrapperposition reset inhw-hero.css). Settings: image picker; corner Position (top-right / top-left / bottom-right / bottom-left) with Override on desktop; Width (mobile + optional desktop override, free-text acceptingpxorrem); Offset X / Y (mobile + optional desktop override, free-text acceptingpx/rem/negative values, applied viatranslatebefore rotation); Rotation (−180–180°, range); Show on (both / mobile only / desktop only); Layer (behind / above) with Override on desktop; Opacity (10–100%). CSS custom properties drive all values; desktop vars fall back to mobile vars when no override is set. Locale keys added toen.default.schema.json.HW Hero — editorial layout mode:
layout_modesetting on HW Hero (overlay/editorial, defaultoverlay). In editorial mode the ratio-box sizes to content (no padding-top/min-height frame),.hw-hero__contentreverts to static positioning, and background media / overlay / section link settings are hidden. Enables 50/50 side-by-side hero layouts via a row-direction Group (HW). Editorial preset added to the section schema. CSS:.hw-hero__ratio-box--editorial,.hw-hero__content--editorial,.hw-hero__content-inner--editorialinhw-hero.css.Group Hover (HW) (
blocks/hw-group-hover.liquid, `snippets/hw-group-hover.liquid`): Full Group (HW) layout, appearance, sticky, link, and padding surface plus optional Hover Effect (background/border/text colour, scale 100–110%, lift, shadow sm–xl, transition duration/easing). Per-instance hover CSS targets `data-hw-group-hover="{{ block.id }}"`. Allowed children:@theme,@app, nested group variants, `hw-inset`, `hw-button`, carousel controls, `hw-flex-spacer`, countdown blocks, etc. Docs: `.agent/docs/blocks/hw-group-hover/EDITOR_GUIDE.md`, `DEVELOPER_GUIDE.md`.Text (HW, align) (
blocks/hw-align-text.liquid): Thin wrapper around `snippets/hw-text.liquid` with `force_alignment: true` — alignment applies at fit-content width, not only when width is Fill. Separate Alignment (desktop) and Alignment (mobile); mobile uses `--text-align-mobile` + **hw-text--align-mobile-*(`assets/hw-text.css`). Full Text (HW) typography, truncation, background, and padding schema. Locale:t:names.hw_align_text**.Collection Count (HW) (
blocks/hw-collection-count.liquid): Picks a collection and replaces `$$$` merge tokens in richtext with `collection.products_count` (falls back to0when unset). Renders via `hw-text` + `text_override`. Default copy:$$$ items. Docs: `.agent/docs/blocks/hw-collection-count/EDITOR_GUIDE.md`, `DEVELOPER_GUIDE.md`.Price typography presets (HW): Theme settings → Prices → Price typography presets defines three shared size scales (Price 1, Price 2, Price 3) with separate mobile/desktop ranges for the main price, compare-at price, and savings label, plus global Price color and Compare price color. Price (HW) (
blocks/hw-price.liquid) adds Price typography preset (price_preset: None / 1 / 2 / 3, default 1);snippets/hw-price.liquidemitsprice-preset-container/price-preset--{n}and acceptsprice_preset. Sizes resolve via CSS custom properties in `snippets/theme-styles-variables.liquid` (--price-preset-{n}-price-size,-compare-size,-label-size) and preset class rules on.price,.compare-at-price,.price__savings-label, and.hw-price__from-amount. Layout flex wrapper in `assets/hw-price.css`. Docs: `.agent/docs/blocks/price/EDITOR_GUIDE.md`, `DEVELOPER_GUIDE.md`; `.cursor/references/hw-price-settings-patterns.md`.Search panel (HW) (
sections/hw-search-panel.liquid, `assets/hw-search.js`, `assets/hw-search.css`): Header-group section (groups: ["header"], limit 1) for a fixed search overlay aligned below the nav. Custom element `hw-search-panel` drives open/close, optional backdrop (sibling node, not a child), scroll lock, `hw-header--search-open` on the header, Escape / ⌘K shortcuts, and focus return to the trigger. States: popular (pill tabs from `pill_collections` with optional label overrides filter static product cards), results (debounced AJAX via?section_id=hw-search-panel&q=...), and no-results. Static `hw-product-card` block for card layout. Section settings cover placeholder/clear colour, close icon (image or SVG), colour scheme, section width (page / full / custom text), backdrop opacity, popular/results labels and count typography (preset + custom), pill styling (button presets, heights, padding, inactive opacity), sidebar width, divider, panel padding, logo area width on the desktop search row, and mobile breakpoint. Storefront copy in `locales/en.default.json` (hw_search.*). `sections/header-group.json` preset adds the section and wires the Glowwa header. Docs: `.agent/docs/sections/hw-search-panel/EDITOR_GUIDE.md`, `DEVELOPER_GUIDE.md`; `.cursor/references/hw-search-panel.md`.Collection (HW): New
hw-collectionsection. Loopscollection.productsand renders each via thehw-product-cardblock usingproduct_context, delegating layout tohw-loop-carousel-grid. No filters — clean grid/carousel with standard HW layout controls. Drop onto any collection template.Loop – Collection Tiles: New
hw-loop-collection-tilessection and_hw-loop-item__collection-tileblock. Loops acollection_listsetting (supports dynamic source connection — e.g.collection.metafields.custom.filter_collectionson a collection template) and renders each collection as a composable tile via child blocks. Active state applied automatically when the tile's collection handle matches the current page collection. Falls back gracefully when no source is configured. CSS registered inhw-stylesheets.liquid.Cart drawer — compact line items (`hw-cart-line-item`): `item_layout: compact` card rows (thumbnail + title/qty pill, meta line, price + text remove) in `snippets/hw-cart-product-compact-row.liquid` / `assets/hw-cart-products.css`. `drawer_layout: compact` (section) and `item_layout` are independent — set both for the Glowwa-style drawer. Compact-only controls: mobile `thumbnail_size` (default 76px) + optional desktop override at 750px+ viewport (
thumbnail_size_desktop, default 82px); `compact_content_gap` (title → details) and `compact_meta_price_gap` (details → price); `compact_vertical_align` (topor space price to thumb bottom). Subscription UI slot: `show_subscription_slot` renders `snippets/hw-cart-line-item-subscription.liquid` in the same<tr>(grid areasubscription) — empty in HW, override in brand themes. `sections/cart-group.json` preset wires compact drawer + line items. Docs: `.agent/docs/cart/cart-drawer/DEVELOPER_GUIDE.md` §11, `.agent/docs/cart/cart-drawer/EDITOR_GUIDE.md`.Sticky buy CTA (HW) (
blocks/hw-sticky-buy-cta.liquid, `assets/hw-sticky-buy-cta.js`): Glowwa-style PDP bottom bar that scrolls to the buy area (not add-to-cart). Shows after scrolling past buy buttons; optional hide near footer; variant morph on `ThemeEvents.variantUpdate`. Mobile: full-width CTA strip. Desktop: centered floating card (Figma 731px max width, configurable side inset), optional product summary (image, title, variant badge, 2-line description, Price (HW)), separate bar / CTA / badge color schemes and button presets (primary, secondary, tertiary). Desktop CTA ideal width targets button column width when summary is visible (flex: 0 1ideal, shrinks when tight). `color-{{ scheme }}` on painted controls only (not wrappers) to avoid global **body,.color-*page background on pills. Rendered at Product information (HW) section level; script inhw-scripts.liquidon product template. Turn offenable_sticky_add_to_cartonhw-product-informationto avoid two bars. Presethw_sticky_buy_cta_glowwaintemplates/product.json. Docs:.agent/docs/blocks/hw-sticky-buy-cta/EDITOR_GUIDE.md,DEVELOPER_GUIDE.md;.cursor/references/hw-sticky-buy-cta.md**.Product linked cards (HW) (
blocks/hw-product-linked-cards.liquid): PDP upsell strip (Style 2 cart-upsell cards) inside `_hw-product-details`. Template `product_list` or current-product metafield list; independent mobile/desktop slider / stack; mobile card-track page-gutter bleed (nav/title inset); card title/description typography presets; description from product, per-linked-product metafield, or block text with dynamic source; optional View button padding/min-height overrides. Reuses `hw-cart-upsells.css` / `hw-cart-upsells.js` (cart drawer gate). Docs: `.agent/docs/blocks/hw-product-linked-cards/EDITOR_GUIDE.md`, `DEVELOPER_GUIDE.md`; `.cursor/references/hw-product-linked-cards.md`.Button modal (HW) (
blocks/hw-button-modal.liquid): Dedicated block for a CTA that opens a modal via Horizon `dialog-component`. Includes static nested `hw-button` trigger (modal_trigger), panel settings (viewport inset, position, max width as px/rem text, overlay, padding, close control), optional desktop overrides (gap, radius, padding, close size, max height) with step 1 ranges. Nested content: `hw-text`, `hw-image`, `hw-group`, etc. Mobile bottom position squares off bottom panel corners (bottom sheet). Docs: `.agent/docs/blocks/hw-button-modal/EDITOR_GUIDE.md`, `DEVELOPER_GUIDE.md`; README blocks table updated.PDP layout and gallery (HW buy area):
`sections/hw-product-information.liquid` — When Equal columns is off, Override desktop column widths exposes Desktop media max width and Desktop details max width text fields (px/rem) plus optional desktop gap override between gallery and product details.
Theme settings → Page layout — Optional text overrides for narrow, normal, and wide content widths; independent mobile and desktop page gutter overrides (emit scoped `--page-margin` from `snippets/theme-styles-variables.liquid` when enabled).
`blocks/_hw-product-media-gallery.liquid` — Extend media to screen edge on mobile toggles full-bleed vs inset gallery on page-width sections; when inset, bottom thumbnail rows can still bleed into the page gutter via `assets/hw-product-media-gallery.css`. Loop carousel checkbox disables slideshow infinite and enables end-state arrow disabling. Icons = Custom adds full Gallery arrow customization (carousel-nav controls, horizontal arrow inset with optional desktop override, arrow/chevron SVG stroke width 0.5–4px step 0.5). Sidebar Gap uses step 1 ranges (0–48px mobile, 0–64px desktop override).
`blocks/hw-product-media-overlay.liquid` — Gallery media overlay (HW) child block: corner stack on the main slider surface (nested text, groups, price, etc.); horizontal/vertical inset with optional desktop override (0–100px, step 1); slider host respects custom arrow horizontal inset. Reparented to `__slider-overlays` by `assets/hw-product-media-gallery.js`.
Docs — `.agent/docs/blocks/product-media-gallery/EDITOR_GUIDE.md`, `DEVELOPER_GUIDE.md`; index link in `.agent/docs/README.md`.
HW Group — width “Content” (intrinsic): New `width` / `width_mobile` value `content` on `hw-group`, `hw-group-basic`, `hw-group-card`, `hw-group-link` (`t:options.hw_group_width_content`). `snippets/hw-group-size-style.liquid` (`max-content`) + `assets/hw-group-layout.css` (`max-width: 100%`); `base.css` `group-block--width-content` uses `flex: none`. Homepage carousel stockist inner group `templates/index.json` uses `width: content`. Paragraph `t:info.hw_group_width_content` shows when Content is selected.
Presence indicator (HW) (
blocks/hw-presence-indicator.liquid, `snippets/hw-presence-indicator.liquid`, `assets/hw-presence-indicator.css`): Online / live status dot with optional animated pulse; dot and pulse ring colours, size, pulse on/off, duration, and optional screen reader label (defaults to `accessibility.hw_presence_indicator` / “Online” in `locales/en.default.json`). Styles registered in `snippets/hw-stylesheets.liquid`. Allowed on `hw-group`, `hw-group-basic`, `hw-group-card`, `hw-group-link`, and `sections/hw-hero`. Docs: `.agent/docs/blocks/hw-presence-indicator/DEVELOPER_GUIDE.md`, `.agent/docs/blocks/hw-presence-indicator/EDITOR_GUIDE.md`.HW Carousel Navigation — button appearance: `blocks/_hw-carousel-navigation.liquid` adds `arrow-button-appearance` (Icon / colour scheme vs Primary / Secondary / Tertiary) and `arrow-button-preset` (1–3, theme typography). Inherited colour scheme settings show only when Icon. `snippets/slideshow-arrow.liquid` accepts optional `arrow_button_style` / `arrow_button_preset`; `snippets/hw-carousel-navigation.liquid` wires theme variants, `hw-carousel-navigation__arrows--theme-button`, and scoped CSS. Locale keys added under `t:settings`, `t:info`, `t:options`. `visible_if` for hyphen ids uses `block.settings['arrow-button-appearance']` for theme-check.
HW Carousel Progress Bar — corner radius: `blocks/_hw-carousel-progress-bar.liquid` range `progress-bar-corner-radius` (0–24px, default 4); appearance paragraph localized as `t:info.hw_carousel_progress_bar_colors`. `snippets/hw-carousel-progress-bar.liquid` sets `--progress-bar-radius` / `--progress-bar-height` and clipped visual radius via Liquid. Colour-scoped track BG uses **
.hw-carousel-progress-bar[class*='color-scheme']so anycolor-{{ scheme }}** token is covered without listing schemes 1–6 only.Ingredient loop section + item block: Added dedicated `hw-loop-ingredient` section (
sections/hw-loop-ingredient.liquid) and `_hw-loop-item__ingredient` block (blocks/_hw-loop-item__ingredient.liquid) following the same loop-section pattern as UGC. Data source is a selected ingredient group metaobject entry plus a list-field key that resolves individualingredientitem entries per slide viaclosest.metaobject.ingredient. Item block includes optionalmin_height/enable_min_heightsettings for controlling card height per slide.UGC loop section + item block: Added dedicated `hw-loop-ugc` section (
sections/hw-loop-ugc.liquid) and `_hw-loop-item__ugc` block (blocks/_hw-loop-item__ugc.liquid) using the proven loop-section pattern (header/footer static blocks + slide capture +hw-loop-carousel-gridrender). Data source is a selected `ugc_video_group` entry plus a list-field key that resolves `ugc_video` item entries per slide viaclosest.metaobject.ugc_video.Global loop carousel wiring asset: Added `assets/hw-loop-carousel-wire.js` and registered it in `snippets/hw-scripts.liquid` to wire loop-section navigation controls to slideshow instances in a single shared place.
Text (HW) — Basic variant: New `hw-text-basic` block (
blocks/hw-text-basic.liquid) reuses `snippets/hw-text.liquid` for RTE-based copy with a streamlined schema beside full `hw-text`. `hw-text` expands settings/rendering for the broader text family and removes `disable_fluid_scaling` — fluid type follows theme typography. `hw-text` / `hw-text-basic` are registered everywhere the theme whitelists text block types (`hw-featured-strip`, `hw-loop-collections`, `hw-blog-card`, `hw-product-card`, `hw-marquee-overlay`, **hw-mobile-drawer-*rich branches,hw-footer/hw-footer-utilities,hw-hero, etc.).hw-featured-strip/hw-loop-collectionssupport optional paired title blocks (hw-text+hw-text-basic) via static ids.locales/en.default.schema.json** block names refreshed for HW text variants.HW FAQ Split — Data-driven list block: `hw-faq-split-data` (
blocks/hw-faq-split-data.liquid) and `_hw-faq-split-data-item` (blocks/_hw-faq-split-data-item.liquid) render FAQs from a FAQ Group entry’s `faqs` list (`faq_group`) or a product list-of-FAQ-reference metafield. Accordion UX (drawer vs accordion, caret vs plus, dividers, borders, heading preset) is configured on the item block; one `hw-accordion` wrapper spans all rows via `snippets/hw-accordion` `accordion_fragment` `open` (first loop iteration) / `close` (last), with loop flags passed on `content_for` (**hw_faq_split_*, analogous toform_objon accelerated checkout). HW FAQ Split (`sections/hw-faq-split.liquid`, v4.3.0) andhw-faq-split-contentaccepthw-faq-split-dataalongside manualhw-accordiontrees;templates/index.json/ locale strings updated as wired. Supporting CSSassets/hw-faq-split.css; docs.agent/docs/blocks/hw-faq-split/DEVELOPER_GUIDE.mdand.agent/docs/blocks/accordion/DEVELOPER_GUIDE.md**.HW Featured Blog — default preset and homepage wiring: Section preset aligns with the storefront HW Featured Blog homepage layout (
templates/index.json): mobile/tablet carousel and desktop grid with slide peeks; Carousel Controlhw-group(column alignment, padded top) holding Button (HW) (“All Blog Posts”), Carousel Progress Bar, and Carousel Navigation; static-headerhw-group+ Text (HW) eyebrow with full truncation/case defaults; `_hw-featured-blog-content` nest `static-blog-card` (featured image, meta row with category pill + Article date (HW), title uppercase, excerpt sentence-limited via Text (HW)). Content block schema declares `hw-blog-card`; `hw-group` schema lists `hw-button` so nested buttons validate with theme-check. Section and content block titles use `t:names.hw_featured_blog` / `hw_featured_blog_content`. Preset prefers valid color_scheme enums (no empty strings); carousel blocks omit preset `disabled` (theme-check caveat) — visibility hides progress/nav similarly when needed.Article date (HW) (
blocks/hw-article-date.liquid): Block for `published_at` vs `updated_at` with Liquid date filter format (date-format). Typography pattern matches Text (HW). Optional child on `hw-blog-card` (blocks/hw-blog-card.liquid).Text (HW) — truncation and preset capitalization override: Setting Limit displayed length with Limit by: characters (strip HTML → plain truncation), lines (richtext retained, CSS line clamp), sentences (period + space split after strip). Override preset case when typography preset is not Custom. Implemented in `snippets/hw-text.liquid`; `hw-text` block schema additions; `hw-text-typography-settings` skill note.
Savings label suffix + gradient background: Theme settings → Prices → Savings label now includes a Suffix input (
prices_savings_label_suffix) for text after the savings value (e.g. "20% off"). The Label background setting was changed fromcolortocolor_backgroundto support gradient fills; CSS usesbackgroundinstead ofbackground-colorforprice__savings-label. (config/settings_schema.json,locales/en.default.schema.json,snippets/hw-price.liquid,assets/hw-price.css,assets/base.css)“From” line typography (separate from main price): Theme settings → Prices and Price (HW) / featured Product price (HW) use the same Preset list as the price block (
from_label_type_preset/price_from_label_type_preset), plus Use theme default and Match main price. Custom on the prefix usestypography-stylewhen the block’s main preset is Custom.snippets/hw-price.liquidsplits prefix + amount (.hw-price__from-line); prefix text usescontent.price_from_prefix. Ad-hoc style options (subdued/body, etc.) were removed in favour of Horizon preset classes.“From” price on product cards (HW): Theme settings → Prices includes "Show "From" price on product cards" (default off). Price (HW) blocks add "From" price (variant prices differ) (use theme default / on / off).
snippets/hw-price.liquidusescontent.price_fromwith the lowest variant price when prices differ, including initial render on swatch cards; after swatch selection, the card price updates to the selected variant.sections/section-rendering-product-card.liquidrendershw-pricefor Section Rendering API cards. Follow-up fix: avoid immediate JS overwrite of initial "From ..." during size-picker auto-init by passinguserInitiatedthroughhw:card-option-changedand skipping init-only replacement inproduct-card.js. (config/settings_schema.json,locales/en.default.schema.json,blocks/hw-price.liquid,blocks/_hw-featured-product-price.liquid,sections/hw-product-information.liquid,assets/hw-card-variant-picker.js,assets/product-card.js)HW Featured Collections - tab controls + preset refresh:
hw-collection-tabsnow supports comma-separated custom tab labels and responsive row behavior controls (overflow scroll or wrap on mobile, with optional desktop override). Added a new section preset "HW Featured Collections (Current Index)" to mirror the currently saved homepage configuration.Files:
blocks/hw-collection-tabs.liquid,assets/hw-featured-collections.css,sections/hw-featured-collections.liquid.Editor simplification: Removed
hw-collection-tabsborder radius controls (unused) and kept capsule radius static in CSS.HW Grid - desktop row gap override (
blocks/hw-grid.liquid,assets/hw-grid.css,locales/en.default.schema.json): When Override row gap is on, merchants can optionally enable Override row gap on desktop (990px+) and set Row gap (desktop) separately from Row gap (below 990px). Tablet row gap continues to match the below-990px value. CSS variables--hw-grid-row-gap-mobile,--hw-grid-row-gap-tablet, and--hw-grid-row-gap-desktopdriverow-gapinassets/hw-grid.css.HW Carousel / Grid - Comparison carousel preset (
sections/hw-carousel-grid.liquid,locales/en.default.schema.json): New section preset Comparison carousel (Storytelling) that recreates the before/after + testimonial carousel layout:carousel-controlsgroup (progress bar + navigation),static-header(eyebrow, title, description), andstatic-carousel-grid-contentwith three HW Grid slides (two Group (HW) image cells plus a full-width HW Grid item for name and quote). Allhw-imagepicks are empty so the theme shows image placeholders; copy usest:html_defaults.hw_comparison_carousel_*keys inhtml_defaults. Section colour scheme in the preset defaults to scheme-1 for portability across stores.Flex spacer (HW) & Group (HW) height (desktop): Composable "spring" flex item and optional per-breakpoint group height.
`blocks/hw-flex-spacer.liquid` - Version 1.0.0 (file comment). Invisible
div.hw-flex-spacerwithflex: 1 1 0in row/column; `display_breakpoint` (all / mobile only / desktop only) viahw-block-displayclasses. Schema uses plain English for block name, preset, and help paragraph to avoidValidSchemaTranslationsissues whenen.default.schema.jsoncontains duplicate keys; optionalt:keys remain in locale file for later use.`assets/hw-flex-spacer.css` - Mobile vs desktop rules for
layout-panel-flex, HW groupdata-hw-desktop-layoutoverrides at 750px, and Hero.hw-hero__content-inner > .hw-flex-spacer. Registered insnippets/hw-stylesheets.liquid.`snippets/hw-group-size-style.liquid` - HW-only duplicate of
size-stylewidth logic; whenoverride_height_desktopis on, sets--size-style-height-mobileand--size-style-heightfor mobile vs 750px+ desktop.snippets/hw-group.liquiduses this instead ofsize-styleand addshw-group--override-height-desktop+hw-group--desktop-h-{fit,fill,custom}when the checkbox is on.`blocks/hw-group.liquid` - Settings:
override_height_desktop(default off),height_desktop,custom_height_desktop; child block type `hw-flex-spacer`. `assets/hw-group-layout.css` - Desktop height/flex overrides fordata-hw-desktop-layout='column'when override classes are present.Sections:
hw-heroblock list includeshw-flex-spacer(and group already allows the block).Locales:
names/info/settingskeys forhw_flex_spacerandhw_group_override_height_desktopinlocales/en.default.schema.json.Docs:
.agent/docs/blocks/hw-flex-spacer/DEVELOPER_GUIDE.md,EDITOR_GUIDE.md; index in.agent/docs/README.md.Tabbed content (HW) (
blocks/hw-tabbed-content.liquid,blocks/_hw-tab.liquid,snippets/hw-tabbed-content.liquid,snippets/hw-tab.liquid,assets/hw-tabbed-content.css,assets/hw-tabbed-content.js): New composable tabs feature.Core: Tabs with Tab (HW) children (
<hw-tab>) and optional `hw-image` blocks. Parent uses{% content_for 'blocks' %}only;hw-tabsJS buildsrole="tablist", reparents tab triggers, toggles panels/ARIA, and handles keyboard support (Arrow / Home / End / Enter / Space).Image hoisting: JS hoists direct-child
.hw-imageinto.hw-tabbed-content__mediaafter the tab row (Shopify does not allow dynamicid/typeon{% content_for 'block' %}).Tab row: Responsive layouts at 990px - flex wrap, grid (column counts + optional desktop column override), or horizontal slider with symmetric page-margin bleed and hidden scrollbar.
Typography: Button preset (primary/secondary/tertiary + preset + scoped
--blockId) or heading/paragraph presets viadata-hw-tabs-type-classand JS.Shell:
content_vertical_position(top / center / bottom) with.hw-tabbed-content__shellfor alignment inside tall columns.Colour scheme: Inherit colour scheme (default) and optional Colour scheme on the shell (
color-{{ scheme }}, same idea as Group HW).Tab (HW): Label, richtext body, open-by-default; optional Panel button under the body (link URL, label, style, preset, new tab); Panel button width - fit content vs full width, with optional desktop override at 990px.
Locales & assets:
accessibility.hw_tabbed_section_tabsadded tolocales/en.default.json;hw-imagegainsdisplay_breakpointselect (desktop only / mobile only / both). Assets wired throughsnippets/hw-stylesheets.liquidandsnippets/hw-scripts.liquid.Docs:
.agent/docs/blocks/hw-tabbed-content/DEVELOPER_GUIDE.md,EDITOR_GUIDE.md; index link in.agent/docs/README.md.HW Card block (`hw-card`) for HW Carousel / Grid: Horizon Works replacement for the base
_card, scoped under.hw-card__*so the base_cardstylesheet (withaspect-ratio: var(--card-ratio, 1)default) cannot leak in. Outer wrapper keeps the.cardclass so the carousel'sslideshow-slides > .cardsizing rules still match.Responsive aspect ratio -
autoorcustom(0-200%, step 2%) expressed as height-as-a-percentage-of-width via a padding-top::beforepseudo (100%= square,56%≈ 16:9,200%= 2:1 portrait). Separate desktop value with aDifferent ratio on desktoptoggle; single@media (min-width: 990px)rule swaps--card-ratio-padding-desktopin. All four combinations render correctly (mobile-only, desktop-only, same, different).Per-card heights -
.hw-cardusesalign-self: start(noheight: 100%on card or content) so different cards in the same carousel/grid can render at different heights without being stretched to the tallest sibling.Gradient-capable overlay -
color_background-typedoverlay_colorsetting renders viahw-overlay(same pattern ashw-stat-split).Click-to-play background video - optional poster + custom play icon mode for background videos, driven by a new
<hw-card-bg-video>custom element (assets/hw-card-bg-video.js). Delegated click pauses/plays the swapped-in<video>andstopPropagationprevents the block link from firing..hw-card--bg-video-clickstack flip: link drops to--layer-flat, media wrapper rises to--layer-raisedwithpointer-events: none+ interactive-children opt-in.Block padding moved to inner -
spacing-styleis applied to.hw-card__inner(not.hw-card__content) so the padding-top ratio base is the full card width, not the card minus merchant padding. When--has-ratiois on,.hw-card__innerusesposition: absolute; inset: 0so flex content sits inside the ratio box (flex + in-flow ratio pseudo cannot co-exist).Content above link -
.hw-card__contentpaints at--layer-raised(above the block link) withpointer-events: nonecascade +a, button, input, textarea, select { pointer-events: auto }. Interactive children inside the card receive clicks; non-interactive areas fall through to the block link. Addresses Gemini high-priority review comment on earlier revision.Registered in
_hw-carousel-grid-contentandsections/hw-carousel-grid.liquid(both schema and default preset). Presets: HW Card, HW Card with video (150% ratio + hw-video child, corner play + overlay), HW Card with image (120% ratio + overlay, bottom-aligned title/description).Docs:
.agent/docs/blocks/hw-card/DEVELOPER_GUIDE.md,EDITOR_GUIDE.md.HW Video block (`hw-video`) and shared video snippet: Fork of base
videoblock /video.liquidadding custom play icon, flexible positioning, and gradient overlay wiring. Renders a<deferred-media>for uploaded Shopify videos or a YouTube/Vimeo<iframe>for external URLs.Custom play icon -
image_pickerfallback,play_icon_sizerange 24-120 px, 9-position grid (play_icon_vertical×play_icon_horizontal). Corner positions insetmax(12px, var(--padding-sm, 0.75rem)); centre positions use true-centre transforms. Icon size driven by--hw-play-icon-sizecustom property.Gradient overlay -
toggle_overlay+color_backgroundoverlay_color, layered between poster image and play icon (poster mode), above the video element (autoplay mode), or above the placeholder (no video yet). All three stacks defined inassets/hw-video.css(registered viasnippets/hw-stylesheets.liquid).Shared with hw-card -
hw-cardbackground video routes throughsnippets/hw-video.liquidwithvideo_class: 'hw-card__bg-video ...'. CSS overrides make the deferred-media element, poster button, poster image, and<video>/<iframe>all fill the card viaposition: absolute; inset: 0; object-fit: cover.External URL query params - autoplay/mute/loop/controls params are inlined into YouTube (
&mute=1&loop=1&playlist={id}&controls=0&enablejsapi=1) and Vimeo (&muted=1&loop=1&controls=0&api=1) src URLs as appropriate.Presets: HW Video (defaults), HW Video (corner play) (bottom-right + overlay on).
Docs:
.agent/docs/blocks/hw-video/DEVELOPER_GUIDE.md,EDITOR_GUIDE.md.Custom font support (Typography): Merchants can now use fonts outside the Shopify font library.
New Use custom font checkbox in Theme settings → Typography.
When enabled: four text inputs for custom font-family names (body, subheading, heading, accent); Shopify font pickers remain in schema but are hidden.
New
metaobject_listsetting (custom_fonts, typecustom_font) - pick one or morecustom_fontmetaobject entries. Each entry definesfont_family_name,source_files(list of file references - woff2, woff, ttf, otf),font_weight,font_style,font_display.New
snippets/hw-custom-fonts.liquid: iterates the metaobject list, auto-detects format from file extension, outputs one@font-faceblock per entry with allsource_filescombined into a singlesrc:line.snippets/fonts.liquid: Shopify font preload<link>tags are suppressed per slot when a custom font name is active.snippets/theme-styles-variables.liquid:--font-body--family,--font-subheading--family,--font-heading--family,--font-accent--familyuse the custom font name when set, falling back totype_*_font.fallback_families(no hardcodedsans-serif).Setup guide in
.agent/docs/theme-settings/typography/EDITOR_GUIDE.md.Typography preset enhancements: All preset blocks (Label, Paragraph, Paragraph 2-6, H1-H6, Link) now have a full and consistent set of controls.
Added
type_font_*,type_weight_*,type_case_*settings to Label, Paragraph, and Paragraph 2-6 (previously only headings had these).Font select: Body / Subheading / Heading / Accent (4 options on all presets).
Font weight select: Default / 100-900.
Text case select: Default / Uppercase / Capitalize / Lowercase (expanded from Uppercase-only on headings and Link).
Unified setting order across every preset: Font → Size → Size (desktop) → Line height → Letter spacing → Weight → Case.
theme-styles-variables.liquidpreset loop unified - single branch handles all preset types;default: 'body'fallback for backward compatibility.Text (HW) - color scheme override: Colors group with Inherit color scheme (default on), same pattern as Group (HW). When inherit is off, Color scheme sets which palette supplies
--color-foreground/ heading / link for the block (including the text / heading / link tone control). Markup wraps incolor-{{ scheme }}viasnippets/hw-text.liquid;assets/hw-text.cssusesdisplay: contentson.hw-text-block__color-schemeso flex/grid sizing matches an unwrapped block.HW Styleguide section (`hw-styleguide`) and `page.styleguide` template: Visual audit page for typography presets, buttons, colour schemes, spacing, and related tokens. Section uses `@theme` blocks only (
{% content_for 'blocks' %}) so the theme editor owns all variants without editing the section Liquid.`sections/hw-styleguide.liquid` - Width, page width preset, colour scheme, optional section padding override, top/bottom dividers, and mobile/desktop gap between blocks (CSS variables
--hw-styleguide-gap-*). Preset HW Styleguide (Custom category).`assets/hw-styleguide.css` - Column layout and responsive gap between top-level block wrappers; registered in
snippets/hw-stylesheets.liquid.`templates/page.styleguide.json` - Assign to a Shopify page to render
main-page(title) plus the styleguide section with a large default block tree; per-block padding/corner noise trimmed so the JSON stays maintainable.Desktop mega menu block-composable panel system (
hw-mega-menu-panel)Five child block types: link column, product cards, collection cards, promo card, image card
Per-dropdown customisation - each top-level nav item can have its own panel layout
Locale-safe panel targeting via
link.handle(not translated title)Bottom bar with CTA button and trust text support
assets/hw-mega-menu.cssfor all mega menu stylesFallback to standard
mega-menu-listrendering for nav items without a custom panelHW Quick-Add Modal (`hw-quick-add-modal`): Zero-fetch quick-add modal for the
hw-product-cardblock. Modal content is server-rendered into a<template data-hw-quick-add-template>inside each product card;hw-quick-add.jsclones the fragment into the dialog on click - no network request on open.Variant picker (`hw-modal-variant-picker`): Custom element extending Horizon's
VariantPicker; overridesfetchUpdatedSectionso no network request is made on option change. All variant data is embedded at render time via<script data-hw-qam-variants>. On selection change: updates per-combination option availability (CSS fade for OOS), keeps swatch legend labels in sync, resolves the matching variant from embedded JSON, builds a minimal fakeDocumentforproduct-price.jsto morph the price display from, dispatchesVariantUpdateEventsoproduct-form-componentupdates the hidden variant ID and enables/disables the ATC button, and updates button text (Add to cart / Sold out / Unavailable) directly via data-attribute-driven translation strings.OOS styling: No strikethrough SVG - OOS size tiles fade via
opacity/ background; OOS color swatches fade viaopacity: 0.7on.swatch. Cart icon is hidden via CSSbutton:disabled .add-to-cart-icon { display: none }when the selected variant is unavailable.Layout:
.buy-buttons-blockis a direct flex child of.product-details(not nested inside.group-block-content), matching the sticky-button layout the nativequick-add-modal.liquidCSS expects -flex: 0 0 auto; margin-top: auto; position: sticky; bottom: 0. Explicitpadding-inline: var(--padding-3xl)compensates for the missing:where(.group-block-content > *)padding.Files:
assets/hw-quick-add.js,assets/hw-modal-variant-picker.js,assets/hw-variant-picker.css,snippets/hw-quick-add.liquid,snippets/hw-quick-add-modal-content.liquid,snippets/hw-quick-add-variant-picker.liquid,snippets/hw-quick-add-buy-buttons.liquid,snippets/hw-quick-add-wrapper.liquid.Docs: Mobile menu drawer (HW) - block tree, split mode, rest-links markup, custom accordion/drawer SVG wiring, Liquid pitfalls (
renderparam shadowing). Linked from header-navigation README and DEVELOPER_GUIDE.Cart upsells card styles (`hw-cart-upsells`): Two visual styles for cart upsell product cards.
Style 1 (default): Vertical card layout with image above title/price.
Style 2: Horizontal layout with configurable columns, custom background options (color scheme inheritance or per-product metafield background colors), and enhanced typography controls.
Card background options: Inherit block color scheme or specify custom color scheme; optional metafield-driven background colors per product (e.g.,
custom.card_background_color).Button customization: Custom ATC button labels, icon toggle, and Quick Add behavior (inherit/theme/show/hide).
Layout improvements: Gap controls, configurable card width (slider mode), column overrides (grid mode), and responsive padding.
Files: Updated
blocks/hw-cart-upsells.liquid,snippets/hw-cart-upsells.liquid,assets/hw-cart-upsells.css,assets/hw-cart-upsells.js; documentation in.agent/docs/blocks/hw-cart-upsells/(DEVELOPER_GUIDE.md, EDITOR_GUIDE.md).Product media area (HW): Composable blocks for the HW product media gallery and related PDP layout.
`hw-media-hotspots` (
blocks/hw-media-hotspots.liquid,blocks/_hw-media-hotspot.liquid) - Template-level hotspots on the first visible gallery image; child blocks for position and popup copy/link. Pin: mobile size, optional desktop size override, color, pulse. Popup: color scheme (content only; overlay stays transparent), corner radius, shadow blur (px), typography presets, padding with optional desktop override. Vanilla JS (hw-media-hotspot-component); not shown in zoom (positioning unreliable).`hw-media-usps` - Loop-style USPs from a product metafield (list of metaobject references), rendered via `_hw-media-usp-item` children and shared group/layout patterns.
`hw-product-info-items` - Replaces `hw-model-stats`: product metafield (list of `product_info_item` metaobject references), `_hw-product-info-item` children, parent-level card styling (scheme, padding, margin, radius). Placeable from the HW gallery or HW product details.
`_hw-product-media-gallery` - Sidebar rail opens for USPs and model info (not hotspot-only). New settings: sidebar position (left/right), max width, gap between sidebar and main image (optional desktop gap override), vertical alignment (top/center). Help text clarifies hotspots do not consume sidebar width. Watch video: optional overlay badge (theme Badges typography/position/padding + block color scheme + optional custom label); click uses
scrollIntoView,slideshow.selectwhen available, then poster button click so deferred video actually plays; video detection treatsslideshow-slide.product-media-container--videoas the media root. `__gallery-media` uses `overflow: hidden` to stop hotspot pins bleeding into the sidebar; hotspots JS prefers `aria-hidden="false"` slide for overlay alignment.Docs -
.agent/docs/blocks/hw-product-media-gallery/HOTSPOTS_TEMPLATE_PLAN.md. Theme editor settings guidance updated in.cursor/skills/theme-editor-settings-planning/SKILL.md(loop parent + child pattern).Product card block system (`hw-product-card`): Composable, block-based product card (
blocks/hw-product-card.liquid) that accepts child blocks for full merchant control of card layout. Renders viasnippets/hw-product-card.liquid. Each preset ships with a curated default block stack; merchants can add, remove, and reorder blocks freely in the theme editor.Child blocks supported -
hw-text,text,image,hw-price,hw-product-title,hw-swatches,hw-size-picker,hw-add-to-cart,hw-view-product,hw-card-popup,hw-icon-text,hw-card-divider,hw-inventory-alert,hw-price-calculation,hw-badge,custom-liquid,@app, and grouped via_product-card-group.Gallery block (`hw-product-card-gallery`) - image carousel/slider with per-block child blocks (badges, overlaid content), secondary image hover, aspect ratio, border radius, and click-through to PDP via
snippets/hw-page-url-variant.liquid.Quick add -
snippets/hw-quick-add-wrapper.liquidwraps the base quick-add with HW visibility (always/hover/never), style (icon-label/icon-only/label-only), and size overrides per gallery block setting.snippets/hw-quick-add-size-picker.liquidadds size-picker variant pre-selection before adding to cart.snippets/hw-quick-add-popup.liquidrenders an inline<details>/<summary>info popup for gallery overlays. Styles inassets/hw-quick-add.css.Files -
blocks/hw-product-card.liquid,blocks/hw-product-card-gallery.liquid,snippets/hw-product-card.liquid,snippets/hw-quick-add-wrapper.liquid,snippets/hw-quick-add-size-picker.liquid,snippets/hw-quick-add-popup.liquid,snippets/hw-page-url-variant.liquid,assets/hw-product-card.css,assets/hw-quick-add.css.Product list section (`hw-product-list`):
sections/hw-product-list.liquid- a dedicated section for rendering a list of products using thehw-product-cardblock system. Supports section-level layout settings (width, color scheme, spacing, gap) with header blocks via_product-list-content.Size picker block (`hw-size-picker`):
blocks/hw-size-picker.liquid- renders clickable size tiles for the variant option whose name matches the theme settinghw_size_option_names. Coordinates withhw-swatchesfor multi-option variant selection. Tile click selects the variant (updates price/image) without adding to cart or changing the URL. Configurable padding per block. Renders viasnippets/hw-size-picker.liquid; JS inassets/hw-size-picker.js; styles inassets/hw-size-picker-block.css.View product block (`hw-view-product`):
blocks/hw-view-product.liquid- renders a "View product" link or button on the product card. Configurable label, button style, icon (theme/custom image/custom SVG), icon position, button height (mobile + optional desktop override), and full-width toggle. Renders viasnippets/hw-view-product.liquid; styles inassets/hw-view-product.css.Card divider block (`hw-card-divider`):
blocks/hw-card-divider.liquid- horizontal rule between card blocks. Style (solid/dashed/dotted), color scheme, top/bottom spacing, full-width toggle, and line thickness. Renders viasnippets/hw-card-divider.liquid; styles inassets/hw-card-divider.css.Icon-text block (`hw-icon-text`):
blocks/hw-icon-text.liquid- icon paired with text label on product cards. Icon source (theme icon / custom image / custom SVG), icon width (mobile + optional desktop override), stroke width, text content, typography preset, icon position (left/right/above), and alignment. Renders viasnippets/hw-icon-text.liquid+snippets/hw-icon-text-icon.liquid; styles inassets/hw-icon-text.css.Inventory alert block (`hw-inventory-alert`):
blocks/hw-inventory-alert.liquid- shows a stock-scarcity alert when inventory is at or below a configurable threshold (1-50 units). Optional progress bar with a per-block color scheme override. Renders viasnippets/hw-inventory-alert.liquid; styles inassets/hw-inventory-alert.css.Price calculation block (`hw-price-calculation`):
blocks/hw-price-calculation.liquid- displays a derived price or unit-cost calculation sourced from a product metafield (namespace.key). Optional label, show/hide toggle. Renders viasnippets/hw-price-calculation.liquid; styles inassets/hw-price-calculation.css.Card popup block (`hw-card-popup`):
blocks/hw-card-popup.liquid- per-block info popup for product cards. Trigger styles: button, plain text link, text with arrow, pill with icon. Popup content: title, image (metafield-aware), richtext body, color scheme. Data stored inert in a<template>element; shared<dialog id="hw-card-popup-dialog">rendered once inlayout/theme.liquidviasnippets/hw-card-popup-modal.liquidand populated byassets/hw-card-popup.js. Trigger rendered viasnippets/hw-card-popup-trigger.liquid(includesaria-haspopup="dialog"). Styles inassets/hw-card-popup.css. Renamed from the originalhw-popupblock name to avoid collision with the returning visitorhw-popupsection.Add to cart block (`hw-add-to-cart`): Block-level enhancements - icon toggle (show/hide), custom icon option, and custom label override for available products on product cards. Styles in
assets/hw-add-to-cart.css. Renders viasnippets/hw-add-to-cart-button.liquid.Returning visitor popup (`hw-popup`): Section group (
sections/popup-group.json,type: custom.popup) that renders a compact dialog for returning visitors. Only outputs HTML whencart.item_count > 0and the current template is in the enabled pages list - fully Liquid-first. Trigger logic (time delay, scroll depth), session suppression (sessionStorage), and min-gap enforcement (localStorage) are handled client-side inassets/hw-popup.js.Cart line items - image (links to PDP), title (anchor to PDP), variant, price, optional product metafield, configurable typography presets per field. Scrollable list with configurable max height. Checkout button and View cart link.
Recently viewed (empty cart) - when the cart is empty and the setting is on, fetches recently viewed products via Section Rendering API (
predictive-searchsection) and injects them. Hides the modal entirely if no recently viewed products exist and "hide when no content" is on.Settings - page targeting (home, product, collection, blog, article, search, page), time delay trigger (on/off + seconds), scroll depth trigger (on/off + %), min-gap between shows, popup width (mobile + optional desktop override), max height (mobile + optional desktop override), position (mobile: top/center/bottom; desktop: top/center/bottom/left/right), corner radius, inner gap, padding (mobile + optional desktop override), backdrop toggle, color scheme, editor preview mode (always-show for theme editor editing).
Files -
sections/hw-popup.liquid,sections/popup-group.json,snippets/hw-popup.liquid,snippets/hw-popup-cart.liquid,snippets/hw-popup-recently-viewed.liquid,assets/hw-popup.css,assets/hw-popup.js. CSS and JS loaded vialayout/theme.liquidconditionally.
Changed
v1.0.0 flagship release: Theme version set to 1.0.0. Default templates and group JSON cleaned for a blank-store install (store-specific product handles cleared from header showcase and cart gift slots; demo templates/groups rebuilt as the Horizon Works preset).
Tab (HW) — panel action blocks:
_hw-tabno longer uses inline panel CTA settings (link, label, style, width). Add Button (HW) or Button modal (HW) as a child block; output renders in.hw-tab__ctabelow the tab body. New Tab preset includes a Read more modal. Docs: `.agent/docs/blocks/hw-tabbed-content/`.Inset (HW) — margin: `blocks/hw-inset.liquid` adds mobile/desktop Margin sliders (−50–50 px per side) and Override on desktop (
override_margin_desktop). `snippets/hw-inset-margin-style.liquid` emits **--margin-*/--margin-*-desktop;assets/hw-inset.cssapplies logical margins with a 750px desktop override (same pattern as padding viahw-spacing-style). Locale:t:content.margin_desktop,t:settings.override_margin_desktop_info. Docs:.agent/docs/blocks/hw-inset/EDITOR_GUIDE.md,DEVELOPER_GUIDE.md**.Text (HW) snippet — alignment + override hooks: `snippets/hw-text.liquid` accepts `text_override` (server-side richtext preprocessing) and `force_alignment` (used by Text (HW, align)). Mobile alignment classes and `--text-align-mobile` CSS var when `alignment_mobile` is set.
Badges — position: Theme settings → Badges → Badge position adds Bottom right (
bottom-right) alongside existing corners.HW Header 2 — search style: `blocks/_hw-header-actions-2.liquid` adds `search_style` (Standard vs Horizon Works, default Standard). `snippets/hw-header-utilities.liquid` keeps `#search-modal/showDialog` for Standard or switches the search utility to `data-hw-search-open` / `aria-controls="hw-search-panel"` when Horizon Works is selected. Non-breaking opt-in — merchants need `search_style: horizon_works` on header actions and `hw-search-panel` in the header group for the new UX.
Cart Upsells — button & heading typography:
button_presetsetting renamed tobutton_type_preset; now supports custom typography preset with inline style bridge. Heading preset extended with custom typography option. Button class composition moved to Liquid (cu_atc_btn_class) and passed as a single pre-composed string to JS viadata-cu-button-class.data-cu-button-stylenow carries an inline style string for custom typography (previously carried the button style class — semantic change). Color scheme override added per-block.Cart Upsells — checkout button controls: New settings for accelerated checkout toggle, button label override, icon (lock / custom SVG), style class, color scheme, and typography preset with custom option.
Cart Upsells — free shipping bar: Amount size and label margin bottom are now configurable. Per-element custom typography supported on the amount span.
Cart Drawer — overflow: Replaced
overflow-x: clipwithoverflow-x: visibleon upsells scroll containers to allow slider horizontal bleed. Compact layout footer top-rule implemented via::beforebleed instead of a border on.cart-drawer__summary.Card Icon Text — text preset options: Extended from
paragraph / paragraph_2 / labelto include all heading levels (h1–h6), additional paragraph scales, and a custom typography option.typography_settingsparam added. Version bumped to1.1.0.Button (HW) — presets: Removed nine theme-editor presets (
Button (Primary / 1–3), Secondary, Tertiary variants). Add block now offers only Button (HW) and Button with icon — style and preset are chosen in block settings instead.Button (HW) vs modal: Removed Opens modal action and nested modal child from `hw-button`. Modals use the dedicated `hw-button-modal` block (see Added). `hw-button` keeps URL / cart / checkout only. Hidden `modal_trigger` setting remains for the static trigger child inside `hw-button-modal`.
PDP buy area — measurement controls: Gallery spacing (grid Gap, sidebar Gap, overlay corner inset, block padding) uses range sliders with step 1 and expanded max values where needed (for example overlay inset 0–100px). Theme Page layout custom width overrides and HW Product information desktop column caps stay text inputs resolved via `hw-resolve-css-length.liquid`. See `.cursor/rules/hw-dimension-inputs.mdc` and `.cursor/references/hw-layout-settings-patterns.md`.
HW Group — desktop horizontal row wrapping (opt-in): From 750px, `flex-flow` reads `--hw-group-flex-wrap-desktop` (
snippets/hw-layout-panel-style.liquid, `assets/hw-group-layout.css`) instead of assuming `nowrap` only implicitly. Default remains non-destructive: `nowrap` (same as historic CSS). Merchant opt-in `wrap-horizontal-row-large-screens` (checkbox on `hw-group` / `hw-group-basic` / `hw-group-card` / `hw-group-link`, `content_direction_desktop == 'row'`) sets `flex-wrap: wrap` so siblings can flow to the next line. Locale `t:settings.wrap-horizontal-row-large-screens`, `t:info.wrap-horizontal-row-large-screens`.HW Marquee: Removed `sections/hw-marquee.liquid`; marquee is block-only (
blocks/hw-marquee.liquid). Homepage `templates/index.json` now uses `hw-section-starter` wrapping an `hw-marquee` block (same nested content and dividers on the starter where applicable). `assets/hw-marquee.css`, `assets/hw-marquee.js`, `blocks/hw-marquee-overlay.liquid`, and schema locale keys for the block/overlay stay in use.HW Carousel progress bar + nav (row layout): In `assets/hw-carousel-progress-bar.css`, the progress bar Shopify block wrapper uses `flex: 1 1 0%` and `min-width: 0` (not `width: 100%` + `flex: 1 1 auto`), and the carousel navigation wrapper’s block uses `flex: 0 0 auto`, so a control row stays on one line under Keep horizontal row on one line without clipping arrows off-screen.
HW Cart Upsells — slider arrow icons: `nav_icon_size` is replaced by `nav-icon-width` and `nav-icon-height` (`blocks/hw-cart-upsells.liquid`) with the same 8–64px ranges and carousel-nav locale labels (`t:settings.hw_carousel_nav_arrow_icon_width` / `_height`). `snippets/hw-cart-upsells-nav.liquid` sizes the hit target from both dimensions; `sections/cart-group.json` updated. Saved JSON that still has `nav_icon_size` is read as a fallback until re-saved.
Cart drawer header — cart title typography: `blocks/hw-cart-drawer-header.liquid` adds Cart title size (paragraph–h6, label, custom) via `hw-type-preset-classes` / `hw-type-preset-style`. Empty-cart header mirrors the same controls on `sections/hw-cart-drawer.liquid` (
empty_header_heading_*). Styles in `assets/hw-cart-drawer.css` on `cart-drawer-component` (drawer is outside.header). Item count bubble size/font remain separate Cart bubble sliders.Cart drawer footer — row typography: `snippets/hw-cart-summary.liquid` applies `row_heading_type_preset` to shipping/subtotal labels and `row_value_type_preset` to calculated-at-checkout copy and money values. `assets/hw-cart-summary.css` adds leaf-preset and custom-typography rules so all four nodes respect the two presets (not legacy cart font-size fallbacks).
Loop sections JS deduplication: Removed duplicated inline
{% javascript %}carousel wiring from `sections/hw-loop.liquid`, `sections/hw-loop-review.liquid`, and `sections/hw-loop-ugc.liquid`. All now rely on the shared theme-level script (hw-loop-carousel-wire.js).UGC data-source editor clarity: Updated `hw-loop-ugc` source field copy to clarify that Source list field expects the field key on the selected group entry (e.g.
entries) and is not the metaobject type name (ugc_video); default updated toentries.HW Loop — FAQ (
sections/hw-loop-faq.liquid): Metaobject mode targets a `faq_group` entry and always reads list handle `faqs` (HW convention). Removed `source_list_field` and the old `faq` picker in favour of a FAQ Group entry picker; empty-state messaging updated (`locales/en.default.schema.json` copy).`snippets/hw-accordion.liquid`: `accordion_fragment` — `full` (default), `open`, or `close` for split shell markup; `accordion_block_settings` only (never a render param named `settings`, which would shadow global theme `settings`).
`snippets/spacing-style.liquid`: Optional `fallback_settings` plus kebab/underscore padding keys so merged accordion settings emit valid **
--padding-*** variables.`snippets/border-override.liquid`: Final defaults when width, style, opacity, or radius stay blank after merge (avoids broken `--border-width: px` / empty style output).
`hw-marquee` (later block-only) + `blocks/hw-marquee-overlay.liquid` + `assets/hw-marquee.css`: Added section divider support and split-overlay divider controls for HW Marquee (section file later removed in favour of `blocks/hw-marquee.liquid` inside `hw-section-starter`).
Section-level:
divider_top,divider_bottom, anddivider_color_scheme(matching HW Carousel / Grid pattern). When enabled, top/bottom lines render from the selected scheme's Borders token (--color-border) while marquee content keeps the main section scheme.Overlay-level (on
hw-marquee-overlay): optional vertical divider between static overlay and scrolling track with independentoverlay_divider_color_schemeandoverlay_divider_margin_inline_endcontrols. Split layout renders the.marquee__static-overlaywrapper from the block so editor settings apply reliably; vertical divider uses the selected scheme's Borders token.`blocks/_hw-carousel-grid-content.liquid`: Allowed `@theme` in the nested
blocksarray so merchants can add theme blocks (e.g. Tabbed content (HW)) inside HW Carousel / Grid content. `hw-card` remains in the allowedblocksarray alongside `_card` so HW Card can nest without invalidating section presets.`sections/hw-carousel-grid.liquid`: Registered
hw-cardat section level. Default preset's four demo cards switched from_card(aspect_ratio: adapt) tohw-card(aspect_ratio: auto).`assets/base.css` (base file, flagged for upstream): Narrowed the
deferred-media[class] :is(.deferred-media__poster-button img, .deferred-media__poster-button ~ video)rule to target.deferred-media__poster-button .deferred-media__poster-imageinstead of any<img>inside the poster button. Previously this rule forcedobject-fit: cover; height: 100%; aspect-ratio: var(--size-style-aspect-ratio, auto)onto custom play-icon<img>tags rendered inside.deferred-media__poster-buttonbyhw-video, stretching them. Poster images still get the cover treatment; custom icons now size freely via--hw-play-icon-size.`snippets/media.liquid` (base file, flagged for upstream): Same scoped tweak as
base.css-.media-block__mediadeferred-media rule targets.deferred-media__poster-imageonly, not any<img>descendant of the poster button.`locales/en.default.schema.json`: Added
settings.video_mute("Mute") andinfo.video_mute(explains mute applies after play in click-to-play cards; browsers may still restrict audio).Group (HW) (
blocks/hw-group.liquid): Layout (small screens) vs Layout (desktop); desktop Direction is only Vertical or Horizontal (removed "Same as mobile"). Removed Vertical on mobile; legacyvertical_on_mobile/content_direction_desktop: match_mobilestill interpreted inhw-group-effective-directions.liquiduntil JSON is cleaned. Presets updated (e.g. HW Reviews Card, Materials slider).Text (HW) (
blocks/hw-text.liquid,snippets/hw-text.liquid): Width controls use fill / fit / custom px andsnippets/hw-size-style.liquid; removedmax_widthsetting; layout CSS moved tohw-text.csswhere noted above.Button (HW) (
blocks/hw-button.liquid,snippets/hw-button.liquid): Custom width usescustom_width_px/custom_width_mobile_px; Full width applies only when both widths are Fill and is hidden in the editor otherwise;hw-button--full-widthno longer overrides Fit content. Locale copy inlocales/en.default.schema.json.`snippets/size-style.liquid`: Emit
--size-style-heightonly whensettings.heightis not blank (avoids invalid empty custom properties).`assets/base.css` (`.size-style`): Fallbacks
fit-content/autowhen width or height variables are unset so blocks without height still layout correctly.`templates/index.json`:
hw-groupblocks - removedvertical_on_mobile, replacedmatch_mobiledesktop direction with explicitcolumn/rowwhere migrated.Mega menu (HW) content: Removed page overlay settings and related CSS vars (overlay was not working reliably). Added a soft bottom
box-shadowon desktop mega.menu-list__submenu[data-mega-target] > .menu-list__submenu-innerso the panel separates slightly from same-colored page content. (blocks/hw-mega-menu-content.liquid,assets/hw-mega-menu.css,locales/en.default.schema.json)`sections/hw-loop-review.liquid`: Refactored source mode from
metaobject_entry_listtometaobject_list.Old: merchants picked a single
reviewsmetaobject entry, then provided a list field handle (e.g.reviews) that contained references to individualreviewentries.New: merchants use a
metaobject_listsetting scoped tometaobject_type: "review"and pick the review entries directly in the theme editor. This removes the intermediate "container" metaobject and the need for editors to know field handles.Presets updated: Reviews Default Loop and Trustpilot reviews now default to
metaobject_listmode with simplified settings (redundant defaults removed). Trustpilot reviews preset rebuilt with a full nested block tree: header heading, review card with rating image + body + author portrait + product title, and footer with carousel progress bar + navigation.New locale keys in
locales/en.default.schema.json:hw_loop_review_block_*names for preset blocks (author, author details, review text, footer row, review card, product, rating image, top content), plus preset names (hw_loop_review_preset_default,hw_loop_review_preset_compact,hw_loop_review_preset_trustpilot).`assets/hw-carousel-progress-bar.css` + `blocks/_hw-carousel-progress-bar.liquid`: Progress bar wrapper now uses
flexdisplay (wasblock) with cross-axis centering (align-self: center,flex-direction: column,justify-content: center). Fixes misalignment when the progress bar sits in a row with carousel navigation arrows.Loop sections - removed hardcoded progress bar overrides:
sections/hw-loop.liquid,sections/hw-loop-faq.liquid,sections/hw-loop-ingredient.liquid,sections/hw-loop-material.liquid, andsections/hw-loop-review.liquidall dropped their local#shopify-section-{{ section.id }} .hw-carousel-progress-bar-wrapperoverrides (which previously forceddisplay: block; min-height: 12px). Progress bar sizing and visibility are now fully governed by the sharedassets/hw-carousel-progress-bar.cssasset and the block-level CSS custom properties.Docs: Updated
.agent/docs/blocks/loop/DEVELOPER_GUIDE.md,EDITOR_GUIDE.md, andFINDINGS.mdto document themetaobject_listsource mode and current preset structures.Template JSON: Removed Liquid dynamic sources from
templates/*.json(metafields,closest.*, etc.). Replaced with static placeholders soshopify theme checkand theme push validate; merchants reconnect dynamic sources in the editor. Addedscripts/check-template-json-no-liquid.sh,npm run check:templates, and GitHub Action Template JSON (no Liquid) onmain/ PRs.Loop (HW): Consolidated generic section to a single preset Loop (HW); removed type-specific presets (Ingredients, Materials, Reviews, FAQs) from
hw-loop.liquid. Type-specific sections (hw-loop-ingredient,hw-loop-material,hw-loop-faq,hw-loop-review) now have schema parity with the generic section and renamed presets: Ingredients Default Loop, FAQs Default Loop, Reviews Default Loop; Materials slider (matched toproduct.json); Trustpilot reviews (matched toindex.json). Presets that mirror templates use a full copy of settings and blocks. Updated.agent/docs/blocks/loop/FINDINGS.mdwith current section list, preset names, and merchant quick reference.Variant picker: All variant-picker OOS and HW-specific styling moved from
base.cssintoassets/hw-variant-picker.css(scoped to.hw-variant-picker). Basevariant-main-picker.liquidno longer references removed "Show Unavailable" setting.Theme settings: Removed "Show Unavailable" label checkbox; removed from schema and
settings_data.jsonper schema/merge safety.HW product siblings: Removed overflow-list / "more" behaviour; list now renders all siblings up to block
max_itemsin a single list.Button (HW): Consolidated icon rendering into a single reusable block; added scoped styles for icon sizing, desktop width override, and SVG effects (opacity, grayscale). SVG stroke/fill styles inherit
currentColorvia color scheme.Base CSS: SVG stroke-width and vector-effect rules for icon defaults.
Badges: Renamed "metafield badge" to "custom badge" in settings and UI.
Badges: Typography preset and case now apply to Sale, Sold out, and custom badges (not just custom).
Badges: Extracted badge and card-gallery styles to static CSS assets (per project rules) instead of Liquid snippets.
Pricing (HW): Added HW duplicate pricing chain (
hw-format-price,hw-price,_hw-featured-product-price) with scoped currency-code controls and trailing-zero formatting, while keeping base Horizon blocks/snippets aligned with upstream.Pricing (HW): Added configurable savings label behavior (enable, prefix, value type, radius) and moved savings badge colors into global color scheme settings for HW price rendering.
Typography: Font sizes changed from select to range sliders (0-100px with element-specific min/max).
Typography: Fluid scaling fix when desktop size < mobile size (clamp bounds corrected).
Config:
settings_data.jsonmigrated for range/select schema compatibility.Docs: Reorganised
.agent/docs/for clearer structure: theme-level docs undertheme-settings/(color-schemes, typography, buttons, cursor) with dev + editor guides together; section/block docs undercart/(cart-drawer, empty-cart) andblocks/(accordion, price, loop, popup, social-links); architecture and process underarchitecture/; dev-only underdev-tools/; design-branch-sync underworkflows/; planning docs in_archive/. README is the index; internal links and references (CONTRIBUTING, root README, CHANGELOG, PR template, workflow) updated to new paths.
Fixed
Price typography presets — global price colour on “From” prefix: `snippets/theme-styles-variables.liquid` no longer sets
coloron bare.price(which matched.hw-price__from-line). Targets.price:not(.hw-price__from-line)and.hw-price__from-amountinstead so From label typography stays independent. Colour rules always emit with| default: 'var(--color-foreground)'when theme pickers are blank. Preset font-size selector updated the same way.Cart free shipping & gifts — custom message typography: Missing inline style on the locked FSB label when `{amount}` is split; component CSS in `assets/hw-cart-free-shipping-gifts.css` for banner, pill, and amount nodes so custom font sizes apply.
Cart drawer header — title and bubble layout: `text-block` on `.cart-drawer__heading` stacked the item count under Cart; `assets/hw-cart-drawer.css` restores `inline-flex` row layout (and empty-cart header in `assets/hw-empty-cart.css`).
Cart upsells — ATC min-height: Removed extra `padding-block` on `.hw-cart-upsells__atc-btn` / options links so the button min-height override controls height.
Sticky buy CTA (HW) — visibility performance: `assets/hw-sticky-buy-cta.js` drops `window` scroll/resize listeners and per-tick `getBoundingClientRect()` on the buy target. Buy-area and footer state are cached from `IntersectionObserver` callbacks; `#syncVisibility` reads only that state (footer `rootMargin` uses `FOOTER_HIDE_VH`).
Cart prices — hide trailing zeros with currency:
snippets/hw-cart-item-price.liquidno longer usesremove: '.00'/remove: ',00'on the full formatted string (which could turn$5,000.00into$50). Strips only decimal.00/,00before the currency code.Cart drawer compact line items: Divider borders now target `.cart-items--dividers` on the table (not the wrapper). Compact row padding-bottom no longer inherits `header-actions`
gap-xlpadding. Thumbnail size respects theme editor on desktop (mobile/desktop CSS vars, not a hardcoded inline--hw-cart-item-thumb-size). `item_layout` is no longer forced to compact when section `drawer_layout` is compact.Sticky buy CTA (HW) — z-index under carousel/grid: Block moved to Product information (HW) section level (
sections/hw-product-information.liquid+ `templates/product.json`); removed from `_hw-product-details` children. Fixes carousel/grid overlap without JS `document.body` portal.Sticky buy CTA (HW) — Badge height: Badge height range (20–80px, default 28) under Badge appearance.
Sticky buy CTA (HW) — CTA labels: Separate CTA label (subscription) and CTA label (standard product); Liquid picks by
product.selling_plan_groups(same signal as subscription pricing).Sticky buy CTA (HW) — “From” price:
hw-priceonly rendered “From” on non-product templates (is_product_card). PDP sticky bar now passes `price_from_variant_prices`, `from_label_type_preset`, and optional **from_label_*custom typography;hw-price.liquidshows “From” on product pages when the effective price range differs (variants and/or selling plan allocations for subscriptions). Replaces the oldshow_price_fromcheckbox with the same control model as Price (HW)**.HW Product media gallery — media corner radius: `media_radius` now clips carousel and grid media via `assets/hw-product-media-gallery-content.css` on `--media-radius` (base `product-media` stylesheet alone did not apply radius on the PDP gallery).
HW Group — desktop row wrap + intrinsic-width children: Three related issues, fixed together.
Wrap setting ignored. Checkbox `wrap-horizontal-row-large-screens` is a hyphenated schema id — Liquid must read `settings['wrap-horizontal-row-large-screens']` (fallback `settings.wrap_horizontal_row_large_screens` for legacy JSON). `snippets/hw-layout-panel-style.liquid` + `snippets/hw-group.liquid` updated; the panel emits `--hw-group-flex-wrap-desktop: wrap` and `data-hw-desktop-flex-wrap="wrap"` / `data-hw-mobile-flex-wrap="wrap"` when the merchant enables wrap (
content_direction(_desktop)is row).Content / Fit row children crushed. `base.css` used `flex: 0` on `.layout-panel-flex--row > .group-block--width-{fit,content,fit-content}` — invalid shorthand → browsers fell back to `flex: 0 1 auto` and flex items collapsed under **
> * { min-width: 0 }.base.cssnow usesflex: none(matching real HW class names);assets/hw-group-layout.cssalso forcesflex: none+min-width: autoon> .group-block--width-content/-fit-contentrow children.width: contentemitsmax-content(notmin(100%, max-content), which leaves descendants'%inmin(100%, Npx)with an indefinite base) withmax-width: 100%capped on.hw-group.group-block--width-content**.Wrap + fill children never wrapped. `base.css` gives `.layout-panel-flex--row > .group-block--width-fill` `flex: 1`; with `min-width: 0` from row children, fill items absorbed all spare space and prevented siblings from wrapping. When the panel has `data-hw-{desktop,mobile}-flex-wrap='wrap'`, fill children get `flex: none` + `max-width: 100%` + `min-width: auto` so the flex line breaks naturally on overflow.
HW Button — custom widths collapse inside intrinsic-width groups (desktop): `snippets/hw-size-style.liquid` custom width emits `width: min(100%, Npx)`. Inside an `hw-group` with `width: content` / `fit-content` (parent `max-content` / `fit-content`), the `100%` is indefinite during intrinsic sizing and falls back to `auto` (≈ label width). Buttons rendered well below their configured `Npx` (e.g. 260 → ~140), the inner group's `max-content` was undercounted, the outer row never saw an overflow → wrap did not trigger → CTAs overlapped sibling text. `hw-size-style.liquid` now also emits `--size-style-width-raw: Npx` / `--size-style-width-mobile-raw` for custom widths; `assets/hw-group-layout.css` uses the raw value on desktop `row` `> .hw-button-block-root` (with `flex-shrink: 0` + `min-width: auto`), so buttons hold their set widths. `.hw-button-block-root { max-width: 100% }` still caps in definite parents. Overflow in narrow rows is handled by `wrap-horizontal-row-large-screens` on the relevant `hw-group` panels — including nested CTA clusters when their own `max-content` still exceeds the available space (homepage stockist preset enables inner wrap too).
HW Carousel progress bar (track vs thumb rounding): Rounding now applies at `.hw-carousel-progress-bar` ( `border-radius` + `overflow: hidden` + `isolation: isolate` ), with `inherit` on `.hw-carousel-progress-bar__track` and `__thumb`, plus an inline `border-radius` from Liquid capped to half bar height — track and indicator stay visually aligned.
`sections/hw-featured-blog.liquid` + `assets/hw-featured-blog.css`: Decorative background — clip horizontal overflow while keeping vertical bleed; `pointer-events: none` on the image wrapper; elevate all direct content children above the bg layer so stacking does not depend on divider wrappers; placement classes default to `center`; `image_tag` adds empty `alt` and `sizes`; opacity control uses `t:settings.opacity`.
`snippets/hw-accordion-row.liquid`: Drawer close `aria-label` uses `accessibility.close_dialog` (existing locale key) instead of `accessibility.close` (missing string).
Text (HW) - "Fit" width in a group row:
hw-text--fill-widthwas applied when either desktop or mobile width was fill, butassets/hw-text.cssused it for desktopflex: 1in row layouts. Blocks with desktop fit + mobile fill incorrectly grew on large viewports. Split intohw-text--fill-width(desktop fill) andhw-text--fill-width-mobile(small-screen fill, including inherited fill). (snippets/hw-text.liquid,assets/hw-text.css)Theme editor / `theme watch` preset validation: Removed obsolete keys from presets -
max_widthon nestedhw-text,vertical_on_mobileonhw-group,custom_width/custom_width_mobileonhw-button(Collections loop HW), and aligned Materials slider / Reviews / Hero / Product card (HW) preset JSON in Liquid templates with current schemas.Menu (HW) - mega submenu gating: Top-level items only get a dropdown when there is fallback nav children or a populated HW mega panel (matching
hw-mega-menu-panelwith child blocks). (blocks/_hw-header-menu.liquid)Mega menu link columns: Removed per-column menu picker and CTA; columns use the header menu linklist. (
blocks/hw-mega-menu-link-column.liquid,snippets/hw-mega-menu-link-column.liquid,sections/header-group.json)Mega menu (HW) content - typography: Heading/link presets use theme
--font-size--*tokens (paragraph / paragraph 2-6); custom preset uses size token selects. Variables scoped to.hw-mega-menu-color-wrap. (blocks/hw-mega-menu-content.liquid)`assets/hw-mega-menu.css`: Replaced invalid
--font-size--small/--font-size--xsmallwith--font-size--sm/--font-size--xs.`blocks/_hw-header-menu.liquid`:
elsiftypo fix (featured collections vs featured products aspect ratio).Liquid: Removed invalid
menu_linklist:argument on{% content_for 'blocks' %}(Theme Check / Shopify parser).Mobile drawer - Mobile menu row (HW): With Parent link handle set on
hw-mobile-menu-drawer-content, 3+ level menus no longer usehw-header-drawer-accordion-modeonly (that path did not renderrich_branch_rows_markup). Rich rows + rest links use the main navigation branches. (snippets/hw-header-drawer-navigation.liquid)`sections/header-group.json`:
block_orderfor static blocks undermobile-menu-drawer-content.Button minimum-height overrides — scoped pills/tabs/nav (`snippets/hw-scoped-button-min-height-style.liquid`, `blocks/hw-collection-tabs.liquid`, `blocks/hw-faq-split-filter.liquid`, `snippets/hw-tabbed-content.liquid`, `snippets/hw-carousel-navigation.liquid`): Assign
scope_idbefore{% render %}where the id is composed withappend, so Liquid does not truncate it (fixes styles not applying to tab/pill/arrow targets). Overrides set bothmin-heightand--button-min-height-mobile/--button-min-height-desktopso theme.buttontokens do not win. With Override minimum height on, slider 0 applies 0px at each breakpoint (both@mediablocks always emit).Button (HW) — minimum height sliders (`snippets/hw-button.liquid`): When override is enabled, mobile/desktop values apply as explicit px for all slider values including 0 (no longer falls back to theme
var(--primary-*-button-min-height-*)when a slider was 0).Product card ATC / quick-add vs global button min-heights (`assets/hw-add-to-cart.css`, `assets/hw-quick-add.css`): Targets `.button.hw-add-to-cart` (and full-width variant on the same node) instead of descendant
.hw-add-to-cart .button, which never matched; sets token +min-heightfrom **--hw-add-to-cart-height-*.hw-quick-add-wrapper .quick-add__button.button** resets theme--button-min-height-*so fixedheight: var(--button-size-md)and compact/large gallery sizes are not blown up by global button preset floors.Mobile menu drawer (HW): Rest-links split mode no longer depends on resolving the static `hw-mobile-drawer-menu-links` block in `block.blocks` when `block_order` is empty. Rest links inject inside the rich-branch panel for both accordion and drawer submenu styles (removed accordion-only gate); removed duplicate post-loop injection and invalid nested-list markup. Gap and rest-list margins live on `hw-mobile-drawer-menu-links`; drawer link typography on `hw-mobile-menu-drawer-content`. Custom accordion + drawer SVGs: avoid
assign x = xforrender/content_forparams; `hw-mobile-drawer-menu` passes icon strings and submenu settings from resolved `hw-mobile-menu-drawer-content`. Files:blocks/hw-mobile-menu-drawer-content.liquid,blocks/hw-mobile-drawer-menu-links.liquid,blocks/hw-mobile-drawer-menu.liquid,snippets/hw-header-drawer-navigation.liquid,sections/header-group.json,locales/en.default.schema.json,assets/hw-header.css,.cursor/rules/blocks.mdc.hw-popup:
visible_ifadded totime_delay_seconds(gated ontrigger_time_enabled) andscroll_percent(gated ontrigger_scroll_enabled) so sub-settings hide when their parent trigger is off.hw-popup: Removed dead
data-hw-popup-dialogattribute from dialog element (never queried by JS).hw-popup: Cleared test metafield value (
custom.test_product) frompopup-group.json.hw-popup: Added missing storefront locale keys -
content.hw_popup_cart_empty,content.hw_popup_recently_viewed_loading,content.hw_popup_recently_viewed_editor_preview.hw-popup: Cart line item title class output no longer duplicates heading preset class (e.g.
h3 h3).Empty cart state (HW): Configurable empty cart state for both the cart page (
hw-main-cart) and cart drawer (hw-cart-drawer). Section settings: custom heading, body copy, CTA button (label + URL), and optional featured product recommendations grid (collection picker + count). Snippet-first:snippets/hw-empty-cart.liquidrenders the full UI in both contexts; static styles inassets/hw-empty-cart.css(loaded viahw-stylesheets). Cart page uses CSS show/hide on.cart-page--emptyancestor; drawer replaces hardcoded heading + continue-shopping button.aria-labelledbyaccessibility preserved in drawer. (Design spec TBC - clean token-based defaults applied.)Estimated delivery (HW):
hw-estimated-deliveryblock displays a calculated delivery date or date range. Configurable min/max days, optional weekday-only calculation (JS), and optional dispatch cutoff message (e.g. "Order before 2pm for same-day dispatch"). Renders viasnippets/hw-estimated-delivery.liquid; base date calculation in Liquid (server-side UTC); weekend exclusion handled client-side inassets/hw-estimated-delivery.jsusing local timezone. CSS inassets/hw-estimated-delivery.css, both registered inhw-stylesheets/hw-scripts. Placeable in cart drawer, cart page, or product page sections.Theme settings (Dev Indicator): Adds
show_dev_indicatortoggle (default: on). When enabled, renders a fixed bar at the bottom of the page showing the theme name - only whentheme.role != 'main'(i.e. not the published live theme). Implemented viasnippets/hw-dev-indicator.liquid+assets/hw-dev-indicator.css, rendered inlayout/theme.liquid.Theme settings (Scripts): Three custom injection points for merchant-defined HTML -
custom_head_content(before</head>, for analytics tags, GA4, Meta Pixel, Hotjar),custom_body_start_content(after<body>, for GTM noscript), andcustom_body_end_content(before</body>, for deferred third-party tools). Rendered via newhw-custom-scriptssnippet registered at all three positions inlayout/theme.liquid.Accordion (HW):
hw-accordioncontainer block extending Horizon's base accordion. Adds adisplay_styletoggle (accordion | drawer)._hw-accordion-rowblock extends_accordion-rowwith: toggle icon position (left/right), icon size with optional desktop override, per-row vertical padding with optional desktop override, and drawer mode - clicking a row in drawer mode opens a right-side<dialog>panel using the theme'sdrawer_color_scheme. Drawer content is moved from the inlinedetails-contentinto the panel on first open.<dialog>handles backdrop click and native Escape key; slide-in animation respectsprefers-reduced-motion.HW variant picker: Snippet-first
hw-variant-main-pickerandhw-strikethrough-variantused only byhw-variant-pickerblock. OOS styling (fade background only, full-opacity labels, visible strikethrough), pill 100% on selected for resize, corner-to-corner strikethrough in grid; screen reader gets "(Unavailable)" in aria-label. Static CSS inassets/hw-variant-picker.css, loaded viahw-stylesheets.Rules: Schema and merge safety (
.cursor/rules/horizon-works-layer.mdc) for removing/adding settings and avoiding merge issues withsettings_data.json.Theme settings (Swatches): Shape (square, circle, pill), size preset (small, medium, large), show option label, and spacing between swatches. CSS variables
--hw-swatch-shape,--hw-swatch-size,--hw-swatch-spacingfor use by HW swatches and product grid.HW Swatches:
hw-swatchesblock available on product cards (alongside baseswatches). Uses theme swatch settings with optional per-block overrides (shape, size, show label). Renders viahw-swatchsnippet with BEM styling inhw-swatches.css. Base blocks unchanged per Clone & Own rule.Icon (HW):
hw-iconblock with three source types-theme icon, custom image upload, and custom SVG code input. Includes configurable width (mobile + optional desktop override), optional link with new-tab support, and SVG effects (opacity, grayscale). Rendering logic inhw-icon-or-imagesnippet with SVG sanitization.Button (HW): Custom SVG code icon type, configurable icon width (mobile + optional desktop override), and SVG effects (opacity, grayscale).
Theme settings (Icons): Per-color-scheme
icon_svg_fillcolor setting, exposed as--color-icon-svg-fillCSS variable.Product card badges (HW): Metafield-powered custom badges as sub-block of Product image (HW). Add Badge (HW) block and connect to a product metafield via dynamic source for per-product badges.
Badge (HW):
hw-badgeblock with color scheme picker, typography preset override, and inherit-from-theme option.Theme settings (Badges): Layout (stack/row), gap between badges, max badges per card, hide Sale when custom badge present.
Theme settings (Badges): Typography preset, text case override (badge case overrides type preset case).
Theme settings (Badges): Badge order-Sale and Sold out first, or Custom badges first. With max 1 and custom first, products with a custom badge show only that; others show Sale/Sold out.
Theme settings (Badges): Padding (vertical, horizontal) for all badges.
Assets:
hw-product-badges.cssandhw-card-gallery.cssloaded once per page viahw-stylesheetssnippet.Add to cart (HW): Icon toggle (show/hide), custom icon option (image picker), and custom label override for available products.
Blocks:
hw-add-to-cart,hw-buy-buttons,_hw-product-detailswith full HW duplicate chain for product page.Sections:
hw-product-informationusing HW add-to-cart blocks (product template opt-in).Button (HW):
hw-buttonblock with icon support-icon type (default/custom), icon picker, icon position (left/right), content alignment (left, right, space-between), fill width option, and divider toggle.Buy buttons: Inherit color scheme and color scheme override (
hw-buy-buttons).Typography: Label preset (smallest text, 10-14px) in Theme settings.
Typography: Line height and letter spacing as range sliders (%).
Typography: Font weight override per heading (h1-h6).
Typography: Logical font-size ranges per element (Label 10-14px, Paragraph 12-24px, H1 24-120px, etc.).
Typography: Six paragraph presets (Paragraph, Paragraph 2-6) in Theme settings-each with size (mobile/desktop), line height, letter spacing. Available in
hw-text,hw-price,hw-product-titleand RTE.Typography: Button typography-line height, letter spacing, font weight, text case (primary/secondary).
Blocks:
hw-text,hw-price,hw-product-titlewith typography presets and fluid scaling toggle.Theme settings docs:
.agent/docs/theme-settings/with README and dev/editor guides for Typography and Buttons. Typography: link typography and underline, paragraph/label/headings, pointer to button typography in Buttons tab. Buttons: typography presets 1-3, Primary/Secondary/Tertiary (border, radius), base vs HW block, file map and CSS vars. Other theme setting tabs to be documented later.
Removed
`hw-model-stats` block - removed in favour of `hw-product-info-items` (update
templates/product.jsonand any custom templates still referencing the old type).
Docs
`.agent/docs/sections/hw-carousel-grid/DEVELOPER_GUIDE.md` + `EDITOR_GUIDE.md`: Static vs dynamic blocks (
content_forids), presets, Comparison carousel. Expanded with Carousel Navigation (`arrow-button-appearance`, presets, `slideshow:select` for JS sync), Carousel Progress Bar (`progress-bar-corner-radius`, nowrap row layout in `hw-carousel-progress-bar.css`).`.cursor/references/hw-layout-settings-patterns.md`: Carousel nav/disabled/progress/sync notes; nowrap row `flex` rules for progress vs nav Shopify block wrappers.
`.agent/docs/blocks/hw-grid/EDITOR_GUIDE.md`: New. Column gaps, row gap overrides (including desktop-only row gap at 990px+), and HW Grid item as a child block.
`.agent/docs/blocks/hw-flex-spacer/DEVELOPER_GUIDE.md` + `EDITOR_GUIDE.md`: New. Flex spacer behaviour vs theme Spacer /
space-between, hero + Group (HW) Fill workflow,display_breakpoint,hw-group-size-styleand desktop height override, file map, CSS notes, theme-check / locale note, pre-push checklist.`.agent/docs/blocks/hw-card/DEVELOPER_GUIDE.md` + `EDITOR_GUIDE.md`: New. Class-scoping rationale, stacking-context tables (default vs
.hw-card--bg-video-click), padding-top ratio technique, mobile/desktop cascade matrix,<hw-card-bg-video>delegation, media branching (hw-background-mediavshw-video), schema map, presets, troubleshooting.`.agent/docs/blocks/hw-video/DEVELOPER_GUIDE.md` + `EDITOR_GUIDE.md`: New. Shared snippet parameters, deferred-media modes (autoplay / poster click / no-poster autoplay), 9-way play icon grid CSS, overlay layering rules per mode, hw-card background-video reuse, schema map, base file edit notes (
assets/base.css+snippets/media.liquidscoping).`.agent/docs/theme-settings/typography/DEVELOPER_GUIDE.md`: Rewritten to cover custom font system (file map, metaobject definition table, unified preset loop, fallback handling).
`.agent/docs/theme-settings/typography/EDITOR_GUIDE.md`: Rewritten with step-by-step metaobject setup, font file upload, entry creation, theme setting configuration, and a worked example.
Breakpoint-specific settings (HW): Separate desktop padding and device visibility for
hw-button,hw-text,hw-groupblocks andhw-herosection.Desktop padding:
padding-*-desktoprange settings (0-100 px) override mobile padding at 750 px+. Uses newsnippets/hw-spacing-style.liquid+assets/hw-spacing-style.css(mirrors basespacing-stylebut scopes desktop custom properties per-node so values never inherit from ancestors).Device visibility:
display_breakpointselect (Both / Desktop / Mobile) on each block; outputshw-desktop-onlyorhw-mobile-onlyclass viaassets/hw-block-display.css(hide withdisplay: none !importantat the relevant viewport).Hero (HW): Switched from
spacing-style/spacing-stylesnippet tohw-spacing-style/hw-spacing-stylesnippet; adds desktop padding settings (defaults: 40 px block, 0 px inline).Button (HW): Wraps output in
.hw-button-block-rootdiv carryinghw-spacing-style, visibility class, andblock.shopify_attributes; preserves fit-content sizing and expands to full-width when child button is full-width.Locale: Added
display_visibility,display_breakpoint,display_breakpoint_desktop/_mobile/_both,padding_desktoptolocales/en.default.schema.json.Hero (HW):
sections/hw-hero.liquidwith composable blocks;assets/hw-hero.cssfor frame, background/overlay/content stacking, and pointer-events so overlays stay clickable where intended. Section-building notes in.agent/docs/architecture/SECTION_BUILDING.md.Group (HW) layout stack:
snippets/hw-group.liquid,snippets/hw-group-effective-directions.liquid,snippets/hw-layout-panel-style.liquid, andassets/hw-group-layout.css(registered viasnippets/hw-stylesheets.liquid) for small-screen vs 750px+ flex direction and alignment variables.Text (HW) width cascade:
assets/hw-text.css-.text-block.hw-text-blockrules so HW width /max-inline-sizevariables win oversnippets/text.liquidand row flex rules inassets/base.css;hw-text-blockclass onsnippets/hw-text.liquid.Shared HW width variables:
snippets/hw-size-style.liquid---size-style-width/ mobile vars for fit, fill, and custom pixel caps (min(100%, Npx)), used with HW text and button sizing.`.cursor/skills/hw-text-typography-settings/SKILL.md`: References
assets/hw-text.cssand the.hw-text-blockcontract.`.cursor/skills/mobile-row-wrap-debugging/SKILL.md`: Documents
hw-groupsmall-screen vs desktop direction (novertical_on_mobileon HW group) vs basegroup/mobile-column.`.agent/docs/header-navigation/` - merchant editor docs split by topic: mega-menu/EDITOR_GUIDE.md, mobile-nav/EDITOR_GUIDE.md; index EDITOR_GUIDE.md. Developer index DEVELOPER_GUIDE.md, mega mega-menu/DEVELOPER_GUIDE.md.
`.agent/docs/header-navigation/MOBILE_MENU_DRAWER.md` - 3+ levels + accordion vs Mobile menu row (HW).
v1.0.0-beta.1#
4 added1 changed
Added
Core: Initial "Flagship" designation.
Docs: Added **Developer Guide** and **Maintainer Guide**.
Docs: Added Strategic Architecture, Action Plan, and Lifecycle Walkthrough.
Repo: Established
horizon-workspackage name and versioning structure.
Changed
Config: Updated
package.jsonto reflect the new framework name and version.