Horizon Works
Changelog
Straight from the thatworkagency/horizonWorks repo — every merge to main shows up here.
v2.1.0
Added
Expanding cards (HW) footer: Static
_hw-expanding-cards-footerslot 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 section width.
Changed
Expanding cards (HW) → colour palette: Section and card shells drop
color_scheme/divider_color_schemefor optionalbackground_color/text_color(and divider pair) applied viasnippets/color-style.liquid, aligned with Shopify’s color palettes migration. Card content keeps transparent paint so media stays visible; white text remains the photo default until a text colour is set.Expanding card content → HW group layout:
_hw-expanding-cardcontent panel now exposes the same mobile/desktop direction, alignment, gap, and padding controls as HW group (viahw-layout-panel-style+hw-spacing-style). Media / stretch-link / progress stay card-owned.Expanding cards pause control: Position (
top-right/top-left/bottom-right/bottom-left) and show-on breakpoint (both/ desktop-only / mobile-only) alongside existing size toggle.
Fixed
Expanding cards (HW) empty-loop placeholder on storefront: Missing group / empty
itemsalways rendered one_hw-loop-item__expanding-cardand forcedcard_countto1. Scaffolding is now gated onrequest.design_mode; live storefront skips the track when there are no entries.Expanding cards (HW) empty header/footer gap: Flex
gapon__innerstill reserved space when header/footer were empty because Shopify wraps blocks (so:emptynever matched). Removed flex gap; hide empty slots with:not(:has(.…-inner *)); spacing is margin only when nested content exists.Expanding cards (HW) rapid-click white flash: Percentage
widthtransitions could leave a frame where inactive widths no longer filled the track (white gap), and progress restart used syncoffsetWidthreflows mid-transition. Desktop sizing now usesflex-growwithflex-basis: 0(always fills the track), progress restarts via doublerequestAnimationFrame, and mouse mousedown on cards prevents focus-driven scroll jumps.Expanding cards (HW) header/footer gutters on full-width: Default
section_widthis full-width so the card track can bleed edge-to-edge, which left header copy flush to the viewport. Header and footer now use--util-page-margin-offsetunder.section--full-widthonly (page-width sections already sit in the center column).
v2.0.0
Added
Colour palette replaces colour schemes (
snippets/color-palette.liquid,snippets/color-style.liquid): one store-wide set of colours in *Theme settings → Colors* — acolor_paletteinput plus 86 individual colour settings — emitted once as--color-*tokens on:rootfromlayout/theme.liquid,layout/password.liquid, andtemplates/gift_card.liquid. A section or block that needs its own colours renderscolor-styleinto itsstyle="", so the override cascades to that subtree and nothing else. 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/.`color-style` parameter contract:
backgroundandtextemit tokens *and* real declarations (needed because nothing inbase.csspaints.section-backgroundor a heading from the tokens alone);border,accent, and thebutton/button_text/button_bordertrio are token-only;button_familyselects which button family to write;paint: falseemits tokens without painting, for elements whose own rule reads the token they are meant to feed. Hover is deliberately not settable —color-palettederives every--color-*-button-hover-*globally.Twelve palette-backed button presets (
snippets/util-button-preset-classes.liquid,util-button-preset-family.liquid,util-button-preset-tokens.liquid): six Fill and six Outline presets, each with its own base and hover trio (background / text / border), plus per-preset border width, corner radius, and mobile/desktop min-height — 36 new geometry settings under *Buttons*, extending the three families that already had them to all twelve. Every button-bearing block now selects a preset instead of carrying its own colour fields.Carousel and linked-card arrows own their colours: base, hover, and disabled pickers (background / icon / border) plus an explicit border width, replacing the button-preset select they used to borrow from. Colours hold on hover and no longer depend on which preset is active.
snippets/util-palette-hover-shift.liquidandsnippets/brightness-opacities.liquid: the derived hover colours and the--opacity-*scale behind the muted and disabled tokens.
Changed
Breaking for existing stores: saved
color_schemevalues no longer resolve to anything, because the schemes they named are gone fromsettings_data.json. Any section or block a merchant had set to a non-default scheme falls back to inheriting the page palette until its colours are set again — a section that read as dark will render light, not error. All 15 JSON templates shipped with the theme were converted, carrying the equivalent hex values across, but client stores holding their own saved data need a pass through the editor after syncing.Every section, block, and shared snippet moved off `color-{scheme}` classes onto inline palette colours — 75 sections, 143 blocks, and 74 shared snippets. Sections that took a scheme now expose a
background_color/text_colorpair (plus adivider_background_color/divider_text_colorpair where dividers can carry their own colour); blocks that inherited a scheme now inherit by leaving their pickers blank.placeholder: "t:options.default"on each picker is what tells the merchant that blank means inherit, so none of them carry adefault.Scheme-era colour *selects* (text / heading / link, and the prefixed token pickers) became real palette colour settings across 27 blocks, and now actually apply rather than naming a token that was never read.
Drawer and popover colours are the only two overrides still applied as classes (
.hw-drawer-colors,.hw-popover-colors, emitted at the bottom ofcolor-palette.liquid) rather than inline, because they are theme-wide and would otherwise be duplicated across the 20 files that use them. They keep single-class specificity, so anything that out-specified the scheme classes they replaced still does.The
tertiarybutton family is now presented as Outline 2;button-tertiaryremains its class so existing markup and saved data are unaffected.Marquee, Compare, and Hero slideshow — the three families that arrived from
mainmid-migration — were brought onto the palette alongside the rest. The hero's arrow and pagination colours are emitted as--hw-hero-arrow-icon/--hw-hero-arrow-bg/--hw-hero-pagination-iconon the section root and read withvar(…, token)fallbacks, becauseslideshow-arrowsandslideshow-controlsaccept a class but never a style.Colour-scheme vocabulary retired from merchant-facing copy, setting labels, code comments, and docs (the changelog's historical entries excepted).
Fixed
color-stylere-derives the muted foreground token from the text colour it is given, so an inverted section no longer renders dark-on-dark muted copy inherited from the page palette.The media hotspots overlay no longer paints the merchant popup background across the whole gallery image: the full-bleed overlay renders
color-stylewithpaint: falseand the colours land on the popup itself.Carousel and linked-card arrow colours hold through hover instead of being taken over by the button preset that used to supply them.
The orphaned
colorkey left on savedtextand_headingblocks was renamed totext_colorin the Hero preset and eight JSON templates, clearing the invalid-preset error it raised.Drawer flag-shadow regression uncovered while retiring the scheme vocabulary,
hw-product-title's scheme-era colour select, and the stale header row colour property names.
Removed
The legacy colour-scheme system:
color_schemesdeleted fromconfig/settings_schema.jsonandconfig/settings_data.json,snippets/color-schemes.liquidand its class emitters removed, and the gift card page repointed at the palette..agent/docs/architecture/COLOR_SCHEMES.mdand.agent/docs/theme-settings/color-schemes/replaced by their palette equivalents.Colour-only wrapper elements, inert
color-*class guards, the deadhw-textcolour wrapper, the unread strikethrough brightness computation, andsnippets/hw-quick-add-popup.liquid.Locale keys and preset/template data orphaned by the migration, including scheme keys left on saved sections and blocks. Two over-limit schema names were brought under the 25-character cap at the same time.
Docs
New:
.agent/docs/architecture/COLOR_PALETTE.md(the model,color-style's full parameter table, the token reference, and a troubleshooting section covering the "transparent instead of inherited" trap that an emptyvar()causes) and.agent/docs/theme-settings/color-palette/(overview, editor, developer).Rewritten for the palette: the colour docs, the architecture cheat sheet and section-building template (which had been emitting dead classes when copy-pasted), the cart and product family guides, and the merchant-facing editor guides. The dead nav-arrow guide was retired and the
button_familytrap recorded.
v1.3.0
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
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` section: New 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
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.
Show 2 earlier releases
v1.0.0
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-card-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.Card icon-text block (`hw-card-icon-text`):
blocks/hw-card-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-card-icon-text.liquid+snippets/hw-card-icon-text-icon.liquid; styles inassets/hw-card-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
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.