From 4ef083f616add6010059edce201cf10f1c7069fc Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 29 Jul 2026 12:45:50 +0200 Subject: [PATCH 1/2] Add Mobile Menu with lifecycle-phase accordion for Services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New parts/mobile-menu.html template part, registered in theme.json, wired to the header's core/navigation via Ollie Menu Designer's mobileMenuSlug (drawer breakpoint raised to 1024px so tablets get it too, not just phones) - All 8 real nav items in order (Work, Solutions, Services, Systems, Pricing, Insights, About, Contact); 6 use native core/details accordions reusing the theme's existing chevron/hover mechanism, with Systems and Contact as plain links Services accordion is phase-grouped (Discover → Evolve) with colour-coded dots and dashed dividers between phase groups, content ported from the existing desktop Services mega menu Footer CTAs (Book a consultation / Start a project) reuse the existing button-secondary / button-secondary-outline styles Added add_theme_support('custom-logo') so core/site-logo renders in the menu's logo row New src/scss/animations/_mobile-menu-motion.scss: styles WP core's native overlay close button to match the header's icon-button treatment, aligns the logo row with it, and normalizes row height/spacing across all accordion and link rows --- assets/css/animations.css | 158 +++++++++ functions.php | 4 + parts/mobile-menu.html | 331 +++++++++++++++++++ patterns/header.php | 2 +- src/scss/animations.scss | 1 + src/scss/animations/_mobile-menu-motion.scss | 176 ++++++++++ theme.json | 5 + 7 files changed, 676 insertions(+), 1 deletion(-) create mode 100644 parts/mobile-menu.html create mode 100644 src/scss/animations/_mobile-menu-motion.scss diff --git a/assets/css/animations.css b/assets/css/animations.css index 705dd93..aa50335 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1485,6 +1485,164 @@ transition: none; } } +/********** Mobile Menu (Ollie Menu Designer mobileMenuSlug drawer) **********/ +/* + * Logo row + close button. The close button is WP core's own + * .wp-block-navigation__responsive-container-close — rendered as a sibling before the injected + * mobileMenuSlug content, not part of this template part — so it can only be reached and aligned + * via a global selector, not through markup here. Core's default top:0/right:0 plus the content's + * default padding-top: calc(2rem + 24px) (wp-includes/blocks/navigation/style.css) is what was + * producing the oversized gap above the logo; both are overridden to the same spacing token here + * so the logo row and the close button land on the same line. Styled with the same circular + * icon-button language as the header's search/theme-toggle buttons + * (styles/blocks/groups/header-icon-button.json) for visual consistency. + */ +.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content { + padding-top: var(--wp--preset--spacing--20); +} + +.wp-block-navigation__responsive-container-close { + position: absolute; + top: var(--wp--preset--spacing--20); + right: var(--wp--preset--spacing--20); + 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); + 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-navigation__responsive-container-close svg { + inline-size: 20px; + block-size: 20px; +} +.wp-block-navigation__responsive-container-close:hover, .wp-block-navigation__responsive-container-close:focus-visible { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); +} + +@media (prefers-reduced-motion: reduce) { + .wp-block-navigation__responsive-container-close { + transition: none; + } +} +/* + * core/details already ships a full card-style accordion (border, background, chevron, hover, + * [open] state) globally via styles/presets/blocks/core-details.json + _details-motion.scss. The + * mobile drawer wants that same disclosure mechanism but flattened into a full-width list row + * (no individual card border/background/shadow, no hover lift) with a divider between siblings + * instead — none of which is expressible in theme.json, so it's layered on here rather than + * duplicating the chevron/open logic that already exists. + */ +.mobile-menu > .mobile-menu-accordion, +.mobile-menu > .mobile-menu-link-row { + border: none; + border-bottom: 1px solid var(--wp--custom--color--border--card); + border-radius: 0; +} +.mobile-menu > .mobile-menu-accordion:last-child, +.mobile-menu > .mobile-menu-link-row:last-child { + border-bottom: none; +} + +.mobile-menu > .mobile-menu-accordion { + background: none; + box-shadow: none; +} +.mobile-menu > .mobile-menu-accordion:hover, .mobile-menu > .mobile-menu-accordion[open], .mobile-menu > .mobile-menu-accordion[open]:hover { + background: none; + border-color: var(--wp--custom--color--border--card); + box-shadow: none; +} +.mobile-menu > .mobile-menu-accordion:last-child { + border-bottom: none; +} +.mobile-menu > .mobile-menu-accordion { + /* + * Fixed min-block-size (rather than relying on padding + line-height parity with + * .mobile-menu-link-row below) so every top-level row — whether a with its chevron + * or a plain — renders at the exact same height regardless of small content differences. + * padding-inline-end keeps a little clearance for the chevron: zeroing it entirely (matching + * the row's flush-left text) let the rotated 10px chevron's diagonal corners get clipped by + * .wp-block-details's own overflow:hidden. + */ +} +.mobile-menu > .mobile-menu-accordion > summary { + min-block-size: 3rem; + padding-inline: 0 var(--wp--preset--spacing--10); + padding-block: 0; + font-size: var(--wp--preset--font-size--200); + font-weight: var(--wp--custom--typography--font-weight--semibold); +} +.mobile-menu > .mobile-menu-accordion > :where(:not(summary)) { + padding-inline: 0; +} +.mobile-menu > .mobile-menu-accordion[open] > summary { + border-bottom-color: var(--wp--custom--color--border--card); +} +.mobile-menu > .mobile-menu-accordion:focus-within { + box-shadow: none; +} + +.mobile-menu-link-row { + margin: 0; +} +.mobile-menu-link-row a { + display: flex; + align-items: center; + min-block-size: 3rem; + font-family: var(--wp--preset--font-family--heading); + font-size: var(--wp--preset--font-size--200); + font-weight: var(--wp--custom--typography--font-weight--semibold); + color: var(--wp--custom--color--text--default); + text-decoration: none; +} + +/* + * Footer CTAs (Book a consultation / Start a project): reuse the existing pill button styles, + * just stretched to the drawer's full width. No top border here — Contact (the row directly + * above) isn't actually :last-child of .mobile-menu once this buttons group follows it, so its + * own row divider already renders; adding one here too produced a doubled-up divider line. + */ +.mobile-menu-actions { + padding-block-start: var(--wp--preset--spacing--20); +} +.mobile-menu-actions .wp-block-button { + width: 100%; +} +.mobile-menu-actions .wp-block-button__link { + display: block; + width: 100%; + text-align: center; +} + +/* + * Services accordion: phase groups are separated by dashed dividers rather than solid ones, + * keeping the lighter-weight hierarchy the desktop Services panel also uses between phase + * columns. core/separator has no "style: dashed" support declared in its block.json, so setting + * that via a block style attribute fails Gutenberg's save-output validation — the dashed look is + * applied here instead, against the plain default-attribute wp:separator markup. + */ +.mobile-menu-accordion-services hr.wp-block-separator { + margin-block: var(--wp--preset--spacing--10); + border-style: dashed; +} + +.mobile-menu-brand { + min-block-size: 44px; + padding-block-end: var(--wp--preset--spacing--20); + border-block-end: 1px solid var(--wp--custom--color--border--card); +} +.mobile-menu-brand .wp-block-site-logo img { + display: block; + max-block-size: 32px; + inline-size: auto; +} + /********** Site Footer **********/ /* * "LightSpeed notes" badge: a flex group with no explicit width constraint defaults to filling diff --git a/functions.php b/functions.php index a13e320..0c94bf5 100644 --- a/functions.php +++ b/functions.php @@ -37,6 +37,10 @@ function ls_theme_setup() { // Add support for editor styles. add_theme_support( 'editor-styles' ); + // Required for core/site-logo to render (used by the mobile menu's logo row); without this, + // the block outputs nothing even when a Site Logo is set in Settings > General. + add_theme_support( 'custom-logo' ); + // Enqueue editor styles. animations.css must be listed here (not just enqueued via // enqueue_block_editor_assets) so the header/footer interactive-control CSS — the collapsed // search field, the icon-toggle button shell — reliably reaches the Site Editor's iframed canvas. diff --git a/parts/mobile-menu.html b/parts/mobile-menu.html new file mode 100644 index 0000000..41372e4 --- /dev/null +++ b/parts/mobile-menu.html @@ -0,0 +1,331 @@ + +
+
+ + + +
Work + +
+ + + +
Solutions +
+

WordPress

+ + + +

Publishing

+ + + +

AI

+ + + +

AI Chatbots

+ + + +

WooCommerce

+ + + +

Tour Operators

+ + + +

Design Systems

+ + + + +
+
+ + + +
Services +
+
+
+ + + +

Discover

+
+ + + +

Discovery

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

Create

+
+ + + +

Content

+ + + +

Design

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

Build

+
+ + + +

Development

+ + + +

Migrations

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

Launch

+
+ + + +

Hosting

+ + + +

Performance

+ + + +

Security

+ + + +

Training

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

Grow

+
+ + + +

Support

+ + + +

SEO

+ + + +

Accessibility

+ + + +

Email marketing

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

Evolve

+
+ + + +

AI

+ + + + +
+
+ + + + + + + +
Pricing + +
+ + + +
Insights + +
+ + + +
About + +
+ + + + + + + + +
+ diff --git a/patterns/header.php b/patterns/header.php index 80b12b9..e2fa509 100644 --- a/patterns/header.php +++ b/patterns/header.php @@ -15,7 +15,7 @@
<?php echo esc_attr__( 'LightSpeed', 'ls-theme' ); ?>
- +
diff --git a/src/scss/animations.scss b/src/scss/animations.scss index 15d5124..c9594a0 100644 --- a/src/scss/animations.scss +++ b/src/scss/animations.scss @@ -6,6 +6,7 @@ @use "animations/button-motion"; @use "animations/faq-motion"; @use "animations/menu-motion"; +@use "animations/mobile-menu-motion"; @use "animations/footer-motion"; @use "animations/header-motion"; @use "animations/taxonomy-filter"; \ No newline at end of file diff --git a/src/scss/animations/_mobile-menu-motion.scss b/src/scss/animations/_mobile-menu-motion.scss new file mode 100644 index 0000000..d1fc0c1 --- /dev/null +++ b/src/scss/animations/_mobile-menu-motion.scss @@ -0,0 +1,176 @@ +@use "../abstracts/mixins/motion"; + +/********** Mobile Menu (Ollie Menu Designer mobileMenuSlug drawer) **********/ + +/* + * Logo row + close button. The close button is WP core's own + * .wp-block-navigation__responsive-container-close — rendered as a sibling before the injected + * mobileMenuSlug content, not part of this template part — so it can only be reached and aligned + * via a global selector, not through markup here. Core's default top:0/right:0 plus the content's + * default padding-top: calc(2rem + 24px) (wp-includes/blocks/navigation/style.css) is what was + * producing the oversized gap above the logo; both are overridden to the same spacing token here + * so the logo row and the close button land on the same line. Styled with the same circular + * icon-button language as the header's search/theme-toggle buttons + * (styles/blocks/groups/header-icon-button.json) for visual consistency. + */ +.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) + .wp-block-navigation__responsive-container-content { + padding-top: var(--wp--preset--spacing--20); +} + +.wp-block-navigation__responsive-container-close { + position: absolute; + top: var(--wp--preset--spacing--20); + right: var(--wp--preset--spacing--20); + 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); + 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); + + svg { + inline-size: 20px; + block-size: 20px; + } + + &:hover, + &:focus-visible { + background-color: var(--wp--custom--color--surface--card-raised); + border-color: var(--wp--custom--color--phase--create); + } +} + +@include motion.reduced-motion { + .wp-block-navigation__responsive-container-close { + transition: none; + } +} + +/* + * core/details already ships a full card-style accordion (border, background, chevron, hover, + * [open] state) globally via styles/presets/blocks/core-details.json + _details-motion.scss. The + * mobile drawer wants that same disclosure mechanism but flattened into a full-width list row + * (no individual card border/background/shadow, no hover lift) with a divider between siblings + * instead — none of which is expressible in theme.json, so it's layered on here rather than + * duplicating the chevron/open logic that already exists. + */ +.mobile-menu > .mobile-menu-accordion, +.mobile-menu > .mobile-menu-link-row { + border: none; + border-bottom: 1px solid var(--wp--custom--color--border--card); + border-radius: 0; + + &:last-child { + border-bottom: none; + } +} + +.mobile-menu > .mobile-menu-accordion { + background: none; + box-shadow: none; + + &:hover, + &[open], + &[open]:hover { + background: none; + border-color: var(--wp--custom--color--border--card); + box-shadow: none; + } + + &:last-child { + border-bottom: none; + } + + /* + * Fixed min-block-size (rather than relying on padding + line-height parity with + * .mobile-menu-link-row below) so every top-level row — whether a with its chevron + * or a plain — renders at the exact same height regardless of small content differences. + * padding-inline-end keeps a little clearance for the chevron: zeroing it entirely (matching + * the row's flush-left text) let the rotated 10px chevron's diagonal corners get clipped by + * .wp-block-details's own overflow:hidden. + */ + > summary { + min-block-size: 3rem; + padding-inline: 0 var(--wp--preset--spacing--10); + padding-block: 0; + font-size: var(--wp--preset--font-size--200); + font-weight: var(--wp--custom--typography--font-weight--semibold); + } + + > :where(:not(summary)) { + padding-inline: 0; + } + + &[open] > summary { + border-bottom-color: var(--wp--custom--color--border--card); + } + + &:focus-within { + box-shadow: none; + } +} + +.mobile-menu-link-row { + margin: 0; + + a { + display: flex; + align-items: center; + min-block-size: 3rem; + font-family: var(--wp--preset--font-family--heading); + font-size: var(--wp--preset--font-size--200); + font-weight: var(--wp--custom--typography--font-weight--semibold); + color: var(--wp--custom--color--text--default); + text-decoration: none; + } +} + +/* + * Footer CTAs (Book a consultation / Start a project): reuse the existing pill button styles, + * just stretched to the drawer's full width. No top border here — Contact (the row directly + * above) isn't actually :last-child of .mobile-menu once this buttons group follows it, so its + * own row divider already renders; adding one here too produced a doubled-up divider line. + */ +.mobile-menu-actions { + padding-block-start: var(--wp--preset--spacing--20); + + .wp-block-button { + width: 100%; + } + + .wp-block-button__link { + display: block; + width: 100%; + text-align: center; + } +} + +/* + * Services accordion: phase groups are separated by dashed dividers rather than solid ones, + * keeping the lighter-weight hierarchy the desktop Services panel also uses between phase + * columns. core/separator has no "style: dashed" support declared in its block.json, so setting + * that via a block style attribute fails Gutenberg's save-output validation — the dashed look is + * applied here instead, against the plain default-attribute wp:separator markup. + */ +.mobile-menu-accordion-services hr.wp-block-separator { + margin-block: var(--wp--preset--spacing--10); + border-style: dashed; +} + +.mobile-menu-brand { + min-block-size: 44px; + padding-block-end: var(--wp--preset--spacing--20); + border-block-end: 1px solid var(--wp--custom--color--border--card); + + .wp-block-site-logo img { + display: block; + max-block-size: 32px; + inline-size: auto; + } +} diff --git a/theme.json b/theme.json index 1f2b7e9..8458fc8 100644 --- a/theme.json +++ b/theme.json @@ -717,6 +717,11 @@ "name": "services-mega-menu", "title": "Services Mega Menu", "area": "uncategorized" + }, + { + "name": "mobile-menu", + "title": "Mobile Menu", + "area": "uncategorized" } ], "customTemplates": [ From b5456d7dbd709a2cdbbe89cff3fc9f31d6589b58 Mon Sep 17 00:00:00 2001 From: Brandonma21 Date: Wed, 29 Jul 2026 13:30:07 +0200 Subject: [PATCH 2/2] Fix mobile menu review feedback: focus outline, localhost links, selector scoping - Restore visible :focus-visible outline on accordion rows and links - Replace hardcoded localhost URLs with root-relative paths (Systems, Contact) - Scope close-button selectors to .site-header - Correct site logo comment wording in functions.php --- assets/css/animations.css | 30 ++++++++++++++++---- functions.php | 2 +- parts/mobile-menu.html | 4 +-- src/scss/animations/_mobile-menu-motion.scss | 26 +++++++++++++++-- 4 files changed, 51 insertions(+), 11 deletions(-) diff --git a/assets/css/animations.css b/assets/css/animations.css index aa50335..73ed961 100644 --- a/assets/css/animations.css +++ b/assets/css/animations.css @@ -1496,12 +1496,16 @@ * so the logo row and the close button land on the same line. Styled with the same circular * icon-button language as the header's search/theme-toggle buttons * (styles/blocks/groups/header-icon-button.json) for visual consistency. + * + * Scoped under .site-header: this theme only has the one core/navigation instance (the header's), + * but these are otherwise generic core class names that would restyle any Navigation block's + * overlay drawer anywhere on the site if left unscoped. */ -.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content { +.site-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content { padding-top: var(--wp--preset--spacing--20); } -.wp-block-navigation__responsive-container-close { +.site-header .wp-block-navigation__responsive-container-close { position: absolute; top: var(--wp--preset--spacing--20); right: var(--wp--preset--spacing--20); @@ -1516,17 +1520,17 @@ background-color: color-mix(in srgb, var(--wp--custom--color--surface--highlight) 4%, transparent); 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-navigation__responsive-container-close svg { +.site-header .wp-block-navigation__responsive-container-close svg { inline-size: 20px; block-size: 20px; } -.wp-block-navigation__responsive-container-close:hover, .wp-block-navigation__responsive-container-close:focus-visible { +.site-header .wp-block-navigation__responsive-container-close:hover, .site-header .wp-block-navigation__responsive-container-close:focus-visible { background-color: var(--wp--custom--color--surface--card-raised); border-color: var(--wp--custom--color--phase--create); } @media (prefers-reduced-motion: reduce) { - .wp-block-navigation__responsive-container-close { + .site-header .wp-block-navigation__responsive-container-close { transition: none; } } @@ -1584,9 +1588,21 @@ .mobile-menu > .mobile-menu-accordion[open] > summary { border-bottom-color: var(--wp--custom--color--border--card); } +.mobile-menu > .mobile-menu-accordion { + /* + * The card-style focus ring from _details-motion.scss's base :focus-within rule doesn't fit + * this flattened row (it draws a border-colour box-shadow meant for the bordered card look), + * so it's suppressed above — but that must not mean no focus indicator at all. :focus-visible + * on the summary itself restores a visible keyboard-focus outline instead. + */ +} .mobile-menu > .mobile-menu-accordion:focus-within { box-shadow: none; } +.mobile-menu > .mobile-menu-accordion > summary:focus-visible { + outline: 2px solid var(--wp--custom--color--focus--ring); + outline-offset: -2px; +} .mobile-menu-link-row { margin: 0; @@ -1601,6 +1617,10 @@ color: var(--wp--custom--color--text--default); text-decoration: none; } +.mobile-menu-link-row a:focus-visible { + outline: 2px solid var(--wp--custom--color--focus--ring); + outline-offset: -2px; +} /* * Footer CTAs (Book a consultation / Start a project): reuse the existing pill button styles, diff --git a/functions.php b/functions.php index 0c94bf5..fefcbd2 100644 --- a/functions.php +++ b/functions.php @@ -38,7 +38,7 @@ function ls_theme_setup() { add_theme_support( 'editor-styles' ); // Required for core/site-logo to render (used by the mobile menu's logo row); without this, - // the block outputs nothing even when a Site Logo is set in Settings > General. + // the block outputs nothing even when a Site Logo is set via the Site Editor. add_theme_support( 'custom-logo' ); // Enqueue editor styles. animations.css must be listed here (not just enqueued via diff --git a/parts/mobile-menu.html b/parts/mobile-menu.html index 41372e4..5a851e9 100644 --- a/parts/mobile-menu.html +++ b/parts/mobile-menu.html @@ -216,7 +216,7 @@ - + @@ -316,7 +316,7 @@ - + diff --git a/src/scss/animations/_mobile-menu-motion.scss b/src/scss/animations/_mobile-menu-motion.scss index d1fc0c1..cbf0b62 100644 --- a/src/scss/animations/_mobile-menu-motion.scss +++ b/src/scss/animations/_mobile-menu-motion.scss @@ -12,13 +12,17 @@ * so the logo row and the close button land on the same line. Styled with the same circular * icon-button language as the header's search/theme-toggle buttons * (styles/blocks/groups/header-icon-button.json) for visual consistency. + * + * Scoped under .site-header: this theme only has the one core/navigation instance (the header's), + * but these are otherwise generic core class names that would restyle any Navigation block's + * overlay drawer anywhere on the site if left unscoped. */ -.wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) +.site-header .wp-block-navigation__responsive-container.is-menu-open:not(.disable-default-overlay) .wp-block-navigation__responsive-container-content { padding-top: var(--wp--preset--spacing--20); } -.wp-block-navigation__responsive-container-close { +.site-header .wp-block-navigation__responsive-container-close { position: absolute; top: var(--wp--preset--spacing--20); right: var(--wp--preset--spacing--20); @@ -47,7 +51,7 @@ } @include motion.reduced-motion { - .wp-block-navigation__responsive-container-close { + .site-header .wp-block-navigation__responsive-container-close { transition: none; } } @@ -111,9 +115,20 @@ border-bottom-color: var(--wp--custom--color--border--card); } + /* + * The card-style focus ring from _details-motion.scss's base :focus-within rule doesn't fit + * this flattened row (it draws a border-colour box-shadow meant for the bordered card look), + * so it's suppressed above — but that must not mean no focus indicator at all. :focus-visible + * on the summary itself restores a visible keyboard-focus outline instead. + */ &:focus-within { box-shadow: none; } + + > summary:focus-visible { + outline: 2px solid var(--wp--custom--color--focus--ring); + outline-offset: -2px; + } } .mobile-menu-link-row { @@ -128,6 +143,11 @@ font-weight: var(--wp--custom--typography--font-weight--semibold); color: var(--wp--custom--color--text--default); text-decoration: none; + + &:focus-visible { + outline: 2px solid var(--wp--custom--color--focus--ring); + outline-offset: -2px; + } } }