From 53f6ca141c5cf7dd40aa76b2d2e7690c3ab519e4 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 16:47:22 -0700 Subject: [PATCH 01/21] Refactor text normalization and accessibility functions Refactor text normalization and accessibility handling in header.js. Introduce cleanNavText function to improve text processing. --- group/blocks/header/header.js | 58 ++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index c8f01de..8430b31 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -35,6 +35,32 @@ function normalizeText(text = '') { return text.replace(/\s+/g, ' ').trim(); } +const NEW_WINDOW_TEXT = 'Open the link in a new window'; + +/** + * Removes accessibility helper text that may have been added to links before + * this header parses and rebuilds the navigation. + * @param {string} text + * @returns {string} + */ +function cleanNavText(text = '') { + return normalizeText(text.split(NEW_WINDOW_TEXT).join('')); +} + +/** + * Gets the visible label text from an element without screen-reader-only copy. + * @param {Element} el + * @returns {string} + */ +function getElementText(el) { + if (!el) return ''; + + const clone = el.cloneNode(true); + clone.querySelectorAll('.sr-only').forEach((srOnly) => srOnly.remove()); + + return cleanNavText(clone.textContent); +} + function slugify(text = '') { return normalizeText(text) .toLowerCase() @@ -85,8 +111,8 @@ function getDirectAnchor(li) { function getDirectTextWithoutNestedList(li) { const clone = li.cloneNode(true); - [...clone.querySelectorAll('ul, ol')].forEach((nested) => nested.remove()); - return normalizeText(clone.textContent); + [...clone.querySelectorAll('ul, ol, .sr-only')].forEach((nested) => nested.remove()); + return cleanNavText(clone.textContent); } function getHrefOrFallback(anchor, fallback = '#') { @@ -138,7 +164,7 @@ function getBooleanMetadata(name) { function isUtilitySection(section) { if (!section) return false; - const text = normalizeText(section.textContent).toLowerCase(); + const text = cleanNavText(section.textContent).toLowerCase(); return ( text.includes('select') @@ -159,7 +185,7 @@ function isUtilitySection(section) { function isBrandSection(section) { if (!section) return false; - const text = normalizeText(section.textContent); + const text = cleanNavText(section.textContent); const list = getFirstList(section); return text.includes(':logo:') || (!list && text.length > 0); @@ -237,7 +263,7 @@ function parseUtilitySection(section) { items.forEach((li) => { const labelAnchor = getDirectAnchor(li); - const label = normalizeText(labelAnchor?.textContent || getDirectTextWithoutNestedList(li)); + const label = getElementText(labelAnchor) || getDirectTextWithoutNestedList(li); const href = getHrefOrFallback(labelAnchor, '#'); const nestedList = getDirectNestedList(li); @@ -246,9 +272,7 @@ function parseUtilitySection(section) { .map((childLi) => { const childAnchor = getDirectAnchor(childLi); return { - label: normalizeText( - childAnchor?.textContent || getDirectTextWithoutNestedList(childLi), - ), + label: getElementText(childAnchor) || getDirectTextWithoutNestedList(childLi), href: getHrefOrFallback(childAnchor, '#'), }; }) @@ -270,7 +294,7 @@ function parseUtilitySection(section) { [...section.querySelectorAll('a')] .filter((anchor) => !anchor.closest('ul, ol')) .forEach((anchor) => { - const label = normalizeText(anchor.textContent); + const label = getElementText(anchor); const href = getHrefOrFallback(anchor, '#'); addUtilityItem(label, href); }); @@ -289,7 +313,7 @@ function parseUtilitySection(section) { }); } else { const anchors = [...section.querySelectorAll('a')].map((anchor) => ({ - label: normalizeText(anchor.textContent), + label: getElementText(anchor), href: getHrefOrFallback(anchor, '#'), })).filter((item) => item.label); @@ -306,7 +330,7 @@ function parseUtilitySection(section) { }); } else { const lines = [...section.querySelectorAll('p')] - .map((p) => normalizeText(p.textContent)) + .map((p) => cleanNavText(p.textContent)) .filter(Boolean); lines.forEach((line) => { @@ -368,14 +392,14 @@ function parseBrandSection(section) { const link = getFirstLink(section); const textCandidates = [...section.querySelectorAll('p, h1, h2, h3, h4, h5, h6')] - .map((el) => normalizeText(el.textContent)) + .map((el) => cleanNavText(el.textContent)) .filter((text) => text && !/^:logo:$/i.test(text)); const brandLabel = textCandidates[0]; result.label = brandLabel || result.label; result.href = getHrefOrFallback(link, HOME_FALLBACK_URL); - result.hasLogoToken = normalizeText(section.textContent).includes(':logo:'); + result.hasLogoToken = cleanNavText(section.textContent).includes(':logo:'); return result; } @@ -400,7 +424,7 @@ function parseMainNavSection(section) { getDirectListItems(list).forEach((li) => { const anchor = getDirectAnchor(li); const nestedList = getDirectNestedList(li); - const label = normalizeText(anchor?.textContent || getDirectTextWithoutNestedList(li)); + const label = getElementText(anchor) || getDirectTextWithoutNestedList(li); const href = getHrefOrFallback(anchor, '#'); if (!label) return; @@ -408,7 +432,7 @@ function parseMainNavSection(section) { if (isPhone(label)) { const detailText = nestedList ? getDirectListItems(nestedList) - .map((childLi) => normalizeText(childLi.textContent)) + .map((childLi) => cleanNavText(childLi.textContent)) .filter(Boolean) .join(' ') : ''; @@ -440,9 +464,7 @@ function parseMainNavSection(section) { .map((childLi) => { const childAnchor = getDirectAnchor(childLi); return { - label: normalizeText( - childAnchor?.textContent || getDirectTextWithoutNestedList(childLi), - ), + label: getElementText(childAnchor) || getDirectTextWithoutNestedList(childLi), href: getHrefOrFallback(childAnchor, '#'), }; }) From bfcb262e6869629cc50f10d5888390e22e2dce44 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 17:09:17 -0700 Subject: [PATCH 02/21] Add styles for new dropdown link detail --- group/blocks/header/header.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 5f6144b..76e3b12 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -464,6 +464,12 @@ header .nav-new-dropdown-link.is-featured { font-weight: 500; } +.nav-new-dropdown-link-detail { + display: block; + font-size: var(--body-font-size-xs); + line-height: 1.3; + font-weight: var(--font-weight-regular); +} /* ========================= LANGUAGE PANEL From 623ba878619ca33175b9271bb78addebe2432308 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 17:10:30 -0700 Subject: [PATCH 03/21] Implement getDropdownChildData for dropdown items Added a function to extract dropdown child label and detail text from navigation items. --- group/blocks/header/header.js | 43 ++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index 8430b31..da5785a 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -115,6 +115,31 @@ function getDirectTextWithoutNestedList(li) { return cleanNavText(clone.textContent); } +/** + * Gets a dropdown child label and any sibling detail text that sits next + * to the authored link in the nav document. + * Example authored text: "(800) 334-5847 (TTY 711) 7 a.m. - 8 p.m. PT; 7 days a week" + * The anchor text is used as the label, and the remaining text is used as detail. + * @param {Element} childLi + * @returns {{label: string, detail: string, href: string}} + */ +function getDropdownChildData(childLi) { + const childAnchor = getDirectAnchor(childLi); + const fullText = getDirectTextWithoutNestedList(childLi); + const label = getElementText(childAnchor) || fullText; + const href = getHrefOrFallback(childAnchor, '#'); + + let detail = ''; + + if (label && fullText && fullText !== label) { + detail = fullText.startsWith(label) + ? cleanNavText(fullText.slice(label.length)) + : cleanNavText(fullText.replace(label, '')); + } + + return { label, detail, href }; +} + function getHrefOrFallback(anchor, fallback = '#') { const href = anchor?.getAttribute('href'); return href || fallback; @@ -461,13 +486,7 @@ function parseMainNavSection(section) { if (nestedList) { item.children = getDirectListItems(nestedList) - .map((childLi) => { - const childAnchor = getDirectAnchor(childLi); - return { - label: getElementText(childAnchor) || getDirectTextWithoutNestedList(childLi), - href: getHrefOrFallback(childAnchor, '#'), - }; - }) + .map((childLi) => getDropdownChildData(childLi)) .filter((child) => child.label); } @@ -726,8 +745,16 @@ function buildDropdownItem(item) { href: child.href || '#', class: `nav-new-dropdown-link${index === 0 ? ' is-featured' : ''}`, }, - child.label, ); + + childLink.append(createTag('span', { class: 'nav-new-dropdown-link-label' }, child.label)); + + if (child.detail) { + childLink.append( + createTag('span', { class: 'nav-new-dropdown-link-detail' }, child.detail), + ); + } + childLi.append(childLink); list.append(childLi); }); From 8dd4954d3c5fecf0ad16c9b5423f15b3488281ed Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 17:14:39 -0700 Subject: [PATCH 04/21] Update header.css --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 76e3b12..b3619c2 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -467,7 +467,7 @@ header .nav-new-dropdown-link.is-featured { .nav-new-dropdown-link-detail { display: block; font-size: var(--body-font-size-xs); - line-height: 1.3; + line-height: 2; font-weight: var(--font-weight-regular); } From 3167ffc56593ac1edc61265aa885d945eb301d63 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 17:17:00 -0700 Subject: [PATCH 05/21] Increase font size from 14px to 16px --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index b3619c2..bd6f920 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -271,7 +271,7 @@ header .nav-new-dropdown-toggle { color: #162c55; text-decoration: none; font: inherit; - font-size: 14px; + font-size: 16px; line-height: 1; font-weight: 300; cursor: pointer; From da0abc34e8c91f6810b8fe60e74ce891d664062d Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 17:18:19 -0700 Subject: [PATCH 06/21] Increase width of nav brand image from 100px to 110px --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index bd6f920..6639d4d 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -216,7 +216,7 @@ header .nav-new-brand-link { header .nav-new-brand-image { display: block; - width: 100px; + width: 110px; height: auto; } From 023750b61a63e38921cad11db6671326786e8235 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 17:20:19 -0700 Subject: [PATCH 07/21] Increase header height from 76px to 88px --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 6639d4d..54670be 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -168,7 +168,7 @@ header .nav-new-shell { align-items: center; align-self: stretch; gap: 32px; - height: 76px; + height: 88px; background: #fbf9f7; border-radius: 104px; padding-left: 40px; From 61b866b238589e94d5e0234264a24e849767a23b Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 17:21:27 -0700 Subject: [PATCH 08/21] Increase header dropdown toggle height to 88px --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 54670be..5d85275 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -263,7 +263,7 @@ header .nav-new-dropdown-toggle { display: flex; align-items: center; gap: 8px; - height: 76px; + height: 88px; padding: 10px 8px; margin: 0; border: 0; From dfb64da62b5178484f8a924162cdc9b4e4130a73 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 19:21:10 -0700 Subject: [PATCH 09/21] Increase font size from 14px to 16px in header --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 5d85275..1f29e68 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -452,7 +452,7 @@ header .nav-new-language-link { padding: 16px 24px; color: #17316b; text-decoration: none; - font-size: 14px; + font-size: 16px; line-height: 1; font-weight: 300; background: transparent; From 13563fc32f48f8a2c95d46346433c04ae67a1c06 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 19:24:23 -0700 Subject: [PATCH 10/21] Add padding and color to nav dropdown link detail --- group/blocks/header/header.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 1f29e68..c7eab4d 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -468,6 +468,8 @@ header .nav-new-dropdown-link.is-featured { display: block; font-size: var(--body-font-size-xs); line-height: 2; + padding-top: 4px; + color: #25282a; font-weight: var(--font-weight-regular); } From 91f250f8f3d05dec5219bdb8607548b86fd44044 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 19:25:22 -0700 Subject: [PATCH 11/21] Adjust line height for nav dropdown link detail --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index c7eab4d..59cd542 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -467,7 +467,7 @@ header .nav-new-dropdown-link.is-featured { .nav-new-dropdown-link-detail { display: block; font-size: var(--body-font-size-xs); - line-height: 2; + line-height: 1.3; padding-top: 4px; color: #25282a; font-weight: var(--font-weight-regular); From 1fff4111585ca754f6593c5a12a0dba61df2a854 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 19:35:03 -0700 Subject: [PATCH 12/21] Reduce margin-bottom of header-wrapper to 40px --- group/blocks/header/header.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 59cd542..89c908d 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -3,7 +3,7 @@ header .header.block { } .header-wrapper { - margin-bottom: 120px; + margin-bottom: 40px; } /* ========================= From b6adc879d9137b42e3ffb9abe80adcdfeeafff53 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 20:29:53 -0700 Subject: [PATCH 13/21] Refactor logo handling and improve brand section logic --- group/blocks/header/header.js | 70 +++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 12 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index da5785a..47acf85 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -4,6 +4,23 @@ import { loadFragment } from '../fragment/fragment.js'; const DESKTOP = window.matchMedia('(min-width: 900px)'); const HOME_FALLBACK_URL = '/'; +const LOGOS = { + default: { + token: ':logo:', + src: '/group/icons/logo.svg', + alt: 'Blue Shield of California', + width: '110', + }, + multiState: { + token: ':multi-state-logo:', + src: '/group/icons/multi-state-logo.svg', + alt: 'Blue Shield California and National Coverage', + width: '190', + }, +}; + +const NEW_WINDOW_TEXT = 'Open the link in a new window'; + /** * Small DOM helper * @param {string} tag @@ -35,8 +52,6 @@ function normalizeText(text = '') { return text.replace(/\s+/g, ' ').trim(); } -const NEW_WINDOW_TEXT = 'Open the link in a new window'; - /** * Removes accessibility helper text that may have been added to links before * this header parses and rebuilds the navigation. @@ -61,6 +76,21 @@ function getElementText(el) { return cleanNavText(clone.textContent); } +/** + * Gets the logo data based on the logo token authored in the nav document. + * @param {Element} section + * @returns {{token: string, src: string, alt: string, width: string}} + */ +function getLogoData(section) { + const text = cleanNavText(section?.textContent || '').toLowerCase(); + + if (text.includes(LOGOS.multiState.token)) { + return LOGOS.multiState; + } + + return LOGOS.default; +} + function slugify(text = '') { return normalizeText(text) .toLowerCase() @@ -210,10 +240,14 @@ function isUtilitySection(section) { function isBrandSection(section) { if (!section) return false; - const text = cleanNavText(section.textContent); + const text = cleanNavText(section.textContent).toLowerCase(); const list = getFirstList(section); - return text.includes(':logo:') || (!list && text.length > 0); + return ( + text.includes(LOGOS.default.token) + || text.includes(LOGOS.multiState.token) + || (!list && text.length > 0) + ); } /** @@ -410,21 +444,28 @@ function parseBrandSection(section) { const result = { label: 'Blue Shield', href: HOME_FALLBACK_URL, + logo: LOGOS.default, hasLogoToken: false, }; if (!section) return result; const link = getFirstLink(section); + const logo = getLogoData(section); + const logoTokens = [LOGOS.default.token, LOGOS.multiState.token]; + const textCandidates = [...section.querySelectorAll('p, h1, h2, h3, h4, h5, h6')] .map((el) => cleanNavText(el.textContent)) - .filter((text) => text && !/^:logo:$/i.test(text)); + .filter((text) => text && !logoTokens.includes(text.toLowerCase())); const brandLabel = textCandidates[0]; - result.label = brandLabel || result.label; + result.label = brandLabel || logo.alt || result.label; result.href = getHrefOrFallback(link, HOME_FALLBACK_URL); - result.hasLogoToken = cleanNavText(section.textContent).includes(':logo:'); + result.logo = logo; + result.hasLogoToken = logoTokens.some((token) => ( + cleanNavText(section.textContent).toLowerCase().includes(token) + )); return result; } @@ -681,7 +722,12 @@ function buildUtilityRow(data) { } function buildBrand(data) { - const brand = createTag('div', { class: 'nav-new-brand' }); + const logoData = data.brand.logo || LOGOS.default; + const isMultiStateLogo = logoData.token === LOGOS.multiState.token; + + const brand = createTag('div', { + class: `nav-new-brand${isMultiStateLogo ? ' nav-new-brand-multi-state' : ''}`, + }); const link = createTag( 'a', @@ -693,10 +739,10 @@ function buildBrand(data) { ); const logo = createTag('img', { - src: '/group/icons/logo.svg', - alt: 'Blue Shield of California', - width: '110', - class: 'nav-new-brand-image', + src: logoData.src || LOGOS.default.src, + alt: logoData.alt || LOGOS.default.alt, + width: logoData.width || LOGOS.default.width, + class: `nav-new-brand-image${isMultiStateLogo ? ' nav-new-brand-image-multi-state' : ''}`, }); link.append(logo); From a4fd718b0d142f729e99542ed0b356f6eeda83a1 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 20:47:35 -0700 Subject: [PATCH 14/21] Refactor header.js to remove logo handling Removed logo configuration and related functions from header.js. --- group/blocks/header/header.js | 72 +++++++---------------------------- 1 file changed, 13 insertions(+), 59 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index 47acf85..f8d3589 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -4,23 +4,6 @@ import { loadFragment } from '../fragment/fragment.js'; const DESKTOP = window.matchMedia('(min-width: 900px)'); const HOME_FALLBACK_URL = '/'; -const LOGOS = { - default: { - token: ':logo:', - src: '/group/icons/logo.svg', - alt: 'Blue Shield of California', - width: '110', - }, - multiState: { - token: ':multi-state-logo:', - src: '/group/icons/multi-state-logo.svg', - alt: 'Blue Shield California and National Coverage', - width: '190', - }, -}; - -const NEW_WINDOW_TEXT = 'Open the link in a new window'; - /** * Small DOM helper * @param {string} tag @@ -52,6 +35,8 @@ function normalizeText(text = '') { return text.replace(/\s+/g, ' ').trim(); } +const NEW_WINDOW_TEXT = 'Open the link in a new window'; + /** * Removes accessibility helper text that may have been added to links before * this header parses and rebuilds the navigation. @@ -76,21 +61,6 @@ function getElementText(el) { return cleanNavText(clone.textContent); } -/** - * Gets the logo data based on the logo token authored in the nav document. - * @param {Element} section - * @returns {{token: string, src: string, alt: string, width: string}} - */ -function getLogoData(section) { - const text = cleanNavText(section?.textContent || '').toLowerCase(); - - if (text.includes(LOGOS.multiState.token)) { - return LOGOS.multiState; - } - - return LOGOS.default; -} - function slugify(text = '') { return normalizeText(text) .toLowerCase() @@ -240,14 +210,10 @@ function isUtilitySection(section) { function isBrandSection(section) { if (!section) return false; - const text = cleanNavText(section.textContent).toLowerCase(); + const text = cleanNavText(section.textContent); const list = getFirstList(section); - return ( - text.includes(LOGOS.default.token) - || text.includes(LOGOS.multiState.token) - || (!list && text.length > 0) - ); + return text.includes(':logo:') || (!list && text.length > 0); } /** @@ -444,28 +410,21 @@ function parseBrandSection(section) { const result = { label: 'Blue Shield', href: HOME_FALLBACK_URL, - logo: LOGOS.default, hasLogoToken: false, }; if (!section) return result; const link = getFirstLink(section); - const logo = getLogoData(section); - const logoTokens = [LOGOS.default.token, LOGOS.multiState.token]; - const textCandidates = [...section.querySelectorAll('p, h1, h2, h3, h4, h5, h6')] .map((el) => cleanNavText(el.textContent)) - .filter((text) => text && !logoTokens.includes(text.toLowerCase())); + .filter((text) => text && !/^:logo:$/i.test(text)); const brandLabel = textCandidates[0]; - result.label = brandLabel || logo.alt || result.label; + result.label = brandLabel || result.label; result.href = getHrefOrFallback(link, HOME_FALLBACK_URL); - result.logo = logo; - result.hasLogoToken = logoTokens.some((token) => ( - cleanNavText(section.textContent).toLowerCase().includes(token) - )); + result.hasLogoToken = cleanNavText(section.textContent).includes(':logo:'); return result; } @@ -722,12 +681,7 @@ function buildUtilityRow(data) { } function buildBrand(data) { - const logoData = data.brand.logo || LOGOS.default; - const isMultiStateLogo = logoData.token === LOGOS.multiState.token; - - const brand = createTag('div', { - class: `nav-new-brand${isMultiStateLogo ? ' nav-new-brand-multi-state' : ''}`, - }); + const brand = createTag('div', { class: 'nav-new-brand' }); const link = createTag( 'a', @@ -739,10 +693,10 @@ function buildBrand(data) { ); const logo = createTag('img', { - src: logoData.src || LOGOS.default.src, - alt: logoData.alt || LOGOS.default.alt, - width: logoData.width || LOGOS.default.width, - class: `nav-new-brand-image${isMultiStateLogo ? ' nav-new-brand-image-multi-state' : ''}`, + src: '/group/icons/logo.svg', + alt: 'Blue Shield of California', + width: '110', + class: 'nav-new-brand-image', }); link.append(logo); @@ -1152,4 +1106,4 @@ export default async function decorate(block) { } else { closeAllDropdowns(block); } -} +}// JavaScript Document From 01515d17a3d9fc42aa333b7edd3c986ab12102c5 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 20:52:01 -0700 Subject: [PATCH 15/21] Refactor dropdown child data extraction Removed the getDropdownChildData function and updated related code to directly extract label and href from child elements. --- group/blocks/header/header.js | 43 +++++++---------------------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index f8d3589..aa19844 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -115,31 +115,6 @@ function getDirectTextWithoutNestedList(li) { return cleanNavText(clone.textContent); } -/** - * Gets a dropdown child label and any sibling detail text that sits next - * to the authored link in the nav document. - * Example authored text: "(800) 334-5847 (TTY 711) 7 a.m. - 8 p.m. PT; 7 days a week" - * The anchor text is used as the label, and the remaining text is used as detail. - * @param {Element} childLi - * @returns {{label: string, detail: string, href: string}} - */ -function getDropdownChildData(childLi) { - const childAnchor = getDirectAnchor(childLi); - const fullText = getDirectTextWithoutNestedList(childLi); - const label = getElementText(childAnchor) || fullText; - const href = getHrefOrFallback(childAnchor, '#'); - - let detail = ''; - - if (label && fullText && fullText !== label) { - detail = fullText.startsWith(label) - ? cleanNavText(fullText.slice(label.length)) - : cleanNavText(fullText.replace(label, '')); - } - - return { label, detail, href }; -} - function getHrefOrFallback(anchor, fallback = '#') { const href = anchor?.getAttribute('href'); return href || fallback; @@ -486,7 +461,13 @@ function parseMainNavSection(section) { if (nestedList) { item.children = getDirectListItems(nestedList) - .map((childLi) => getDropdownChildData(childLi)) + .map((childLi) => { + const childAnchor = getDirectAnchor(childLi); + return { + label: getElementText(childAnchor) || getDirectTextWithoutNestedList(childLi), + href: getHrefOrFallback(childAnchor, '#'), + }; + }) .filter((child) => child.label); } @@ -745,16 +726,8 @@ function buildDropdownItem(item) { href: child.href || '#', class: `nav-new-dropdown-link${index === 0 ? ' is-featured' : ''}`, }, + child.label, ); - - childLink.append(createTag('span', { class: 'nav-new-dropdown-link-label' }, child.label)); - - if (child.detail) { - childLink.append( - createTag('span', { class: 'nav-new-dropdown-link-detail' }, child.detail), - ); - } - childLi.append(childLink); list.append(childLi); }); From 840c95d040e272052f0e482691f0f1e92b68ecdd Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 21:14:01 -0700 Subject: [PATCH 16/21] Implement getDropdownChildData function Added a function to extract dropdown child data including label and detail text. --- group/blocks/header/header.js | 43 ++++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index aa19844..f8d3589 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -115,6 +115,31 @@ function getDirectTextWithoutNestedList(li) { return cleanNavText(clone.textContent); } +/** + * Gets a dropdown child label and any sibling detail text that sits next + * to the authored link in the nav document. + * Example authored text: "(800) 334-5847 (TTY 711) 7 a.m. - 8 p.m. PT; 7 days a week" + * The anchor text is used as the label, and the remaining text is used as detail. + * @param {Element} childLi + * @returns {{label: string, detail: string, href: string}} + */ +function getDropdownChildData(childLi) { + const childAnchor = getDirectAnchor(childLi); + const fullText = getDirectTextWithoutNestedList(childLi); + const label = getElementText(childAnchor) || fullText; + const href = getHrefOrFallback(childAnchor, '#'); + + let detail = ''; + + if (label && fullText && fullText !== label) { + detail = fullText.startsWith(label) + ? cleanNavText(fullText.slice(label.length)) + : cleanNavText(fullText.replace(label, '')); + } + + return { label, detail, href }; +} + function getHrefOrFallback(anchor, fallback = '#') { const href = anchor?.getAttribute('href'); return href || fallback; @@ -461,13 +486,7 @@ function parseMainNavSection(section) { if (nestedList) { item.children = getDirectListItems(nestedList) - .map((childLi) => { - const childAnchor = getDirectAnchor(childLi); - return { - label: getElementText(childAnchor) || getDirectTextWithoutNestedList(childLi), - href: getHrefOrFallback(childAnchor, '#'), - }; - }) + .map((childLi) => getDropdownChildData(childLi)) .filter((child) => child.label); } @@ -726,8 +745,16 @@ function buildDropdownItem(item) { href: child.href || '#', class: `nav-new-dropdown-link${index === 0 ? ' is-featured' : ''}`, }, - child.label, ); + + childLink.append(createTag('span', { class: 'nav-new-dropdown-link-label' }, child.label)); + + if (child.detail) { + childLink.append( + createTag('span', { class: 'nav-new-dropdown-link-detail' }, child.detail), + ); + } + childLi.append(childLink); list.append(childLi); }); From fd50fecb7fddb1277e998dec60f0c757d215d3a7 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 21:20:04 -0700 Subject: [PATCH 17/21] Enhance logo data management and brand section logic Refactor logo handling and improve brand section parsing. --- group/blocks/header/header.js | 72 ++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 13 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index f8d3589..47acf85 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -4,6 +4,23 @@ import { loadFragment } from '../fragment/fragment.js'; const DESKTOP = window.matchMedia('(min-width: 900px)'); const HOME_FALLBACK_URL = '/'; +const LOGOS = { + default: { + token: ':logo:', + src: '/group/icons/logo.svg', + alt: 'Blue Shield of California', + width: '110', + }, + multiState: { + token: ':multi-state-logo:', + src: '/group/icons/multi-state-logo.svg', + alt: 'Blue Shield California and National Coverage', + width: '190', + }, +}; + +const NEW_WINDOW_TEXT = 'Open the link in a new window'; + /** * Small DOM helper * @param {string} tag @@ -35,8 +52,6 @@ function normalizeText(text = '') { return text.replace(/\s+/g, ' ').trim(); } -const NEW_WINDOW_TEXT = 'Open the link in a new window'; - /** * Removes accessibility helper text that may have been added to links before * this header parses and rebuilds the navigation. @@ -61,6 +76,21 @@ function getElementText(el) { return cleanNavText(clone.textContent); } +/** + * Gets the logo data based on the logo token authored in the nav document. + * @param {Element} section + * @returns {{token: string, src: string, alt: string, width: string}} + */ +function getLogoData(section) { + const text = cleanNavText(section?.textContent || '').toLowerCase(); + + if (text.includes(LOGOS.multiState.token)) { + return LOGOS.multiState; + } + + return LOGOS.default; +} + function slugify(text = '') { return normalizeText(text) .toLowerCase() @@ -210,10 +240,14 @@ function isUtilitySection(section) { function isBrandSection(section) { if (!section) return false; - const text = cleanNavText(section.textContent); + const text = cleanNavText(section.textContent).toLowerCase(); const list = getFirstList(section); - return text.includes(':logo:') || (!list && text.length > 0); + return ( + text.includes(LOGOS.default.token) + || text.includes(LOGOS.multiState.token) + || (!list && text.length > 0) + ); } /** @@ -410,21 +444,28 @@ function parseBrandSection(section) { const result = { label: 'Blue Shield', href: HOME_FALLBACK_URL, + logo: LOGOS.default, hasLogoToken: false, }; if (!section) return result; const link = getFirstLink(section); + const logo = getLogoData(section); + const logoTokens = [LOGOS.default.token, LOGOS.multiState.token]; + const textCandidates = [...section.querySelectorAll('p, h1, h2, h3, h4, h5, h6')] .map((el) => cleanNavText(el.textContent)) - .filter((text) => text && !/^:logo:$/i.test(text)); + .filter((text) => text && !logoTokens.includes(text.toLowerCase())); const brandLabel = textCandidates[0]; - result.label = brandLabel || result.label; + result.label = brandLabel || logo.alt || result.label; result.href = getHrefOrFallback(link, HOME_FALLBACK_URL); - result.hasLogoToken = cleanNavText(section.textContent).includes(':logo:'); + result.logo = logo; + result.hasLogoToken = logoTokens.some((token) => ( + cleanNavText(section.textContent).toLowerCase().includes(token) + )); return result; } @@ -681,7 +722,12 @@ function buildUtilityRow(data) { } function buildBrand(data) { - const brand = createTag('div', { class: 'nav-new-brand' }); + const logoData = data.brand.logo || LOGOS.default; + const isMultiStateLogo = logoData.token === LOGOS.multiState.token; + + const brand = createTag('div', { + class: `nav-new-brand${isMultiStateLogo ? ' nav-new-brand-multi-state' : ''}`, + }); const link = createTag( 'a', @@ -693,10 +739,10 @@ function buildBrand(data) { ); const logo = createTag('img', { - src: '/group/icons/logo.svg', - alt: 'Blue Shield of California', - width: '110', - class: 'nav-new-brand-image', + src: logoData.src || LOGOS.default.src, + alt: logoData.alt || LOGOS.default.alt, + width: logoData.width || LOGOS.default.width, + class: `nav-new-brand-image${isMultiStateLogo ? ' nav-new-brand-image-multi-state' : ''}`, }); link.append(logo); @@ -1106,4 +1152,4 @@ export default async function decorate(block) { } else { closeAllDropdowns(block); } -}// JavaScript Document +} From cc0c0d23b65b4fc5543fd75511f91ab03268729e Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Wed, 10 Jun 2026 21:31:19 -0700 Subject: [PATCH 18/21] Add logo selectors for multi-state and default logos --- group/blocks/header/header.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index 47acf85..0656019 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -7,12 +7,14 @@ const HOME_FALLBACK_URL = '/'; const LOGOS = { default: { token: ':logo:', + selector: '.icon-logo, img[src*="/logo.svg"]', src: '/group/icons/logo.svg', alt: 'Blue Shield of California', width: '110', }, multiState: { token: ':multi-state-logo:', + selector: '.icon-multi-state-logo, img[src*="multi-state-logo.svg"]', src: '/group/icons/multi-state-logo.svg', alt: 'Blue Shield California and National Coverage', width: '190', @@ -78,13 +80,18 @@ function getElementText(el) { /** * Gets the logo data based on the logo token authored in the nav document. + * Also supports the case where AEM has already converted :multi-state-logo: + * into an icon span/image before this header parses the fragment. * @param {Element} section - * @returns {{token: string, src: string, alt: string, width: string}} + * @returns {{token: string, selector: string, src: string, alt: string, width: string}} */ function getLogoData(section) { const text = cleanNavText(section?.textContent || '').toLowerCase(); - if (text.includes(LOGOS.multiState.token)) { + if ( + text.includes(LOGOS.multiState.token) + || section?.querySelector(LOGOS.multiState.selector) + ) { return LOGOS.multiState; } @@ -246,6 +253,8 @@ function isBrandSection(section) { return ( text.includes(LOGOS.default.token) || text.includes(LOGOS.multiState.token) + || section.querySelector(LOGOS.default.selector) + || section.querySelector(LOGOS.multiState.selector) || (!list && text.length > 0) ); } @@ -465,7 +474,7 @@ function parseBrandSection(section) { result.logo = logo; result.hasLogoToken = logoTokens.some((token) => ( cleanNavText(section.textContent).toLowerCase().includes(token) - )); + )) || !!section.querySelector(logo.selector); return result; } From fbcdb58374005c7cfebf7eb50304a54586c5c3e8 Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Thu, 11 Jun 2026 07:36:41 -0700 Subject: [PATCH 19/21] Update header.js --- group/blocks/header/header.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index 0656019..d628a20 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -17,7 +17,7 @@ const LOGOS = { selector: '.icon-multi-state-logo, img[src*="multi-state-logo.svg"]', src: '/group/icons/multi-state-logo.svg', alt: 'Blue Shield California and National Coverage', - width: '190', + width: '240', }, }; From 6f872df87a870ee4fca0f59e2b1dad0e339e200d Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Thu, 11 Jun 2026 07:45:36 -0700 Subject: [PATCH 20/21] Add multi-state brand image styles to header --- group/blocks/header/header.css | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/group/blocks/header/header.css b/group/blocks/header/header.css index 89c908d..1ea36a1 100644 --- a/group/blocks/header/header.css +++ b/group/blocks/header/header.css @@ -220,6 +220,12 @@ header .nav-new-brand-image { height: auto; } +header .nav-new-brand-image-multi-state { + display: block; + width: 240px; + height: auto; +} + /* ========================= MAIN NAV AREA ========================= */ From 6e897676f6f03e362b7c9e387598fa3cfae21cab Mon Sep 17 00:00:00 2001 From: mattsd23-bsc Date: Thu, 11 Jun 2026 09:59:30 -0700 Subject: [PATCH 21/21] Update header.js --- group/blocks/header/header.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/group/blocks/header/header.js b/group/blocks/header/header.js index d628a20..b109c12 100644 --- a/group/blocks/header/header.js +++ b/group/blocks/header/header.js @@ -152,6 +152,11 @@ function getDirectTextWithoutNestedList(li) { return cleanNavText(clone.textContent); } +function getHrefOrFallback(anchor, fallback = '#') { + const href = anchor?.getAttribute('href'); + return href || fallback; +} + /** * Gets a dropdown child label and any sibling detail text that sits next * to the authored link in the nav document. @@ -177,11 +182,6 @@ function getDropdownChildData(childLi) { return { label, detail, href }; } -function getHrefOrFallback(anchor, fallback = '#') { - const href = anchor?.getAttribute('href'); - return href || fallback; -} - function getPagePath() { return window.location.pathname.replace(/\/$/, '') || '/'; }