diff --git a/blocks/banner/banner.css b/blocks/banner/banner.css index 8211226d..fbb7a106 100644 --- a/blocks/banner/banner.css +++ b/blocks/banner/banner.css @@ -22,6 +22,32 @@ margin: 0; } +.block.banner.toast { + position: fixed; + left: 0; + z-index: 100; + width: 100%; + bottom: 42px; + padding: 36px 32px; + border-radius: 8px 8px 0 0; + max-width: initial; +} + +.block.banner.toast::before { + content: ''; + position: absolute; + bottom: -42px; + left: -36px; + width: 100%; + height: 100%; + background-image: url('/icons/row-promo-banner-lines-mobile.svg'); + background-repeat: no-repeat; + background-position: bottom right; + z-index: 0; + transform: scale(-1, 1); +} + + .block.banner.fixed-to-bottom { position: fixed; left: 0; @@ -67,6 +93,10 @@ color: var(--color-white); margin-left: 10px; } + + .block.banner.toast { + border-radius: 12px 12px 0 0; + } } .block.banner > .banner-content > .banner-content-page * { @@ -77,6 +107,21 @@ text-decoration: underline; } +.block.banner.toast > .banner-content > .banner-content-page * { + font-size: 18px; + font-style: normal; + font-weight: 700; + line-height: 140%; /* 25.2px */ + letter-spacing: 0.18px; + text-decoration: none; + display: flex; + align-items: center; + justify-content: center; + text-align: center; + gap: 24px; + flex-direction: column; +} + .block.banner > .banner-content > .banner-content-page { display: none; } @@ -115,18 +160,19 @@ font-size: 12px; } +.block.banner.toast > .banner-actions > .close-button { + position: absolute; + top: 10px; + right: 10px; +} + + .block.banner > .banner-actions > .close-button::before { content: "X"; color: var(--color-white); margin: auto; } -.block.banner p:has(a) a:any-link, -.block.banner p:has(strong+a) a:any-link, -.block.banner p:has(strong > a) a:any-link { - color: var(--color-yellow); -} - .block.banner p.button-container a:any-link { background-color: var(--color-button-primary); border-color: var(--color-button-primary); @@ -168,4 +214,27 @@ button.fa-chevron-right { overflow: unset; text-overflow: unset; border-radius: unset; +} + +@media (min-width: 1400px /* breakpoint-xl */) { + .block.banner.toast { + bottom: 0; + } + + .block.banner.toast::before { + left: 0; + bottom: 0; + background-size: contain; + } + + .block.banner.toast > .banner-actions > .close-button { + position: relative; + top: 0; + right: 0; + } + + .block.banner.toast > .banner-content > .banner-content-page * { + flex-direction: row; + gap: 48px; + } } \ No newline at end of file diff --git a/blocks/banner/banner.js b/blocks/banner/banner.js index c63040cf..4ad59f58 100644 --- a/blocks/banner/banner.js +++ b/blocks/banner/banner.js @@ -2,6 +2,25 @@ import { decorateButtons } from '../../scripts/aem.js'; import { loadFragment } from '../fragment/fragment.js'; import { span, button, p } from '../../scripts/dom-helpers.js'; +const TOAST_BANNER = 'toast'; +const TOAST_BANNER_COOKIE = 'bannerDismissed'; + +function setCookie(name, value, days) { + const date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + const expires = `expires=${date.toUTCString()}`; + document.cookie = `${name}=${value}; ${expires}; path=/`; +} + +export function getCookie(name) { + const value = `; ${document.cookie}`; + const parts = value.split(`; ${name}=`); + if (parts.length === 2) { + return parts.pop().split(';').shift(); + } + return null; +} + function decorateCloseButton(block) { const buttonWrapper = document.createElement('div'); buttonWrapper.classList.add('banner-actions'); @@ -30,6 +49,11 @@ function navigateContent(block, direction) { } export default async function decorate(block) { + if (getCookie(TOAST_BANNER_COOKIE) && block.classList.contains(TOAST_BANNER)) { + block.closest('.section').remove(); + return; + } + // Set default background if no other is present if (!(/-background/.test(block.className))) { block.classList.add('blue-background'); @@ -53,12 +77,32 @@ export default async function decorate(block) { block.append(child); child.classList.add('banner-content-page'); - const arrowRightEl = p( - { class: 'button-container' }, - span({ class: 'icon icon-right-arrow-bolder', 'data-icon-src': '/icons/right-arrow-bolder.svg', style: '--mask-image: url(/icons/right-arrow-bolder.svg);' }), - ); + if (block.classList.contains(TOAST_BANNER)) { + const ctas = child.querySelectorAll('a'); - child.append(arrowRightEl); + ctas.forEach((cta) => { + const originalParent = cta.parentNode; + const topLevel = originalParent.parentNode; + + if (topLevel && (topLevel.tagName === 'P' || topLevel.tagName === 'DIV')) { + const actionButton = originalParent.querySelector('a'); + if (originalParent.tagName === 'STRONG') { + actionButton.classList.add('button', 'primary'); + } + + if (originalParent.tagName === 'EM') { + actionButton.classList.add('button', 'secondary'); + } + } + }); + } else { + const arrowRightEl = p( + { class: 'button-container' }, + span({ class: 'icon icon-right-arrow-bolder', 'data-icon-src': '/icons/right-arrow-bolder.svg', style: '--mask-image: url(/icons/right-arrow-bolder.svg);' }), + ); + + child.append(arrowRightEl); + } if (idx === 0) { child.classList.add('active'); @@ -77,7 +121,7 @@ export default async function decorate(block) { decorateCloseButton(block); - if (block.classList.contains('fixed-to-top')) { + if (block.classList.contains('fixed-to-top') || block.classList.contains(TOAST_BANNER)) { block.classList.add('cmp-notification-bar'); // analytics trigger contentWrapper.classList.add('cmp-carousel__item__content'); // analytics trigger const body = block.closest('body'); @@ -96,8 +140,17 @@ export default async function decorate(block) { copyBlock.prepend(leftChevron); copyBlock.append(rightChevron); } + const closeButton = copyBlock.querySelector('button.close-button'); closeButton.classList.add('cmp-carousel__action--close'); // analytics trigger + + // Add event to set a cookie when the banner is closed + if (block.classList.contains(TOAST_BANNER)) { + closeButton.addEventListener('click', () => { + setCookie(TOAST_BANNER_COOKIE, 'true', 3); + }); + } + closeButton.addEventListener('click', () => copyBlock.remove()); body.prepend(copySection); copySection.style = '{display: block;}'; diff --git a/blocks/header/nav.css b/blocks/header/nav.css index cc242066..3fd13d78 100644 --- a/blocks/header/nav.css +++ b/blocks/header/nav.css @@ -1,4 +1,10 @@ /* Updated CSS with .header added to each rule */ +:root { + --menu-offset-without-banner: 0px; + --menu-offset-with-banner: 146px; + --menu-desktop-offset-without-banner: 0px; + --menu-desktop-offset-with-banner: 168px; +} header { position: sticky; @@ -26,7 +32,7 @@ header { } .header nav.nav-alert { - top: 124px; + top: var(--menu-offset-with-banner); } .header nav.open { @@ -89,7 +95,7 @@ header { } .header .submenu-alert.submenu { - top: 124px; + top: var(--menu-offset-with-banner); } .header .nav-item:not(.hover) { @@ -797,11 +803,11 @@ header { } .header .submenu-alert.submenu { - top: 168px; + top: var(--menu-desktop-offset-with-banner); } .header .submenu-alert.submenu-scrolled { - top: 168px; + top: var(--menu-desktop-offset-with-banner); } .header .submenu.open { diff --git a/blocks/header/utils.js b/blocks/header/utils.js index 75dd0b03..d20cb1a4 100644 --- a/blocks/header/utils.js +++ b/blocks/header/utils.js @@ -20,6 +20,7 @@ const FOCUSABLE_ELEMENTS = const MOBILE_MENU_OPEN_CLASSNAME = 'open'; const DESKTOP_MENU_OPEN_CLASSNAME = 'hover'; +const ALERT_MENU_CLASSNAME = 'submenu-alert'; const handleScroll = (submenu) => { if (window.scrollY > 50) { @@ -200,7 +201,10 @@ export function generateMenuFromSection( } const nav = document.createElement('nav'); - const hasAlertBanner = document.querySelector('.cmp-notification-bar'); + + const hasAlertBanner = document.querySelector( + '.cmp-notification-bar:not(.toast)', + ); nav.role = 'menu'; nav.id = 'nav'; @@ -266,10 +270,10 @@ export function generateMenuFromSection( if (hasAlertBanner) { const alertCloseButton = hasAlertBanner.querySelector('.close-button'); - submenu.classList.add('submenu-alert'); + submenu.classList.add(ALERT_MENU_CLASSNAME); alertCloseButton.addEventListener('click', () => { - submenu.classList.remove('submenu-alert'); + submenu.classList.remove(ALERT_MENU_CLASSNAME); nav.classList.remove('nav-alert'); }); @@ -746,8 +750,9 @@ export function buildCtasSection( const contactModalButtonTitle = placeHolders.contactustext || 'Open Contact Us Information'; const searchModalButtonTitle = placeHolders.searchtext || 'Open Search box'; - const hasAlertBanner = document.querySelector('.cmp-notification-bar'); - + const hasAlertBanner = document.querySelector( + '.cmp-notification-bar:not(.toast)', + ); const contactModalButton = button({ class: 'icon-button button contact-button', 'aria-label': contactModalButtonTitle, @@ -819,7 +824,7 @@ export function buildCtasSection( const contactModal = div( { class: `submenu modal contact-modal ${ - hasAlertBanner ? 'submenu-alert' : '' + hasAlertBanner ? ALERT_MENU_CLASSNAME : '' }`, id: 'contact-modal', }, @@ -841,7 +846,9 @@ export function buildCtasSection( const searchModal = div( { - class: `submenu search-modal ${hasAlertBanner ? 'submenu-alert' : ''}`, + class: `submenu search-modal ${ + hasAlertBanner ? ALERT_MENU_CLASSNAME : '' + }`, id: 'search-modal', }, div( @@ -880,8 +887,8 @@ export function buildCtasSection( const alertCloseButton = hasAlertBanner.querySelector('.close-button'); alertCloseButton.addEventListener('click', () => { - searchModal.classList.remove('submenu-alert'); - contactModal.classList.remove('submenu-alert'); + searchModal.classList.remove(ALERT_MENU_CLASSNAME); + contactModal.classList.remove(ALERT_MENU_CLASSNAME); }); }