v2.1.0

Added

  • Expanding cards (HW) footer: Static _hw-expanding-cards-footer slot below the card track (same nested text/group/button model as the header) so merchants can add CTAs or copy under the carousel without changing section width.

Changed

  • Expanding cards (HW) → colour palette: Section and card shells drop color_scheme / divider_color_scheme for optional background_color / text_color (and divider pair) applied via snippets/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-card content panel now exposes the same mobile/desktop direction, alignment, gap, and padding controls as HW group (via hw-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 items always rendered one _hw-loop-item__expanding-card and forced card_count to 1. Scaffolding is now gated on request.design_mode; live storefront skips the track when there are no entries.

  • Expanding cards (HW) empty header/footer gap: Flex gap on __inner still reserved space when header/footer were empty because Shopify wraps blocks (so :empty never 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 width transitions could leave a frame where inactive widths no longer filled the track (white gap), and progress restart used sync offsetWidth reflows mid-transition. Desktop sizing now uses flex-grow with flex-basis: 0 (always fills the track), progress restarts via double requestAnimationFrame, and mouse mousedown on cards prevents focus-driven scroll jumps.

  • Expanding cards (HW) header/footer gutters on full-width: Default section_width is 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-offset under .section--full-width only (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* — a color_palette input plus 86 individual colour settings — emitted once as --color-* tokens on :root from layout/theme.liquid, layout/password.liquid, and templates/gift_card.liquid. A section or block that needs its own colours renders color-style into its style="", 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: background and text emit tokens *and* real declarations (needed because nothing in base.css paints .section-background or a heading from the tokens alone); border, accent, and the button / button_text / button_border trio are token-only; button_family selects which button family to write; paint: false emits tokens without painting, for elements whose own rule reads the token they are meant to feed. Hover is deliberately not settable — color-palette derives 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.liquid and snippets/brightness-opacities.liquid: the derived hover colours and the --opacity-* scale behind the muted and disabled tokens.

Changed

  • Breaking for existing stores: saved color_scheme values no longer resolve to anything, because the schemes they named are gone from settings_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_color pair (plus a divider_background_color / divider_text_color pair 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 a default.

  • 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 of color-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 tertiary button family is now presented as Outline 2; button-tertiary remains its class so existing markup and saved data are unaffected.

  • Marquee, Compare, and Hero slideshow — the three families that arrived from main mid-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-icon on the section root and read with var(…, token) fallbacks, because slideshow-arrows and slideshow-controls accept 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-style re-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-style with paint: false and 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 color key left on saved text and _heading blocks was renamed to text_color in 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_schemes deleted from config/settings_schema.json and config/settings_data.json, snippets/color-schemes.liquid and its class emitters removed, and the gift card page repointed at the palette. .agent/docs/architecture/COLOR_SCHEMES.md and .agent/docs/theme-settings/color-schemes/ replaced by their palette equivalents.

  • Colour-only wrapper elements, inert color-* class guards, the dead hw-text colour wrapper, the unread strikethrough brightness computation, and snippets/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 empty var() 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_family trap 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 (vh or px, 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 via hw-hero-wrapper. Prefer over bare Horizon slideshow for new HW work. Docs: .agent/docs/sections/hw-hero-slideshow/.

v1.2.0

Added

  • hw-product-card-gallery: optional image_metafield setting 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_text option 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 with prefers-reduced-motion support; script registered once via hw-scripts.liquid. Presets: scrolling text + logo strip. Core templates (index, collection, product, page.general) migrated off nested hw-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-listhw-compare-columnhw-compare-cell, hw-compare-row-listhw-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.liquid preset removed; Marquee preset removed from hw-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's type_preset (visual size only) with its semantic HTML tag. Step 4 now states explicitly that the semantic tag and type_preset are independent decisions; Step 7 and the delivery checklist now require writing down real Figma numbers as a spec table and diffing live getComputedStyle/getBoundingClientRect values against it, on every breakpoint, before calling composition work done.

  • `.cursor/rules/design-to-template.mdc`: New alwaysApply: true Cursor rule (scoped to templates/*.json, sections/*.liquid, sections/*-group.json) that forces a read of the playbook, COMPOSITION_RULES.md, and CAPABILITY_MATRIX.md before 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 .liquid file directly; the catalog is reserved for genuine cross-component surveys only.

Fixed

  • `.size-style` height inheritance: Scoped the height rule in assets/base.css to .group-block.size-style so nested hw-button/hw-text elements 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.* }} in templates/*.json with static placeholders so CI (check-template-json-no-liquid.sh) passes and storefronts do not print literal tokens.

  • PDP/client price fallback: product-price.js honors data-show-sale-price-first when morphing from embedded variant JSON misses the live data-block-id.

  • Variant ATC labels: hw-modal-variant-picker updates every product-form-component label in the section/dialog (main + sticky). Product cards distinguish sold-out vs unavailable label copy.

  • PDP enrichment: hw-pdp-variant-picker section fetch uses async/await. hw-add-to-cart-button translates locale-key add_to_cart_text values 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-wrapper position reset in hw-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 accepting px or rem); Offset X / Y (mobile + optional desktop override, free-text accepting px/rem/negative values, applied via translate before 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 to en.default.schema.json.

  • HW Hero — editorial layout mode: layout_mode setting on HW Hero (overlay / editorial, default overlay). In editorial mode the ratio-box sizes to content (no padding-top/min-height frame), .hw-hero__content reverts 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--editorial in hw-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 to 0 when 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.liquid emits price-preset-container / price-preset--{n} and accepts price_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-collection section. Loops collection.products and renders each via the hw-product-card block using product_context, delegating layout to hw-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-tiles section and _hw-loop-item__collection-tile block. Loops a collection_list setting (supports dynamic source connection — e.g. collection.metafields.custom.filter_collections on 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 in hw-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` (top or space price to thumb bottom). Subscription UI slot: `show_subscription_slot` renders `snippets/hw-cart-line-item-subscription.liquid` in the same <tr> (grid area subscription) — 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 1 ideal, 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 in hw-scripts.liquid on product template. Turn off enable_sticky_add_to_cart on hw-product-information to avoid two bars. Preset hw_sticky_buy_cta_glowwa in templates/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 any color-{{ 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 individual ingredient item entries per slide via closest.metaobject.ingredient. Item block includes optional min_height / enable_min_height settings 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-grid render). Data source is a selected `ugc_video_group` entry plus a list-field key that resolves `ugc_video` item entries per slide via closest.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-collections support 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 to form_obj on accelerated checkout). HW FAQ Split (`sections/hw-faq-split.liquid`, v4.3.0) and hw-faq-split-content accept hw-faq-split-data alongside manual hw-accordion trees; templates/index.json / locale strings updated as wired. Supporting CSS assets/hw-faq-split.css; docs .agent/docs/blocks/hw-faq-split/DEVELOPER_GUIDE.md and .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 Control hw-group (column alignment, padded top) holding Button (HW) (“All Blog Posts”), Carousel Progress Bar, and Carousel Navigation; static-header hw-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 from color to color_background to support gradient fills; CSS uses background instead of background-color for price__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 uses typography-style when the block’s main preset is Custom. snippets/hw-price.liquid splits prefix + amount (.hw-price__from-line); prefix text uses content.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.liquid uses content.price_from with 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.liquid renders hw-price for Section Rendering API cards. Follow-up fix: avoid immediate JS overwrite of initial "From ..." during size-picker auto-init by passing userInitiated through hw:card-option-changed and skipping init-only replacement in product-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-tabs now 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-tabs border 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-desktop drive row-gap in assets/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-controls group (progress bar + navigation), static-header (eyebrow, title, description), and static-carousel-grid-content with three HW Grid slides (two Group (HW) image cells plus a full-width HW Grid item for name and quote). All hw-image picks are empty so the theme shows image placeholders; copy uses t:html_defaults.hw_comparison_carousel_* keys in html_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-spacer with flex: 1 1 0 in row/column; `display_breakpoint` (all / mobile only / desktop only) via hw-block-display classes. Schema uses plain English for block name, preset, and help paragraph to avoid ValidSchemaTranslations issues when en.default.schema.json contains duplicate keys; optional t: keys remain in locale file for later use.

    `assets/hw-flex-spacer.css` - Mobile vs desktop rules for layout-panel-flex, HW group data-hw-desktop-layout overrides at 750px, and Hero .hw-hero__content-inner > .hw-flex-spacer. Registered in snippets/hw-stylesheets.liquid.

    `snippets/hw-group-size-style.liquid` - HW-only duplicate of size-style width logic; when override_height_desktop is on, sets --size-style-height-mobile and --size-style-height for mobile vs 750px+ desktop. snippets/hw-group.liquid uses this instead of size-style and adds hw-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 for data-hw-desktop-layout='column' when override classes are present.

    Sections: hw-hero block list includes hw-flex-spacer (and group already allows the block).

    Locales: names / info / settings keys for hw_flex_spacer and hw_group_override_height_desktop in locales/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-tabs JS builds role="tablist", reparents tab triggers, toggles panels/ARIA, and handles keyboard support (Arrow / Home / End / Enter / Space).

    Image hoisting: JS hoists direct-child .hw-image into .hw-tabbed-content__media after the tab row (Shopify does not allow dynamic id/type on {% 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 via data-hw-tabs-type-class and JS.

    Shell: content_vertical_position (top / center / bottom) with .hw-tabbed-content__shell for 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_tabs added to locales/en.default.json; hw-image gains display_breakpoint select (desktop only / mobile only / both). Assets wired through snippets/hw-stylesheets.liquid and snippets/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 _card stylesheet (with aspect-ratio: var(--card-ratio, 1) default) cannot leak in. Outer wrapper keeps the .card class so the carousel's slideshow-slides > .card sizing rules still match.

    Responsive aspect ratio - auto or custom (0-200%, step 2%) expressed as height-as-a-percentage-of-width via a padding-top ::before pseudo (100% = square, 56% ≈ 16:9, 200% = 2:1 portrait). Separate desktop value with a Different ratio on desktop toggle; single @media (min-width: 990px) rule swaps --card-ratio-padding-desktop in. All four combinations render correctly (mobile-only, desktop-only, same, different).

    Per-card heights - .hw-card uses align-self: start (no height: 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-typed overlay_color setting renders via hw-overlay (same pattern as hw-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> and stopPropagation prevents the block link from firing. .hw-card--bg-video-click stack flip: link drops to --layer-flat, media wrapper rises to --layer-raised with pointer-events: none + interactive-children opt-in.

    Block padding moved to inner - spacing-style is 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-ratio is on, .hw-card__inner uses position: absolute; inset: 0 so flex content sits inside the ratio box (flex + in-flow ratio pseudo cannot co-exist).

    Content above link - .hw-card__content paints at --layer-raised (above the block link) with pointer-events: none cascade + 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-content and sections/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 video block / video.liquid adding 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_picker fallback, play_icon_size range 24-120 px, 9-position grid (play_icon_vertical × play_icon_horizontal). Corner positions inset max(12px, var(--padding-sm, 0.75rem)); centre positions use true-centre transforms. Icon size driven by --hw-play-icon-size custom property.

    Gradient overlay - toggle_overlay + color_background overlay_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 in assets/hw-video.css (registered via snippets/hw-stylesheets.liquid).

    Shared with hw-card - hw-card background video routes through snippets/hw-video.liquid with video_class: 'hw-card__bg-video ...'. CSS overrides make the deferred-media element, poster button, poster image, and <video>/<iframe> all fill the card via position: 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_list setting (custom_fonts, type custom_font) - pick one or more custom_font metaobject entries. Each entry defines font_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-face block per entry with all source_files combined into a single src: 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--family use the custom font name when set, falling back to type_*_font.fallback_families (no hardcoded sans-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.liquid preset 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 in color-{{ scheme }} via snippets/hw-text.liquid; assets/hw-text.css uses display: contents on .hw-text-block__color-scheme so 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.css for all mega menu styles

  • Fallback to standard mega-menu-list rendering for nav items without a custom panel

  • HW Quick-Add Modal (`hw-quick-add-modal`): Zero-fetch quick-add modal for the hw-product-card block. Modal content is server-rendered into a <template data-hw-quick-add-template> inside each product card; hw-quick-add.js clones the fragment into the dialog on click - no network request on open.

    Variant picker (`hw-modal-variant-picker`): Custom element extending Horizon's VariantPicker; overrides fetchUpdatedSection so 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 fake Document for product-price.js to morph the price display from, dispatches VariantUpdateEvent so product-form-component updates 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 via opacity: 0.7 on .swatch. Cart icon is hidden via CSS button:disabled .add-to-cart-icon { display: none } when the selected variant is unavailable.

    Layout: .buy-buttons-block is a direct flex child of .product-details (not nested inside .group-block-content), matching the sticky-button layout the native quick-add-modal.liquid CSS expects - flex: 0 0 auto; margin-top: auto; position: sticky; bottom: 0. Explicit padding-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 (render param 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.select when available, then poster button click so deferred video actually plays; video detection treats slideshow-slide.product-media-container--video as 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 via snippets/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.liquid wraps 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.liquid adds size-picker variant pre-selection before adding to cart. snippets/hw-quick-add-popup.liquid renders an inline <details>/<summary> info popup for gallery overlays. Styles in assets/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 the hw-product-card block 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 setting hw_size_option_names. Coordinates with hw-swatches for 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 via snippets/hw-size-picker.liquid; JS in assets/hw-size-picker.js; styles in assets/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 via snippets/hw-view-product.liquid; styles in assets/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 via snippets/hw-card-divider.liquid; styles in assets/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 via snippets/hw-card-icon-text.liquid + snippets/hw-card-icon-text-icon.liquid; styles in assets/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 via snippets/hw-inventory-alert.liquid; styles in assets/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 via snippets/hw-price-calculation.liquid; styles in assets/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 in layout/theme.liquid via snippets/hw-card-popup-modal.liquid and populated by assets/hw-card-popup.js. Trigger rendered via snippets/hw-card-popup-trigger.liquid (includes aria-haspopup="dialog"). Styles in assets/hw-card-popup.css. Renamed from the original hw-popup block name to avoid collision with the returning visitor hw-popup section.

  • 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 via snippets/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 when cart.item_count > 0 and 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 in assets/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-search section) 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 via layout/theme.liquid conditionally.

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-tab no 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__cta below 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.css applies logical margins with a 750px desktop override (same pattern as padding via hw-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_preset setting renamed to button_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 via data-cu-button-class. data-cu-button-style now 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: clip with overflow-x: visible on upsells scroll containers to allow slider horizontal bleed. Compact layout footer top-rule implemented via ::before bleed instead of a border on .cart-drawer__summary.

  • Card Icon Text — text preset options: Extended from paragraph / paragraph_2 / label to include all heading levels (h1h6), additional paragraph scales, and a custom typography option. typography_settings param added. Version bumped to 1.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 to entries.

  • 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, and divider_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 independent overlay_divider_color_scheme and overlay_divider_margin_inline_end controls. Split layout renders the .marquee__static-overlay wrapper 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 blocks array so merchants can add theme blocks (e.g. Tabbed content (HW)) inside HW Carousel / Grid content. `hw-card` remains in the allowed blocks array alongside `_card` so HW Card can nest without invalidating section presets.

  • `sections/hw-carousel-grid.liquid`: Registered hw-card at section level. Default preset's four demo cards switched from _card (aspect_ratio: adapt) to hw-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-image instead of any <img> inside the poster button. Previously this rule forced object-fit: cover; height: 100%; aspect-ratio: var(--size-style-aspect-ratio, auto) onto custom play-icon <img> tags rendered inside .deferred-media__poster-button by hw-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__media deferred-media rule targets .deferred-media__poster-image only, not any <img> descendant of the poster button.

  • `locales/en.default.schema.json`: Added settings.video_mute ("Mute") and info.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; legacy vertical_on_mobile / content_direction_desktop: match_mobile still interpreted in hw-group-effective-directions.liquid until 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 and snippets/hw-size-style.liquid; removed max_width setting; layout CSS moved to hw-text.css where noted above.

  • Button (HW) (blocks/hw-button.liquid, snippets/hw-button.liquid): Custom width uses custom_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-width no longer overrides Fit content. Locale copy in locales/en.default.schema.json.

  • `snippets/size-style.liquid`: Emit --size-style-height only when settings.height is not blank (avoids invalid empty custom properties).

  • `assets/base.css` (`.size-style`): Fallbacks fit-content / auto when width or height variables are unset so blocks without height still layout correctly.

  • `templates/index.json`: hw-group blocks - removed vertical_on_mobile, replaced match_mobile desktop direction with explicit column/row where migrated.

  • Mega menu (HW) content: Removed page overlay settings and related CSS vars (overlay was not working reliably). Added a soft bottom box-shadow on desktop mega .menu-list__submenu[data-mega-target] > .menu-list__submenu-inner so 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_list to metaobject_list.

    Old: merchants picked a single reviews metaobject entry, then provided a list field handle (e.g. reviews) that contained references to individual review entries.

    New: merchants use a metaobject_list setting scoped to metaobject_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_list mode 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 flex display (was block) 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, and sections/hw-loop-review.liquid all dropped their local #shopify-section-{{ section.id }} .hw-carousel-progress-bar-wrapper overrides (which previously forced display: block; min-height: 12px). Progress bar sizing and visibility are now fully governed by the shared assets/hw-carousel-progress-bar.css asset and the block-level CSS custom properties.

  • Docs: Updated .agent/docs/blocks/loop/DEVELOPER_GUIDE.md, EDITOR_GUIDE.md, and FINDINGS.md to document the metaobject_list source mode and current preset structures.

  • Template JSON: Removed Liquid dynamic sources from templates/*.json (metafields, closest.*, etc.). Replaced with static placeholders so shopify theme check and theme push validate; merchants reconnect dynamic sources in the editor. Added scripts/check-template-json-no-liquid.sh, npm run check:templates, and GitHub Action Template JSON (no Liquid) on main / 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 to product.json); Trustpilot reviews (matched to index.json). Presets that mirror templates use a full copy of settings and blocks. Updated .agent/docs/blocks/loop/FINDINGS.md with current section list, preset names, and merchant quick reference.

  • Variant picker: All variant-picker OOS and HW-specific styling moved from base.css into assets/hw-variant-picker.css (scoped to .hw-variant-picker). Base variant-main-picker.liquid no longer references removed "Show Unavailable" setting.

  • Theme settings: Removed "Show Unavailable" label checkbox; removed from schema and settings_data.json per schema/merge safety.

  • HW product siblings: Removed overflow-list / "more" behaviour; list now renders all siblings up to block max_items in 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 currentColor via 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.json migrated for range/select schema compatibility.

  • Docs: Reorganised .agent/docs/ for clearer structure: theme-level docs under theme-settings/ (color-schemes, typography, buttons, cursor) with dev + editor guides together; section/block docs under cart/ (cart-drawer, empty-cart) and blocks/ (accordion, price, loop, popup, social-links); architecture and process under architecture/; dev-only under dev-tools/; design-branch-sync under workflows/; 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 color on bare .price (which matched .hw-price__from-line). Targets .price:not(.hw-price__from-line) and .hw-price__from-amount instead 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.liquid no longer uses remove: '.00' / remove: ',00' on the full formatted string (which could turn $5,000.00 into $50). Strips only decimal .00 / ,00 before 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-xl padding. 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-price only 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.liquid shows “From” on product pages when the effective price range differs (variants and/or selling plan allocations for subscriptions). Replaces the old show_price_from checkbox 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.css now uses flex: none (matching real HW class names); assets/hw-group-layout.css also forces flex: none + min-width: auto on > .group-block--width-content / -fit-content row children. width: content emits max-content (not min(100%, max-content), which leaves descendants' % in min(100%, Npx) with an indefinite base) with max-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-width was applied when either desktop or mobile width was fill, but assets/hw-text.css used it for desktop flex: 1 in row layouts. Blocks with desktop fit + mobile fill incorrectly grew on large viewports. Split into hw-text--fill-width (desktop fill) and hw-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_width on nested hw-text, vertical_on_mobile on hw-group, custom_width / custom_width_mobile on hw-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-panel with 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--xsmall with --font-size--sm / --font-size--xs.

  • `blocks/_hw-header-menu.liquid`: elsif typo 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 use hw-header-drawer-accordion-mode only (that path did not render rich_branch_rows_markup). Rich rows + rest links use the main navigation branches. (snippets/hw-header-drawer-navigation.liquid)

  • `sections/header-group.json`: block_order for static blocks under mobile-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_id before {% render %} where the id is composed with append, so Liquid does not truncate it (fixes styles not applying to tab/pill/arrow targets). Overrides set both min-height and --button-min-height-mobile / --button-min-height-desktop so theme .button tokens do not win. With Override minimum height on, slider 0 applies 0px at each breakpoint (both @media blocks 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-height from **--hw-add-to-cart-height-*. hw-quick-add-wrapper .quick-add__button.button** resets theme --button-min-height-* so fixed height: 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 = x for render / content_for params; `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_if added to time_delay_seconds (gated on trigger_time_enabled) and scroll_percent (gated on trigger_scroll_enabled) so sub-settings hide when their parent trigger is off.

  • hw-popup: Removed dead data-hw-popup-dialog attribute from dialog element (never queried by JS).

  • hw-popup: Cleared test metafield value (custom.test_product) from popup-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.liquid renders the full UI in both contexts; static styles in assets/hw-empty-cart.css (loaded via hw-stylesheets). Cart page uses CSS show/hide on .cart-page--empty ancestor; drawer replaces hardcoded heading + continue-shopping button. aria-labelledby accessibility preserved in drawer. (Design spec TBC - clean token-based defaults applied.)

  • Estimated delivery (HW): hw-estimated-delivery block 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 via snippets/hw-estimated-delivery.liquid; base date calculation in Liquid (server-side UTC); weekend exclusion handled client-side in assets/hw-estimated-delivery.js using local timezone. CSS in assets/hw-estimated-delivery.css, both registered in hw-stylesheets / hw-scripts. Placeable in cart drawer, cart page, or product page sections.

  • Theme settings (Dev Indicator): Adds show_dev_indicator toggle (default: on). When enabled, renders a fixed bar at the bottom of the page showing the theme name - only when theme.role != 'main' (i.e. not the published live theme). Implemented via snippets/hw-dev-indicator.liquid + assets/hw-dev-indicator.css, rendered in layout/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), and custom_body_end_content (before </body>, for deferred third-party tools). Rendered via new hw-custom-scripts snippet registered at all three positions in layout/theme.liquid.

  • Accordion (HW): hw-accordion container block extending Horizon's base accordion. Adds a display_style toggle (accordion | drawer). _hw-accordion-row block extends _accordion-row with: 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's drawer_color_scheme. Drawer content is moved from the inline details-content into the panel on first open. <dialog> handles backdrop click and native Escape key; slide-in animation respects prefers-reduced-motion.

  • HW variant picker: Snippet-first hw-variant-main-picker and hw-strikethrough-variant used only by hw-variant-picker block. 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 in assets/hw-variant-picker.css, loaded via hw-stylesheets.

  • Rules: Schema and merge safety (.cursor/rules/horizon-works-layer.mdc) for removing/adding settings and avoiding merge issues with settings_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-spacing for use by HW swatches and product grid.

  • HW Swatches: hw-swatches block available on product cards (alongside base swatches). Uses theme swatch settings with optional per-block overrides (shape, size, show label). Renders via hw-swatch snippet with BEM styling in hw-swatches.css. Base blocks unchanged per Clone & Own rule.

  • Icon (HW): hw-icon block 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 in hw-icon-or-image snippet 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_fill color setting, exposed as --color-icon-svg-fill CSS 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-badge block 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.css and hw-card-gallery.css loaded once per page via hw-stylesheets snippet.

  • 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-details with full HW duplicate chain for product page.

  • Sections: hw-product-information using HW add-to-cart blocks (product template opt-in).

  • Button (HW): hw-button block 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-title and RTE.

  • Typography: Button typography-line height, letter spacing, font weight, text case (primary/secondary).

  • Blocks: hw-text, hw-price, hw-product-title with 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.json and 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_for ids), 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-style and 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-media vs hw-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.liquid scoping).

  • `.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-group blocks and hw-hero section.

    Desktop padding: padding-*-desktop range settings (0-100 px) override mobile padding at 750 px+. Uses new snippets/hw-spacing-style.liquid + assets/hw-spacing-style.css (mirrors base spacing-style but scopes desktop custom properties per-node so values never inherit from ancestors).

    Device visibility: display_breakpoint select (Both / Desktop / Mobile) on each block; outputs hw-desktop-only or hw-mobile-only class via assets/hw-block-display.css (hide with display: none !important at the relevant viewport).

    Hero (HW): Switched from spacing-style / spacing-style snippet to hw-spacing-style / hw-spacing-style snippet; adds desktop padding settings (defaults: 40 px block, 0 px inline).

    Button (HW): Wraps output in .hw-button-block-root div carrying hw-spacing-style, visibility class, and block.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_desktop to locales/en.default.schema.json.

  • Hero (HW): sections/hw-hero.liquid with composable blocks; assets/hw-hero.css for 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, and assets/hw-group-layout.css (registered via snippets/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-block rules so HW width / max-inline-size variables win over snippets/text.liquid and row flex rules in assets/base.css; hw-text-block class on snippets/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.css and the .hw-text-block contract.

  • `.cursor/skills/mobile-row-wrap-debugging/SKILL.md`: Documents hw-group small-screen vs desktop direction (no vertical_on_mobile on HW group) vs base group / 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-works package name and versioning structure.

Changed

  • Config: Updated package.json to reflect the new framework name and version.