Purpose#
LayoutA.liquid is the main layout template of Noir.
It renders the global HTML skeleton and composes the page using:It also loads the theme's CSS/JS and injects runtime globals used by client-side code.
Where it's used#
This layout is the entry point for page rendering in Noir.
All pages that use Layout A will pass through this template.
High-level structure#
Page identity#
The layout relies on a server-provided Root.Page.Identity.* object for page-level decisions:RendersOwnPageTitle — when true, the layout hides its own H1 (the page renders its title itself).
BodyClass — the page-level class added to <body>.
Template — emitted as data-template on <body>.
Handle — emitted as data-handle on <body>.
HTML & language mode#
Sets <html lang="..."> using Root.Page.Metas.IsoLang
Adds the dark class only when both conditions are true:Root.GlobalData.Settings.ShowDarkMode is enabled (settings gate), and
Root.Page.Data.Darkmode is enabled (per-page flag)
Head includes#
In <head>, the layout renders shared infrastructure includes:Structure\CssVariables (rendered with GlobalData.Settings as Settings)
Structure\GoogleTagManager
It also loads core styles:vendor CSS (swiper, lightgallery, nouislider, flatpickr, leaflet, markercluster)
theme CSS bundle: output.css
optional custom CSS: GlobalData.Settings.CustomCss
Preloads the Ubuntu font family (woff2) and the icon font (icons.woff).
Does not load the standard recaptcha/api.js globally. The Form component injects it on demand, only on pages where a form shows the captcha. Conditionally loads reCAPTCHA Enterprise based on GlobalData.Settings.Captcha (only when at least one captcha action is enabled). The script variant depends on Captcha.GoogleRecaptchaEnterprise.KeyRequiresChallenge.
Asset paths and versioningAsset base path is GlobalData.RootPath
Cache busting uses GlobalData.Settings.Version
Page composition (Header / Body / Footer)#
{% render 'Structure\\ComponentsList' with HeaderComponents as components %}
Main section (#app)#
Inside <main id="app">, Noir renders:A Skip to content accessibility link (targets #app)
Toast reusable: Reusables\Toast\Default
Breadcrumb component: Components\Breadcrumb\Default
Page title block (H1, shown/hidden based on the page flag and breadcrumb depth)
{% render 'Structure\\ComponentsList' with BodyComponents as components %}
Body class#
The layout builds the <body> class list from server-provided values:Root.Page.Identity.BodyClass — page-level class provided by the platform (used for styling/targeting without hardcoding routes).
{{GlobalCssClass}} — platform-provided extra global CSS class.
It also sets data attributes for targeting:data-template="{{ Root.Page.Identity.Template }}"
data-handle="{{ Root.Page.Identity.Handle }}"
handheld-toolbar-enabled — enables layout behavior for the mobile toolbar.
plain-page — added only when the page title block is visible (i.e. when hide_div == false; see H1 visibility rules below).
H1 / page title block#
The layout includes an H1 in a .page-title wrapper:<div class="page-title{% if hide_div %} sr-only{% endif %}" {% if hide_div %} tabindex="-1"{% endif %}>
<div class="container">
<h1 class="-mb-6 lg:-mb-20">{{ Page.metas.title }}</h1>
</div>
</div>
Visibility rules (the hide_div flag):The title block is visually hidden (sr-only) when Root.Page.Identity.RendersOwnPageTitle is true (the page renders its own title, e.g. cart/checkout/product/category).
It is also hidden when Root.Page.Breadcrumbs.size > 2 (deeper navigation paths where the breadcrumb already provides context).
When the title block is visible (hide_div == false), the layout adds plain-page on the <body>.
Breadcrumbs#
The breadcrumb UI is always rendered from the layout:{% render 'Components\\Breadcrumb\\Default' %}
Breadcrumb data comes from Root.Page.Breadcrumbs.
Breadcrumb depth is also used by the layout to decide whether the H1/title block should be shown.
The footer is rendered after </main> and is composed via the footer component list.{% render 'Structure\\ComponentsList' with FooterComponents as components %}
Global UI / Modals outside main#
After <main>, the layout renders shared UI that should exist on all pages:Reusables\BackToTop\Default
Reusables\ProductComparisonFloatingButton\Default
Reusables\PromptModal\Default
Reusables\ProductModal\Default
Reusables\LoginModal\Default
Debugging helpers#
Near the end of the body, the layout includes:Structure\\StagingButton (staging/dev helper)
When the environment is staging, Structure\\StagingButton renders Structure\\Json to log a serialized view model in DevTools.This is very useful when validating component model contracts.Script loading order (very important)#
The layout loads scripts using defer to ensure predictable execution after parsing.Vendor libraries: axios, swiper, lightgallery, nouislider, flatpickr, leaflet, markercluster
/Themes/Noir/componentscripts.js
/Themes/Noir/Assets/js/theme.js
Why this matters#
componentscripts.js typically registers component/reusable JS objects.
theme.js contains shared utilities and Alpine stores used across components.
Alpine runs after the above are available.
Runtime globals injected into window#
LayoutA.liquid defines configuration flags used by client-side behavior:window.translations (example keys used by UI, e.g. Shopping Lists)
window.showProductComparison
These values are referenced by Assets/js/theme.js and various components/reusables.Recommendations#
Page title visibility: title block visibility depends on page type and breadcrumb size.
Global UI: modals/toast exist on every page by design; avoid duplicating them inside components.
Versioning: always keep version query params for asset updates to propagate correctly in cached environments.
Modified at 2026-10-15 13:48:29