From 44e135cff8c54806406bde42d265b5e54ab480e6 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Fri, 22 Sep 2023 15:27:04 -0400 Subject: [PATCH 01/10] feat(solution-header): add block and scroll to behavior --- blocks/solution-header/solution-header.css | 136 +++++++++++++++++++++ blocks/solution-header/solution-header.js | 98 +++++++++++++++ scripts/lib-franklin.js | 12 ++ scripts/scripts.js | 5 +- 4 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 blocks/solution-header/solution-header.css create mode 100644 blocks/solution-header/solution-header.js diff --git a/blocks/solution-header/solution-header.css b/blocks/solution-header/solution-header.css new file mode 100644 index 00000000..b11a440d --- /dev/null +++ b/blocks/solution-header/solution-header.css @@ -0,0 +1,136 @@ +/* Header - Solution Header */ +header .solution-header-wrapper { + position: fixed; + border-bottom: 1px solid var(--neutral-sand); + width: 100%; + height: 130px; + background-color: var(--neutral-bone); + left: 0; + top: 0; + transition: all .5s ease; + z-index: 99999; + font-family: var(--sans-serif-font-medium); +} + +header .solution-header-wrapper.is-sticky { + background-color: var(--neutral-carbon); + border-bottom: 1px solid var(--neutral-carbon); + color: var(--neutral-white); + padding: 0; + height: var(--sticky-nav-height); +} + +header .solution-header-wrapper.is-sticky .solution-header__inner { + transition: padding-bottom .5s ease; + padding-bottom: var(--spacer-element-05); +} + +header .solution-header-wrapper.is-sticky .solution-header .icon { + display: block; + width: var(--spacer-element-09); + height: var(--spacer-element-09); + clip-path: circle(50% at 50% 50%); +} + +header .solution-header-wrapper.is-sticky .solution-header .icon svg { + height: var(--spacer-element-09); +} + +header .solution-header-wrapper.is-sticky .solution-header ul { + margin: 0 0 -16px 0; +} + +header .solution-header-wrapper.is-sticky .solution-header ul li a { + color: var(--neutral-white); + padding: var(--spacer-element-07) 0; +} + +/* Block - Solution Header */ +.solution-header { + max-width: var(--secton-max-container); + height: 100%; + margin: 0 auto; + width: calc(100% - var(--spacer-layout-04)); +} + +.solution-header .solution-header__inner { + display: flex; + height: 100%; + align-items: flex-end; + flex-shrink: 0; + padding-bottom: var(--spacer-element-07); +} + +.solution-header .solution-header__inner .solution-header__col-1 { + width: 20%; +} + +.solution-header .solution-header__inner .solution-header__col-2 { + width: 80%; + display: flex; + justify-content: space-between; + align-items: flex-end; +} + +.solution-header p { + margin: 0; +} + +.solution-header .icon { + display: block; + line-height: 0; +} + +.solution-header .icon svg { + width: auto; + height: 48px; +} + +.solution-header ul { + display: flex; + align-items: center; + margin: 0 0 -24px 0; + gap: var(--spacer-element-08); +} + +.solution-header ul li { + list-style: none; +} + +.solution-header ul li a { + display: inline-block; + color: var(--neutral-carbon, #20201F); + font-size: var(--font-size-16); + font-weight: 500; + text-decoration: none; + padding: var(--spacer-layout-036) 0; + background-image: var(--gradient-left-right); + background-size: 0 var(--spacer-element-02), auto; + background-repeat: no-repeat; + background-position: center bottom; + transition: all .2s ease-out; +} + +.solution-header ul li a:hover, +.solution-header ul li a:focus, +.solution-header ul li a.active { + background-size: 100% var(--spacer-element-02), auto; + z-index: 6; +} + +/* Tablet */ +@media only screen and (min-width: 768px) { + .solution-header { + width: calc(100% - 72px); + } +} + +/* Desktop */ +@media (min-width: 1200px) { + + .solution-header { + margin: 0 auto; + width: 100%; + } + +} \ No newline at end of file diff --git a/blocks/solution-header/solution-header.js b/blocks/solution-header/solution-header.js new file mode 100644 index 00000000..885edb36 --- /dev/null +++ b/blocks/solution-header/solution-header.js @@ -0,0 +1,98 @@ +export default function decorate(block) { + const blockName = block.getAttribute('data-block-name'); + if (!blockName) { + return; + } + + [...block.children].forEach((element) => { + element.classList.add(`${blockName}__inner`); + + // Find all the div elements within the inner content class + const innerElements = element.querySelectorAll(`.${blockName}__inner div`); + + // Add the class to column and append a number to each of these div elements + let counter = 1; + innerElements.forEach((divElement) => { + const newClass = `${blockName}__col-${counter}`; + divElement.classList.add(`${blockName}__col`, newClass); + counter += 1; + }); + }); + + // Add page scroll listener to know when header turns to sticky + const header = block.parentNode; + window.addEventListener('scroll', () => { + const scrollAmount = window.scrollY; + if (scrollAmount > header.offsetHeight) { + header.classList.add('is-sticky'); + } else { + header.classList.remove('is-sticky'); + } + }); + + // Get all the list items within the ul +const listItems = document.querySelectorAll('.solution-header__col-2 ul li'); + +// Get all the sections on the page with IDs that match the href values in the list items +const sections = Array.from(listItems).map((item) => { + const targetId = item.querySelector('a').getAttribute('href').substring(1); + return document.getElementById(targetId); +}); + +// Function to add the active class to the corresponding list item +function setActiveClass(index) { + listItems.forEach((listItem, i) => { + if (i === index) { + listItem.classList.add('active'); + listItem.querySelector('a').classList.add('active'); + } else { + listItem.classList.remove('active'); + listItem.querySelector('a').classList.remove('active'); + } + }); +} + +// Function to handle scrolling and active class removal +function handleScroll() { + const scrollPosition = window.scrollY; + let activeSectionIndex = -1; + + // Find the index of the currently active section + for (let i = 0; i < sections.length; i++) { + const section = sections[i]; + if (section) { + const sectionTop = section.offsetTop; + const sectionHeight = section.offsetHeight; + + if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) { + activeSectionIndex = i; + break; + } + } + } + + setActiveClass(activeSectionIndex); +} + +// Add an event listener to run the function when scrolling +window.addEventListener('scroll', handleScroll); + +// Function to handle anchor links in the URL +function handleAnchorLink() { + const currentHash = window.location.hash; + const targetIndex = Array.from(listItems).findIndex((item) => { + return item.querySelector('a').getAttribute('href') === currentHash; + }); + + setActiveClass(targetIndex); +} + +// Add an event listener to run the function when the hash changes +window.addEventListener('hashchange', handleAnchorLink); + +// Call the functions initially to set the active class based on the current scroll position and URL hash +handleScroll(); +handleAnchorLink(); + + +} diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 18ef04ac..334639ea 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -820,6 +820,18 @@ export function loadHeader(header) { return loadBlock(headerBlock); } +/** + * loads a block named 'solution-header' into header + */ + +export function loadSolutionHeader(header) { + const solutionHeaderBlock = document.querySelector('.solution-header-wrapper'); + header.append(solutionHeaderBlock); + document.querySelector('body').classList.add('header-visible'); + + return loadBlock(solutionHeaderBlock); +} + /** * loads a block named 'footer' into footer */ diff --git a/scripts/scripts.js b/scripts/scripts.js index 6b359651..b27d11e7 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -2,6 +2,7 @@ import { sampleRUM, buildBlock, loadHeader, + loadSolutionHeader, loadFooter, decorateButtons, decorateIcons, @@ -923,10 +924,12 @@ async function loadLazy(doc) { const element = hash ? main.querySelector(hash) : false; if (hash && element) element.scrollIntoView(); - if (!locationCheck('block-library') && !locationCheck('quick-links')) { + if (!locationCheck('block-library') && !locationCheck('quick-links') && !locationCheck('campaigns')) { loadHeader(doc.querySelector('header')); loadFooter(doc.querySelector('footer')); await buildBreadcrumb(); + } else if(locationCheck('campaigns')) { + loadSolutionHeader(doc.querySelector('header')); } loadCSS(`${window.hlx.codeBasePath}/styles/lazy-styles.css`); From e50c06a0d2ac8e535804d43d0db4f6061cc21254 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Mon, 25 Sep 2023 09:47:17 -0400 Subject: [PATCH 02/10] feat(solution-header): adjust scroll to behavior --- blocks/solution-header/solution-header.css | 1 - blocks/solution-header/solution-header.js | 103 ++++++++++----------- 2 files changed, 50 insertions(+), 54 deletions(-) diff --git a/blocks/solution-header/solution-header.css b/blocks/solution-header/solution-header.css index b11a440d..c63c4373 100644 --- a/blocks/solution-header/solution-header.css +++ b/blocks/solution-header/solution-header.css @@ -112,7 +112,6 @@ header .solution-header-wrapper.is-sticky .solution-header ul li a { } .solution-header ul li a:hover, -.solution-header ul li a:focus, .solution-header ul li a.active { background-size: 100% var(--spacer-element-02), auto; z-index: 6; diff --git a/blocks/solution-header/solution-header.js b/blocks/solution-header/solution-header.js index 885edb36..2a856652 100644 --- a/blocks/solution-header/solution-header.js +++ b/blocks/solution-header/solution-header.js @@ -30,69 +30,66 @@ export default function decorate(block) { } }); - // Get all the list items within the ul -const listItems = document.querySelectorAll('.solution-header__col-2 ul li'); +// Define the list of navigation links +const navigationLinks = document.querySelectorAll('.solution-header__col-2 ul li a'); -// Get all the sections on the page with IDs that match the href values in the list items -const sections = Array.from(listItems).map((item) => { - const targetId = item.querySelector('a').getAttribute('href').substring(1); - return document.getElementById(targetId); +// Extract section IDs from navigation links +const sectionIds = Array.from(navigationLinks).map((link) => { + return link.getAttribute('href').substring(1); }); -// Function to add the active class to the corresponding list item -function setActiveClass(index) { - listItems.forEach((listItem, i) => { - if (i === index) { - listItem.classList.add('active'); - listItem.querySelector('a').classList.add('active'); - } else { - listItem.classList.remove('active'); - listItem.querySelector('a').classList.remove('active'); +// Define the Intersection Observer options +const observerOptions = { + root: null, + rootMargin: '0px', + threshold: 0.5, // Trigger when 50% of the element is in the viewport +}; + +// Create Intersection Observer callback function +const handleIntersection = (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const targetId = entry.target.getAttribute('id'); + const correspondingLink = document.querySelector(`.solution-header__col-2 ul li a[href="#${targetId}"]`); + + // Remove 'active' class from all links + navigationLinks.forEach((link) => { + link.classList.remove('active'); + }); + + // Add 'active' class to the corresponding link + correspondingLink.classList.add('active'); } }); -} +}; -// Function to handle scrolling and active class removal -function handleScroll() { - const scrollPosition = window.scrollY; - let activeSectionIndex = -1; - - // Find the index of the currently active section - for (let i = 0; i < sections.length; i++) { - const section = sections[i]; - if (section) { - const sectionTop = section.offsetTop; - const sectionHeight = section.offsetHeight; - - if (scrollPosition >= sectionTop && scrollPosition < sectionTop + sectionHeight) { - activeSectionIndex = i; - break; - } - } - } +// Create Intersection Observer instance +const observer = new IntersectionObserver(handleIntersection, observerOptions); - setActiveClass(activeSectionIndex); -} - -// Add an event listener to run the function when scrolling -window.addEventListener('scroll', handleScroll); +// Add observer to each section +sectionIds.forEach((sectionId) => { + const section = document.getElementById(sectionId); + if (section) { + observer.observe(section); + } +}); -// Function to handle anchor links in the URL -function handleAnchorLink() { - const currentHash = window.location.hash; - const targetIndex = Array.from(listItems).findIndex((item) => { - return item.querySelector('a').getAttribute('href') === currentHash; +// Smooth scroll to anchor when a navigation link is clicked +navigationLinks.forEach((link) => { + link.addEventListener('click', (e) => { + e.preventDefault(); + const targetId = link.getAttribute('href').substring(1); + const targetSection = document.getElementById(targetId); + + if (targetSection) { + window.scrollTo({ + top: targetSection.offsetTop, + behavior: 'smooth', + }); + } }); +}); - setActiveClass(targetIndex); -} - -// Add an event listener to run the function when the hash changes -window.addEventListener('hashchange', handleAnchorLink); - -// Call the functions initially to set the active class based on the current scroll position and URL hash -handleScroll(); -handleAnchorLink(); } From c54b9a04876b21976535664a17c49cd0e4cafd30 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Tue, 26 Sep 2023 17:09:25 -0400 Subject: [PATCH 03/10] feat(solution-header): add nav list --- blocks/solution-header/solution-header.js | 147 ++++++++++++++-------- scripts/lib-franklin.js | 24 +++- 2 files changed, 115 insertions(+), 56 deletions(-) diff --git a/blocks/solution-header/solution-header.js b/blocks/solution-header/solution-header.js index 2a856652..cfeb7f8b 100644 --- a/blocks/solution-header/solution-header.js +++ b/blocks/solution-header/solution-header.js @@ -1,3 +1,5 @@ +import { toSentenceCase } from '../../scripts/lib-franklin.js'; + export default function decorate(block) { const blockName = block.getAttribute('data-block-name'); if (!blockName) { @@ -30,66 +32,101 @@ export default function decorate(block) { } }); -// Define the list of navigation links -const navigationLinks = document.querySelectorAll('.solution-header__col-2 ul li a'); - -// Extract section IDs from navigation links -const sectionIds = Array.from(navigationLinks).map((link) => { - return link.getAttribute('href').substring(1); -}); - -// Define the Intersection Observer options -const observerOptions = { - root: null, - rootMargin: '0px', - threshold: 0.5, // Trigger when 50% of the element is in the viewport -}; - -// Create Intersection Observer callback function -const handleIntersection = (entries) => { - entries.forEach((entry) => { - if (entry.isIntersecting) { - const targetId = entry.target.getAttribute('id'); - const correspondingLink = document.querySelector(`.solution-header__col-2 ul li a[href="#${targetId}"]`); - - // Remove 'active' class from all links - navigationLinks.forEach((link) => { - link.classList.remove('active'); - }); - - // Add 'active' class to the corresponding link - correspondingLink.classList.add('active'); - } - }); -}; + // Define a function to generate and append a semantic navigation component + function generateAnchorLinkNav() { + const sectionsWithTitles = document.querySelectorAll('.section[data-title]'); + const navItemsContainer = document.querySelector('.solution-header .solution-header__col-2'); + + // Create an unordered list for the navigation + const navList = document.createElement('ul'); + navList.classList.add('nav-list'); // Add a class for styling or accessibility + + sectionsWithTitles.forEach((section, index) => { + // Get the value of the data-title attribute + const sectionTitle = toSentenceCase(section.getAttribute('data-title')); + + // Create a list item for each section + const listItem = document.createElement('li'); + listItem.classList.add('nav-item'); // Add a class for styling or accessibility -// Create Intersection Observer instance -const observer = new IntersectionObserver(handleIntersection, observerOptions); + // Create an anchor link element + const anchorLink = document.createElement('a'); + anchorLink.textContent = sectionTitle; + anchorLink.href = `#${section.getAttribute('data-title')}`; -// Add observer to each section -sectionIds.forEach((sectionId) => { - const section = document.getElementById(sectionId); - if (section) { - observer.observe(section); + // Append the anchor link to the list item + listItem.appendChild(anchorLink); + + // Append the list item to the navigation list + navList.appendChild(listItem); + }); + + // Append the navigation list to the nav items container + navItemsContainer.appendChild(navList); } -}); - -// Smooth scroll to anchor when a navigation link is clicked -navigationLinks.forEach((link) => { - link.addEventListener('click', (e) => { - e.preventDefault(); - const targetId = link.getAttribute('href').substring(1); - const targetSection = document.getElementById(targetId); - - if (targetSection) { - window.scrollTo({ - top: targetSection.offsetTop, - behavior: 'smooth', - }); - } + + // Call the function to generate and append the semantic navigation component + generateAnchorLinkNav(); + + + + // Define the list of navigation links + const navigationLinks = document.querySelectorAll('.solution-header__col-2 ul li a'); + + // Extract section IDs from navigation links + const sectionIds = Array.from(navigationLinks).map((link) => { + return link.getAttribute('href').substring(1); }); -}); + // Define the Intersection Observer options + const observerOptions = { + root: null, + rootMargin: '0px', + threshold: 0.5, // Trigger when 50% of the element is in the viewport + }; + + // Create Intersection Observer callback function + const handleIntersection = (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + const targetId = entry.target.getAttribute('id'); + const correspondingLink = document.querySelector(`.solution-header__col-2 ul li a[href="#${targetId}"]`); + + // Remove 'active' class from all links + navigationLinks.forEach((link) => { + link.classList.remove('active'); + }); + + // Add 'active' class to the corresponding link + correspondingLink.classList.add('active'); + } + }); + }; + + // Create Intersection Observer instance + const observer = new IntersectionObserver(handleIntersection, observerOptions); + // Add observer to each section + sectionIds.forEach((sectionId) => { + const section = document.getElementById(sectionId); + if (section) { + observer.observe(section); + } + }); + // Smooth scroll to anchor when a navigation link is clicked + navigationLinks.forEach((link) => { + link.addEventListener('click', (e) => { + e.preventDefault(); + const targetId = link.getAttribute('href').substring(1); + const targetSection = document.getElementById(targetId); + + if (targetSection) { + window.scrollTo({ + top: targetSection.offsetTop, + behavior: 'smooth', + }); + } + }); + }); } diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 334639ea..173e1df2 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -123,6 +123,24 @@ export function toCamelCase(name) { return toClassName(name).replace(/-([a-z])/g, (g) => g[1].toUpperCase()); } +/** + * Convert text to sentence case. + * @param {string} text property + * @returns {string} The sentence case value(s) + */ +export function toSentenceCase(text) { + // Split the text by hyphens or other non-word characters + const words = text.split(/[-\s]+/); + + // Capitalize the first letter of each word and convert the rest to lowercase + const sentenceCaseWords = words.map(word => { + return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); + }); + + // Join the words back together with spaces + return sentenceCaseWords.join(' '); +} + /** * Replace icons with inline SVG and prefix with codeBasePath. * @param {Element} element @@ -268,9 +286,13 @@ export function decorateSections(main) { Object.keys(meta).forEach((key) => { if (key === 'style') { const styles = meta.style.split(',').map((style) => toClassName(style.trim())); + styles.forEach((style) => section.classList.add(style)); - } if (key === 'theme') { + } else if (key === 'theme') { section.setAttribute('data-theme', meta.theme); + } else if (key === 'id') { + section.setAttribute('data-title', toClassName(meta.id)); + section.setAttribute('id', toClassName(meta.id)); } else { section.dataset[toCamelCase(key)] = meta[key]; } From f961a217806625652bec18c13371460316bd1955 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Wed, 27 Sep 2023 16:38:43 -0400 Subject: [PATCH 04/10] feat(solution-header): add cleanup --- blocks/columns/columns.css | 1 + blocks/solution-header/solution-header.css | 266 +++++++++++++++------ blocks/solution-header/solution-header.js | 12 +- scripts/lib-franklin.js | 7 +- scripts/scripts.js | 2 +- styles/styles.css | 1 + 6 files changed, 202 insertions(+), 87 deletions(-) diff --git a/blocks/columns/columns.css b/blocks/columns/columns.css index c568e7a7..2d674a10 100644 --- a/blocks/columns/columns.css +++ b/blocks/columns/columns.css @@ -63,6 +63,7 @@ main .columns-container h4 { font-weight: var(--font-weight-light); line-height: var(--line-height-160); letter-spacing: var(--letter-spacing-1); + padding-inline-start: var(--spacer-element-02); } .columns.terms div, .columns.terms div strong, diff --git a/blocks/solution-header/solution-header.css b/blocks/solution-header/solution-header.css index c63c4373..c5c73ac3 100644 --- a/blocks/solution-header/solution-header.css +++ b/blocks/solution-header/solution-header.css @@ -1,75 +1,17 @@ -/* Header - Solution Header */ -header .solution-header-wrapper { - position: fixed; - border-bottom: 1px solid var(--neutral-sand); - width: 100%; - height: 130px; - background-color: var(--neutral-bone); - left: 0; - top: 0; - transition: all .5s ease; - z-index: 99999; - font-family: var(--sans-serif-font-medium); -} - -header .solution-header-wrapper.is-sticky { - background-color: var(--neutral-carbon); - border-bottom: 1px solid var(--neutral-carbon); - color: var(--neutral-white); - padding: 0; - height: var(--sticky-nav-height); -} - -header .solution-header-wrapper.is-sticky .solution-header__inner { - transition: padding-bottom .5s ease; - padding-bottom: var(--spacer-element-05); -} - -header .solution-header-wrapper.is-sticky .solution-header .icon { - display: block; - width: var(--spacer-element-09); - height: var(--spacer-element-09); - clip-path: circle(50% at 50% 50%); -} - -header .solution-header-wrapper.is-sticky .solution-header .icon svg { - height: var(--spacer-element-09); -} - -header .solution-header-wrapper.is-sticky .solution-header ul { - margin: 0 0 -16px 0; -} - -header .solution-header-wrapper.is-sticky .solution-header ul li a { - color: var(--neutral-white); - padding: var(--spacer-element-07) 0; +/* Block - Solution Header */ +.header-visible .solution-header[data-block-status="loaded"] { + opacity: 1; } -/* Block - Solution Header */ .solution-header { + opacity: 0; max-width: var(--secton-max-container); height: 100%; margin: 0 auto; - width: calc(100% - var(--spacer-layout-04)); } .solution-header .solution-header__inner { - display: flex; - height: 100%; - align-items: flex-end; - flex-shrink: 0; - padding-bottom: var(--spacer-element-07); -} - -.solution-header .solution-header__inner .solution-header__col-1 { - width: 20%; -} - -.solution-header .solution-header__inner .solution-header__col-2 { - width: 80%; - display: flex; - justify-content: space-between; - align-items: flex-end; + position: relative; } .solution-header p { @@ -83,27 +25,58 @@ header .solution-header-wrapper.is-sticky .solution-header ul li a { .solution-header .icon svg { width: auto; - height: 48px; + height: var(--spacer-layout-036); +} + +.solution-header .solution-header__inner .solution-header__col-1 { + margin: var(--spacer-element-05) var(--spacer-element-07); +} + +.solution-header .solution-header__inner .solution-header__col-2 { + overflow-x: auto; + height: 100%; + margin: var(--spacer-element-07) 0 calc(var(--spacer-element-07) * -1) 0; + border-top: 1px solid var(--neutral-sand); +} + +.solution-header .solution-header__inner .solution-header__col-3 { + display: none; +} + +.solution-header .solution-header__inner .solution-header__col-3 p:not(.button-container) { + display: none; + visibility: hidden; } .solution-header ul { display: flex; - align-items: center; - margin: 0 0 -24px 0; - gap: var(--spacer-element-08); + align-items: flex-end; + justify-content: flex-start; + height: 100%; + margin: 0; } .solution-header ul li { + font-family: var(--sans-serif-font-medium); list-style: none; + height: 100%; + padding-left: var(--spacer-element-07); +} + +.solution-header ul li:last-child { + padding-right: var(--spacer-element-07); } .solution-header ul li a { - display: inline-block; - color: var(--neutral-carbon, #20201F); + display: flex; + align-items: center; + height: 100%; + white-space: nowrap; + color: var(--neutral-carbon); font-size: var(--font-size-16); font-weight: 500; text-decoration: none; - padding: var(--spacer-layout-036) 0; + padding: var(--spacer-element-05) 0; background-image: var(--gradient-left-right); background-size: 0 var(--spacer-element-02), auto; background-repeat: no-repeat; @@ -117,19 +90,164 @@ header .solution-header-wrapper.is-sticky .solution-header ul li a { z-index: 6; } +/* Sticky - Solution Header */ +.solution-header-wrapper { + position: fixed; + border-bottom: 1px solid var(--neutral-sand); + width: 100%; + height: var(--nav-height-desktop); + background-color: var(--neutral-bone); + left: 0; + top: 0; + transition: all .5s ease; + z-index: 99999; +} + +.solution-header-wrapper.is-sticky { + background-color: var(--neutral-carbon); + border-bottom: 1px solid var(--neutral-carbon); + color: var(--neutral-white); + padding: 0; + height: var(--spacer-layout-05); +} + +.solution-header-wrapper.is-sticky .solution-header__inner { + display: flex; + align-items: flex-end; + height: 100%; + transition: padding-bottom .5s ease; +} + +.solution-header-wrapper.is-sticky .solution-header .icon { + display: block; + width: var(--spacer-layout-03); + height: var(--spacer-layout-03); + clip-path: circle(50% at 50% 50%); +} + +.solution-header-wrapper.is-sticky .solution-header .icon svg { + height: var(--spacer-layout-03); +} + +.solution-header-wrapper.is-sticky .solution-header .solution-header__col-2 { + border-color: transparent; + margin: 0; + border-left: 1px solid var(--neutral-grey-tint140); +} + +.solution-header-wrapper.is-sticky .solution-header ul li a { + color: var(--neutral-white); + padding: var(--spacer-element-05) 0; +} + /* Tablet */ @media only screen and (min-width: 768px) { - .solution-header { - width: calc(100% - 72px); + /* Block - Solution Header */ + .solution-header ul { + justify-content: center; + } + + .solution-header .solution-header__inner .solution-header__col-1 { + margin: var(--spacer-element-05) var(--spacer-element-08); + } + + .solution-header .solution-header__inner .solution-header__col-3 { + display: block; + max-width: 225px; + position: absolute; + top: calc(var(--spacer-element-02) * -1); + right: var(--spacer-layout-036); + } + + /* Sticky - Solution Header */ + .solution-header-wrapper.is-sticky .solution-header__inner .solution-header__col-3 { + display: none; } } /* Desktop */ -@media (min-width: 1200px) { - +@media (min-width: 1200px) { + /* Block - Solution Header */ .solution-header { margin: 0 auto; width: 100%; } + .solution-header .solution-header__inner { + display: flex; + height: 100%; + align-items: flex-end; + justify-content: space-between; + flex-shrink: 0; + gap: var(--spacer-layout-04); + padding-top: 0; + padding-bottom: var(--spacer-element-08); + } + + .solution-header ul li { + padding-left: var(--spacer-element-08); + } + + .solution-header ul li:last-child { + padding-right: var(--spacer-element-08); + } + + .solution-header .icon svg { + height: var(--spacer-layout-04); + } + + .solution-header .solution-header__inner .solution-header__col-1 { + margin: 0; + } + + .solution-header .solution-header__inner .solution-header__col-2 { + overflow-x: auto; + max-width: 650px; + height: 100%; + margin: 0 0 calc(var(--spacer-element-08) * -1) 0; + border-top: 0; + } + + .solution-header .solution-header__inner .solution-header__col-3 { + position: relative; + top: inherit; + right: inherit; + } + + .solution-header ul li a { + padding: var(--spacer-layout-036) 0; + } + + /* Sticky - Solution Header */ + .solution-header-wrapper.is-sticky { + height: var(--sticky-nav-height); + } + + .solution-header-wrapper.is-sticky .solution-header__inner { + padding-bottom: var(--spacer-element-05); + } + + .solution-header-wrapper.is-sticky .solution-header ul li a { + padding: var(--spacer-element-07) 0; + } + + .solution-header-wrapper.is-sticky .solution-header .solution-header__col-2 { + border-color: transparent; + margin: 0 0 calc(var(--spacer-element-05) * -1) 0; + } + + .solution-header-wrapper.is-sticky .solution-header__inner .solution-header__col-3 { + display: block; + } + + .solution-header-wrapper.is-sticky .solution-header .icon { + display: block; + width: var(--spacer-element-09); + height: var(--spacer-element-09); + clip-path: circle(50% at 50% 50%); + } + + .solution-header-wrapper.is-sticky .solution-header .icon svg { + height: var(--spacer-element-09); + } } \ No newline at end of file diff --git a/blocks/solution-header/solution-header.js b/blocks/solution-header/solution-header.js index cfeb7f8b..28fd3ad1 100644 --- a/blocks/solution-header/solution-header.js +++ b/blocks/solution-header/solution-header.js @@ -35,13 +35,13 @@ export default function decorate(block) { // Define a function to generate and append a semantic navigation component function generateAnchorLinkNav() { const sectionsWithTitles = document.querySelectorAll('.section[data-title]'); - const navItemsContainer = document.querySelector('.solution-header .solution-header__col-2'); + const navItemsContainer = document.querySelector(`.${blockName} .${blockName}__col-2`); // Create an unordered list for the navigation const navList = document.createElement('ul'); navList.classList.add('nav-list'); // Add a class for styling or accessibility - sectionsWithTitles.forEach((section, index) => { + sectionsWithTitles.forEach((section) => { // Get the value of the data-title attribute const sectionTitle = toSentenceCase(section.getAttribute('data-title')); @@ -68,15 +68,11 @@ export default function decorate(block) { // Call the function to generate and append the semantic navigation component generateAnchorLinkNav(); - - // Define the list of navigation links const navigationLinks = document.querySelectorAll('.solution-header__col-2 ul li a'); // Extract section IDs from navigation links - const sectionIds = Array.from(navigationLinks).map((link) => { - return link.getAttribute('href').substring(1); - }); + const sectionIds = Array.from(navigationLinks).map((link) => link.getAttribute('href').substring(1)); // Define the Intersection Observer options const observerOptions = { @@ -120,7 +116,7 @@ export default function decorate(block) { e.preventDefault(); const targetId = link.getAttribute('href').substring(1); const targetSection = document.getElementById(targetId); - + if (targetSection) { window.scrollTo({ top: targetSection.offsetTop, diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 173e1df2..28718ce3 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -133,9 +133,8 @@ export function toSentenceCase(text) { const words = text.split(/[-\s]+/); // Capitalize the first letter of each word and convert the rest to lowercase - const sentenceCaseWords = words.map(word => { - return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase(); - }); + // eslint-disable-next-line max-len + const sentenceCaseWords = words.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()); // Join the words back together with spaces return sentenceCaseWords.join(' '); @@ -850,7 +849,7 @@ export function loadSolutionHeader(header) { const solutionHeaderBlock = document.querySelector('.solution-header-wrapper'); header.append(solutionHeaderBlock); document.querySelector('body').classList.add('header-visible'); - + return loadBlock(solutionHeaderBlock); } diff --git a/scripts/scripts.js b/scripts/scripts.js index b27d11e7..37bc5f9e 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -928,7 +928,7 @@ async function loadLazy(doc) { loadHeader(doc.querySelector('header')); loadFooter(doc.querySelector('footer')); await buildBreadcrumb(); - } else if(locationCheck('campaigns')) { + } else if (locationCheck('campaigns')) { loadSolutionHeader(doc.querySelector('header')); } diff --git a/styles/styles.css b/styles/styles.css index a8f65e73..c5a60166 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -135,6 +135,7 @@ --neutral-160: #454544; --neutral-grey-tint100: #B9B5AE; --neutral-grey-tint120: #8B8883; + --neutral-grey-tint140: #5C5A57; /* Feedback */ --feedback-green: #059641; From df330b55d59d64935543227e0851a7261161ed57 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Thu, 28 Sep 2023 15:55:59 -0400 Subject: [PATCH 05/10] feat(solution-header): add solution footer block --- blocks/solution-footer/solution-footer.css | 65 ++++++++++++++++++++++ blocks/solution-footer/solution-footer.js | 46 +++++++++++++++ scripts/lib-franklin.js | 8 ++- 3 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 blocks/solution-footer/solution-footer.css create mode 100644 blocks/solution-footer/solution-footer.js diff --git a/blocks/solution-footer/solution-footer.css b/blocks/solution-footer/solution-footer.css new file mode 100644 index 00000000..44d23374 --- /dev/null +++ b/blocks/solution-footer/solution-footer.css @@ -0,0 +1,65 @@ +/* Section - Solution Footer */ +main .section.solution-footer-container { + background-color: var(--neutral-carbon); + color: var(--neutral-white); +} + +main .section.solution-footer-container > div:last-child { + padding: var(--spacer-element-08) 0; +} + +/* Block - Solution Footer */ +.solution-footer { + display: flex; + justify-content: space-between; + align-items: center; +} + +.solution-footer .default-content-wrapper { + display: flex; + align-items: center; + flex-grow: 0; +} + +.solution-footer ul { + display: flex; + justify-content: flex-end; + align-items: center; + gap: var(--gap-24); + list-style: none; + margin: 0; + padding-top: var(--spacer-element-03); + padding-inline-start: 0; +} + +.solution-footer ul li { + color: var(--neutral-grey-tint100); + margin: 0; +} + +.solution-footer ul li a, +.solution-footer ul li a:not(.button):any-link { + color: var(--neutral-white); + border-bottom: 0; +} + +.solution-footer ul + p { + margin: 0 0 0 var(--spacer-element-08); +} + +/* Tablet */ +@media only screen and (min-width: 768px) { + +} + +/* Desktop */ +@media (min-width: 1200px) { + main .section.solution-footer-container > div:last-child { + padding: var(--spacer-element-10) 0; + } + + .solution-footer ul + p { + margin: 0 0 0 var(--spacer-element-10); + line-height: 0; + } +} \ No newline at end of file diff --git a/blocks/solution-footer/solution-footer.js b/blocks/solution-footer/solution-footer.js new file mode 100644 index 00000000..786fee8f --- /dev/null +++ b/blocks/solution-footer/solution-footer.js @@ -0,0 +1,46 @@ +import { decorateMain } from '../../scripts/scripts.js'; +import { loadBlocks, decorateIcons, decorateButtons } from '../../scripts/lib-franklin.js'; + +/** + * Loads a fragment. + * @param {string} path The path to the fragment + * @returns {HTMLElement} The root element of the fragment + */ +async function loadFragment(path) { + if (path && path.startsWith('/')) { + const resp = await fetch(`${path}.plain.html`); + if (resp.ok) { + const main = document.createElement('div'); + main.innerHTML = await resp.text(); + decorateMain(main); + await loadBlocks(main); + return main; + } + } +} + +export default async function decorate(block) { + const blockName = block.getAttribute('data-block-name'); + if (!blockName) { + return; + } + + decorateButtons(block, { decorateClasses: false }); + + const lastRow = [...block.children][1]; + + if (lastRow) { + const link = lastRow.querySelector('a'); + const path = link ? link.getAttribute('href') : lastRow.textContent.trim(); + const fragment = await loadFragment(path); + + if (fragment) { + const fragmentSection = fragment.querySelector(':scope .section'); + if (fragmentSection) { + lastRow.classList.add(...fragmentSection.classList); + lastRow.replaceWith(...fragmentSection.childNodes); + } + } + } + +} diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 28718ce3..1af5e8c2 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -847,10 +847,12 @@ export function loadHeader(header) { export function loadSolutionHeader(header) { const solutionHeaderBlock = document.querySelector('.solution-header-wrapper'); - header.append(solutionHeaderBlock); - document.querySelector('body').classList.add('header-visible'); - return loadBlock(solutionHeaderBlock); + if (solutionHeaderBlock) { + header.append(solutionHeaderBlock); + document.querySelector('body').classList.add('header-visible'); + } + } /** From dc17aa9d495dd82974c99e1c6868e455e0685d12 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Thu, 28 Sep 2023 16:44:32 -0400 Subject: [PATCH 06/10] feat(solution-footer): add responsive styling --- blocks/solution-footer/solution-footer.css | 74 +++++++++++++++++++--- 1 file changed, 64 insertions(+), 10 deletions(-) diff --git a/blocks/solution-footer/solution-footer.css b/blocks/solution-footer/solution-footer.css index 44d23374..32f2dbee 100644 --- a/blocks/solution-footer/solution-footer.css +++ b/blocks/solution-footer/solution-footer.css @@ -10,22 +10,37 @@ main .section.solution-footer-container > div:last-child { /* Block - Solution Footer */ .solution-footer { + position: relative; display: flex; - justify-content: space-between; - align-items: center; + flex-direction: column; +} + +.solution-footer .icon { + display: block; + line-height: 0; +} + +.solution-footer svg { + width: auto; + height: 38px; +} + +.solution-footer svg #text * { + fill: var(--neutral-white); } .solution-footer .default-content-wrapper { - display: flex; - align-items: center; - flex-grow: 0; + width: 100%; + margin-top: var(--spacer-element-08); + padding-top: var(--spacer-element-07); + border-top: 1px solid var(--neutral-grey-tint140); } .solution-footer ul { display: flex; - justify-content: flex-end; + flex-direction: column; align-items: center; - gap: var(--gap-24); + gap: var(--gap-16); list-style: none; margin: 0; padding-top: var(--spacer-element-03); @@ -33,6 +48,7 @@ main .section.solution-footer-container > div:last-child { } .solution-footer ul li { + font-size: var(--font-size-14); color: var(--neutral-grey-tint100); margin: 0; } @@ -43,13 +59,43 @@ main .section.solution-footer-container > div:last-child { border-bottom: 0; } -.solution-footer ul + p { +.solution-footer .default-content-wrapper ul + p { margin: 0 0 0 var(--spacer-element-08); + position: absolute; + top: 0; + right: 0; } /* Tablet */ @media only screen and (min-width: 768px) { - + .solution-footer { + display: flex; + justify-content: space-between; + flex-direction: row; + align-items: center; + } + + .solution-footer ul { + display: flex; + justify-content: flex-end; + flex-direction: row; + align-items: center; + gap: var(--gap-24); + } + + .solution-footer .default-content-wrapper { + display: flex; + align-items: center; + flex-grow: 0; + width: auto; + margin-top: 0; + padding-top: 0; + border-top: 0; + } + + .solution-footer .default-content-wrapper ul + p { + position: relative; + } } /* Desktop */ @@ -58,7 +104,15 @@ main .section.solution-footer-container > div:last-child { padding: var(--spacer-element-10) 0; } - .solution-footer ul + p { + .solution-footer svg { + height: 48px; + } + + .solution-footer ul li { + font-size: var(--font-size-16); + } + + .solution-footer .default-content-wrapper ul + p { margin: 0 0 0 var(--spacer-element-10); line-height: 0; } From 46b6516c3670119e4d3c1d9c5c205925db28d963 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Fri, 29 Sep 2023 11:33:27 -0400 Subject: [PATCH 07/10] feat(solution-footer): add more styles and fixes --- blocks/cta/cta.css | 12 +++-- blocks/solution-footer/solution-footer.css | 17 +++++- blocks/solution-footer/solution-footer.js | 63 ++++++++++++++++------ blocks/solution-header/solution-header.js | 11 ++-- scripts/lib-franklin.js | 32 ++++++----- 5 files changed, 95 insertions(+), 40 deletions(-) diff --git a/blocks/cta/cta.css b/blocks/cta/cta.css index 74a86cbb..83cb735b 100644 --- a/blocks/cta/cta.css +++ b/blocks/cta/cta.css @@ -18,14 +18,9 @@ .section.cta-container .cta.block > div, .section.cta-container .cta.block .cta__inner { text-align: center; - max-width: var(--text-max-container); margin: 0 auto; } -.cta.block .button-group p { - margin: 0; -} - .section.cta-container .cta.block h2 { margin: unset; font-size: 32px; @@ -39,6 +34,13 @@ line-height: 160%; text-align: center; letter-spacing: 0.01em; + max-width: var(--text-max-container); + margin: 0 auto; +} + +.section.cta-container .cta.block p picture { + display: block; + line-height: 0; } .section.cta-container .cta.block p.only-picture:last-child { diff --git a/blocks/solution-footer/solution-footer.css b/blocks/solution-footer/solution-footer.css index 32f2dbee..b349ba1c 100644 --- a/blocks/solution-footer/solution-footer.css +++ b/blocks/solution-footer/solution-footer.css @@ -53,11 +53,26 @@ main .section.solution-footer-container > div:last-child { margin: 0; } +.solution-footer ul li .cookie-consent { + color: var(--neutral-white); +} + +/* stylelint-disable no-descending-specificity */ .solution-footer ul li a, .solution-footer ul li a:not(.button):any-link { color: var(--neutral-white); - border-bottom: 0; + border-color: transparent; +} + +.solution-footer ul li a:hover, +.solution-footer ul li a:not(.button):any-link:hover { + background: none; + color: var(--neutral-white); + border-bottom: 1px solid var(--neutral-white); + border-image-source: unset; + -webkit-text-fill-color: var(--neutral-white); } +/* stylelint-enable no-descending-specificity */ .solution-footer .default-content-wrapper ul + p { margin: 0 0 0 var(--spacer-element-08); diff --git a/blocks/solution-footer/solution-footer.js b/blocks/solution-footer/solution-footer.js index 786fee8f..3e81bba1 100644 --- a/blocks/solution-footer/solution-footer.js +++ b/blocks/solution-footer/solution-footer.js @@ -1,46 +1,77 @@ import { decorateMain } from '../../scripts/scripts.js'; -import { loadBlocks, decorateIcons, decorateButtons } from '../../scripts/lib-franklin.js'; +import { loadBlocks, decorateButtons } from '../../scripts/lib-franklin.js'; /** * Loads a fragment. - * @param {string} path The path to the fragment - * @returns {HTMLElement} The root element of the fragment + * @param {string} path - The path to the fragment + * @returns {HTMLElement} - The root element of the fragment */ async function loadFragment(path) { - if (path && path.startsWith('/')) { - const resp = await fetch(`${path}.plain.html`); - if (resp.ok) { - const main = document.createElement('div'); - main.innerHTML = await resp.text(); - decorateMain(main); - await loadBlocks(main); - return main; + try { + const url = new URL(path, window.location.origin); // Parse the URL + + if (url.pathname.startsWith('/')) { + const resp = await fetch(`${url.pathname}.plain.html`); + if (resp.ok) { + const main = document.createElement('div'); + main.innerHTML = await resp.text(); + // Decorate main element and load additional blocks + decorateMain(main); + await loadBlocks(main); + return main; + } + throw new Error('Failed to fetch fragment'); + } else { + throw new Error('Invalid path'); } + } catch (error) { + // eslint-disable-next-line no-console + console.error(`Error loading fragment: ${error}`); } + return null; } export default async function decorate(block) { + // Get the block name attribute from the block element const blockName = block.getAttribute('data-block-name'); if (!blockName) { return; } + // Decorate buttons within the block, ignoring class decoration decorateButtons(block, { decorateClasses: false }); + // Get the last child element of the block const lastRow = [...block.children][1]; if (lastRow) { const link = lastRow.querySelector('a'); - const path = link ? link.getAttribute('href') : lastRow.textContent.trim(); - const fragment = await loadFragment(path); + // Extract the href attribute from the link, if it exists + const { href } = link || {}; + const fragment = await loadFragment(href); if (fragment) { + // Extract the section element from the loaded fragment const fragmentSection = fragment.querySelector(':scope .section'); - if (fragmentSection) { - lastRow.classList.add(...fragmentSection.classList); + const { classList } = fragmentSection || []; + if (classList) { + // Add classes from the fragment's section element to the last row + lastRow.classList.add(...classList); + // Replace the last row content with fragment section content lastRow.replaceWith(...fragmentSection.childNodes); } } + + // Handle click events on footer base links + block.addEventListener('click', (e) => { + const { target } = e; + // Check if the clicked element is a footer base link with an empty href attribute + if (target.tagName === 'A' && target.getAttribute('href') === '') { + e.preventDefault(); + // Call the OneTrust function to toggle info display + // eslint-disable-next-line no-undef + OneTrust.ToggleInfoDisplay(); + } + }); } - } diff --git a/blocks/solution-header/solution-header.js b/blocks/solution-header/solution-header.js index 28fd3ad1..7544da80 100644 --- a/blocks/solution-header/solution-header.js +++ b/blocks/solution-header/solution-header.js @@ -1,5 +1,3 @@ -import { toSentenceCase } from '../../scripts/lib-franklin.js'; - export default function decorate(block) { const blockName = block.getAttribute('data-block-name'); if (!blockName) { @@ -43,7 +41,7 @@ export default function decorate(block) { sectionsWithTitles.forEach((section) => { // Get the value of the data-title attribute - const sectionTitle = toSentenceCase(section.getAttribute('data-title')); + const sectionTitle = section.getAttribute('data-title'); // Create a list item for each section const listItem = document.createElement('li'); @@ -52,7 +50,8 @@ export default function decorate(block) { // Create an anchor link element const anchorLink = document.createElement('a'); anchorLink.textContent = sectionTitle; - anchorLink.href = `#${section.getAttribute('data-title')}`; + const achorLinkID = section.getAttribute('id'); + anchorLink.href = `#${achorLinkID}`; // Append the anchor link to the list item listItem.appendChild(anchorLink); @@ -69,7 +68,7 @@ export default function decorate(block) { generateAnchorLinkNav(); // Define the list of navigation links - const navigationLinks = document.querySelectorAll('.solution-header__col-2 ul li a'); + const navigationLinks = document.querySelectorAll(`.${blockName}__col-2 ul li a`); // Extract section IDs from navigation links const sectionIds = Array.from(navigationLinks).map((link) => link.getAttribute('href').substring(1)); @@ -86,7 +85,7 @@ export default function decorate(block) { entries.forEach((entry) => { if (entry.isIntersecting) { const targetId = entry.target.getAttribute('id'); - const correspondingLink = document.querySelector(`.solution-header__col-2 ul li a[href="#${targetId}"]`); + const correspondingLink = document.querySelector(`.${blockName}__col-2 ul li a[href="#${targetId}"]`); // Remove 'active' class from all links navigationLinks.forEach((link) => { diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index 1af5e8c2..ee58e4dc 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -281,21 +281,30 @@ export function decorateSections(main) { const sectionMeta = section.querySelector('div.section-metadata'); if (sectionMeta) { const meta = readBlockConfig(sectionMeta); + let styles; Object.keys(meta).forEach((key) => { - if (key === 'style') { - const styles = meta.style.split(',').map((style) => toClassName(style.trim())); - - styles.forEach((style) => section.classList.add(style)); - } else if (key === 'theme') { - section.setAttribute('data-theme', meta.theme); - } else if (key === 'id') { - section.setAttribute('data-title', toClassName(meta.id)); - section.setAttribute('id', toClassName(meta.id)); - } else { - section.dataset[toCamelCase(key)] = meta[key]; + switch (key) { + case 'style': + styles = meta.style.split(',').map((style) => toClassName(style.trim())); + styles.forEach((style) => section.classList.add(style)); + break; + case 'theme': + section.setAttribute('data-theme', meta.theme); + break; + case 'id': + section.setAttribute('id', toClassName(meta.id)); + if (key === 'title') { + section.setAttribute('data-title', meta.title); + } else { + section.setAttribute('data-title', toSentenceCase(meta.id)); + } + break; + default: + section.dataset[toCamelCase(key)] = meta[key]; } }); + sectionMeta.parentNode.remove(); } }); @@ -852,7 +861,6 @@ export function loadSolutionHeader(header) { header.append(solutionHeaderBlock); document.querySelector('body').classList.add('header-visible'); } - } /** From 15f95a537c7d2d91daee79a04f593307bc257950 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Fri, 29 Sep 2023 15:42:47 -0400 Subject: [PATCH 08/10] feat(solution-header): check for when the append is fired --- blocks/solution-header/solution-header.css | 30 ++++++++++++---------- scripts/lib-franklin.js | 9 ++++++- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/blocks/solution-header/solution-header.css b/blocks/solution-header/solution-header.css index c5c73ac3..fddd1ff6 100644 --- a/blocks/solution-header/solution-header.css +++ b/blocks/solution-header/solution-header.css @@ -1,6 +1,15 @@ /* Block - Solution Header */ -.header-visible .solution-header[data-block-status="loaded"] { - opacity: 1; +.solution-header-wrapper { + opacity: 0; + position: fixed; + border-bottom: 1px solid var(--neutral-sand); + width: 100%; + height: var(--nav-height-desktop); + background-color: var(--neutral-bone); + left: 0; + top: 0; + transition: all .5s ease; + z-index: 99999; } .solution-header { @@ -10,6 +19,11 @@ margin: 0 auto; } +.header-visible .solution-header-wrapper, +.header-visible .solution-header[data-block-status="loaded"] { + opacity: 1; +} + .solution-header .solution-header__inner { position: relative; } @@ -91,18 +105,6 @@ } /* Sticky - Solution Header */ -.solution-header-wrapper { - position: fixed; - border-bottom: 1px solid var(--neutral-sand); - width: 100%; - height: var(--nav-height-desktop); - background-color: var(--neutral-bone); - left: 0; - top: 0; - transition: all .5s ease; - z-index: 99999; -} - .solution-header-wrapper.is-sticky { background-color: var(--neutral-carbon); border-bottom: 1px solid var(--neutral-carbon); diff --git a/scripts/lib-franklin.js b/scripts/lib-franklin.js index ee58e4dc..8cc14357 100644 --- a/scripts/lib-franklin.js +++ b/scripts/lib-franklin.js @@ -859,7 +859,14 @@ export function loadSolutionHeader(header) { if (solutionHeaderBlock) { header.append(solutionHeaderBlock); - document.querySelector('body').classList.add('header-visible'); + + // Create a promise that resolves when the next animation frame is available + const waitForAnimationFrame = () => new Promise(requestAnimationFrame); + + // Wait for the next animation frame before adding the class + waitForAnimationFrame().then(() => { + document.querySelector('body').classList.add('header-visible'); + }); } } From 913472ee513e4538310b98f5cb075d4a0f8ee764 Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Tue, 3 Oct 2023 10:54:36 -0400 Subject: [PATCH 09/10] feat(solution-header): add spacing fixes based on feedback --- blocks/solution-header/solution-header.css | 47 ++++++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/blocks/solution-header/solution-header.css b/blocks/solution-header/solution-header.css index fddd1ff6..e5067eef 100644 --- a/blocks/solution-header/solution-header.css +++ b/blocks/solution-header/solution-header.css @@ -4,7 +4,7 @@ position: fixed; border-bottom: 1px solid var(--neutral-sand); width: 100%; - height: var(--nav-height-desktop); + height: 140px; background-color: var(--neutral-bone); left: 0; top: 0; @@ -39,11 +39,11 @@ .solution-header .icon svg { width: auto; - height: var(--spacer-layout-036); + height: 38px; } .solution-header .solution-header__inner .solution-header__col-1 { - margin: var(--spacer-element-05) var(--spacer-element-07); + margin: var(--spacer-element-07); } .solution-header .solution-header__inner .solution-header__col-2 { @@ -105,6 +105,10 @@ } /* Sticky - Solution Header */ +body.microsites.header-visible main { + padding-top: 140px; +} + .solution-header-wrapper.is-sticky { background-color: var(--neutral-carbon); border-bottom: 1px solid var(--neutral-carbon); @@ -131,6 +135,10 @@ height: var(--spacer-layout-03); } +.solution-header-wrapper.is-sticky .solution-header .solution-header__col-1 { + margin: var(--spacer-element-05) var(--spacer-element-07); +} + .solution-header-wrapper.is-sticky .solution-header .solution-header__col-2 { border-color: transparent; margin: 0; @@ -150,7 +158,7 @@ } .solution-header .solution-header__inner .solution-header__col-1 { - margin: var(--spacer-element-05) var(--spacer-element-08); + margin: var(--spacer-element-07) var(--spacer-element-08); } .solution-header .solution-header__inner .solution-header__col-3 { @@ -162,7 +170,10 @@ } /* Sticky - Solution Header */ - .solution-header-wrapper.is-sticky .solution-header__inner .solution-header__col-3 { + .solution-header-wrapper.is-sticky .solution-header .solution-header__inner .solution-header__col-1 { + margin: var(--spacer-element-05) var(--spacer-element-08); + } + .solution-header-wrapper.is-sticky .solution-header .solution-header__inner .solution-header__col-3 { display: none; } } @@ -186,12 +197,20 @@ padding-bottom: var(--spacer-element-08); } + .solution-header ul { + justify-content: flex-start; + } + .solution-header ul li { padding-left: var(--spacer-element-08); } + + .solution-header ul li:first-child { + padding-left: 0; + } .solution-header ul li:last-child { - padding-right: var(--spacer-element-08); + padding-right: 0; } .solution-header .icon svg { @@ -221,6 +240,14 @@ } /* Sticky - Solution Header */ + body.microsites.header-visible main { + padding-top: var(--nav-height-desktop); + } + + .solution-header-wrapper { + height: var(--nav-height-desktop); + } + .solution-header-wrapper.is-sticky { height: var(--sticky-nav-height); } @@ -233,12 +260,16 @@ padding: var(--spacer-element-07) 0; } - .solution-header-wrapper.is-sticky .solution-header .solution-header__col-2 { + .solution-header-wrapper.is-sticky .solution-header .solution-header__inner .solution-header__col-1 { + margin: 0; + } + + .solution-header-wrapper.is-sticky .solution-header .solution-header__inner .solution-header__col-2 { border-color: transparent; margin: 0 0 calc(var(--spacer-element-05) * -1) 0; } - .solution-header-wrapper.is-sticky .solution-header__inner .solution-header__col-3 { + .solution-header-wrapper.is-sticky .solution-header .solution-header__inner .solution-header__col-3 { display: block; } From 2e836a7595a536667df3301a98441a219482260d Mon Sep 17 00:00:00 2001 From: Putra Bonaccorsi Date: Tue, 3 Oct 2023 11:06:12 -0400 Subject: [PATCH 10/10] feat(solution-header): fix lint issue --- blocks/solution-header/solution-header.css | 1 + 1 file changed, 1 insertion(+) diff --git a/blocks/solution-header/solution-header.css b/blocks/solution-header/solution-header.css index e5067eef..1cdb6fea 100644 --- a/blocks/solution-header/solution-header.css +++ b/blocks/solution-header/solution-header.css @@ -173,6 +173,7 @@ body.microsites.header-visible main { .solution-header-wrapper.is-sticky .solution-header .solution-header__inner .solution-header__col-1 { margin: var(--spacer-element-05) var(--spacer-element-08); } + .solution-header-wrapper.is-sticky .solution-header .solution-header__inner .solution-header__col-3 { display: none; }