Site chrome and navigation
The header, footer, mega menu, mobile menu, country switcher and the logged-in/logged-out state in the header. All of it is shared across the whole multisite network and varies by blog id.
Why it exists
Section titled “Why it exists”One theme serves six or seven country sites with the same design but different menus, LMS links and localisation. The nav is a three-level mega menu that WordPress’s default walker cannot render, so the theme carries both a bespoke renderer and two custom walkers.
The logged-in state is injected client-side because the pages are cached by WP Engine — the server-rendered HTML must be identical for guests and members.
Entry points
Section titled “Entry points”| Path | What it does |
|---|---|
wp-content/themes/supplychainschool/header.php | Header markup, logo, nav, search, country selector, LMS buttons |
wp-content/themes/supplychainschool/footer.php | Footer, four menu locations |
wp-content/themes/supplychainschool/functions.php:471 | supply_chain_mega_menu() — the bespoke three-level renderer |
wp-content/themes/supplychainschool/inc/classes/MegaMenuWalker.php | Fallback walker for other blogs |
wp-content/themes/supplychainschool/inc/classes/MobileMenuWalker.php | Mobile menu walker |
wp-content/themes/supplychainschool/inc/simple-nav-walker.php | A plain walker for footer menus |
wp-content/themes/supplychainschool/functions.php:48 | Registers four menu locations |
wp-content/themes/supplychainschool/inc/geolocation-functions.php | Country suggestion popup |
wp-content/themes/supplychainschool/inc/sitemap-shortcode.php | [simple-sitemap] |
How it works
Section titled “How it works”Menu locations
Section titled “Menu locations”primary-menu, footer, footer-notices, footer-topics
(functions.php:48). Note the primary location is registered as
primary-menu but header.php:227 requests 'theme_location' => 'primary'
on the fallback path — the two do not match, so the fallback renders
WordPress’s default “no menu assigned” behaviour rather than the primary menu.
Only the mega-menu path (which passes 'primary-menu') works as intended.
Two nav renderers
Section titled “Two nav renderers”header.php:218 branches on blog id:
- Blogs 1, 2, 6, 7 (UK, Australia, Carbon, USA) →
supply_chain_mega_menu('primary-menu'). - Everything else (France, SBCC) →
wp_nav_menu()withMegaMenuWalker.
supply_chain_mega_menu() (functions.php:471) fetches all menu items with
wp_get_nav_menu_items(), then for each top-level item calls
get_menu_item_children() (functions.php:603) — which re-fetches the whole
menu each time. For a menu with N top-level items and M children this is
O(N×M) calls to wp_get_nav_menu_items(). It is cached by WordPress’s object
cache within a request, but the loop is still doing a lot of array walking.
Three shapes come out of it:
| Condition | Markup |
|---|---|
| Children that themselves have children | li.has-mega-menu + .mega-menu with a two-column nested nav |
| Children with no grandchildren | li.has-standard-menu + .standard-menu |
| No children | plain li |
Each dropdown gets a “View all X” link whose text comes from
view_all_link_text() (functions.php:585) — “Contact us” for Contact,
“View all Learning resources” for Learn, otherwise “View all {title}”. For
the Learn item the URL is rewritten to
LMS_URL . get_lms_url('resources'), i.e. it points at Moodle rather than
WordPress (functions.php:524 and :560).
Logged-in state
Section titled “Logged-in state”header.php renders two empty containers, .header-logged-in and
.mobile-logged-in, plus static Log In / Sign Up links. The theme JS fills
them in, using the strings localised at functions.php:160:
Dashboard, Logout, Welcome, along with ajax_url and theme_uri
under the scss global.
This is why the header does not break under page caching: the guest markup is what gets cached and the member state is a client-side swap.
Country switching
Section titled “Country switching”Two independent mechanisms:
- The header selector (
header.php:258and again for mobile at:330) — a hard-coded list of UK, IE, US, AU with absolute production URLs. Australia and the SBCC/Climate Action Hub sites are not both represented. - The geolocation popup (
inc/geolocation-functions.php) — reads Cloudflare’sHTTP_CF_IPCOUNTRYheader, defaults toGB, and enqueuescountry-popup.jswith a suggested URL.
Localisation
Section titled “Localisation”CMS_LOG_IN_PATH (/log-in, or /se-connecter on the French site) and
TOPICS_SLUG (topics / sujets) come from the per-blog constants in
LMSClient.php — see [[moodle-sso-and-sessions]]. get_topic_url()
(functions.php:451) walks a term’s parents to build a nested topic URL under
TOPICS_SLUG.
Blog 3 (France) also gets a different stylesheet:
navigation-legacy.css instead of navigation.css (functions.php:139).
Other chrome
Section titled “Other chrome”X-UA-Compatible: IE=edgesent for IE user agents (functions.php:305).wp_generatorandwlwmanifest_linkremoved fromwp_head(functions.php:319).- Google Tag Manager in
header.php(conditional). [simple-sitemap]shortcode lists all public post types exceptattachment,product,tipsites, orderingpagethenpostfirst (inc/sitemap-shortcode.php:9).
Configuration
Section titled “Configuration”- Menus are assigned in Appearance → Menus, per subsite.
- The logo is an ACF option field
logoon the Company Info options page, falling back toassets/images/logo.svg(header.php:193). - Typekit fonts load from
//use.typekit.net/vgd7cqv.css(functions.php:136). - No environment variables.
Invariants and gotchas
Section titled “Invariants and gotchas”- The
primaryvsprimary-menumismatch atheader.php:227means the non-mega-menu blogs (France, SBCC) do not render the registered primary menu location. If a French menu “won’t show”, start there. - Adding a country site means editing three hard-coded lists: the blog-id
branch at
header.php:218, the desktop country selector at:258, the mobile one at:330, and the$country_sitesmap ininc/geolocation-functions.php:14. Nothing derives these fromget_sites(). - The geolocation popup dereferences
$country_sites[$visitor_country]without checking the key exists (inc/geolocation-functions.php:31), so a visitor from an unlisted country produces a PHP warning. The staging branch of that map only hasGBandAU, and mapsAUto the Ireland staging URL. $current_countryin the same file is initialised to''and never set — the comment says “Map blog ID to country code” and the whole same-country check is commented out below (:33). The popup therefore has no idea which site you are already on.get_menu_item_children()re-fetches the entire menu per call. Do not add another nested level without restructuring; the cost compounds.- Menu output is unescaped —
echo $menu_item->urlandecho $titlethroughoutsupply_chain_mega_menu(). Menu content is admin-entered, but it is worth knowing before pulling any of it from an external source. - Header markup is duplicated between desktop and mobile (search form, country selector, LMS buttons). Change one, check the other.
Changing it safely
Section titled “Changing it safely”- Nav structure changes go in
supply_chain_mega_menu()for blogs 1/2/6/7 and inMegaMenuWalkerfor the rest. Both must be updated, or the smaller sites diverge. - New “View all” wording:
view_all_link_text()only. There is a duplicateswitchinline atfunctions.php:554that predates the helper and computes$view_all_textinto a variable which is then never used — the helper’s return value is what renders. Delete the dead switch rather than editing it. - Header/footer CSS lives in
resources/assets/sass/;header.scssandsidebar.scssin the Carbon theme are separate. Runnpm run prodand commitassets/. - Verify by hand on at least two blogs — one mega-menu blog and one fallback blog — logged out and logged in, at desktop and mobile widths. The logged-in swap is JS, so check with caching enabled.
- Deliberately not abstracted: the two renderers. The mega menu needs
three levels with a specific two-column DOM that a
Walker_Nav_Menucannot express cleanly; the walker path is the simpler legacy fallback.
None.
Related: [[moodle-sso-and-sessions]], [[acf-flexible-content-blocks]], [[site-search]], [[forms-spam-and-marketing-tracking]].