From 0c3d2c92f4bcbb71766d00c8be2a11c656819837 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 22 Jul 2026 21:21:29 +0200 Subject: [PATCH 1/9] Add mega menu patterns for Ollie Menu Designer (LS-1618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - patterns/menu/mega-menu-default.php: new pattern for the Default mega menu (Work-style dropdown) — two-column featured item grid with icon wells, header/footer CTA rows. Flagged with Block Types: core/template-part/menu and Categories: menu so it registers as starter content for Ollie's Dropdown Menu block. - patterns/menu/mega-menu-service.php: new pattern for the Service mega menu — six lifecycle-phase columns (Discover/Create/Build/ Launch/Grow/Evolve), each colour-coded via the new phase.* tokens. - styles/sections/menu/mega-menu-panel.json: new section style (is-style-mega-menu-panel) for the dropdown panel shell — card surface, popover shadow, card-family border. - styles/blocks/groups/icon-frame-glow-link-accent.json: new companion style overriding Icon Frame Glow's accent colour via link.accent, for the mega menu's icon wells. - theme.json: added phase-one..phase-six palette presets (plus -strong variants for AA-safe light-mode contrast), the phase.* semantic token family, text.subtle, and a shadow.popover token. - styles/dark.json: mirrored phase.*, text.subtle, and shadow.popover for dark mode (using the vivid, non-strong presets). - styles/presets/shadows.json: added a "popover" shadow preset wrapping shadow.popover.menu for use via var:preset|shadow|popover. - assets/css/animations.css: added the mega menu panel/divider border colours (using border.card, not the glass-card highlight trick, so dividers stay visible in both light and dark mode), icon-well sizing, item hover/click-target styling, and the phase-dot utility (fixed 8px circle, padding reset to counter WordPress's default has-background padding). - CHANGELOG.md: documented the above under [Unreleased]. --- CHANGELOG.md | 4 + assets/css/animations.css | 54 +++++ patterns/menu/mega-menu-default.php | 210 +++++++++++++++++ patterns/menu/mega-menu-service.php | 218 ++++++++++++++++++ .../groups/icon-frame-glow-link-accent.json | 13 ++ styles/dark.json | 12 + styles/presets/shadows.json | 5 + styles/sections/menu/mega-menu-panel.json | 31 +++ theme.json | 72 ++++++ 9 files changed, 619 insertions(+) create mode 100644 patterns/menu/mega-menu-default.php create mode 100644 patterns/menu/mega-menu-service.php create mode 100644 styles/blocks/groups/icon-frame-glow-link-accent.json create mode 100644 styles/sections/menu/mega-menu-panel.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 8784f40..f2307f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added `Mega Menu - Default` and `Mega Menu - Service` patterns (`patterns/menu/`) for Ollie Menu Designer, using `Block Types: core/template-part/menu` so they register as selectable mega-menu content per nav item (LS-1618). +- Added a `Mega Menu Panel` section style (`styles/sections/menu/mega-menu-panel.json`) plus a `shadow.popover` custom shadow token for the dropdown panel shell. +- Added a `phase.discover|create|build|launch|grow|evolve` semantic colour token family (six new `phase-*` / `phase-*-strong` palette presets) for the Services mega menu's lifecycle-phase colour coding, with light-mode values darkened to meet WCAG AA 4.5:1 against the light card surface. +- Added a `text.subtle` semantic colour token (`neutral-600` light / `neutral-500` dark) for a tertiary text tier between `text.muted` and full-contrast text. - Added the Search Results template (`search.html` + `template-search.php`), a Page (No Title) custom template (`page-no-title.html` + `template-page-no-title.php`, registered in `theme.json` `customTemplates`), and a shared Taxonomy template (`taxonomy.html` + `template-taxonomy.php`) covering all four Portfolio taxonomies (LS-1226). - Extracted `front-page.html`, `index.html`, `page.html`, `single.html`, and `archive.html` into dedicated pattern files (`hero` + `front-page-latest-posts`, `template-index`, `template-page`, `template-single`, `template-archive`) so every template body is a single pattern injection, per the new template/pattern naming convention (LS-1226). - Built out `archive.html`'s main content (query title, term description, paginated post query loop), which previously rendered no content at all (LS-1226). diff --git a/assets/css/animations.css b/assets/css/animations.css index 2d05f20..1e99a57 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1155,3 +1155,57 @@ transition: none; } } + +/********** Mega Menu Panel **********/ +.is-style-mega-menu-panel { + border-color: var(--wp--custom--color--border--card); +} + +.ls-mega-menu__divider { + border-color: var(--wp--custom--color--border--card); +} + +.ls-mega-menu__item { + position: relative; + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.ls-mega-menu__item:hover, +.ls-mega-menu__item:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); +} +.ls-mega-menu__item .ls-mega-menu__item-link::before { + content: ""; + position: absolute; + inset: 0; + z-index: var(--wp--custom--z-index--content); +} +.ls-mega-menu__item .ls-mega-menu__item-link { + position: relative; +} + +.ls-mega-menu__icon-well { + min-inline-size: 2.25rem; + min-block-size: 2.25rem; + padding: var(--wp--preset--spacing--5); +} + +.ls-mega-menu__item .wp-block-heading, +.ls-mega-menu__item h5, +.ls-mega-menu__item p { + margin: 0 !important; +} + +.ls-mega-menu__phase-dot { + display: inline-block; + flex: none; + inline-size: 8px; + block-size: 8px; + padding: 0; + border-radius: var(--wp--preset--border-radius--500); +} + +@media (prefers-reduced-motion: reduce) { + .ls-mega-menu__item { + transition: none; + } +} diff --git a/patterns/menu/mega-menu-default.php b/patterns/menu/mega-menu-default.php new file mode 100644 index 0000000..7fa1eb9 --- /dev/null +++ b/patterns/menu/mega-menu-default.php @@ -0,0 +1,210 @@ + + +
+ + +
+ +
+ +

+ + + +

+ +
+ + + + + +
+ + + +
+ + +
+ + +
+ + + + + +
+ +
+ + + +

+ +
+ + + +
+ + + +
+ + + + + +
+ +
+ + + +

+ +
+ + + +
+ + + +
+ + + + + +
+ +
+ + + +

+ +
+ + + +
+ +
+ + + +
+ + +
+ + + + + +
+ +
+ + + +

+ +
+ + + +
+ + + +
+ + + + + +
+ +
+ + + +

+ +
+ + + +
+ + + +
+ + + + + +
+ +
+ + + +

+ +
+ + + +
+ +
+ + +
+ + + +
+ +

+ + + + + +
+ + +
+ diff --git a/patterns/menu/mega-menu-service.php b/patterns/menu/mega-menu-service.php new file mode 100644 index 0000000..f83b26e --- /dev/null +++ b/patterns/menu/mega-menu-service.php @@ -0,0 +1,218 @@ + + +
+ + +
+ +
+ +

+ + + +

+ +
+ + + + + +
+ + + +
+ + +
+ +
+ +
+ + + +

+ + + +
+ + + +

+ +
+ + + +
+ +
+ +
+ + + +

+ + + +
+ + + +

+ + + +

+ +
+ + + +
+ +
+ +
+ + + +

+ + + +
+ + + +

+ + + +

+ +
+ + + +
+ +
+ +
+ + + +

+ + + +
+ + + +

+ + + +

+ + + +

+ + + +

+ +
+ + + +
+ +
+ +
+ + + +

+ + + +
+ + + +

+ + + +

+ + + +

+ + + +

+ +
+ + + +
+ +
+ +
+ + + +

+ +
+ + + +

+ +
+ + +
+ + + +
+ +

