Header, footer and navigation
The header and footer are on every page: a mega-menu built by hand from the
header menu, a search box, a phone number and CTA from Global Options, a
ReviewSolicitors rating widget, the Moneypenny live-chat script, GTM and legacy
Universal Analytics, then a footer of four menus, office details, accreditation
logos, a newsletter capture and a timed opening-hours popup.
Anything here appears on 100% of pages, so a mistake in this feature is a site-wide mistake.
Why it exists
Section titled “Why it exists”The mega menu is hand-rolled rather than a nav walker because it has to inject
non-menu content: a column of latest insights, a sticky-post pod, or a custom
promo card, all configured per top-level menu item. WordPress nav walkers cannot
easily do that, so inc/navigation.php reads the menu items itself and renders
the tree directly.
The footer carries the trust signals a regulated firm needs — accreditations, a Cyber Essentials certificate, office addresses — and those come from content, not markup.
Entry points
Section titled “Entry points”| Path | What it does |
|---|---|
wp-content/themes/bpcollins/header.php |
<head>, third-party scripts, Organization JSON-LD, header markup, search forms |
wp-content/themes/bpcollins/inc/navigation.php |
The desktop mega menu / dropdown menu |
wp-content/themes/bpcollins/inc/extended-nav-walker.php |
ExtendedNavWalker — used for the mobile menu |
wp-content/themes/bpcollins/inc/simple-nav-walker.php |
SimpleNavWalker — used for the footer link row |
wp-content/themes/bpcollins/footer.php |
Newsletter, footer menus, offices, logos, social, popups |
wp-content/themes/bpcollins/functions.php:46-53 |
Registered menu locations |
How it works
Section titled “How it works”Menu locations registered in strategiq_setup(): primary, footer-other,
footer-services-business, footer-services-individual, footer-sectors,
footer.
Desktop navigation. inc/navigation.php calls
wp_get_nav_menu_items('header') — note: a menu identified by the name/slug
header, not by a registered theme location. It flattens every item into an
array carrying its ACF fields (show_latest_insights, is_mega_menu,
toggle_custom_fields, custom_image, custom_title, custom_text,
custom_link, column_layout), then links children to parents by
menu_item_parent.
Each top-level item renders either:
- a mega menu (
is_mega_menu == 1) — alevel-02→level-05nested list, plus two extra columns:site-header__faux-columnshowing two sticky posts whenshow_latest_insightsis set, andsite-header__post-columnshowing either the custom promo card (whentoggle_custom_fieldsis on) or the single latest sticky post as apost-podpartial; or - a plain dropdown (
site-header__dropdown-menu) with levels 02–04.
Active state is computed by comparing home_url($wp->request) . '/' against each
item’s URL, descending up to six levels via nested foreach loops
(inc/navigation.php:66-116).
Mobile navigation uses wp_nav_menu() with the primary theme location and
ExtendedNavWalker, depth => 5 (header.php:380).
Header extras. Logo and phone number come from the logo_dark and
telephone_number global options; the CTA button from header_cta +
header_cta_icon. Two identical search forms exist — desktop (#searchform) and
mobile (#searchformmobile) — both plain GET forms posting s to the site root.
A ReviewSolicitors widget is embedded twice — desktop rswidget_0QfPE with the
transparent template, mobile rswidget_0QfPE1 with wide-simple — both via
rs.loadWidget(...) against business id 13861.
The Trustpilot equivalents are present but commented out.
Above-the-fold CSS is inlined into <head> with
file_get_contents(get_template_directory() . '/assets/css/abovethefold.css')
(header.php:8-10), and a <noscript> block neutralises the GSAP fade-in classes
so content is visible without JS.
Third-party scripts in <head>: Moneypenny live chat (loaded from a
storage.googleapis.com URL, with callbacks that push events into the legacy ga
object), Universal Analytics analytics.js with ga('create', ...), Google Tag
Manager, and preconnect hints for Google Fonts and ajax.googleapis.com. A
static Organization JSON-LD block with the firm’s address, phone, email, logo
and social profiles is printed directly in header.php.
Footer. Optional newsletter band (enable_newsletter, footer_text,
footer_button_text) opening a modal with a CF7 form
(newsletter_popup_form + heading/image/content). Four wp_nav_menu() calls for
the business services, individual services, sectors and “other” locations. An
offices list built from up to 10 office posts ordered by menu_order, linking
via each office’s page_link. A logo row: logo_light, a repeater of
footer_logos with optional links, a hardcoded Cyber Essentials certificate
iframe from registry.blockmarktech.com, and an optional raw footer_iframe
field. Social links from the social_links repeater, rendered as background
images. A footer link row using SimpleNavWalker, a copyright notice, and a
UTM-tagged credit link back to StrategiQ.
Opening-hours popup. footer.php:378+ reads enable_opening_hours_popup,
opening_popup_display_type and pop_up_display and shows one of three
configured popups after a delay (popup_1_time default 5s, popup_2_time 3s,
popup_3_time 0s), each with its own title, content, image and CTA fields.
flowchart TD A[header.php] --> B[inline abovethefold.css] A --> C[Moneypenny + Universal Analytics + GTM] A --> D[static Organization JSON-LD] A --> E[inc/navigation.php: wp_get_nav_menu_items header] E --> F{is_mega_menu?} F -->|yes| G[levels 02-05 + insights column + sticky pod or promo card] F -->|no| H[dropdown levels 02-04] A --> I[mobile: wp_nav_menu primary + ExtendedNavWalker] J[footer.php] --> K[newsletter modal with CF7 form] J --> L[4 footer menus + offices from office CPT] J --> M[logos, Cyber Essentials iframe, social links] J --> N[timed opening-hours popup]Configuration
Section titled “Configuration”Global Options ACF: logo_dark, logo_light, telephone_number, header_cta,
header_cta_icon, copyright_notice, footer_logos, social_links,
footer_iframe, enable_newsletter, footer_text, footer_button_text,
newsletter_popup_form, newsletter_popup_heading, newsletter_popup_image,
newsletter_popup_content, enable_opening_hours_popup,
opening_popup_display_type, pop_up_display, and the popup_1/popup_2/
popup_3 field sets including their *_time delays.
Per menu item ACF: is_mega_menu, show_latest_insights, column_layout,
toggle_custom_fields, custom_image, custom_title, custom_text,
custom_link.
Hardcoded in header.php / footer.php (not options): the GTM container id, the
Universal Analytics property id, the Moneypenny script URL, the Facebook
domain-verification token, the ReviewSolicitors business id, the Cyber Essentials
certificate iframe URL, and the Organization JSON-LD values.
Cookie consent is provided by the CookieHub plugin (a required plugin), not by theme code.
Invariants and gotchas
Section titled “Invariants and gotchas”- The desktop menu is bound to a menu named
header, the mobile menu to theprimarylocation. They are not guaranteed to be the same menu. If desktop and mobile navigation disagree, check whether theheadermenu is also assigned toprimary. wp_get_nav_menu_items('header')returnsfalseif no such menu exists, andinc/navigation.php:13immediatelyforeaches it — a renamed menu is a PHP warning and an empty header, not a graceful fallback.- Active-state detection compares full URLs and nests six levels deep
(
inc/navigation.php:66-116). Any URL mismatch — missing trailing slash,www, http/https, a query string — loses the highlight. This is also why canonicals are force-trailing-slashed elsewhere. - The inner
foreachloops all reuse$childas the loop variable (inc/navigation.php:76-106), shadowing the outer loops. It happens to work because each level re-reads from$filtered_nav, but do not add logic after a nested loop expecting$childto be intact. $check_child_elementsusesarray_search(...)on a column of parents (inc/navigation.php:119), which returns the array key of the first match — falsy when that key is0. A top-level item whose first child happens to be the first element of$filtered_navloses itshas-childrenclass and its dropdown.- Two live analytics implementations coexist: GTM and a directly embedded
Universal Analytics snippet.
ga()is also the transport for every Moneypenny event callback, so removing the UA snippet silently kills chat event tracking. Universal Analytics itself no longer processes data. - Moneypenny and ReviewSolicitors load synchronously in
<head>(the RS widget script has noasync), beforewp_head(). They are render-blocking third parties on every page. - The Cyber Essentials badge is an
<iframe>with a hardcoded certificate URL (footer.php:~296) — it expires when the certification does, and nothing in the CMS controls it. footer_iframeis echoed raw — it is an editor-supplied HTML field with no filtering. Only trusted editors should have access.- Offices in the footer link to
page_link, not the office permalink, becauseofficeis a non-public post type. An office with nopage_linkrenders an emptyhref. - The footer offices query is capped at 10 (
footer.php:180). - The
primarylocation is registered but only used by the mobile menu; the desktop tree ignores it.
Changing it safely
Section titled “Changing it safely”- Adding an item to the mega menu → do it in Appearance → Menus on the
headermenu, and set the ACF fields on the parent item. No code change needed for up to five levels. - Adding a sixth level of nesting → the render loops stop at
level-05(inc/navigation.php:165-175) even though the active-state check descends six. Both need extending. - Adding a third-party script → prefer GTM. If it must be in the theme, add it to
header.phpbelowwp_head()unless it genuinely must run first, and give itasync/defer. - Changing header markup → check the mobile menu, both search forms, and the two ReviewSolicitors widget containers; they have distinct DOM ids that the vendor script targets by string.
- Deliberately not abstracted:
inc/navigation.phprenders the tree with explicit nested loops rather than recursion, because each level has different markup and extra columns. A recursive rewrite would need to preserve all of that — do not attempt it as a tidy-up. - Verify after any change: a top-level item with a mega menu, one with a plain dropdown, one with no children, a deep page (highlighting), mobile menu open/close, and both search boxes.
None. No automated tests exist in this repository. Header and footer changes affect every page, so check at least one page of each template type.