Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
96503b5
Finish the mega menu: full-bleed panels with section headers, white h…
mlaetitia Jul 28, 2026
f998aeb
Add a partner-editable featured content card to megamenu panels via A…
mlaetitia Jul 30, 2026
87a113f
Use wp_get_attachment_image for the featured card so the theme's widt…
mlaetitia Jul 30, 2026
63e295e
Move rationale comments to docblocks/file headers and drop comments t…
mlaetitia Jul 30, 2026
c314141
Apply the megamenu feedback round: two-column panels aligned to the h…
mlaetitia Aug 12, 2026
d0604fb
Center the header buttons in the sticky header row instead of pinning…
mlaetitia Aug 13, 2026
aae56bc
Use core's admin-bar height variable instead of hardcoded offsets and…
mlaetitia Aug 13, 2026
502dd8a
Stop panel switches from cross-fading (dim flashed through two transl…
mlaetitia Aug 13, 2026
f914dfe
Trim CSS comments to the ones that prevent silent breakage
mlaetitia Aug 13, 2026
3953bcd
Make the mobile menu CTA editable via a link field on the primary men…
mlaetitia Aug 13, 2026
b9cefed
Hide the mobile menu CTA when the field is empty instead of falling b…
mlaetitia Aug 13, 2026
de9d399
Remove a stray local screenshot and ignore root-level images
mlaetitia Aug 13, 2026
2103ca4
Remove the root-level image ignore rule
mlaetitia Aug 13, 2026
d0dec0a
Match develop's trimmed SCSS comments in the megamenu subnav styles a…
mlaetitia Aug 26, 2026
d6590b8
Bring the trunk branch up to date with the megamenu work merged to de…
mlaetitia Sep 1, 2026
6e41e29
Remove the AI secondary navigation, its styles and its menu location
mlaetitia Sep 7, 2026
217d478
Fix review findings: Escape closing, dropdown contrast, mobile accord…
mlaetitia Sep 7, 2026
a1aca36
Mark the current page with weight alone, dropping its underline at re…
mlaetitia Sep 9, 2026
3c7997c
Let the current page keep the hover underline, so it behaves like eve…
mlaetitia Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -418,3 +418,6 @@ $RECYCLE.BIN/
# .pnp.*

# End of https://www.toptal.com/developers/gitignore/api/macos,linux,windows,node,yarn,composer,phpstorm+all,visualstudiocode,sublimetext

# local browser-test artifacts
.playwright-mcp/
52 changes: 6 additions & 46 deletions themes/osi/assets/css/editor-style.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion themes/osi/assets/css/editor-style.css.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion themes/osi/assets/js/build/theme.asset.php
Original file line number Diff line number Diff line change
@@ -1 +1 @@
<?php return array('dependencies' => array(), 'version' => '48775f60a0024fbe4d0a');
<?php return array('dependencies' => array(), 'version' => 'f179507f6e77a9763a06');
2 changes: 1 addition & 1 deletion themes/osi/assets/js/build/theme.js

Large diffs are not rendered by default.

152 changes: 152 additions & 0 deletions themes/osi/assets/js/src/theme/mega-menu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
/*
Name: Mega Menu
Author: Team 51
Version: 0.1
License: GPLv2

Desktop behavior for .menu-item.megamenu items: keeps aria-expanded in sync,
enforces one open panel at a time, closes on Escape or press outside, and
mirrors the open state onto the header (`is-nav-open`) as the no-:has()
fallback for the CSS opaque-header rule, with a close-delay grace period.
Placeholder (href="#") triggers also toggle on click because taps don't
reliably fire mouseenter/focusin at desktop widths (iPadOS).
*/

// keep in sync with $break-nav (assets/scss/_1_settings.breakpoints.scss)
const desktopNav = window.matchMedia( '(min-width: 1200px)' );
const CLOSE_DELAY = 120;

const header = document.querySelector( '.header-main' );
const megaItems = document.querySelectorAll( '.nav-main--menu > .menu-item.megamenu' );

if ( header && megaItems.length ) {
const triggerOf = ( item ) => item.querySelector( ':scope > a' );

const syncHeader = () => {
header.classList.toggle(
'is-nav-open',
Boolean( document.querySelector( '.nav-main--menu > .menu-item.megamenu.is-open' ) )
);
};

const dismiss = ( item ) => {
closeItem( item );
item.classList.add( 'is-dismissed' );
syncHeader();
};

const closeItem = ( item ) => {
item.classList.remove( 'is-open' );
const trigger = triggerOf( item );
if ( trigger ) {
trigger.setAttribute( 'aria-expanded', 'false' );
}
};

const closeAll = () => {
megaItems.forEach( closeItem );
syncHeader();
};

megaItems.forEach( ( item ) => {
const trigger = triggerOf( item );
let closeTimer = null;
let suppressOpen = false;

if ( trigger ) {
trigger.setAttribute( 'aria-expanded', 'false' );
}

const open = () => {
if ( suppressOpen || ! desktopNav.matches ) {
return;
}
window.clearTimeout( closeTimer );
megaItems.forEach( ( other ) => {
if ( other !== item ) {
closeItem( other );
}
} );
item.classList.add( 'is-open' );
if ( trigger ) {
trigger.setAttribute( 'aria-expanded', 'true' );
}
syncHeader();
};

const close = ( immediately ) => {
window.clearTimeout( closeTimer );
const doClose = () => {
closeItem( item );
syncHeader();
};
if ( immediately ) {
doClose();
} else {
closeTimer = window.setTimeout( doClose, CLOSE_DELAY );
}
};

if ( trigger && '#' === trigger.getAttribute( 'href' ) ) {
trigger.addEventListener( 'click', ( event ) => {
if ( ! desktopNav.matches ) {
return;
}
event.preventDefault();
if ( item.classList.contains( 'is-open' ) ) {
close( true );
} else {
open();
}
} );
}

item.addEventListener( 'mouseenter', open );
item.addEventListener( 'mouseleave', () => {
item.classList.remove( 'is-dismissed' );
close( false );
} );
item.addEventListener( 'focusin', open );
item.addEventListener( 'focusout', ( event ) => {
if ( ! item.contains( event.relatedTarget ) ) {
close( false );
}
} );
// returning focus to the trigger fires focusin, which would reopen the panel
item.addEventListener( 'keydown', ( event ) => {
if ( 'Escape' === event.key && item.classList.contains( 'is-open' ) ) {
suppressOpen = true;
dismiss( item );
if ( trigger ) {
trigger.focus();
}
suppressOpen = false;
}
} );
} );

document.addEventListener( 'pointerdown', ( event ) => {
if (
! event.target.closest( '.nav-main--menu > .menu-item.megamenu' ) &&
document.querySelector( '.nav-main--menu > .menu-item.megamenu.is-open' )
) {
closeAll();
}
} );

// a panel opened by hover holds focus nowhere, so Escape has to be caught globally
document.addEventListener( 'keydown', ( event ) => {
if (
'Escape' === event.key &&
document.querySelector( '.nav-main--menu > .menu-item.megamenu.is-open' )
) {
megaItems.forEach( dismiss );
}
} );

desktopNav.addEventListener( 'change', ( event ) => {
if ( ! event.matches ) {
closeAll();
}
} );
}
74 changes: 57 additions & 17 deletions themes/osi/assets/js/src/theme/mobile-menu-toggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,69 @@ License: GPLv2
http://www.gnu.org/licenses/gpl-2.0.html
*/

// toggle buttons are inserted as siblings of the links: a <button> inside an <a> is invalid markup
(function($) {

function toggleSubmenu($button) {
var $item = $button.closest('.menu-item-has-children');
var $submenu = $button.siblings('.sub-menu').first();
var opening = $submenu.hasClass('menu-collapse');

// accordion: opening an item closes its open siblings at the same level
if (opening) {
$item.siblings('.tab-active').each(function() {
toggleSubmenu($(this).children('.menu-toggle').first());
});
}

$submenu.toggleClass('menu-collapse', !opening);
$item.toggleClass('tab-active', opening);
$item.parent().closest('.sub-menu').toggleClass('can-overflow', opening);
if (!opening) {
$item.find('.sub-menu').addClass('menu-collapse').removeClass('can-overflow');
$item.find('.tab-active').removeClass('tab-active');
$item.find('.menu-toggle').removeClass('menu-toggle-active').attr('aria-expanded', 'false');
$item.find('a[aria-expanded]').attr('aria-expanded', 'false');
}
$button.toggleClass('menu-toggle-active', opening).attr('aria-expanded', String(opening));
$button.siblings('a[aria-expanded]').first().attr('aria-expanded', String(opening));
}

$('.sub-menu').addClass('menu-collapse');
$('.menu-item-has-children > a').append('<button aria-label="Toggle Submenu" class="menu-toggle"></button>');
$('.menu-item-has-children > a').each(function( index ) {
var $link = $(this);
var $submenu = $link.siblings('.sub-menu').first();

$('.menu-toggle').on('click', function(e) {
if (!$submenu.length) {
return;
}

e.preventDefault();
var id = $submenu.attr('id') || 'osi-submenu-' + index;
$submenu.attr('id', id);
var $button = $('<button class="menu-toggle"></button>')
.attr({
'aria-label': 'Toggle submenu for ' + $link.text().trim(),
'aria-expanded': 'false',
'aria-controls': id
})
.insertAfter($link);

if ($(this).parent().next('.sub-menu').hasClass('menu-collapse')) {
$(this).parent().next('.sub-menu').removeClass('menu-collapse');
$(this).parent().parent('.menu-item-has-children').addClass('tab-active');
$(this).parent().parent().parent('.sub-menu').addClass('can-overflow');
$(this).addClass('menu-toggle-active');
e.stopPropagation();
} else {
$(this).parent().next('.sub-menu').addClass('menu-collapse');
$(this).parent().parent('.menu-item-has-children').removeClass('tab-active');
$(this).parent().parent().parent('.sub-menu').removeClass('can-overflow');
$(this).parent().parent().find('.sub-menu').removeClass('can-overflow');
$(this).removeClass('menu-toggle-active');
e.stopPropagation();
}
// the mobile menu's whole row is the disclosure: the link toggles instead of
// navigating, and the caret stays a visual/tap affordance outside the tab order
if ($link.closest('.nav-mobile--menu').length) {
$link.attr('aria-expanded', 'false');
$button.attr({ 'aria-hidden': 'true', tabindex: '-1' });
$link.on('click', function(e) {
e.preventDefault();
toggleSubmenu($button);
});
}
});

$('.menu-toggle').on('click', function(e) {
e.preventDefault();
e.stopPropagation();
toggleSubmenu($(this));
});

})( jQuery );
Loading
Loading