+ + + + + +
+ + +
+ diff --git a/styles/blocks/groups/icon-frame-glow-link-accent.json b/styles/blocks/groups/icon-frame-glow-link-accent.json new file mode 100644 index 0000000..03146ee --- /dev/null +++ b/styles/blocks/groups/icon-frame-glow-link-accent.json @@ -0,0 +1,13 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Icon Frame Glow - Link Accent", + "slug": "icon-frame-glow-link-accent", + "blockTypes": [ + "core/group" + ], + "description": "Accent-colour override for Icon Frame Glow, tinted with the link accent token. Combine with is-style-icon-frame-glow.", + "styles": { + "css": "&{--ls-card-solutions-accent-colour:var(--wp--custom--color--link--accent);}" + } +} diff --git a/styles/dark.json b/styles/dark.json index d64a5c3..c4e7941 100644 --- a/styles/dark.json +++ b/styles/dark.json @@ -9,6 +9,7 @@ "default": "var:preset|color|base", "inverse": "var:preset|color|contrast", "muted": "var:preset|color|neutral-400", + "subtle": "var:preset|color|neutral-500", "brand": "var:preset|color|cta-500", "brand-strong": "var:preset|color|cta-200", "on-dark": "var:preset|color|base", @@ -43,6 +44,14 @@ "accent": "var:preset|color|accent-three-500" } }, + "phase": { + "discover": "var:preset|color|phase-one", + "create": "var:preset|color|phase-two", + "build": "var:preset|color|phase-three", + "launch": "var:preset|color|phase-four", + "grow": "var:preset|color|phase-five", + "evolve": "var:preset|color|phase-six" + }, "text-shadow": { "contrast": "var:preset|color|base", "base": "var:preset|color|contrast" @@ -100,6 +109,9 @@ }, "card": { "hover": "var(--wp--custom--shadow--elevation--400), 0 28px 56px -28px color-mix(in srgb, var(--wp--custom--color--link--accent) 52%, transparent), 0 14px 30px -18px color-mix(in srgb, var(--wp--custom--color--link--accent) 32%, transparent)" + }, + "popover": { + "menu": "0 32px 80px 0 color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 18%, transparent), var(--wp--custom--shadow--elevation--200)" } } } diff --git a/styles/presets/shadows.json b/styles/presets/shadows.json index b5bfcde..34558be 100644 --- a/styles/presets/shadows.json +++ b/styles/presets/shadows.json @@ -34,6 +34,11 @@ "name": "X-Large", "slug": "600", "shadow": "var(--wp--custom--shadow--elevation--600)" + }, + { + "name": "Popover", + "slug": "popover", + "shadow": "var(--wp--custom--shadow--popover--menu)" } ] } diff --git a/styles/sections/menu/mega-menu-panel.json b/styles/sections/menu/mega-menu-panel.json new file mode 100644 index 0000000..6b0baba --- /dev/null +++ b/styles/sections/menu/mega-menu-panel.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Mega Menu Panel", + "slug": "mega-menu-panel", + "blockTypes": [ + "core/group" + ], + "description": "Dropdown panel shell for Ollie Menu Designer mega menus. Card surface with a highlight-tinted border and popover shadow. Border tint lives in assets/css/animations.css.", + "styles": { + "color": { + "background": "var:custom|color|surface|card", + "text": "var:custom|color|text|default" + }, + "border": { + "radius": "var:preset|border-radius|300", + "style": "solid", + "width": "1px" + }, + "shadow": "var:preset|shadow|popover", + "spacing": { + "blockGap": "var:preset|spacing|20", + "padding": { + "top": "var:preset|spacing|30", + "right": "var:preset|spacing|20", + "bottom": "var:preset|spacing|30", + "left": "var:preset|spacing|20" + } + } + } +} diff --git a/theme.json b/theme.json index b82d85f..8b94b0b 100644 --- a/theme.json +++ b/theme.json @@ -399,6 +399,66 @@ "name": "Success Foreground", "slug": "success-foreground", "color": "#10B981" + }, + { + "name": "Phase One", + "slug": "phase-one", + "color": "#5DE81C" + }, + { + "name": "Phase One Strong", + "slug": "phase-one-strong", + "color": "#32800D" + }, + { + "name": "Phase Two", + "slug": "phase-two", + "color": "#18CAE9" + }, + { + "name": "Phase Two Strong", + "slug": "phase-two-strong", + "color": "#0E7B8E" + }, + { + "name": "Phase Three", + "slug": "phase-three", + "color": "#FE5EAD" + }, + { + "name": "Phase Three Strong", + "slug": "phase-three-strong", + "color": "#DB016D" + }, + { + "name": "Phase Four", + "slug": "phase-four", + "color": "#FADC2F" + }, + { + "name": "Phase Four Strong", + "slug": "phase-four-strong", + "color": "#826F03" + }, + { + "name": "Phase Five", + "slug": "phase-five", + "color": "#22CD94" + }, + { + "name": "Phase Five Strong", + "slug": "phase-five-strong", + "color": "#157F5C" + }, + { + "name": "Phase Six", + "slug": "phase-six", + "color": "#7A7CF9" + }, + { + "name": "Phase Six Strong", + "slug": "phase-six-strong", + "color": "#585AF7" } ] }, @@ -419,6 +479,7 @@ "default": "var:preset|color|contrast", "inverse": "var:preset|color|base", "muted": "var:preset|color|neutral-700", + "subtle": "var:preset|color|neutral-600", "brand": "var:preset|color|brand-500", "brand-strong": "var:preset|color|brand-800", "on-dark": "var:preset|color|base", @@ -453,6 +514,14 @@ "accent": "var:preset|color|accent-500" } }, + "phase": { + "discover": "var:preset|color|phase-one-strong", + "create": "var:preset|color|phase-two-strong", + "build": "var:preset|color|phase-three-strong", + "launch": "var:preset|color|phase-four-strong", + "grow": "var:preset|color|phase-five-strong", + "evolve": "var:preset|color|phase-six-strong" + }, "text-shadow": { "contrast": "var:preset|color|contrast", "base": "var:preset|color|base" @@ -543,6 +612,9 @@ }, "card": { "hover": "0 9px 28px -16px color-mix(in srgb, var(--wp--custom--color--text--brand) 80%, transparent), 0 8px 18px -14px color-mix(in srgb, var(--wp--custom--color--text--brand-strong) 24%, transparent)" + }, + "popover": { + "menu": "0 32px 80px 0 color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 18%, transparent), var(--wp--custom--shadow--elevation--200)" } }, "animation": { From 0aae15252d7e4f13d0c271cf26bff18161de4a3a Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Thu, 23 Jul 2026 16:11:55 +0200 Subject: [PATCH 2/9] Finish Default mega menu rows: 36px icons, single-source hover, link underline fix (LS-1618) Builds out the 6 mega menu template parts to final content and settles the row/icon interaction model after a few false starts. - Consolidated each row to one registered style (`mega-menu-item-default.json`); the icon well is now a plain inline-styled group with no style/className of its own, so it can't independently react to hover, and is capped at 36px instead of inheriting the shared icon-frame-glow class's 5rem minimum. - Row hover/focus (background highlight + arrow reveal) moved to `_menu-motion.scss`: WordPress's global-styles engine only generates `:hover`/`:focus` CSS for the `elements` allowlist, not arbitrary block style variations, so this couldn't live in JSON. - Rebuilt `animations.css`, which had drifted out of sync with its SCSS source and still carried dead rules from an earlier `.ls-mega-menu__*` approach. - Added 22 Phosphor icons across About/Insights/Pricing/Solutions, matched to each row (no `wordpress-logo` in Phosphor, so `browser` substitutes for Solutions' "WordPress" row). - Fixed link underline-on-hover: WordPress core's default `core/navigation` link-hover style overrides the theme's generic link fix due to higher specificity; added the matching override in `links.json`. - Deleted the two superseded pattern files (`mega-menu-default.php`/`mega-menu-service.php`) and an orphaned style file (`icon-frame-glow-link-accent.json`), and corrected the CHANGELOG entry to match what actually shipped. --- CHANGELOG.md | 6 +- assets/css/animations.css | 59 ++--- parts/about-mega-menu.html | 165 +++++++++++++ parts/insights-mega-menu.html | 165 +++++++++++++ parts/pricing-mega-menu.html | 165 +++++++++++++ parts/services-mega-menu.html | 153 ++++++++++++ parts/solutions-mega-menu.html | 177 ++++++++++++++ parts/work-mega-menu.html | 165 +++++++++++++ patterns/menu/mega-menu-default.php | 210 ----------------- patterns/menu/mega-menu-service.php | 218 ------------------ src/scss/animations.scss | 3 +- src/scss/animations/_menu-motion.scss | 37 +++ .../groups/icon-frame-glow-link-accent.json | 13 -- .../blocks/groups/mega-menu-item-default.json | 50 ++++ .../blocks/groups/mega-menu-item-service.json | 23 ++ styles/presets/links.json | 19 ++ styles/sections/menu/mega-menu-panel.json | 3 +- theme.json | 30 +++ 18 files changed, 1173 insertions(+), 488 deletions(-) create mode 100644 parts/about-mega-menu.html create mode 100644 parts/insights-mega-menu.html create mode 100644 parts/pricing-mega-menu.html create mode 100644 parts/services-mega-menu.html create mode 100644 parts/solutions-mega-menu.html create mode 100644 parts/work-mega-menu.html delete mode 100644 patterns/menu/mega-menu-default.php delete mode 100644 patterns/menu/mega-menu-service.php create mode 100644 src/scss/animations/_menu-motion.scss delete mode 100644 styles/blocks/groups/icon-frame-glow-link-accent.json create mode 100644 styles/blocks/groups/mega-menu-item-default.json create mode 100644 styles/blocks/groups/mega-menu-item-service.json diff --git a/CHANGELOG.md b/CHANGELOG.md index f2307f1..45c3ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,8 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added `Mega Menu - Default` and `Mega Menu - Service` patterns (`patterns/menu/`) for Ollie Menu Designer, using `Block Types: core/template-part/menu` so they register as selectable mega-menu content per nav item (LS-1618). -- Added a `Mega Menu Panel` section style (`styles/sections/menu/mega-menu-panel.json`) plus a `shadow.popover` custom shadow token for the dropdown panel shell. +- Added 6 real mega menu template parts for Ollie Menu Designer's Dropdown Menu block, each with final content, no shared placeholder scaffold: `parts/work-mega-menu.html`, `parts/solutions-mega-menu.html`, `parts/pricing-mega-menu.html`, `parts/insights-mega-menu.html`, `parts/about-mega-menu.html` (Default item styling), and `parts/services-mega-menu.html` (Service item styling, 6 lifecycle-phase columns). All registered in `theme.json` `templateParts` (LS-1618). +- Added `Mega Menu Item - Default` and `Mega Menu Item - Service` block styles (`styles/blocks/groups/mega-menu-item-default.json`, `mega-menu-item-service.json`) as the single reusable, registered source of a row's structural styling (padding, radius) — the icon well itself carries no separate style, just plain inline block attributes, so it has no independent interactive behaviour of its own. +- Added `.is-style-mega-menu-item-default` hover/focus behaviour (row background highlight + trailing arrow icon reveal) in `src/scss/animations/_menu-motion.scss`, compiled to `assets/css/animations.css`. This couldn't be expressed in the JSON style itself — WordPress's global-styles engine only generates `:hover`/`:focus` CSS for the built-in `elements` allowlist (link/button/etc.), not for arbitrary block style variations — so it's authored here instead, scoped tightly to this one selector. +- Added a `Mega Menu Panel` section style (`styles/sections/menu/mega-menu-panel.json`) plus a `shadow.popover` custom shadow token for the dropdown panel shell, and moved its border colour onto native JSON `border.color` instead of a CSS override. - Added a `phase.discover|create|build|launch|grow|evolve` semantic colour token family (six new `phase-*` / `phase-*-strong` palette presets) for the Services mega menu's lifecycle-phase colour coding, with light-mode values darkened to meet WCAG AA 4.5:1 against the light card surface. - Added a `text.subtle` semantic colour token (`neutral-600` light / `neutral-500` dark) for a tertiary text tier between `text.muted` and full-contrast text. - Added the Search Results template (`search.html` + `template-search.php`), a Page (No Title) custom template (`page-no-title.html` + `template-page-no-title.php`, registered in `theme.json` `customTemplates`), and a shared Taxonomy template (`taxonomy.html` + `template-taxonomy.php`) covering all four Portfolio taxonomies (LS-1226). diff --git a/assets/css/animations.css b/assets/css/animations.css index 1e99a57..90f4b66 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1155,57 +1155,30 @@ transition: none; } } - -/********** Mega Menu Panel **********/ -.is-style-mega-menu-panel { - border-color: var(--wp--custom--color--border--card); -} - -.ls-mega-menu__divider { - border-color: var(--wp--custom--color--border--card); -} - -.ls-mega-menu__item { - position: relative; +/********** Mega Menu Item **********/ +/* + * Hover/focus states for .is-style-mega-menu-item-default (styles/blocks/groups/mega-menu-item-default.json). + * WordPress's global-styles engine only generates :hover/:focus CSS for the elements.link/button/etc. + * allowlist — arbitrary block style variations never get pseudo-state rules, so this piece can't be + * expressed in the JSON style and lives here instead. Base (non-hover) styles for this row, and the + * icon well's own appearance, remain authored in theme.json/style JSON. + */ +.is-style-mega-menu-item-default { transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); } -.ls-mega-menu__item:hover, -.ls-mega-menu__item:focus-within { +.is-style-mega-menu-item-default:hover, .is-style-mega-menu-item-default:focus-within { background-color: var(--wp--custom--color--surface--card-raised); } -.ls-mega-menu__item .ls-mega-menu__item-link::before { - content: ""; - position: absolute; - inset: 0; - z-index: var(--wp--custom--z-index--content); -} -.ls-mega-menu__item .ls-mega-menu__item-link { - position: relative; -} - -.ls-mega-menu__icon-well { - min-inline-size: 2.25rem; - min-block-size: 2.25rem; - padding: var(--wp--preset--spacing--5); -} - -.ls-mega-menu__item .wp-block-heading, -.ls-mega-menu__item h5, -.ls-mega-menu__item p { - margin: 0 !important; +.is-style-mega-menu-item-default .wp-block-outermost-icon-block { + transition: color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); } - -.ls-mega-menu__phase-dot { - display: inline-block; - flex: none; - inline-size: 8px; - block-size: 8px; - padding: 0; - border-radius: var(--wp--preset--border-radius--500); +.is-style-mega-menu-item-default:hover .wp-block-outermost-icon-block, .is-style-mega-menu-item-default:focus-within .wp-block-outermost-icon-block { + color: var(--wp--custom--color--text--subtle); } @media (prefers-reduced-motion: reduce) { - .ls-mega-menu__item { + .is-style-mega-menu-item-default, + .is-style-mega-menu-item-default .wp-block-outermost-icon-block { transition: none; } } diff --git a/parts/about-mega-menu.html b/parts/about-mega-menu.html new file mode 100644 index 0000000..fb9fb48 --- /dev/null +++ b/parts/about-mega-menu.html @@ -0,0 +1,165 @@ + +
+
+
+

About

+ + + +

Who we are and what we commit to.

+
+ + + + +
+ + + +
+
+
+
+
+
+ + + +
+
Why LightSpeed
+ + + +

The thinking behind a specialist WordPress and WooCommerce partner.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Getting started
+ + + +

What the first conversation produces and the route into delivery.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
AI governance
+ + + +

How we handle AI tools, data and editorial responsibility.

+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
Our process
+ + + +

Six lifecycle stages — Discover, Create, Build, Launch, Grow, Evolve.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Guarantees
+ + + +

What we commit to before, during and after launch.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Accessibility
+ + + +

Our accessibility statement and how we work to WCAG 2.2 AA.

+
+ + + +
+
+
+
+ + + +
+

Prefer to talk?

+ + + + +
+
+ diff --git a/parts/insights-mega-menu.html b/parts/insights-mega-menu.html new file mode 100644 index 0000000..08aea1d --- /dev/null +++ b/parts/insights-mega-menu.html @@ -0,0 +1,165 @@ + +
+
+
+

Insights

+ + + +

Writing, notes and resources from real engagements.

+
+ + + + +
+ + + +
+
+
+
+
+
+ + + +
+
Block themes & FSE
+ + + +

Notes on running production sites on block-first WordPress.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Design systems
+ + + +

theme.json, patterns and reusable visual language for editors.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Migrations & rebuilds
+ + + +

How we plan replatforming as risk management.

+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
WooCommerce in depth
+ + + +

Subscriptions, performance and store operations at scale.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
AI readiness
+ + + +

Governance, content structure and search before any AI add-ons.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Resources & guides
+ + + +

Practical checklists, briefs and downloadable references.

+
+ + + +
+
+
+
+ + + +
+

Want it in your inbox?

+ + + + +
+
+ diff --git a/parts/pricing-mega-menu.html b/parts/pricing-mega-menu.html new file mode 100644 index 0000000..f8ba9e9 --- /dev/null +++ b/parts/pricing-mega-menu.html @@ -0,0 +1,165 @@ + +
+
+
+

Pricing

+ + + +

Find where you are likely to fit.

+
+ + + + +
+ + + +
+
+
+
+
+
+ + + +
+
Foundation
+ + + +

A cleaner platform baseline for teams resetting their site.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Enterprise
+ + + +

Larger orgs where governance and integrations matter more deeply.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Website packages
+ + + +

The three routes side-by-side, with scope and outcomes.

+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
Growth
+ + + +

For organisations with traction that need the platform to support more.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
How we price work
+ + + +

The principles behind a LightSpeed engagement, not a price grid.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Guarantees
+ + + +

What we commit to before, during and after launch.

+
+ + + +
+
+
+
+ + + +
+

Need a tailored estimate?

+ + + + +
+
+ diff --git a/parts/services-mega-menu.html b/parts/services-mega-menu.html new file mode 100644 index 0000000..312704d --- /dev/null +++ b/parts/services-mega-menu.html @@ -0,0 +1,153 @@ + +
+
+
+

Services by lifecycle phase

+ + + +

Services that span the WordPress lifecycle — grouped by phase.

+
+ + + + +
+ + + +
+
+
+ + +

Discover

+ + + +
+ + + +

Discovery

+
+ + + +
+
+ + +

Create

+ + + +
+ + + +

Content

+ + +

Design

+
+ + + +
+
+ + +

Build

+ + + +
+ + + +

Development

+ + +

Migrations

+
+ + + +
+
+ + +

Launch

+ + + +
+ + + +

Hosting

+ + +

Performance

+ + +

Security

+ + +

Training

+
+ + + +
+
+ + +

Grow

+ + + +
+ + + +

Support

+ + +

SEO

+ + +

Accessibility

+ + +

Email marketing

+
+ + + +
+
+ + +

Evolve

+ +
+ + + +

AI

+
+
+ + + +
+

Each service is colour-coded by its lifecycle phase

+ + + + +
+
+ diff --git a/parts/solutions-mega-menu.html b/parts/solutions-mega-menu.html new file mode 100644 index 0000000..38fbeee --- /dev/null +++ b/parts/solutions-mega-menu.html @@ -0,0 +1,177 @@ + +
+
+
+

Solutions

+ + + +

Pick a route by the problem you're solving for.

+
+ + + + +
+ + + +
+
+
+
+
+
+ + + +
+
WordPress
+ + + +

A stronger platform foundation, cleaner structure and more long-term control.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Publishing
+ + + +

For editorial platforms dealing with scale, structure and long-term maintainability.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
AI
+ + + +

For teams preparing content, governance and platform structure for deliberate AI use.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
AI Chatbots
+ + + +

For teams considering chatbot delivery once content, governance and escalation routes are in place.

+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
WooCommerce
+ + + +

For teams managing product complexity, subscriptions and ecommerce operational pressure.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Tour Operators
+ + + +

For travel websites that need rich content, clearer booking journeys and operational clarity.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Design Systems
+ + + +

For teams that need stronger consistency between content, design and implementation.

+
+ + + +
+
+
+
+
+ diff --git a/parts/work-mega-menu.html b/parts/work-mega-menu.html new file mode 100644 index 0000000..325592b --- /dev/null +++ b/parts/work-mega-menu.html @@ -0,0 +1,165 @@ + +
+
+
+

Work

+ + + +

Real platform work, not portfolio decoration.

+
+ + + + +
+ + + +
+
+
+
+
+
+ + + +
+
Travel publisher rebuild
+ + + +

Block-first WordPress for a high-volume travel publisher.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Design system rollout
+ + + +

Reusable patterns and theme.json discipline across products.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
All case studies
+ + + +

Every engagement, with the business context up front.

+
+ + + +
+
+
+ + + +
+
+
+
+
+ + + +
+
Subscriptions reset
+ + + +

WooCommerce Subscriptions, performance and self-serve.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Migration care
+ + + +

Replatforming as risk management, not a content move.

+
+ + + +
+
+ + + +
+
+
+
+ + + +
+
Testimonials
+ + + +

Approved client praise — names, roles, companies.

+
+ + + +
+
+
+
+ + + +
+

Starting a project?

+ + + + +
+
+ diff --git a/patterns/menu/mega-menu-default.php b/patterns/menu/mega-menu-default.php deleted file mode 100644 index 7fa1eb9..0000000 --- a/patterns/menu/mega-menu-default.php +++ /dev/null @@ -1,210 +0,0 @@ - - -
- - -
- -
- -

- - - -

- -
- - - - - -
- - - -
- - -
- - -
- - - - - -
- -
- - - -

- -
- - - -
- - - -
- - - - - -
- -
- - - -

- -
- - - -
- - - -
- - - - - -
- -
- - - -

- -
- - - -
- -
- - - -
- - -
- - - - - -
- -
- - - -

- -
- - - -
- - - -
- - - - - -
- -
- - - -

- -
- - - -
- - - -
- - - - - -
- -
- - - -

- -
- - - -
- -
- - -
- - - -
- -

- - - - - -
- - -
- diff --git a/patterns/menu/mega-menu-service.php b/patterns/menu/mega-menu-service.php deleted file mode 100644 index f83b26e..0000000 --- a/patterns/menu/mega-menu-service.php +++ /dev/null @@ -1,218 +0,0 @@ - - -
- - -
- -
- -

- - - -

- -
- - - - - -
- - - -
- - -
- -
- -
- - - -

- - - -
- - - -

- -
- - - -
- -
- -
- - - -

- - - -
- - - -

- - - -

- -
- - - -
- -
- -
- - - -

- - - -
- - - -

- - - -

- -
- - - -
- -
- -
- - - -

- - - -
- - - -

- - - -

- - - -

- - - -

- -
- - - -
- -
- -
- - - -

- - - -
- - - -

- - - -

- - - -

- - - -

- -
- - - -
- -
- -
- - - -

- -
- - - -

- -
- - -
- - - -
- -

- - - - - -
- - -
- diff --git a/src/scss/animations.scss b/src/scss/animations.scss index 4540db6..ec10eac 100644 --- a/src/scss/animations.scss +++ b/src/scss/animations.scss @@ -4,4 +4,5 @@ @use "animations/details-motion"; @use "animations/card-motion"; @use "animations/button-motion"; -@use "animations/faq-motion"; \ No newline at end of file +@use "animations/faq-motion"; +@use "animations/menu-motion"; \ No newline at end of file diff --git a/src/scss/animations/_menu-motion.scss b/src/scss/animations/_menu-motion.scss new file mode 100644 index 0000000..d74b383 --- /dev/null +++ b/src/scss/animations/_menu-motion.scss @@ -0,0 +1,37 @@ +@use "../abstracts/mixins/motion"; + +/********** Mega Menu Item **********/ + +/* + * Hover/focus states for .is-style-mega-menu-item-default (styles/blocks/groups/mega-menu-item-default.json). + * WordPress's global-styles engine only generates :hover/:focus CSS for the elements.link/button/etc. + * allowlist — arbitrary block style variations never get pseudo-state rules, so this piece can't be + * expressed in the JSON style and lives here instead. Base (non-hover) styles for this row, and the + * icon well's own appearance, remain authored in theme.json/style JSON. + */ +.is-style-mega-menu-item-default { + transition: background-color var(--wp--custom--animation--duration--base) + var(--wp--custom--animation--easing--standard); + + &:hover, + &:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); + } + + .wp-block-outermost-icon-block { + transition: color var(--wp--custom--animation--duration--base) + var(--wp--custom--animation--easing--standard); + } + + &:hover .wp-block-outermost-icon-block, + &:focus-within .wp-block-outermost-icon-block { + color: var(--wp--custom--color--text--subtle); + } +} + +@include motion.reduced-motion { + .is-style-mega-menu-item-default, + .is-style-mega-menu-item-default .wp-block-outermost-icon-block { + transition: none; + } +} diff --git a/styles/blocks/groups/icon-frame-glow-link-accent.json b/styles/blocks/groups/icon-frame-glow-link-accent.json deleted file mode 100644 index 03146ee..0000000 --- a/styles/blocks/groups/icon-frame-glow-link-accent.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "https://schemas.wp.org/wp/6.9/theme.json", - "version": 3, - "title": "Icon Frame Glow - Link Accent", - "slug": "icon-frame-glow-link-accent", - "blockTypes": [ - "core/group" - ], - "description": "Accent-colour override for Icon Frame Glow, tinted with the link accent token. Combine with is-style-icon-frame-glow.", - "styles": { - "css": "&{--ls-card-solutions-accent-colour:var(--wp--custom--color--link--accent);}" - } -} diff --git a/styles/blocks/groups/mega-menu-item-default.json b/styles/blocks/groups/mega-menu-item-default.json new file mode 100644 index 0000000..c9eb9be --- /dev/null +++ b/styles/blocks/groups/mega-menu-item-default.json @@ -0,0 +1,50 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Mega Menu Item - Default", + "slug": "mega-menu-item-default", + "blockTypes": [ + "core/group" + ], + "description": "Single style for the whole Default mega-menu row (icon well + title/description + trailing arrow). Only this row reacts to hover/focus: its background highlights and the trailing arrow icon becomes visible. The icon well itself carries no className/registered style of its own, so it has no hover behaviour to trigger.", + "styles": { + "border": { + "radius": "var:preset|border-radius|300" + }, + "spacing": { + "padding": { + "top": "var:preset|spacing|10", + "right": "var:preset|spacing|10", + "bottom": "var:preset|spacing|10", + "left": "var:preset|spacing|10" + } + }, + ":hover": { + "color": { + "background": "var:custom|color|surface|card-raised" + } + }, + ":focus": { + "color": { + "background": "var:custom|color|surface|card-raised" + } + }, + "blocks": { + "outermost/icon-block": { + "color": { + "text": "transparent" + }, + ":hover": { + "color": { + "text": "var:custom|color|text|subtle" + } + }, + ":focus": { + "color": { + "text": "var:custom|color|text|subtle" + } + } + } + } + } +} diff --git a/styles/blocks/groups/mega-menu-item-service.json b/styles/blocks/groups/mega-menu-item-service.json new file mode 100644 index 0000000..21fa94c --- /dev/null +++ b/styles/blocks/groups/mega-menu-item-service.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Mega Menu Item - Service", + "slug": "mega-menu-item-service", + "blockTypes": [ + "core/paragraph" + ], + "description": "Menu-item row for the Service mega menu's phase columns. Structural padding/radius only for now — hover treatment to follow once the Service-specific interaction reference is provided.", + "styles": { + "border": { + "radius": "var:preset|border-radius|300" + }, + "spacing": { + "padding": { + "top": "var:preset|spacing|10", + "right": "var:preset|spacing|10", + "bottom": "var:preset|spacing|10", + "left": "var:preset|spacing|10" + } + } + } +} diff --git a/styles/presets/links.json b/styles/presets/links.json index 1a6e35d..ad09807 100644 --- a/styles/presets/links.json +++ b/styles/presets/links.json @@ -10,6 +10,25 @@ ":hover": { "color": { "text": "var:custom|color|link|accent-hover" + }, + "typography": { + + "textDecoration": "none" + + } + + } + } + }, + "blocks": { + "core/navigation": { + "elements": { + "link": { + ":hover": { + "typography": { + "textDecoration": "none" + } + } } } } diff --git a/styles/sections/menu/mega-menu-panel.json b/styles/sections/menu/mega-menu-panel.json index 6b0baba..c64679a 100644 --- a/styles/sections/menu/mega-menu-panel.json +++ b/styles/sections/menu/mega-menu-panel.json @@ -6,13 +6,14 @@ "blockTypes": [ "core/group" ], - "description": "Dropdown panel shell for Ollie Menu Designer mega menus. Card surface with a highlight-tinted border and popover shadow. Border tint lives in assets/css/animations.css.", + "description": "Dropdown panel shell for Ollie Menu Designer mega menus. Card surface with a card-family border and popover shadow.", "styles": { "color": { "background": "var:custom|color|surface|card", "text": "var:custom|color|text|default" }, "border": { + "color": "var:custom|color|border|card", "radius": "var:preset|border-radius|300", "style": "solid", "width": "1px" diff --git a/theme.json b/theme.json index 8b94b0b..4a1e565 100644 --- a/theme.json +++ b/theme.json @@ -674,6 +674,36 @@ "name": "footer", "title": "Footer", "area": "footer" + }, + { + "name": "work-mega-menu", + "title": "Work Mega Menu", + "area": "uncategorized" + }, + { + "name": "solutions-mega-menu", + "title": "Solutions Mega Menu", + "area": "uncategorized" + }, + { + "name": "pricing-mega-menu", + "title": "Pricing Mega Menu", + "area": "uncategorized" + }, + { + "name": "insights-mega-menu", + "title": "Insights Mega Menu", + "area": "uncategorized" + }, + { + "name": "about-mega-menu", + "title": "About Mega Menu", + "area": "uncategorized" + }, + { + "name": "services-mega-menu", + "title": "Services Mega Menu", + "area": "uncategorized" } ], "customTemplates": [ From 1ed0225303559ffd9ad210626907cfd5368de8e9 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Thu, 23 Jul 2026 18:44:23 +0200 Subject: [PATCH 3/9] Rebuild Services mega menu to match Figma: icons, per-phase hover, panel sizing (LS-1618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Brings `parts/services-mega-menu.html` in line with the approved Figma frame, following the same JSON-first/SCSS-only-when-required approach as the Default menu. - Replaced invisible phase dots and separator arrows (empty placeholders) with real Phosphor icons (`circle-fill` dots, `caret-right` arrows at 50% opacity). - Fixed the outer panel to hug its own content (`width: fit-content`) instead of clamping to a fixed contentSize and clipping the columns. - Fixed `core/columns` squeezing phase labels mid-word: added a 120px column minimum with flex-wrap, so a column wraps to a new row instead of the label wrapping. - Added per-phase hover colour — each phase's header pill and its rows below now hover in that column's own phase colour, scoped via `:nth-child` rather than a per-instance attribute (avoids the block-validation break from an earlier attempt). - Reverted the CTA links ("See all services"/"See our process") to the same brand accent as the eyebrow label, instead of the Figma-literal cyan. --- assets/css/animations.css | 149 ++++++++++++++++++ parts/services-mega-menu.html | 98 +++++++----- src/scss/animations/_menu-motion.scss | 113 +++++++++++++ .../blocks/groups/mega-menu-item-service.json | 2 +- 4 files changed, 324 insertions(+), 38 deletions(-) diff --git a/assets/css/animations.css b/assets/css/animations.css index 90f4b66..ac8f2d3 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1182,3 +1182,152 @@ transition: none; } } +/* + * Hover/focus states for .is-style-mega-menu-item-service (styles/blocks/groups/mega-menu-item-service.json), + * matching the Default menu's row-background convention. Same WordPress core limitation as above applies. + */ +.is-style-mega-menu-item-service { + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-mega-menu-item-service:hover, .is-style-mega-menu-item-service:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); +} + +@media (prefers-reduced-motion: reduce) { + .is-style-mega-menu-item-service { + transition: none; + } +} +/* + * Services mega menu panel structural fixes. + */ +.is-style-mega-menu-panel-services { + /* + * "Hug" the panel to its own content instead of stretching to whatever width the Ollie dropdown + * container's ancestor happens to give it. Without this, the panel's background/border render at + * the ancestor's (narrower) width while the flex-wrapped columns inside still need their full + * natural width, so content visibly overflows past the card edge. There's no theme.json dimensions + * property for width/fit-content, so this has to be CSS. + */ + width: fit-content; + max-width: 100vw; + /* + * core/columns divides its row into equal shares with no per-column minimum, so at narrower + * widths the phase labels (Discover, Create, etc.) run out of room and wrap mid-word. Neither a + * column minimum-width nor a guaranteed-no-wrap label is expressible via theme.json (dimensions + * only supports aspectRatio/minHeight, and typography has no whiteSpace property), so both are + * set here: each phase column gets a floor matching Figma's 120px minimum, wrapping onto a new + * row via flex-wrap once six no longer fit, instead of shrinking below that floor; the label text + * itself is also pinned to one line as a belt-and-suspenders guarantee. + */ +} +.is-style-mega-menu-panel-services .wp-block-columns { + flex-wrap: wrap; +} +.is-style-mega-menu-panel-services .wp-block-column { + flex: 1 1 120px; + min-width: 120px; +} + +.is-style-mega-menu-panel-services .wp-block-column > .wp-block-group p { + white-space: nowrap; +} + +/* + * Per-phase hover colour for the Services mega menu: the phase-header group (dot + label + arrow) + * and every service row underneath it hover in that same column's phase colour. Each phase always + * occupies the same column position (Discover 1st ... Evolve 6th), so this is targeted structurally + * via :nth-child rather than adding a per-instance custom property to the markup — the same custom + * hover mechanic that broke block validation earlier in this file. var(--wp--custom--color--phase--*) + * already resolves to the "strong" preset in light mode and the plain preset in dark mode, so one + * token covers both the header hover and the "Phase X strong" row hover in a single declaration. + */ +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(1) > .wp-block-group:first-child { + border-radius: var(--wp--preset--border-radius--300); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(1) > .wp-block-group:first-child:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(1) > .wp-block-group:first-child:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--discover) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(1) .is-style-mega-menu-item-service:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(1) .is-style-mega-menu-item-service:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--discover) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(1) .is-style-mega-menu-item-service:hover a, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(1) .is-style-mega-menu-item-service:focus-within a { + color: var(--wp--custom--color--phase--discover); +} + +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(2) > .wp-block-group:first-child { + border-radius: var(--wp--preset--border-radius--300); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(2) > .wp-block-group:first-child:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(2) > .wp-block-group:first-child:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--create) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(2) .is-style-mega-menu-item-service:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(2) .is-style-mega-menu-item-service:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--create) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(2) .is-style-mega-menu-item-service:hover a, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(2) .is-style-mega-menu-item-service:focus-within a { + color: var(--wp--custom--color--phase--create); +} + +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(3) > .wp-block-group:first-child { + border-radius: var(--wp--preset--border-radius--300); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(3) > .wp-block-group:first-child:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(3) > .wp-block-group:first-child:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--build) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(3) .is-style-mega-menu-item-service:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(3) .is-style-mega-menu-item-service:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--build) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(3) .is-style-mega-menu-item-service:hover a, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(3) .is-style-mega-menu-item-service:focus-within a { + color: var(--wp--custom--color--phase--build); +} + +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(4) > .wp-block-group:first-child { + border-radius: var(--wp--preset--border-radius--300); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(4) > .wp-block-group:first-child:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(4) > .wp-block-group:first-child:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--launch) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(4) .is-style-mega-menu-item-service:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(4) .is-style-mega-menu-item-service:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--launch) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(4) .is-style-mega-menu-item-service:hover a, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(4) .is-style-mega-menu-item-service:focus-within a { + color: var(--wp--custom--color--phase--launch); +} + +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(5) > .wp-block-group:first-child { + border-radius: var(--wp--preset--border-radius--300); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(5) > .wp-block-group:first-child:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(5) > .wp-block-group:first-child:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--grow) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(5) .is-style-mega-menu-item-service:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(5) .is-style-mega-menu-item-service:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--grow) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(5) .is-style-mega-menu-item-service:hover a, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(5) .is-style-mega-menu-item-service:focus-within a { + color: var(--wp--custom--color--phase--grow); +} + +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(6) > .wp-block-group:first-child { + border-radius: var(--wp--preset--border-radius--300); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(6) > .wp-block-group:first-child:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(6) > .wp-block-group:first-child:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--evolve) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(6) .is-style-mega-menu-item-service:hover, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(6) .is-style-mega-menu-item-service:focus-within { + background-color: color-mix(in srgb, var(--wp--custom--color--phase--evolve) 15%, transparent); +} +.is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(6) .is-style-mega-menu-item-service:hover a, .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(6) .is-style-mega-menu-item-service:focus-within a { + color: var(--wp--custom--color--phase--evolve); +} + +@media (prefers-reduced-motion: reduce) { + .is-style-mega-menu-panel-services .wp-block-column > .wp-block-group:first-child { + transition: none; + } +} diff --git a/parts/services-mega-menu.html b/parts/services-mega-menu.html index 312704d..432d17e 100644 --- a/parts/services-mega-menu.html +++ b/parts/services-mega-menu.html @@ -1,12 +1,12 @@ - -
-
-
-

