/**
* Chrome Tint
* -
* Tint the browser chrome (status bar + home indicator area) to match
* the site header / footer when those elements are in view.
*
* How it works on each browser:
*
* - Chrome and iOS ≤ 18:
* drives the top status bar tint. We
* update it dynamically. (Ignored by iOS 26+, but kept for
* compatibility with older browsers and Chrome on Android.)
*
* - iOS 26+ Safari:
* The tag is ignored. Instead, iOS
* samples the background-color of `position: fixed` elements
* within 4px of the top or 3px of the bottom of the viewport
* (at least 80% wide, at least 12px tall) and tints the chrome
* to match. We set the inline `style.backgroundColor` of two
* real fixed
s (`.chrome-tint--top` and
* `.chrome-tint--bottom`) declared in header.php.
*
* Importantly, iOS 26 only takes ONE color per region (top OR
* bottom), so we can run the two regions independently. This
* lets the top go header-color while the bottom is footer-color
* when both elements are in view (e.g. on short pages).
*
* Colors:
* - HEADER_COLOR (#032F46) is the darker end of the .site-header
* gradient.
* - FOOTER_COLOR (#102C45) is the sRGB equivalent of the
* --color-cwc-blue-02 token used by .site-footer.
*
* "In view" = any pixel of the element overlaps the viewport.
*
* No-ops if either the header or footer is missing, or if
* IntersectionObserver isn't supported (older browsers).
*/
const HEADER_COLOR = '#032F46';
const FOOTER_COLOR = '#102C45';
const THEME_META_NAME = 'theme-color';
function ensureThemeColorMeta() {
let meta = document.head.querySelector(`meta[name="${THEME_META_NAME}"]`);
if (!meta) {
meta = document.createElement('meta');
meta.setAttribute('name', THEME_META_NAME);
document.head.appendChild(meta);
}
return meta;
}
function setThemeColor(color) {
const meta = ensureThemeColorMeta();
if (color == null) {
meta.removeAttribute('content');
} else {
meta.setAttribute('content', color);
}
}
function setFixedBackground(el, color) {
if (!el) return;
if (color == null) {
el.style.removeProperty('background-color');
} else {
el.style.backgroundColor = color;
}
}
function initChromeTint() {
if (typeof IntersectionObserver === 'undefined') return;
const header = document.querySelector('.site-header');
const footer = document.querySelector('.site-footer');
if (!header && !footer) return;
const topEl = document.querySelector('.chrome-tint--top');
const bottomEl = document.querySelector('.chrome-tint--bottom');
// Top tint state. Truthy means the header is currently in view.
let headerInView = false;
// Bottom tint state. Truthy means the footer is currently in view.
let footerInView = false;
const apply = () => {
// Top tint = header color ONLY when the header is in view.
const topColor = headerInView ? HEADER_COLOR : null;
setFixedBackground(topEl, topColor);
setThemeColor(topColor);
// Bottom tint = footer color ONLY when the footer is in view.
const bottomColor = footerInView ? FOOTER_COLOR : null;
setFixedBackground(bottomEl, bottomColor);
};
const buildObserver = (el, onChange) => {
const io = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
onChange(entry.isIntersecting);
}
apply();
},
// Zero threshold + no rootMargin = "any pixel in view".
{ threshold: 0, rootMargin: '0px' },
);
io.observe(el);
return io;
};
if (header) buildObserver(header, (v) => { headerInView = v; });
if (footer) buildObserver(footer, (v) => { footerInView = v; });
}
export default initChromeTint;