diff --git a/libs/blocks/brand-concierge/brand-concierge.css b/libs/blocks/brand-concierge/brand-concierge.css index 268679dfaa4..b3ce7cba10b 100644 --- a/libs/blocks/brand-concierge/brand-concierge.css +++ b/libs/blocks/brand-concierge/brand-concierge.css @@ -659,12 +659,15 @@ opacity: 0.4; } -.floating-input .bc-floating-input .bc-textarea-grow-wrap { +.floating-input .bc-floating-input .bc-textarea-grow-wrap, +.brand-concierge.marquee .bc-textarea-grow-wrap { overflow: hidden; } .floating-input .bc-floating-input .bc-input-field-container textarea, -.floating-input .bc-floating-input .bc-textarea-grow-wrap::after { +.floating-input .bc-floating-input .bc-textarea-grow-wrap::after, +.brand-concierge.marquee .bc-input-field-container textarea, +.brand-concierge.marquee .bc-textarea-grow-wrap::after { max-height: 48px; min-height: 48px; overflow: hidden; @@ -1198,3 +1201,276 @@ max-width: 80vw; } } + +/* marquee */ +/* stylelint-disable no-descending-specificity */ +.brand-concierge.marquee { + position: relative; + overflow: hidden; + isolation: isolate; + display: flex; + flex-direction: column; + min-height: 560px; + padding: 0; +} + +.brand-concierge.marquee .background { + position: absolute; + inset: 0; + z-index: -2; +} + +.brand-concierge.marquee .background > div { + position: absolute; + inset: 0; +} + +.brand-concierge.marquee .background picture { + display: block; + position: absolute; + inset: 0; + width: 100%; + height: 100%; +} + +.brand-concierge.marquee .background img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; + object-position: 65%; +} + +.brand-concierge.marquee .background::after { + content: ''; + position: absolute; + inset: 0; + z-index: 1; +} + +.brand-concierge.marquee.dark .background::after { + background: linear-gradient(270deg, rgba(0 0 0 / 0%) -100%, #000 100%); +} + +.brand-concierge.marquee.light .background::after { + background: linear-gradient(270deg, rgba(255 255 255 / 0%) 0%, #FFF 100%); +} + +.brand-concierge.marquee .background .tablet-only, +.brand-concierge.marquee .background .desktop-only { + display: none; +} + +.brand-concierge.marquee .background .mobile-only { + display: block; +} + +.brand-concierge.marquee .foreground.container { + position: relative; + z-index: 1; + flex: 1 0 auto; + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 16px; + padding: 64px 0 30px; +} + +.brand-concierge.marquee .bc-header, +.brand-concierge.marquee .bc-input-field, +.brand-concierge.marquee .bc-prompt-cards, +.brand-concierge.marquee .bc-legal { + width: 100%; + max-width: 540px; + margin: 0; + text-align: left; + align-items: flex-start; +} + +.brand-concierge.marquee .bc-header { + gap: 8px; + margin-bottom: 8px; + padding: 0; +} + +.brand-concierge.marquee .bc-header-eyebrow { + margin: 0; + font-size: 16px; + font-weight: 700; + line-height: 1.25; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.brand-concierge.marquee .bc-header-title { + margin: 0; + font-size: 36px; + font-weight: 800; + line-height: 1.25; +} + +.brand-concierge.marquee .bc-header-subtitle { + margin: 0; + font-size: 18px; + font-weight: 400; + line-height: 1.5; +} + +.brand-concierge.marquee.dark .bc-header { + color: var(--bc-dark-text-color); +} + +.brand-concierge.marquee .bc-input-field { + margin: 16px 0 0; + max-width: 480px; +} + +.brand-concierge.marquee .bc-input-field-container { + margin-bottom: 0; +} + +.brand-concierge.marquee .bc-prompt-cards { + flex-flow: column nowrap; + gap: 12px; + padding: 8px 0 64px; +} + +.brand-concierge.marquee .prompt-card-button { + width: fit-content; + max-width: 100%; + border-radius: 24px; +} + +.brand-concierge.marquee .prompt-card-text { + grid-template-columns: auto; + padding: 6px 16px; +} + +.brand-concierge.marquee .card-icon { + display: none; +} + +.brand-concierge.marquee.dark .prompt-card-button { + background: rgba(0 0 0 / 48%); +} + +.brand-concierge.marquee.light .prompt-card-button, +.brand-concierge.marquee.dark .prompt-card-button { + border-color: #8f8f8f; +} + +.brand-concierge.marquee.dark .prompt-card-button:hover { + background: rgba(255 255 255 / 18%); + border-color: rgba(255 255 255 / 40%); +} + +.brand-concierge.marquee.dark .prompt-card-text, +.brand-concierge.marquee.dark .prompt-card-text p { + color: var(--bc-dark-text-color); +} + +.brand-concierge.marquee .bc-legal { + margin: auto 0 0; + max-width: 540px; +} + +.brand-concierge.marquee.dark .bc-legal, +.brand-concierge.marquee.dark .bc-legal a { + color: rgba(255 255 255 / 80%); +} + +@media screen and (min-width: 600px) { + .brand-concierge.marquee { + min-height: 540px; + } + + .brand-concierge.marquee .foreground.container { + padding: 56px 0; + } + + .brand-concierge.marquee .background .mobile-only, + .brand-concierge.marquee .background .desktop-only { + display: none; + } + + .brand-concierge.marquee .background .tablet-only { + display: block; + } + + .brand-concierge.marquee .bc-header, + .brand-concierge.marquee .bc-input-field, + .brand-concierge.marquee .bc-prompt-cards, + .brand-concierge.marquee .bc-legal { + max-width: 600px; + } +} + +@media screen and (min-width: 768px) { + .brand-concierge.marquee .foreground.container { + padding: 72px 0; + } + + .brand-concierge.marquee .bc-header-title, + .brand-concierge.marquee .bc-header-subtitle { + max-width: 545px; + } + + .brand-concierge.marquee .bc-input-field { + max-width: 100%; + } +} + +@media screen and (min-width: 1200px) { + .brand-concierge.marquee { + min-height: 700px; + } + + .brand-concierge.marquee .foreground.container { + padding: 120px 0 64px; + } + + .brand-concierge.marquee .background .mobile-only, + .brand-concierge.marquee .background .tablet-only { + display: none; + } + + .brand-concierge.marquee .background .desktop-only { + display: block; + } + + .brand-concierge.marquee .foreground .bc-header, + .brand-concierge.marquee .foreground .bc-input-field, + .brand-concierge.marquee .foreground .bc-prompt-cards, + .brand-concierge.marquee .foreground .bc-legal { + max-width: 705px; + } + + .brand-concierge.marquee .bc-header-title { + font-size: 80px; + line-height: 1.1; + } + + .brand-concierge.marquee .bc-header-subtitle { + margin-top: 4px; + font-size: 20px; + } + + .brand-concierge.marquee .bc-prompt-cards { + margin: 8px 0 56px; + } + + .brand-concierge.marquee.dark .background::after { + background: linear-gradient(270deg, rgba(0 0 0 / 0%) -0.39%, #000 100%); + } + + .brand-concierge.marquee.light .background::after { + background: linear-gradient(270deg, rgba(255 255 255 / 0%) -0.39%, #FFF 100%); + } + + .brand-concierge.marquee .bc-header-title, + .brand-concierge.marquee .bc-header-subtitle { + max-width: 100%; + } +} + +/* stylelint-enable no-descending-specificity */ diff --git a/libs/blocks/brand-concierge/brand-concierge.js b/libs/blocks/brand-concierge/brand-concierge.js index dfe219101a9..dfb859ddb7b 100644 --- a/libs/blocks/brand-concierge/brand-concierge.js +++ b/libs/blocks/brand-concierge/brand-concierge.js @@ -504,20 +504,50 @@ function decorateBackground(el, background) { if (el.contains(background)) el.removeChild(background); } -function decorateHeader(el, header) { - const hTag = header.querySelector('h1, h2, h3, h4, h5, h6'); - const subTitle = header.querySelector('p'); +function decorateMarqueeBackground(el, background) { + const pictures = [...background.querySelectorAll('picture')]; + if (!pictures.length) { + decorateBackground(el, background); + return; + } + const backgroundLayer = createTag('div', { class: 'background' }); + if (pictures.length === 1) { + backgroundLayer.append(pictures[0]); + } else { + const viewports = ['desktop-only', 'tablet-only', 'mobile-only']; + pictures.forEach((picture, index) => { + backgroundLayer.append(createTag('div', { class: viewports[index] || 'mobile-only' }, picture)); + }); + } + el.prepend(backgroundLayer); + if (el.contains(background)) el.removeChild(background); +} + +function decorateHeader(el, header, { eyebrow: withEyebrow = false } = {}) { const headerSection = createTag('section', { class: 'bc-header' }); + let title; + let eyebrow = null; + + if (withEyebrow) { + [eyebrow, title] = header.querySelectorAll('h1, h2, h3, h4, h5, h6'); + } else { + title = header.querySelector('h1, h2, h3, h4, h5, h6'); + } + const subTitle = header.querySelector('p'); - if (hTag) { - hTag.classList.add('bc-header-title'); - headerSection.append(hTag); + if (eyebrow) { + eyebrow.classList.add('bc-header-eyebrow'); + headerSection.append(eyebrow); + } + if (title) { + title.classList.add('bc-header-title'); + headerSection.append(title); } if (subTitle) { subTitle.classList.add('bc-header-subtitle'); headerSection.append(subTitle); } - if (!hTag && !subTitle) { + if (!eyebrow && !title && !subTitle) { headerSection.append(createTag('p', { class: 'bc-header-subtitle' }, header.textContent.trim())); } @@ -683,6 +713,9 @@ function handleConsent(el) { } export default async function init(el) { + // Reset variant flags so each block decorates independently of any prior init. + Object.keys(variants).forEach((key) => delete variants[key]); + handleConsent(el); window.addEventListener('adobePrivacy:PrivacyReject', () => handleConsent(el)); window.addEventListener('adobePrivacy:PrivacyCustom', () => handleConsent(el)); @@ -697,7 +730,9 @@ export default async function init(el) { setAuthoredContent(rows); // set variant - if (!el.classList.contains('hero') + if (el.classList.contains('marquee')) { + variants.isMarquee = true; + } else if (!el.classList.contains('hero') && !el.classList.contains('floating-button-only') && !el.classList.contains('floating-input-only')) { el.classList.add('inline'); @@ -773,6 +808,24 @@ export default async function init(el) { decorateLegal(el, legal); } + if (variants.isMarquee) { + const [background, header, cards, input, legal] = rows; + decorateMarqueeBackground(el, background); + decorateHeader(el, header, { eyebrow: true }); + decorateInput(el, input); + decorateCards(el, cards); + decorateLegal(el, legal); + + const foreground = createTag('div', { class: 'foreground container' }); + foreground.append( + el.querySelector('.bc-header'), + el.querySelector('.bc-input-field'), + el.querySelector('.bc-prompt-cards'), + el.querySelector('.bc-legal'), + ); + el.append(foreground); + } + if (variants.isFloatingInput) { const [, , cards, input] = rows; decorateFloatingInput(el, cards, input); diff --git a/libs/blocks/fallback/fallback.js b/libs/blocks/fallback/fallback.js index fff3d106cd3..4aeec9cd8b0 100644 --- a/libs/blocks/fallback/fallback.js +++ b/libs/blocks/fallback/fallback.js @@ -23,6 +23,7 @@ const SYNTHETIC_BLOCKS = [ 'featured-card', 'product-card', 'links-card', + 'promo-card', 'promo-card-small', ]; diff --git a/libs/c2/blocks/global-navigation/global-navigation.js b/libs/c2/blocks/global-navigation/global-navigation.js index e879637acab..a835eb68bf3 100644 --- a/libs/c2/blocks/global-navigation/global-navigation.js +++ b/libs/c2/blocks/global-navigation/global-navigation.js @@ -86,5 +86,6 @@ export default async function init(el) { }); return {}; }); + gnavPromise.then(() => requestAnimationFrame(() => window.lenis?.resize())); config.federal = { fedsGlobalNavigation: gnavPromise }; } diff --git a/libs/c2/styles/styles.css b/libs/c2/styles/styles.css index a0fe24e75d1..f44b8bd10bb 100644 --- a/libs/c2/styles/styles.css +++ b/libs/c2/styles/styles.css @@ -1396,7 +1396,7 @@ span.hang-opening-quote { z-index: 0; animation-name: parallax-move-up-fast; animation-timeline: scroll(root block); - animation-range: 0 80vh; + animation-range: var(--feds-promo-bar-height, 0px) calc(var(--feds-promo-bar-height, 0px) + 80vh); animation-timing-function: ease-out; /* Shorter viewports in the common laptop width band need more travel */ @@ -1413,7 +1413,7 @@ span.hang-opening-quote { pointer-events: none; animation: parallax-fade-to-dark linear both; animation-timeline: scroll(root block); - animation-range: 0 80vh; + animation-range: var(--feds-promo-bar-height, 0px) calc(var(--feds-promo-bar-height, 0px) + 80vh); z-index: 2; } } diff --git a/libs/features/dynamic-navigation/status.css b/libs/features/dynamic-navigation/status.css index 6605b910e26..1a7b04a5ed3 100644 --- a/libs/features/dynamic-navigation/status.css +++ b/libs/features/dynamic-navigation/status.css @@ -115,7 +115,7 @@ width: 6px; height: 6px; left: 4px; - top: -8px; + top: -7px; font-size: 18px; font-weight: 600; } @@ -183,6 +183,35 @@ padding: 8px 10px; } +[dir="rtl"] .dynamic-nav-status { + right: auto; + left: 32px; + justify-content: center; +} + +[dir="rtl"] .dynamic-nav-status .title { + align-items: center; +} + +[dir="rtl"] .dynamic-nav-status .dns-badge { + margin: 4px 0 4px 8px; +} + +[dir="rtl"] .dynamic-nav-status .details { + right: auto; + left: -14px; +} + +[dir="rtl"] .dynamic-nav-status .dns-close::after { + top: -4px; + left: 6px; +} + +[dir="rtl"] .dynamic-nav-status .details::before { + right: auto; + left: 75px; +} + @media screen and (max-width: 600px) { .dynamic-nav-status { display: none; diff --git a/test/blocks/brand-concierge/brand-concierge.test.js b/test/blocks/brand-concierge/brand-concierge.test.js index 4b9cd8db534..beb57781116 100644 --- a/test/blocks/brand-concierge/brand-concierge.test.js +++ b/test/blocks/brand-concierge/brand-concierge.test.js @@ -285,6 +285,77 @@ describe('Brand Concierge', () => { delete window.adobe; }); + describe('Marquee variant', () => { + it('decorates the header with eyebrow (h3), title (h2) and subtitle (p) in order', async () => { + document.body.innerHTML = await readFile({ path: './mocks/marquee.html' }); + const block = document.querySelector('.brand-concierge.marquee'); + await init(block); + + const header = block.querySelector('.bc-header'); + expect(header).to.exist; + expect(header.querySelector('.bc-header-eyebrow').textContent.trim()).to.equal('Adobe for business'); + expect(header.querySelector('.bc-header-title').textContent.trim()).to.equal('Grow your business with Adobe.'); + expect(header.querySelector('.bc-header-subtitle').textContent.trim()).to.equal('Unify data, content, and workflows.'); + + const kids = [...header.children]; + expect(kids[0].classList.contains('bc-header-eyebrow')).to.be.true; + expect(kids[1].classList.contains('bc-header-title')).to.be.true; + expect(kids[2].classList.contains('bc-header-subtitle')).to.be.true; + }); + + it('builds a .background layer with per-breakpoint image wrappers in authored order', async () => { + document.body.innerHTML = await readFile({ path: './mocks/marquee.html' }); + const block = document.querySelector('.brand-concierge.marquee'); + await init(block); + + const background = block.querySelector('.background'); + expect(background).to.exist; + const wrappers = background.querySelectorAll(':scope > div'); + expect(wrappers.length).to.equal(3); + expect(wrappers[0].classList.contains('desktop-only')).to.be.true; + expect(wrappers[1].classList.contains('tablet-only')).to.be.true; + expect(wrappers[2].classList.contains('mobile-only')).to.be.true; + wrappers.forEach((wrapper) => expect(wrapper.querySelector('picture')).to.exist); + }); + + it('wraps the content in a grid-constrained .foreground.container', async () => { + document.body.innerHTML = await readFile({ path: './mocks/marquee.html' }); + const block = document.querySelector('.brand-concierge.marquee'); + await init(block); + + const foreground = block.querySelector('.foreground.container'); + expect(foreground).to.exist; + expect(foreground.querySelector('.bc-header')).to.exist; + expect(foreground.querySelector('.bc-input-field')).to.exist; + expect(foreground.querySelectorAll('.bc-prompt-cards .prompt-card-button').length).to.equal(3); + expect(foreground.querySelector('.bc-legal').textContent).to.contain('Terms'); + + // the background stays outside the constrained container + expect(foreground.querySelector('.background')).to.be.null; + }); + + it('applies a single authored image without per-breakpoint wrappers', async () => { + document.body.innerHTML = await readFile({ path: './mocks/marquee-single-image.html' }); + const block = document.querySelector('.brand-concierge.marquee'); + await init(block); + + const background = block.querySelector('.background'); + expect(background).to.exist; + expect(background.querySelectorAll('picture').length).to.equal(1); + expect(background.querySelector('.desktop-only, .tablet-only, .mobile-only')).to.be.null; + }); + }); + + it('does not render an eyebrow when a single heading is authored', async () => { + document.body.innerHTML = await readFile({ path: './mocks/default.html' }); + const block = document.querySelector('.brand-concierge'); + await init(block); + + const header = block.querySelector('.bc-header'); + expect(header.querySelector('.bc-header-eyebrow')).to.be.null; + expect(header.querySelector('.bc-header-title').textContent.trim()).to.equal('AI Assistant'); + }); + it('createSusiComponentForModal creates SUSI component with correct properties and event listeners', () => { const onCloseRedirect = sinon.spy(); const onSuccessfulToken = sinon.spy(); diff --git a/test/blocks/brand-concierge/mocks/marquee-single-image.html b/test/blocks/brand-concierge/mocks/marquee-single-image.html new file mode 100644 index 00000000000..757bf4327cb --- /dev/null +++ b/test/blocks/brand-concierge/mocks/marquee-single-image.html @@ -0,0 +1,25 @@ +
+
+
+
+
+
+
+

Adobe for business

+

Grow your business with Adobe.

+

Unify data, content, and workflows.

+
+
+
What creative tools does Adobe have for business?
+
+
+

What do you need help with?

+
+
+
+

Use of this beta AI chatbot is subject to Adobe's Privacy Policy. Terms

+
+
+
+
+
diff --git a/test/blocks/brand-concierge/mocks/marquee.html b/test/blocks/brand-concierge/mocks/marquee.html new file mode 100644 index 00000000000..81bbaf81932 --- /dev/null +++ b/test/blocks/brand-concierge/mocks/marquee.html @@ -0,0 +1,29 @@ +
+
+
+
+
+
+
+
+
+

Adobe for business

+

Grow your business with Adobe.

+

Unify data, content, and workflows.

+
+
+
What creative tools does Adobe have for business?
+
How can I unlock my content supply chain?
+
What are the latest PDF solutions?
+
+
+

What do you need help with?

+
+
+
+

Use of this beta AI chatbot is subject to Adobe's Privacy Policy. Terms

+
+
+
+
+