Services by lifecycle phase

+ +
+
+
+

Services by lifecycle phase

-

Services that span the WordPress lifecycle — grouped by phase.

+

Services that span the WordPress lifecycle grouped by phase.

@@ -17,15 +17,18 @@

Services that span the WordPress
-
-
+
+
+
+ - +

Discover

- -
+ +
+
@@ -34,105 +37,126 @@

Services that span the WordPress -
-
+
+
+
+ - +

Create

- -
+ +
+

Content

+

Design

-
-
+
+
+
+ - +

Build

- -
+ +
+

Development

+

Migrations

-
-
+
+
+
+ - +

Launch

- -
+ +
+

Hosting

+

Performance

+

Security

+

Training

-
-
+
+
+
+ - +

Grow

- -
+ +
+

Support

+

SEO

+

Accessibility

+

Email marketing

-
-
+
+
+
+ - +

Evolve

- -
+
@@ -142,7 +166,7 @@

Services that span the WordPress -
+

Each service is colour-coded by its lifecycle phase

diff --git a/src/scss/animations/_menu-motion.scss b/src/scss/animations/_menu-motion.scss index d74b383..ea3b600 100644 --- a/src/scss/animations/_menu-motion.scss +++ b/src/scss/animations/_menu-motion.scss @@ -1,3 +1,4 @@ +@use "sass:list"; @use "../abstracts/mixins/motion"; /********** Mega Menu Item **********/ @@ -35,3 +36,115 @@ transition: none; } } + +/* + * Hover/focus states for .is-style-mega-menu-item-service (styles/blocks/groups/mega-menu-item-service.json), + * matching the Default menu's row-background convention. Same WordPress core limitation as above applies. + */ +.is-style-mega-menu-item-service { + transition: background-color var(--wp--custom--animation--duration--base) + var(--wp--custom--animation--easing--standard); + + &:hover, + &:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); + } +} + +@include motion.reduced-motion { + .is-style-mega-menu-item-service { + transition: none; + } +} + +/* + * Services mega menu panel structural fixes. + */ +.is-style-mega-menu-panel-services { + /* + * "Hug" the panel to its own content instead of stretching to whatever width the Ollie dropdown + * container's ancestor happens to give it. Without this, the panel's background/border render at + * the ancestor's (narrower) width while the flex-wrapped columns inside still need their full + * natural width, so content visibly overflows past the card edge. There's no theme.json dimensions + * property for width/fit-content, so this has to be CSS. + */ + width: fit-content; + max-width: 100vw; + + /* + * core/columns divides its row into equal shares with no per-column minimum, so at narrower + * widths the phase labels (Discover, Create, etc.) run out of room and wrap mid-word. Neither a + * column minimum-width nor a guaranteed-no-wrap label is expressible via theme.json (dimensions + * only supports aspectRatio/minHeight, and typography has no whiteSpace property), so both are + * set here: each phase column gets a floor matching Figma's 120px minimum, wrapping onto a new + * row via flex-wrap once six no longer fit, instead of shrinking below that floor; the label text + * itself is also pinned to one line as a belt-and-suspenders guarantee. + */ + .wp-block-columns { + flex-wrap: wrap; + } + + .wp-block-column { + flex: 1 1 120px; + min-width: 120px; + } +} + +.is-style-mega-menu-panel-services .wp-block-column > .wp-block-group p { + white-space: nowrap; +} + +/* + * Per-phase hover colour for the Services mega menu: the phase-header group (dot + label + arrow) + * and every service row underneath it hover in that same column's phase colour. Each phase always + * occupies the same column position (Discover 1st ... Evolve 6th), so this is targeted structurally + * via :nth-child rather than adding a per-instance custom property to the markup — the same custom + * hover mechanic that broke block validation earlier in this file. var(--wp--custom--color--phase--*) + * already resolves to the "strong" preset in light mode and the plain preset in dark mode, so one + * token covers both the header hover and the "Phase X strong" row hover in a single declaration. + */ +$ls-mega-menu-phases: 1 discover, 2 create, 3 build, 4 launch, 5 grow, 6 evolve; + +@each $pair in $ls-mega-menu-phases { + $index: list.nth($pair, 1); + $phase: list.nth($pair, 2); + + .is-style-mega-menu-panel-services .wp-block-columns > .wp-block-column:nth-child(#{$index}) { + > .wp-block-group:first-child { + border-radius: var(--wp--preset--border-radius--300); + transition: background-color var(--wp--custom--animation--duration--base) + var(--wp--custom--animation--easing--standard); + + &:hover, + &:focus-within { + background-color: color-mix( + in srgb, + var(--wp--custom--color--phase--#{$phase}) 15%, + transparent + ); + } + } + + .is-style-mega-menu-item-service { + &:hover, + &:focus-within { + background-color: color-mix( + in srgb, + var(--wp--custom--color--phase--#{$phase}) 15%, + transparent + ); + } + + &:hover a, + &:focus-within a { + color: var(--wp--custom--color--phase--#{$phase}); + } + } + } +} + +@include motion.reduced-motion { + .is-style-mega-menu-panel-services .wp-block-column > .wp-block-group:first-child { + transition: none; + } +} diff --git a/styles/blocks/groups/mega-menu-item-service.json b/styles/blocks/groups/mega-menu-item-service.json index 21fa94c..e3a273d 100644 --- a/styles/blocks/groups/mega-menu-item-service.json +++ b/styles/blocks/groups/mega-menu-item-service.json @@ -6,7 +6,7 @@ "blockTypes": [ "core/paragraph" ], - "description": "Menu-item row for the Service mega menu's phase columns. Structural padding/radius only for now — hover treatment to follow once the Service-specific interaction reference is provided.", + "description": "Menu-item row for the Service mega menu's phase columns. Structural padding/radius live here; the row background-on-hover/focus treatment (matching the Default menu's convention) is authored in src/scss/animations/_menu-motion.scss, since WordPress doesn't generate :hover/:focus CSS for arbitrary block style variations.", "styles": { "border": { "radius": "var:preset|border-radius|300" From 6326bb792242708eae6883b1729a936e1dfac964 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Thu, 23 Jul 2026 20:38:29 +0200 Subject: [PATCH 4/9] Rebuild site footer to match Figma design (LS-1618) Rewrites `patterns/footer.php` (previously an LS-1226 placeholder stub) into the full approved design: notes panel, company summary with proof points, 5-column nav grid, and legal/social bottom bar. `parts/footer.html` already injected this pattern, so no template-part changes needed. - Real Phosphor icons throughout, including social logos (not Figma's literal "in/gh/fb" text placeholders). - New style files hold structural JSON only; hover states and decorative background gradients live in a new `_footer-motion.scss` (WordPress doesn't generate `:hover` CSS for block style variations, and there's no width/fit-content property in theme.json). - New tokens `status.success` and `shadow.footer.notes`, both with light/dark parity, reusing existing presets rather than inventing colours. - Fixed the "LightSpeed notes" badge stretching full-width instead of hugging its text. --- assets/css/animations.css | 105 ++++++ patterns/footer.php | 312 +++++++++++++++++- src/scss/animations.scss | 3 +- src/scss/animations/_footer-motion.scss | 137 ++++++++ styles/blocks/groups/footer-proof-card.json | 31 ++ styles/blocks/groups/footer-social-icon.json | 33 ++ styles/blocks/paragraphs/footer-nav-link.json | 24 ++ styles/dark.json | 6 + styles/presets/shadows.json | 5 + .../sections/footer/footer-notes-panel.json | 32 ++ theme.json | 6 + 11 files changed, 682 insertions(+), 12 deletions(-) create mode 100644 src/scss/animations/_footer-motion.scss create mode 100644 styles/blocks/groups/footer-proof-card.json create mode 100644 styles/blocks/groups/footer-social-icon.json create mode 100644 styles/blocks/paragraphs/footer-nav-link.json create mode 100644 styles/sections/footer/footer-notes-panel.json diff --git a/assets/css/animations.css b/assets/css/animations.css index ac8f2d3..1efc82f 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1331,3 +1331,108 @@ transition: none; } } +/********** Site Footer **********/ +/* + * "LightSpeed notes" badge: a flex group with no explicit width constraint defaults to filling + * its row, so the border/background stretch across the notes panel instead of hugging the label + * text. There's no theme.json dimensions property for width/fit-content, so this has to be CSS — + * same fix as the Services mega menu panel. + */ +.footer-notes-badge { + width: fit-content; +} + +/* + * Decorative background: top edge glow bar + ambient radial-gradient glow behind the footer. + * Authored as layered CSS gradients rather than embedding Figma's raw multi-layer SVG data-URIs, + * which would be extremely brittle to maintain. Every colour route through the same semantic + * custom properties used everywhere else in the theme (phase.create, effect.spotlight), so the + * glow automatically tones itself down under the light global-styles variation instead of needing + * a separate light/dark branch — there's no runtime class/attribute toggle in this theme, dark mode + * is a full alternate theme.json variation, so re-resolving the same custom properties is enough. + */ +.site-footer { + position: relative; + overflow: clip; + background: radial-gradient(ellipse 480px 420px at 12% 0%, color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 16%, transparent) 0%, transparent 60%), radial-gradient(ellipse 460px 400px at 92% 22%, color-mix(in srgb, var(--wp--custom--color--phase--create) 12%, transparent) 0%, transparent 60%), var(--wp--custom--color--surface--canvas); +} +.site-footer::before { + content: ""; + position: absolute; + inset-block-start: 0; + inset-inline: 0; + block-size: 4px; + z-index: var(--wp--custom--z-index--content); + background-image: linear-gradient(90deg, transparent 0%, color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 24%, var(--wp--custom--color--phase--create) 50%, color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 76%, transparent 100%); + box-shadow: 0 0 16px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 70%, transparent), 0 8px 32px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 30%, transparent); +} + +/* + * Blurred accent blob behind the nav-column grid, echoing Figma's "Mask Group" glow. Purely + * decorative and non-interactive, so it's safe to skip under reduced motion (nothing here + * animates — it's a static blur, included in the reduced-motion block only for parity/consistency). + */ +.site-footer .wp-block-columns:nth-of-type(2) { + position: relative; +} +.site-footer .wp-block-columns:nth-of-type(2)::after { + content: ""; + position: absolute; + inset-block-start: 10%; + inset-inline-end: 8%; + inline-size: min(38vw, 520px); + block-size: min(30vw, 360px); + background: radial-gradient(ellipse closest-side, color-mix(in srgb, var(--wp--custom--color--phase--create) 14%, transparent) 0%, transparent 70%); + filter: blur(40px); + pointer-events: none; + z-index: 0; +} +.site-footer .wp-block-columns:nth-of-type(2) > * { + position: relative; + z-index: 1; +} + +/* + * Footer nav links (is-style-footer-nav-link, styles/blocks/paragraphs/footer-nav-link.json): + * underline reveals on hover/focus by animating decoration-colour from transparent, rather than + * toggling text-decoration-line itself. Matches the approved reference screenshot, and matches the + * pattern already used for is-style-link-underline-accent elsewhere in this file family. + */ +.is-style-footer-nav-link a { + color: inherit; + text-decoration-line: underline; + text-decoration-color: transparent; + text-underline-offset: 3px; + transition: color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), text-decoration-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} + +.is-style-footer-nav-link a:hover, +.is-style-footer-nav-link a:focus-visible { + color: var(--wp--custom--color--phase--create); + text-decoration-color: var(--wp--custom--color--phase--create); +} + +/* + * Proof-point cards and social icon circles (styles/blocks/groups/footer-proof-card.json, + * footer-social-icon.json): subtle background lift on hover/focus, matching the rest of the + * theme's card-hover conventions. No hover state shown in the Figma reference — added per + * explicit approval as a consistency default. + */ +.is-style-footer-proof-card, +.is-style-footer-social-icon { + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-footer-proof-card:hover, .is-style-footer-proof-card:focus-within, +.is-style-footer-social-icon:hover, +.is-style-footer-social-icon:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); +} + +@media (prefers-reduced-motion: reduce) { + .is-style-footer-nav-link a, + .is-style-footer-proof-card, + .is-style-footer-social-icon { + transition: none; + } +} diff --git a/patterns/footer.php b/patterns/footer.php index fef0ee2..4df1be8 100644 --- a/patterns/footer.php +++ b/patterns/footer.php @@ -4,19 +4,309 @@ * Slug: ls-theme/footer * Categories: footer * Block Types: core/template-part/footer - * Description: Footer columns with logo, title, tagline and links. + * Description: Full site footer — editorial notes panel, company summary with proof points, 5-column link grid, and legal/social bottom bar. */ ?> - -
- -
- -

©

- -
- -
+ + diff --git a/src/scss/animations.scss b/src/scss/animations.scss index ec10eac..c97587e 100644 --- a/src/scss/animations.scss +++ b/src/scss/animations.scss @@ -5,4 +5,5 @@ @use "animations/card-motion"; @use "animations/button-motion"; @use "animations/faq-motion"; -@use "animations/menu-motion"; \ No newline at end of file +@use "animations/menu-motion"; +@use "animations/footer-motion"; \ No newline at end of file diff --git a/src/scss/animations/_footer-motion.scss b/src/scss/animations/_footer-motion.scss new file mode 100644 index 0000000..93374b5 --- /dev/null +++ b/src/scss/animations/_footer-motion.scss @@ -0,0 +1,137 @@ +@use "../abstracts/mixins/motion"; + +/********** Site Footer **********/ + +/* + * "LightSpeed notes" badge: a flex group with no explicit width constraint defaults to filling + * its row, so the border/background stretch across the notes panel instead of hugging the label + * text. There's no theme.json dimensions property for width/fit-content, so this has to be CSS — + * same fix as the Services mega menu panel. + */ +.footer-notes-badge { + width: fit-content; +} + +/* + * Decorative background: top edge glow bar + ambient radial-gradient glow behind the footer. + * Authored as layered CSS gradients rather than embedding Figma's raw multi-layer SVG data-URIs, + * which would be extremely brittle to maintain. Every colour route through the same semantic + * custom properties used everywhere else in the theme (phase.create, effect.spotlight), so the + * glow automatically tones itself down under the light global-styles variation instead of needing + * a separate light/dark branch — there's no runtime class/attribute toggle in this theme, dark mode + * is a full alternate theme.json variation, so re-resolving the same custom properties is enough. + */ +.site-footer { + position: relative; + overflow: clip; + background: + radial-gradient( + ellipse 480px 420px at 12% 0%, + color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 16%, transparent) 0%, + transparent 60% + ), + radial-gradient( + ellipse 460px 400px at 92% 22%, + color-mix(in srgb, var(--wp--custom--color--phase--create) 12%, transparent) 0%, + transparent 60% + ), + var(--wp--custom--color--surface--canvas); + + &::before { + content: ""; + position: absolute; + inset-block-start: 0; + inset-inline: 0; + block-size: 4px; + z-index: var(--wp--custom--z-index--content); + background-image: linear-gradient( + 90deg, + transparent 0%, + color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 24%, + var(--wp--custom--color--phase--create) 50%, + color-mix(in srgb, var(--wp--custom--color--phase--create) 60%, transparent) 76%, + transparent 100% + ); + box-shadow: + 0 0 16px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 70%, transparent), + 0 8px 32px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 30%, transparent); + } +} + +/* + * Blurred accent blob behind the nav-column grid, echoing Figma's "Mask Group" glow. Purely + * decorative and non-interactive, so it's safe to skip under reduced motion (nothing here + * animates — it's a static blur, included in the reduced-motion block only for parity/consistency). + */ +.site-footer .wp-block-columns:nth-of-type(2) { + position: relative; + + &::after { + content: ""; + position: absolute; + inset-block-start: 10%; + inset-inline-end: 8%; + inline-size: min(38vw, 520px); + block-size: min(30vw, 360px); + background: radial-gradient( + ellipse closest-side, + color-mix(in srgb, var(--wp--custom--color--phase--create) 14%, transparent) 0%, + transparent 70% + ); + filter: blur(40px); + pointer-events: none; + z-index: 0; + } + + > * { + position: relative; + z-index: 1; + } +} + +/* + * Footer nav links (is-style-footer-nav-link, styles/blocks/paragraphs/footer-nav-link.json): + * underline reveals on hover/focus by animating decoration-colour from transparent, rather than + * toggling text-decoration-line itself. Matches the approved reference screenshot, and matches the + * pattern already used for is-style-link-underline-accent elsewhere in this file family. + */ +.is-style-footer-nav-link a { + color: inherit; + text-decoration-line: underline; + text-decoration-color: transparent; + text-underline-offset: 3px; + transition: color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), + text-decoration-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} + +.is-style-footer-nav-link a:hover, +.is-style-footer-nav-link a:focus-visible { + color: var(--wp--custom--color--phase--create); + text-decoration-color: var(--wp--custom--color--phase--create); +} + +/* + * Proof-point cards and social icon circles (styles/blocks/groups/footer-proof-card.json, + * footer-social-icon.json): subtle background lift on hover/focus, matching the rest of the + * theme's card-hover conventions. No hover state shown in the Figma reference — added per + * explicit approval as a consistency default. + */ +.is-style-footer-proof-card, +.is-style-footer-social-icon { + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), + border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); + + &:hover, + &:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); + } +} + +@include motion.reduced-motion { + .is-style-footer-nav-link a, + .is-style-footer-proof-card, + .is-style-footer-social-icon { + transition: none; + } +} diff --git a/styles/blocks/groups/footer-proof-card.json b/styles/blocks/groups/footer-proof-card.json new file mode 100644 index 0000000..09a967b --- /dev/null +++ b/styles/blocks/groups/footer-proof-card.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Footer Proof Card", + "slug": "footer-proof-card", + "blockTypes": [ + "core/group" + ], + "description": "Compact glass stat card for the footer's proof points (15+/AA/100%). Structural styling only — the hover background lift is authored in src/scss/animations/_footer-motion.scss (WordPress doesn't generate :hover CSS for arbitrary block style variations).", + "styles": { + "color": { + "background": "var:custom|color|surface|card", + "text": "var:custom|color|text|muted" + }, + "border": { + "color": "var:custom|color|border|card", + "radius": "var:preset|border-radius|300", + "style": "solid", + "width": "1px" + }, + "spacing": { + "blockGap": "var:preset|spacing|5", + "padding": { + "top": "var:preset|spacing|20", + "right": "var:preset|spacing|20", + "bottom": "var:preset|spacing|20", + "left": "var:preset|spacing|20" + } + } + } +} diff --git a/styles/blocks/groups/footer-social-icon.json b/styles/blocks/groups/footer-social-icon.json new file mode 100644 index 0000000..6de641a --- /dev/null +++ b/styles/blocks/groups/footer-social-icon.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Footer Social Icon", + "slug": "footer-social-icon", + "blockTypes": [ + "core/group" + ], + "description": "Circular social icon button for the footer. Structural styling only — the hover background lift is authored in src/scss/animations/_footer-motion.scss (WordPress doesn't generate :hover CSS for arbitrary block style variations).", + "styles": { + "color": { + "background": "var:custom|color|surface|card", + "text": "var:custom|color|text|default" + }, + "border": { + "color": "var:custom|color|border|card", + "radius": "var:preset|border-radius|500", + "style": "solid", + "width": "1px" + }, + "dimensions": { + "minHeight": "44px" + }, + "spacing": { + "padding": { + "top": "var:preset|spacing|10", + "right": "var:preset|spacing|10", + "bottom": "var:preset|spacing|10", + "left": "var:preset|spacing|10" + } + } + } +} diff --git a/styles/blocks/paragraphs/footer-nav-link.json b/styles/blocks/paragraphs/footer-nav-link.json new file mode 100644 index 0000000..53cf830 --- /dev/null +++ b/styles/blocks/paragraphs/footer-nav-link.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Footer Nav Link", + "slug": "footer-nav-link", + "blockTypes": [ + "core/paragraph" + ], + "description": "Footer nav column link row. Static colour/spacing only — the underline-reveal-on-hover treatment lives in src/scss/animations/_footer-motion.scss, since WordPress doesn't generate :hover CSS for arbitrary block style variations.", + "styles": { + "color": { + "text": "var:custom|color|text|muted" + }, + "spacing": { + "margin": { + "top": "0", + "bottom": "0" + } + }, + "typography": { + "textDecoration": "none" + } + } +} diff --git a/styles/dark.json b/styles/dark.json index c4e7941..5a1caf5 100644 --- a/styles/dark.json +++ b/styles/dark.json @@ -52,6 +52,9 @@ "grow": "var:preset|color|phase-five", "evolve": "var:preset|color|phase-six" }, + "status": { + "success": "var:preset|color|success-foreground" + }, "text-shadow": { "contrast": "var:preset|color|base", "base": "var:preset|color|contrast" @@ -112,6 +115,9 @@ }, "popover": { "menu": "0 32px 80px 0 color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 18%, transparent), var(--wp--custom--shadow--elevation--200)" + }, + "footer": { + "notes": "0 0 0 1px color-mix(in srgb, var(--wp--custom--color--phase--create) 24%, transparent), 0 0 34px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 18%, transparent)" } } } diff --git a/styles/presets/shadows.json b/styles/presets/shadows.json index 34558be..13c2daf 100644 --- a/styles/presets/shadows.json +++ b/styles/presets/shadows.json @@ -39,6 +39,11 @@ "name": "Popover", "slug": "popover", "shadow": "var(--wp--custom--shadow--popover--menu)" + }, + { + "name": "Footer Notes Glow", + "slug": "footer-notes", + "shadow": "var(--wp--custom--shadow--footer--notes)" } ] } diff --git a/styles/sections/footer/footer-notes-panel.json b/styles/sections/footer/footer-notes-panel.json new file mode 100644 index 0000000..125a54f --- /dev/null +++ b/styles/sections/footer/footer-notes-panel.json @@ -0,0 +1,32 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Footer Notes Panel", + "slug": "footer-notes-panel", + "blockTypes": [ + "core/group" + ], + "description": "Cyan-tinted glass panel for the footer's editorial teaser (badge + heading + paragraph). Static — no hover state, unlike the interactive is-style-glass-card.", + "styles": { + "color": { + "background": "color-mix(in srgb, var(--wp--custom--color--phase--create) 6%, var(--wp--custom--color--surface--card))", + "text": "var:custom|color|text|muted" + }, + "border": { + "color": "color-mix(in srgb, var(--wp--custom--color--phase--create) 24%, transparent)", + "radius": "var:preset|border-radius|400", + "style": "solid", + "width": "1px" + }, + "shadow": "var:preset|shadow|footer-notes", + "spacing": { + "blockGap": "var:preset|spacing|10", + "padding": { + "top": "var:preset|spacing|40", + "right": "var:preset|spacing|30", + "bottom": "var:preset|spacing|40", + "left": "var:preset|spacing|30" + } + } + } +} diff --git a/theme.json b/theme.json index 4a1e565..47b2d30 100644 --- a/theme.json +++ b/theme.json @@ -522,6 +522,9 @@ "grow": "var:preset|color|phase-five-strong", "evolve": "var:preset|color|phase-six-strong" }, + "status": { + "success": "var:preset|color|success-foreground" + }, "text-shadow": { "contrast": "var:preset|color|contrast", "base": "var:preset|color|base" @@ -615,6 +618,9 @@ }, "popover": { "menu": "0 32px 80px 0 color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 18%, transparent), var(--wp--custom--shadow--elevation--200)" + }, + "footer": { + "notes": "0 0 0 1px color-mix(in srgb, var(--wp--custom--color--phase--create) 14%, transparent), 0 0 24px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 10%, transparent)" } }, "animation": { From 0fac3f6a64c5843c8ffd6cf81ba3ac00d402b4a1 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Thu, 23 Jul 2026 21:50:34 +0200 Subject: [PATCH 5/9] Rebuild site header to match Figma design (LS-1618) Rewrites `patterns/header.php` (previously an LS-1226 placeholder stub): logo, nav, search, CTA. Nav uses plain `navigation-link` items so Ollie's Dropdown Menu blocks can be attached manually, same as the other menus. - Adds a fully functional light/dark toggle: new `theme-utils.mjs generate-theme-toggle` command auto-generates `assets/css/theme-toggle.css` from `theme.json` + `styles/dark.json`, plus JS for click/persistence. - Search uses native `core/search`, with a small JS assist for expand-on-click (core has no built-in behavior for that). - New reusable button/icon styles and hover states in `_header-motion.scss`. - Fixed a gradient-in-wrong-property bug and the header being clamped to content-width instead of full-width. --- assets/css/animations.css | 93 +++++++++++ assets/css/theme-toggle.css | 146 ++++++++++++++++++ assets/js/header-search.js | 49 ++++++ assets/js/theme-toggle.js | 46 ++++++ functions.php | 6 + inc/header.php | 26 ++++ inc/theme-toggle.php | 68 ++++++++ package.json | 3 +- patterns/header.php | 40 +++-- src/scss/animations.scss | 3 +- src/scss/animations/_header-motion.scss | 113 ++++++++++++++ .../blocks/buttons/button-cta-gradient.json | 35 +++++ styles/blocks/groups/header-icon-button.json | 33 ++++ styles/presets/shadows.json | 5 + theme-utils.mjs | 109 +++++++++++++ 15 files changed, 763 insertions(+), 12 deletions(-) create mode 100644 assets/css/theme-toggle.css create mode 100644 assets/js/header-search.js create mode 100644 assets/js/theme-toggle.js create mode 100644 inc/header.php create mode 100644 inc/theme-toggle.php create mode 100644 src/scss/animations/_header-motion.scss create mode 100644 styles/blocks/buttons/button-cta-gradient.json create mode 100644 styles/blocks/groups/header-icon-button.json diff --git a/assets/css/animations.css b/assets/css/animations.css index 1efc82f..a49517a 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1436,3 +1436,96 @@ transition: none; } } +/********** Site Header **********/ +/* + * Header icon buttons (search, light/dark toggle) and the gradient CTA: subtle hover treatment, + * matching the rest of the theme's interactive-control conventions. WordPress doesn't generate + * :hover CSS for arbitrary block style variations, so this lives here rather than in JSON. + */ +.is-style-header-icon-button { + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-header-icon-button:hover, .is-style-header-icon-button:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); +} + +.is-style-button-cta-gradient .wp-block-button__link { + transition: transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.is-style-button-cta-gradient .wp-block-button__link:hover, .is-style-button-cta-gradient .wp-block-button__link:focus-visible { + transform: translateY(-1px); + box-shadow: var(--wp--custom--shadow--interactive--accent), var(--wp--custom--shadow--elevation--300); +} + +/* + * Header search (core/search, buttonPosition: button-only): the input is collapsed to 0 width by + * default and expands via the .site-header__search--expanded class. core/search's "button-only" + * position only changes layout (it hides the input and renders just a submit button) — there's no + * built-in click-to-expand behaviour in core, and a pure-CSS :focus-within trick can't reveal a + * zero-width input either (nothing to click into until it's already visible). The class is toggled + * by assets/js/header-search.js, which intercepts the button's first click to expand + focus the + * field instead of submitting immediately. + */ +.site-header__search .wp-block-search__button { + display: flex; + align-items: center; + justify-content: center; + min-inline-size: 44px; + min-block-size: 44px; + padding: var(--wp--preset--spacing--10); + border: 1px solid var(--wp--custom--color--border--card); + border-radius: var(--wp--preset--border-radius--500); + background-color: color-mix(in srgb, var(--wp--custom--color--surface--highlight) 4%, transparent); + color: var(--wp--custom--color--text--default); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); +} +.site-header__search .wp-block-search__button:hover, +.site-header__search .wp-block-search__button:focus-visible { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); +} +.site-header__search .wp-block-search__input { + inline-size: 0; + min-inline-size: 0; + padding-inline: 0; + border-width: 0; + opacity: 0; + transition: inline-size var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard), opacity var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), padding-inline var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard); +} +.site-header__search.site-header__search--expanded .wp-block-search__input { + inline-size: 12rem; + padding-inline: var(--wp--preset--spacing--10); + border-width: 1px; + opacity: 1; +} + +/* + * Light/dark toggle: sun/moon icon swap based on the current mode. Default (no [data-theme] + * override present) is the site's light-mode default, so the moon shows first — clicking it + * offers a switch *to* dark. Once [data-theme="dark"] is set (assets/js/theme-toggle.js), the + * icons swap to offer a switch back to light. + */ +.ls-theme-toggle__icon--sun { + display: none; +} + +.ls-theme-toggle__icon--moon { + display: block; +} + +:root[data-theme=dark] .ls-theme-toggle__icon--sun { + display: block; +} +:root[data-theme=dark] .ls-theme-toggle__icon--moon { + display: none; +} + +@media (prefers-reduced-motion: reduce) { + .is-style-header-icon-button, + .is-style-button-cta-gradient .wp-block-button__link, + .site-header__search .wp-block-search__button, + .site-header__search .wp-block-search__input { + transition: none; + } +} diff --git a/assets/css/theme-toggle.css b/assets/css/theme-toggle.css new file mode 100644 index 0000000..77764b0 --- /dev/null +++ b/assets/css/theme-toggle.css @@ -0,0 +1,146 @@ +/* Auto-generated from theme.json + styles/dark.json by `node theme-utils.mjs generate-theme-toggle`. */ +/* Do not edit this file by hand — edit the source JSON and regenerate. */ + +:root[data-theme="light"] { + --wp--custom--color--text--default: var(--wp--preset--color--contrast); + --wp--custom--color--text--inverse: var(--wp--preset--color--base); + --wp--custom--color--text--muted: var(--wp--preset--color--neutral-700); + --wp--custom--color--text--subtle: var(--wp--preset--color--neutral-600); + --wp--custom--color--text--brand: var(--wp--preset--color--brand-500); + --wp--custom--color--text--brand-strong: var(--wp--preset--color--brand-800); + --wp--custom--color--text--on-dark: var(--wp--preset--color--base); + --wp--custom--color--text--on-dark-muted: var(--wp--preset--color--neutral-400); + --wp--custom--color--surface--canvas: var(--wp--preset--color--base); + --wp--custom--color--surface--card: var(--wp--preset--color--neutral-100); + --wp--custom--color--surface--card-raised: var(--wp--preset--color--neutral-200); + --wp--custom--color--surface--highlight: var(--wp--preset--color--base); + --wp--custom--color--surface--brand-light: var(--wp--preset--color--brand-100); + --wp--custom--color--surface--brand: var(--wp--preset--color--brand-500); + --wp--custom--color--surface--cta-light: var(--wp--preset--color--cta-100); + --wp--custom--color--surface--cta: var(--wp--preset--color--cta-500); + --wp--custom--color--surface--accent-light: var(--wp--preset--color--accent-100); + --wp--custom--color--surface--accent-two-light: var(--wp--preset--color--accent-two-100); + --wp--custom--color--surface--accent-three-light: var(--wp--preset--color--accent-three-100); + --wp--custom--color--surface--band-start: var(--wp--preset--color--surface-700); + --wp--custom--color--surface--band-end: var(--wp--preset--color--surface-300); + --wp--custom--color--surface--on-dark-card: var(--wp--preset--color--surface-200); + --wp--custom--color--border--card: var(--wp--preset--color--neutral-300); + --wp--custom--color--icon--background: var(--wp--preset--color--brand-500); + --wp--custom--color--icon--color: var(--wp--preset--color--base); + --wp--custom--color--card--solutions--brand: var(--wp--preset--color--brand-500); + --wp--custom--color--card--solutions--accent: var(--wp--preset--color--accent-500); + --wp--custom--color--phase--discover: var(--wp--preset--color--phase-one-strong); + --wp--custom--color--phase--create: var(--wp--preset--color--phase-two-strong); + --wp--custom--color--phase--build: var(--wp--preset--color--phase-three-strong); + --wp--custom--color--phase--launch: var(--wp--preset--color--phase-four-strong); + --wp--custom--color--phase--grow: var(--wp--preset--color--phase-five-strong); + --wp--custom--color--phase--evolve: var(--wp--preset--color--phase-six-strong); + --wp--custom--color--status--success: var(--wp--preset--color--success-foreground); + --wp--custom--color--text-shadow--contrast: var(--wp--preset--color--contrast); + --wp--custom--color--text-shadow--base: var(--wp--preset--color--base); + --wp--custom--color--link--default: var(--wp--preset--color--contrast); + --wp--custom--color--link--decoration-start: var(--wp--preset--color--brand-600); + --wp--custom--color--link--decoration-end: var(--wp--preset--color--accent-700); + --wp--custom--color--link--accent: var(--wp--preset--color--brand-500); + --wp--custom--color--link--accent-hover: var(--wp--preset--color--brand-700); + --wp--custom--color--button--fill--background: var(--wp--preset--color--brand-500); + --wp--custom--color--button--fill--text: var(--wp--preset--color--base); + --wp--custom--color--button--fill--border: var(--wp--preset--color--brand-500); + --wp--custom--color--button--fill--background-hover: var(--wp--preset--color--brand-500); + --wp--custom--color--button--fill--text-hover: var(--wp--preset--color--contrast); + --wp--custom--color--button--fill--border-hover: var(--wp--preset--color--brand-500); + --wp--custom--color--button--outline--background: var(--wp--preset--color--surface-400); + --wp--custom--color--button--outline--text: var(--wp--preset--color--brand-500); + --wp--custom--color--button--outline--border: var(--wp--preset--color--brand-500); + --wp--custom--color--button--outline--background-hover: var(--wp--preset--color--brand-500); + --wp--custom--color--button--outline--text-hover: var(--wp--preset--color--base); + --wp--custom--color--focus--ring: var(--wp--preset--color--contrast); + --wp--custom--color--effect--spotlight--primary: var(--wp--preset--color--brand-500); + --wp--custom--color--effect--spotlight--secondary: var(--wp--preset--color--cta-500); + --wp--custom--color--effect--hero--brand: var(--wp--preset--color--brand-500); + --wp--custom--color--effect--hero--cta: var(--wp--preset--color--cta-500); + --wp--custom--color--effect--hero--accent: var(--wp--preset--color--accent-two-500); + --wp--custom--shadow--elevation--100: 0.5px 2px 3px 0.5px rgba(17, 17, 17, 0.2); + --wp--custom--shadow--elevation--200: 0.5px 2px 6px 1px rgba(17, 17, 17, 0.2); + --wp--custom--shadow--elevation--300: 1px 4px 12px 4px rgba(17, 17, 17, 0.2); + --wp--custom--shadow--elevation--400: 1px 4px 12px 4px rgba(17, 17, 17, 0.3); + --wp--custom--shadow--elevation--500: 1px 4px 12px 4px rgba(17, 17, 17, 0.4); + --wp--custom--shadow--elevation--600: 2px 6px 12px 6px rgba(17, 17, 17, 0.3); + --wp--custom--shadow--heading--contrast: 0.5px 2px 3px color-mix(in srgb, var(--wp--custom--color--text-shadow--contrast) 18%, transparent); + --wp--custom--shadow--heading--base: 0.5px 2px 3px color-mix(in srgb, var(--wp--custom--color--text-shadow--base) 24%, transparent); + --wp--custom--shadow--interactive--accent: 1px 4px 12px 4px color-mix(in srgb, var(--wp--custom--color--link--accent) 50%, transparent); + --wp--custom--shadow--card--hover: 0 9px 28px -16px color-mix(in srgb, var(--wp--custom--color--text--brand) 80%, transparent), 0 8px 18px -14px color-mix(in srgb, var(--wp--custom--color--text--brand-strong) 24%, transparent); + --wp--custom--shadow--popover--menu: 0 32px 80px 0 color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 18%, transparent), var(--wp--custom--shadow--elevation--200); + --wp--custom--shadow--footer--notes: 0 0 0 1px color-mix(in srgb, var(--wp--custom--color--phase--create) 14%, transparent), 0 0 24px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 10%, transparent); +} + +:root[data-theme="dark"] { + --wp--custom--color--text--default: var(--wp--preset--color--base); + --wp--custom--color--text--inverse: var(--wp--preset--color--contrast); + --wp--custom--color--text--muted: var(--wp--preset--color--neutral-400); + --wp--custom--color--text--subtle: var(--wp--preset--color--neutral-500); + --wp--custom--color--text--brand: var(--wp--preset--color--cta-500); + --wp--custom--color--text--brand-strong: var(--wp--preset--color--cta-200); + --wp--custom--color--text--on-dark: var(--wp--preset--color--base); + --wp--custom--color--text--on-dark-muted: var(--wp--preset--color--neutral-400); + --wp--custom--color--surface--canvas: var(--wp--preset--color--contrast); + --wp--custom--color--surface--card: var(--wp--preset--color--neutral-900); + --wp--custom--color--surface--card-raised: var(--wp--preset--color--neutral-800); + --wp--custom--color--surface--highlight: var(--wp--preset--color--base); + --wp--custom--color--surface--brand-light: var(--wp--preset--color--brand-900); + --wp--custom--color--surface--brand: var(--wp--preset--color--cta-500); + --wp--custom--color--surface--cta-light: var(--wp--preset--color--cta-900); + --wp--custom--color--surface--cta: var(--wp--preset--color--accent-two-500); + --wp--custom--color--surface--accent-light: var(--wp--preset--color--accent-900); + --wp--custom--color--surface--accent-two-light: var(--wp--preset--color--accent-two-900); + --wp--custom--color--surface--accent-three-light: var(--wp--preset--color--accent-three-900); + --wp--custom--color--surface--band-start: var(--wp--preset--color--surface-700); + --wp--custom--color--surface--band-end: var(--wp--preset--color--surface-300); + --wp--custom--color--surface--on-dark-card: var(--wp--preset--color--surface-200); + --wp--custom--color--border--card: var(--wp--preset--color--neutral-800); + --wp--custom--color--icon--background: var(--wp--preset--color--cta-500); + --wp--custom--color--icon--color: var(--wp--preset--color--contrast); + --wp--custom--color--card--solutions--brand: var(--wp--preset--color--cta-500); + --wp--custom--color--card--solutions--accent: var(--wp--preset--color--accent-three-500); + --wp--custom--color--phase--discover: var(--wp--preset--color--phase-one); + --wp--custom--color--phase--create: var(--wp--preset--color--phase-two); + --wp--custom--color--phase--build: var(--wp--preset--color--phase-three); + --wp--custom--color--phase--launch: var(--wp--preset--color--phase-four); + --wp--custom--color--phase--grow: var(--wp--preset--color--phase-five); + --wp--custom--color--phase--evolve: var(--wp--preset--color--phase-six); + --wp--custom--color--status--success: var(--wp--preset--color--success-foreground); + --wp--custom--color--text-shadow--contrast: var(--wp--preset--color--base); + --wp--custom--color--text-shadow--base: var(--wp--preset--color--contrast); + --wp--custom--color--link--default: var(--wp--preset--color--base); + --wp--custom--color--link--decoration-start: var(--wp--preset--color--brand-400); + --wp--custom--color--link--decoration-end: var(--wp--preset--color--accent-300); + --wp--custom--color--link--accent: var(--wp--preset--color--cta-500); + --wp--custom--color--link--accent-hover: var(--wp--preset--color--cta-300); + --wp--custom--color--button--fill--background: var(--wp--preset--color--cta-500); + --wp--custom--color--button--fill--text: var(--wp--preset--color--contrast); + --wp--custom--color--button--fill--border: var(--wp--preset--color--cta-500); + --wp--custom--color--button--fill--background-hover: var(--wp--preset--color--cta-400); + --wp--custom--color--button--fill--text-hover: var(--wp--preset--color--contrast); + --wp--custom--color--button--fill--border-hover: var(--wp--preset--color--cta-400); + --wp--custom--color--button--outline--background: var(--wp--preset--color--surface-400); + --wp--custom--color--button--outline--text: var(--wp--preset--color--cta-500); + --wp--custom--color--button--outline--border: var(--wp--preset--color--cta-500); + --wp--custom--color--button--outline--background-hover: var(--wp--preset--color--cta-400); + --wp--custom--color--button--outline--text-hover: var(--wp--preset--color--contrast); + --wp--custom--color--focus--ring: var(--wp--preset--color--base); + --wp--custom--color--effect--spotlight--primary: var(--wp--preset--color--brand-500); + --wp--custom--color--effect--spotlight--secondary: var(--wp--preset--color--accent-three-500); + --wp--custom--color--effect--hero--brand: var(--wp--preset--color--brand-300); + --wp--custom--color--effect--hero--cta: var(--wp--preset--color--cta-300); + --wp--custom--color--effect--hero--accent: var(--wp--preset--color--accent-two-300); + --wp--custom--shadow--elevation--100: 0.5px 2px 3px 0.5px rgba(8, 8, 8, 0.36); + --wp--custom--shadow--elevation--200: 0.5px 2px 6px 1px rgba(8, 8, 8, 0.38); + --wp--custom--shadow--elevation--300: 1px 4px 12px 4px rgba(8, 8, 8, 0.42); + --wp--custom--shadow--elevation--400: 1px 4px 12px 4px rgba(8, 8, 8, 0.48); + --wp--custom--shadow--elevation--500: 1px 4px 12px 4px rgba(8, 8, 8, 0.54); + --wp--custom--shadow--elevation--600: 2px 6px 12px 6px rgba(8, 8, 8, 0.6); + --wp--custom--shadow--interactive--accent: 1px 4px 12px 4px color-mix(in srgb, var(--wp--custom--color--link--accent) 62%, transparent); + --wp--custom--shadow--card--hover: var(--wp--custom--shadow--elevation--400), 0 28px 56px -28px color-mix(in srgb, var(--wp--custom--color--link--accent) 52%, transparent), 0 14px 30px -18px color-mix(in srgb, var(--wp--custom--color--link--accent) 32%, transparent); + --wp--custom--shadow--popover--menu: 0 32px 80px 0 color-mix(in srgb, var(--wp--custom--color--effect--spotlight--primary) 18%, transparent), var(--wp--custom--shadow--elevation--200); + --wp--custom--shadow--footer--notes: 0 0 0 1px color-mix(in srgb, var(--wp--custom--color--phase--create) 24%, transparent), 0 0 34px 0 color-mix(in srgb, var(--wp--custom--color--phase--create) 18%, transparent); +} diff --git a/assets/js/header-search.js b/assets/js/header-search.js new file mode 100644 index 0000000..a60db4f --- /dev/null +++ b/assets/js/header-search.js @@ -0,0 +1,49 @@ +/** + * Header search expand-on-click. + * + * core/search in "button-only" position renders just a submit button with the input hidden — + * there is no built-in core behavior to reveal and focus that input on click. A pure-CSS + * :focus-within trick can't do it either, since there's nothing visible to click into until the + * input is already revealed. This intercepts the button's first click to expand + focus the field + * instead of submitting immediately; a second click (now that the field has focus) submits + * normally, matching standard expand-to-search UX. + */ +( function () { + 'use strict'; + + var EXPANDED_CLASS = 'site-header__search--expanded'; + + document.addEventListener( 'click', function ( event ) { + var button = event.target.closest( '.site-header__search .wp-block-search__button' ); + if ( ! button ) { + return; + } + + var container = button.closest( '.site-header__search' ); + if ( ! container || container.classList.contains( EXPANDED_CLASS ) ) { + return; + } + + event.preventDefault(); + container.classList.add( EXPANDED_CLASS ); + + var input = container.querySelector( '.wp-block-search__input' ); + if ( input ) { + input.focus(); + } + } ); + + document.addEventListener( 'focusout', function ( event ) { + var container = event.target.closest && event.target.closest( '.site-header__search' ); + if ( ! container ) { + return; + } + // Wait a tick so focus can land on the next element inside the same container (e.g. the + // button itself) before deciding the field has genuinely lost focus. + window.setTimeout( function () { + if ( ! container.contains( document.activeElement ) ) { + container.classList.remove( EXPANDED_CLASS ); + } + }, 0 ); + } ); +} )(); diff --git a/assets/js/theme-toggle.js b/assets/js/theme-toggle.js new file mode 100644 index 0000000..d7410b4 --- /dev/null +++ b/assets/js/theme-toggle.js @@ -0,0 +1,46 @@ +/** + * Light/dark mode toggle click handling. + * + * Assumes theme.json (light) is the site's baked-in default whenever no `data-theme` override is + * present on — true for this theme's setup, where styles/dark.json is an opt-in variation + * rather than the deployed default. If that ever changes, the initial "no attribute" case here + * would need a server-provided default instead of the light-mode assumption below. + */ +( function () { + 'use strict'; + + var STORAGE_KEY = 'ls-theme-mode'; + var TOGGLE_SELECTOR = '[data-ls-theme-toggle]'; + + function getEffectiveMode() { + return 'dark' === document.documentElement.getAttribute( 'data-theme' ) ? 'dark' : 'light'; + } + + function syncToggleButtons( mode ) { + var toggles = document.querySelectorAll( TOGGLE_SELECTOR ); + for ( var i = 0; i < toggles.length; i++ ) { + toggles[ i ].setAttribute( 'aria-pressed', 'dark' === mode ? 'true' : 'false' ); + } + } + + function applyMode( mode ) { + document.documentElement.setAttribute( 'data-theme', mode ); + try { + localStorage.setItem( STORAGE_KEY, mode ); + } catch ( error ) {} + syncToggleButtons( mode ); + } + + document.addEventListener( 'click', function ( event ) { + var toggle = event.target.closest( TOGGLE_SELECTOR ); + if ( ! toggle ) { + return; + } + event.preventDefault(); + applyMode( 'dark' === getEffectiveMode() ? 'light' : 'dark' ); + } ); + + document.addEventListener( 'DOMContentLoaded', function () { + syncToggleButtons( getEffectiveMode() ); + } ); +} )(); diff --git a/functions.php b/functions.php index e9eee83..1018d86 100644 --- a/functions.php +++ b/functions.php @@ -18,6 +18,12 @@ /** Loads the presets integration files */ require get_template_directory() . '/inc/presets.php'; +/** Loads the light/dark mode toggle */ +require get_template_directory() . '/inc/theme-toggle.php'; + +/** Loads site header assets */ +require get_template_directory() . '/inc/header.php'; + /** * Sets up theme supports. */ diff --git a/inc/header.php b/inc/header.php new file mode 100644 index 0000000..361f359 --- /dev/null +++ b/inc/header.php @@ -0,0 +1,26 @@ +, applying any stored + * mode preference before first paint to avoid a flash of the wrong theme. + */ +function ls_theme_output_theme_toggle_bootstrap() { + ?> + + - - + + diff --git a/src/scss/animations.scss b/src/scss/animations.scss index c97587e..17fc0c5 100644 --- a/src/scss/animations.scss +++ b/src/scss/animations.scss @@ -6,4 +6,5 @@ @use "animations/button-motion"; @use "animations/faq-motion"; @use "animations/menu-motion"; -@use "animations/footer-motion"; \ No newline at end of file +@use "animations/footer-motion"; +@use "animations/header-motion"; \ No newline at end of file diff --git a/src/scss/animations/_header-motion.scss b/src/scss/animations/_header-motion.scss new file mode 100644 index 0000000..62dbdc2 --- /dev/null +++ b/src/scss/animations/_header-motion.scss @@ -0,0 +1,113 @@ +@use "../abstracts/mixins/motion"; + +/********** Site Header **********/ + +/* + * Header icon buttons (search, light/dark toggle) and the gradient CTA: subtle hover treatment, + * matching the rest of the theme's interactive-control conventions. WordPress doesn't generate + * :hover CSS for arbitrary block style variations, so this lives here rather than in JSON. + */ +.is-style-header-icon-button { + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), + border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); + + &:hover, + &:focus-within { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); + } +} + +.is-style-button-cta-gradient .wp-block-button__link { + transition: transform var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), + box-shadow var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); + + &:hover, + &:focus-visible { + transform: translateY(-1px); + box-shadow: var(--wp--custom--shadow--interactive--accent), var(--wp--custom--shadow--elevation--300); + } +} + +/* + * Header search (core/search, buttonPosition: button-only): the input is collapsed to 0 width by + * default and expands via the .site-header__search--expanded class. core/search's "button-only" + * position only changes layout (it hides the input and renders just a submit button) — there's no + * built-in click-to-expand behaviour in core, and a pure-CSS :focus-within trick can't reveal a + * zero-width input either (nothing to click into until it's already visible). The class is toggled + * by assets/js/header-search.js, which intercepts the button's first click to expand + focus the + * field instead of submitting immediately. + */ +.site-header__search { + .wp-block-search__button { + display: flex; + align-items: center; + justify-content: center; + min-inline-size: 44px; + min-block-size: 44px; + padding: var(--wp--preset--spacing--10); + border: 1px solid var(--wp--custom--color--border--card); + border-radius: var(--wp--preset--border-radius--500); + background-color: color-mix(in srgb, var(--wp--custom--color--surface--highlight) 4%, transparent); + color: var(--wp--custom--color--text--default); + transition: background-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), + border-color var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard); + } + + .wp-block-search__button:hover, + .wp-block-search__button:focus-visible { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); + } + + .wp-block-search__input { + inline-size: 0; + min-inline-size: 0; + padding-inline: 0; + border-width: 0; + opacity: 0; + transition: inline-size var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard), + opacity var(--wp--custom--animation--duration--base) var(--wp--custom--animation--easing--standard), + padding-inline var(--wp--custom--animation--duration--medium) var(--wp--custom--animation--easing--standard); + } + + &.site-header__search--expanded .wp-block-search__input { + inline-size: 12rem; + padding-inline: var(--wp--preset--spacing--10); + border-width: 1px; + opacity: 1; + } +} + +/* + * Light/dark toggle: sun/moon icon swap based on the current mode. Default (no [data-theme] + * override present) is the site's light-mode default, so the moon shows first — clicking it + * offers a switch *to* dark. Once [data-theme="dark"] is set (assets/js/theme-toggle.js), the + * icons swap to offer a switch back to light. + */ +.ls-theme-toggle__icon--sun { + display: none; +} + +.ls-theme-toggle__icon--moon { + display: block; +} + +:root[data-theme="dark"] { + .ls-theme-toggle__icon--sun { + display: block; + } + + .ls-theme-toggle__icon--moon { + display: none; + } +} + +@include motion.reduced-motion { + .is-style-header-icon-button, + .is-style-button-cta-gradient .wp-block-button__link, + .site-header__search .wp-block-search__button, + .site-header__search .wp-block-search__input { + transition: none; + } +} diff --git a/styles/blocks/buttons/button-cta-gradient.json b/styles/blocks/buttons/button-cta-gradient.json new file mode 100644 index 0000000..901f238 --- /dev/null +++ b/styles/blocks/buttons/button-cta-gradient.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Button CTA Gradient", + "slug": "button-cta-gradient", + "blockTypes": [ + "core/button" + ], + "description": "Gradient-filled pill CTA button (header \"Start a project\"). The gradient is a literal color.gradient value (theme.json's dedicated gradient property — color.background only accepts solid colours, which is why this previously rendered with no visible fill), fully expressible in JSON — only the hover lift lives in src/scss/animations/_header-motion.scss.", + "styles": { + "border": { + "color": "var:custom|color|border|card", + "radius": "var:preset|border-radius|500", + "style": "solid", + "width": "1px" + }, + "color": { + "gradient": "linear-gradient(135deg, var(--wp--custom--color--phase--create) 0%, var(--wp--custom--color--link--accent) 60%, var(--wp--custom--color--link--accent) 100%)", + "text": "var:custom|color|text|on-dark" + }, + "shadow": "var:preset|shadow|interactive-accent", + "spacing": { + "padding": { + "top": "var:preset|spacing|10", + "right": "var:preset|spacing|20", + "bottom": "var:preset|spacing|10", + "left": "var:preset|spacing|20" + } + }, + "typography": { + "fontWeight": "var:custom|typography|font-weight|extrabold", + "fontSize": "var:preset|font-size|100" + } + } +} diff --git a/styles/blocks/groups/header-icon-button.json b/styles/blocks/groups/header-icon-button.json new file mode 100644 index 0000000..8c14623 --- /dev/null +++ b/styles/blocks/groups/header-icon-button.json @@ -0,0 +1,33 @@ +{ + "$schema": "https://schemas.wp.org/wp/6.9/theme.json", + "version": 3, + "title": "Header Icon Button", + "slug": "header-icon-button", + "blockTypes": [ + "core/group" + ], + "description": "Circular icon button for the header (search, light/dark toggle). Structural styling only — the hover background lift is authored in src/scss/animations/_header-motion.scss (WordPress doesn't generate :hover CSS for arbitrary block style variations).", + "styles": { + "color": { + "background": "color-mix(in srgb, var(--wp--custom--color--surface--highlight) 4%, transparent)", + "text": "var:custom|color|text|default" + }, + "border": { + "color": "var:custom|color|border|card", + "radius": "var:preset|border-radius|500", + "style": "solid", + "width": "1px" + }, + "dimensions": { + "minHeight": "44px" + }, + "spacing": { + "padding": { + "top": "var:preset|spacing|10", + "right": "var:preset|spacing|10", + "bottom": "var:preset|spacing|10", + "left": "var:preset|spacing|10" + } + } + } +} diff --git a/styles/presets/shadows.json b/styles/presets/shadows.json index 13c2daf..cabaec7 100644 --- a/styles/presets/shadows.json +++ b/styles/presets/shadows.json @@ -44,6 +44,11 @@ "name": "Footer Notes Glow", "slug": "footer-notes", "shadow": "var(--wp--custom--shadow--footer--notes)" + }, + { + "name": "Interactive Accent Glow", + "slug": "interactive-accent", + "shadow": "var(--wp--custom--shadow--interactive--accent)" } ] } diff --git a/theme-utils.mjs b/theme-utils.mjs index 8224ec1..557535b 100644 --- a/theme-utils.mjs +++ b/theme-utils.mjs @@ -24,7 +24,9 @@ const require = createRequire( import.meta.url ); // Root of the theme repository. const ROOT = resolve( '.' ); const THEME_JSON_PATH = join( ROOT, 'theme.json' ); +const DARK_JSON_PATH = join( ROOT, 'styles', 'dark.json' ); const BREAKPOINT_SCSS_PATH = join( ROOT, 'src', 'scss', 'abstracts', 'mixins', '_breakpoints-theme.scss' ); +const THEME_TOGGLE_CSS_PATH = join( ROOT, 'assets', 'css', 'theme-toggle.css' ); // Colours for terminal output. const RESET = '\x1b[0m'; @@ -157,6 +159,106 @@ async function syncBreakpoints() { success( `Updated ${ relative( ROOT, BREAKPOINT_SCSS_PATH ) } from theme.json break-points.` ); } +/** + * Recursively flatten a nested settings.custom.* object into + * { 'color--text--default': 'var:preset|color|base', ... } entries, joined with '--' + * to match WordPress's own custom-property naming convention. + * + * @param {object} obj Object to flatten. + * @param {string[]} pathParts Path segments accumulated so far. + * @param {object} out Accumulator. + */ +function flattenCustomTree( obj, pathParts, out ) { + for ( const [ key, value ] of Object.entries( obj ) ) { + const parts = [ ...pathParts, key ]; + if ( value && 'object' === typeof value && ! Array.isArray( value ) ) { + flattenCustomTree( value, parts, out ); + } else { + out[ parts.join( '--' ) ] = value; + } + } +} + +/** + * Convert a theme.json `var:preset|type|slug` reference into a real CSS `var(...)` reference. + * Values that aren't preset references (e.g. literal shadow formulas) are returned unchanged — + * they already use `var(--wp--custom--...)` internally, which resolves correctly once those + * referenced custom properties are also re-pointed by the same override block. + * + * @param {string} value Raw theme.json value. + * @returns {string} CSS-ready value. + */ +function presetRefToCssVar( value ) { + const match = /^var:preset\|([a-z-]+)\|([a-z0-9-]+)$/iu.exec( value ); + if ( ! match ) { + return value; + } + const [ , type, slug ] = match; + return `var(--wp--preset--${ type }--${ slug })`; +} + +/** + * Generate assets/css/theme-toggle.css: two `:root[data-theme="…"]` blocks that re-point every + * settings.custom.color / settings.custom.shadow token to the light (theme.json) or dark + * (styles/dark.json) preset it should resolve to. Powers the header's client-side light/dark + * toggle — WordPress's own light/dark "mode" is a full theme.json style variation picked by an + * admin in the Site Editor, not something a site visitor can switch at runtime, so this file is + * what makes an actual visitor-facing toggle possible without hand-duplicating every token. + */ +async function generateThemeToggleCss() { + heading( 'Generating theme-toggle CSS from theme.json + styles/dark.json…' ); + + const themeJSON = readJSON( THEME_JSON_PATH ); + const darkJSON = readJSON( DARK_JSON_PATH ); + + if ( ! themeJSON || ! darkJSON ) { + error( 'Could not read theme.json or styles/dark.json.' ); + process.exit( 1 ); + } + + const lightFlat = {}; + flattenCustomTree( themeJSON?.settings?.custom?.color || {}, [ 'color' ], lightFlat ); + flattenCustomTree( themeJSON?.settings?.custom?.shadow || {}, [ 'shadow' ], lightFlat ); + + const darkFlat = {}; + flattenCustomTree( darkJSON?.settings?.custom?.color || {}, [ 'color' ], darkFlat ); + flattenCustomTree( darkJSON?.settings?.custom?.shadow || {}, [ 'shadow' ], darkFlat ); + + if ( 0 === Object.keys( lightFlat ).length || 0 === Object.keys( darkFlat ).length ) { + error( 'No settings.custom.color/shadow tokens found in theme.json or styles/dark.json.' ); + process.exit( 1 ); + } + + const toBlock = ( flat ) => + Object.entries( flat ) + .map( ( [ path, value ] ) => ` --wp--custom--${ path }: ${ presetRefToCssVar( value ) };` ) + .join( '\n' ); + + const css = [ + '/* Auto-generated from theme.json + styles/dark.json by `node theme-utils.mjs generate-theme-toggle`. */', + '/* Do not edit this file by hand — edit the source JSON and regenerate. */', + '', + ':root[data-theme="light"] {', + toBlock( lightFlat ), + '}', + '', + ':root[data-theme="dark"] {', + toBlock( darkFlat ), + '}', + '', + ].join( '\n' ); + + const existing = existsSync( THEME_TOGGLE_CSS_PATH ) ? readFileSync( THEME_TOGGLE_CSS_PATH, 'utf8' ) : null; + + if ( existing === css ) { + success( `${ relative( ROOT, THEME_TOGGLE_CSS_PATH ) } is already up to date.` ); + return; + } + + writeFileSync( THEME_TOGGLE_CSS_PATH, css, 'utf8' ); + success( `Generated ${ relative( ROOT, THEME_TOGGLE_CSS_PATH ) } from theme.json + styles/dark.json.` ); +} + // --------------------------------------------------------------------------- // Command: validate-schema // --------------------------------------------------------------------------- @@ -610,6 +712,10 @@ Commands: ${ CYAN }sync-breakpoints${ RESET } Sync the Sass breakpoint map from theme.json custom layout break-points. + ${ CYAN }generate-theme-toggle${ RESET } + Generate assets/css/theme-toggle.css from theme.json + styles/dark.json, powering the + header's client-side light/dark toggle. + ${ CYAN }validate-schema${ RESET } Validate theme.json and all styles/*.json files against the WordPress schema. Also validates JSON files in styles/blocks/ and styles/sections/ if present. @@ -638,6 +744,9 @@ switch ( command ) { case 'sync-breakpoints': await syncBreakpoints(); break; + case 'generate-theme-toggle': + await generateThemeToggleCss(); + break; case 'validate-schema': await validateSchema(); break; From d47011e3d05c42aeb266125a824dae1f3909a1be Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 24 Jul 2026 08:54:55 +0200 Subject: [PATCH 6/9] Fix header controls, footer nav grid, and footer icon colours (LS-1618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Header: added the missing base circular-button shell for the light/dark toggle (.is-style-header-icon-button had only hover CSS), and enqueued animations.css via add_editor_style() so it reliably reaches the Site Editor's iframed canvas, fixing the toggle button and the always-expanded search field in the backend editor. - Footer: fixed the nav link grid's second row (Company/Studio) using a fixed 226px column against an auto column, so it split differently than the first row's three auto-equal columns. Both rows now use the same auto-equal column pattern with a spacer column completing the second row. - Footer: fixed the phase-colour nav dots and social icons rendering black in both light and dark mode — their SVGs never declared fill="currentColor", so the browser's default SVG fill (black) was used instead of the semantic colour token already set via inline style. - Added a site-logo--dark / site-logo--light CSS swap convention for the header logo, matching the existing sun/moon icon-swap pattern. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 5 ++ assets/css/animations.css | 65 +++++++++++++++++++++++-- functions.php | 5 +- patterns/footer.php | 8 ++- src/scss/animations/_footer-motion.scss | 12 +++++ src/scss/animations/_header-motion.scss | 56 +++++++++++++++++++-- 6 files changed, 142 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 45c3ff0..93504fa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -100,6 +100,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Fixed an invalid nested `wp:site-title` block inside `patterns/footer.php`'s paragraph markup that broke block parsing on every template (LS-1226). - Fixed `patterns/breadcrumbs.php` rendering full-bleed instead of content width by removing an unnecessary `align:full` (LS-1226). +- Fixed the header light/dark toggle button (`.is-style-header-icon-button`) rendering with no circular button shell — only its `:hover`/`:focus-within` treatment existed; added the base border/background/sizing CSS documented in `styles/blocks/groups/header-icon-button.json`, plus a `.ls-theme-toggle` reset so the inner ` @@ -33,7 +33,7 @@
diff --git a/src/scss/animations/_footer-motion.scss b/src/scss/animations/_footer-motion.scss index ffd4abc..b51ea15 100644 --- a/src/scss/animations/_footer-motion.scss +++ b/src/scss/animations/_footer-motion.scss @@ -62,8 +62,11 @@ * Blurred accent blob behind the nav-column grid, echoing Figma's "Mask Group" glow. Purely * decorative and non-interactive, so it's safe to skip under reduced motion (nothing here * animates — it's a static blur, included in the reduced-motion block only for parity/consistency). + * Targets the explicit .site-footer__columns class rather than :nth-of-type(2) — that selector + * counted by tag name among siblings sharing a parent, not a global count of wp-block-columns, so + * it was also accidentally matching the nested Company/Studio sub-grid two levels down. */ -.site-footer .wp-block-columns:nth-of-type(2) { +.site-footer__columns { position: relative; &::after { diff --git a/styles/blocks/groups/mega-menu-item-default.json b/styles/blocks/groups/mega-menu-item-default.json index c9eb9be..67badf9 100644 --- a/styles/blocks/groups/mega-menu-item-default.json +++ b/styles/blocks/groups/mega-menu-item-default.json @@ -19,30 +19,10 @@ "left": "var:preset|spacing|10" } }, - ":hover": { - "color": { - "background": "var:custom|color|surface|card-raised" - } - }, - ":focus": { - "color": { - "background": "var:custom|color|surface|card-raised" - } - }, "blocks": { "outermost/icon-block": { "color": { "text": "transparent" - }, - ":hover": { - "color": { - "text": "var:custom|color|text|subtle" - } - }, - ":focus": { - "color": { - "text": "var:custom|color|text|subtle" - } } } } From 57fe3322fab477e64580f85241dd86e62f956470 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Fri, 24 Jul 2026 12:44:02 +0200 Subject: [PATCH 8/9] Remove temporary CSS theme-toggle in favor of ls-plugin's native switcher (LS-1618) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The header's light/dark toggle was a temporary CSS/JS workaround built before discovering the site already has a proper switcher: the ls-plugin/style-switcher block, registered by the LightSpeed Site Plugin (inc/class-style-switcher.php), which reads styles/*.json variations directly and requires no theme-side duplicate mechanism. Removed: - inc/theme-toggle.php, assets/js/theme-toggle.js, assets/css/theme-toggle.css - the generate-theme-toggle command in theme-utils.mjs (and its now-unused flattenCustomTree/presetRefToCssVar helpers, the DARK_JSON_PATH/THEME_TOGGLE_CSS_PATH constants) and its package.json script/build:css reference - the toggle button markup in patterns/header.php - the .ls-theme-toggle, sun/moon icon-swap, and site-logo--dark/light CSS in src/scss/animations/_header-motion.scss (the logo swap keyed off the same [data-theme] mechanism and needs to be re-keyed to .dark-mode when the ls-plugin block is wired in) The header currently has no light/dark toggle until the ls-plugin/style-switcher block is wired in as follow-up work. Nothing here is lost — it's fully recoverable from this commit's parent via `git show :inc/theme-toggle.php` etc., or `git checkout ^{commit} -- inc/theme-toggle.php assets/js/theme-toggle.js assets/css/theme-toggle.css`. Co-Authored-By: Claude Sonnet 5 --- CHANGELOG.md | 1 + assets/css/animations.css | 57 --------- assets/css/theme-toggle.css | 146 ------------------------ assets/js/theme-toggle.js | 46 -------- functions.php | 3 - inc/theme-toggle.php | 68 ----------- package.json | 3 +- patterns/header.php | 9 -- src/scss/animations/_header-motion.scss | 63 ---------- theme-utils.mjs | 109 ------------------ 10 files changed, 2 insertions(+), 503 deletions(-) delete mode 100644 assets/css/theme-toggle.css delete mode 100644 assets/js/theme-toggle.js delete mode 100644 inc/theme-toggle.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 93504fa..ea41918 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -95,6 +95,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed the experimental vanilla JavaScript spotlight card implementation pending GSAP evaluation. - Removed the section style contract loader for `styles/sections/*.json` runtime CSS generation. - Removed the temporary experimental button variation and its temporary selector path. +- Removed the temporary CSS/JS light-dark toggle built for the header (`inc/theme-toggle.php`, `assets/js/theme-toggle.js`, `assets/css/theme-toggle.css`, the `generate-theme-toggle` command in `theme-utils.mjs`, and the toggle button markup in `patterns/header.php`). This was a workaround built before discovering the site already has a proper, native light/dark switcher — the `ls-plugin/style-switcher` block registered by the LightSpeed Site Plugin, which reads `styles/*.json` variations directly and needs no theme-side duplicate. The header currently has no light/dark toggle until that block is wired in as follow-up work; fully recoverable from git history if needed sooner (LS-1618). ### Fixed diff --git a/assets/css/animations.css b/assets/css/animations.css index 80ab8a8..6781532 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1476,20 +1476,6 @@ border-color: var(--wp--custom--color--phase--create); } -/* The toggle

- -
- -
- -
diff --git a/src/scss/animations/_header-motion.scss b/src/scss/animations/_header-motion.scss index 47958c0..0228da4 100644 --- a/src/scss/animations/_header-motion.scss +++ b/src/scss/animations/_header-motion.scss @@ -29,20 +29,6 @@ } } -/* The toggle