From 43321a4c352e2a187dc8c28e1d756c896443ff07 Mon Sep 17 00:00:00 2001 From: Laura Jove Date: Fri, 11 Aug 2023 12:08:20 +0200 Subject: [PATCH 01/15] truck carousel first draft --- .../v2-tabbed-carousel/v2-tabbed-carousel.css | 300 ++++++++++++++++++ .../v2-tabbed-carousel/v2-tabbed-carousel.js | 256 +++++++++++++++ scripts/scripts.js | 124 ++++++++ styles/styles.css | 2 + 4 files changed, 682 insertions(+) create mode 100644 blocks/v2-tabbed-carousel/v2-tabbed-carousel.css create mode 100644 blocks/v2-tabbed-carousel/v2-tabbed-carousel.js diff --git a/blocks/v2-tabbed-carousel/v2-tabbed-carousel.css b/blocks/v2-tabbed-carousel/v2-tabbed-carousel.css new file mode 100644 index 000000000..133a88c5d --- /dev/null +++ b/blocks/v2-tabbed-carousel/v2-tabbed-carousel.css @@ -0,0 +1,300 @@ +/* stylelint-disable selector-class-pattern */ +:root { + --tabbed-carousel-img-width: 60%; +} + +@keyframes truck-entry { + 0% { + transform: translateX(100%); + opacity: 0; + } + + 25% { + opacity: 1; + } + + 100% { + transform: translateX(0); + } +} + +/* Full width block */ +main .section.v2-tabbed-carousel-container .v2-tabbed-carousel-wrapper { + margin: 0; + padding: 0; + width: 100%; + max-width: none; +} + +main .section.v2-tabbed-carousel-container { + padding: 0; +} + +/* End Full width block */ + +.v2-tabbed-carousel { + display: flex; + flex-direction: column; +} + +.v2-tabbed-carousel__images-container, +.v2-tabbed-carousel__description-container { + display: flex; + flex-flow: row nowrap; + padding-left: 0; + margin: 0; +} + +.v2-tabbed-carousel__description-container { + order: 2; + overflow: hidden; +} + +.v2-tabbed-carousel__images-container { + align-items: flex-end; + overflow: scroll hidden; + scroll-behavior: smooth; + scroll-snap-type: x mandatory; + scrollbar-width: none; +} + +.v2-tabbed-carousel__images-container::-webkit-scrollbar { + display: none; +} + +.v2-tabbed-carousel__image-item { + flex: none; + scroll-snap-align: center; + text-align: center; + width: var(--tabbed-carousel-img-width); +} + +.v2-tabbed-carousel__image-item picture { + display: block; + animation-duration: 1s; + animation-delay: 0.5s; + animation-name: truck-entry; + animation-timing-function: ease-in; +} + +.v2-tabbed-carousel__image-item:first-child { + margin-left: calc(100% - var(--tabbed-carousel-img-width)/2); +} + +.v2-tabbed-carousel__images-container::after { + content: ''; + display: block; + flex: 0 0 calc(100% - var(--tabbed-carousel-img-width)/2); +} + +.v2-tabbed-carousel__image-item img { + width: 100%; + object-fit: contain; + max-height: 570px; +} + +.v2-tabbed-carousel__content { + width: 90%; + margin: 0 auto 32px; + text-align: center; +} + +.v2-tabbed-carousel__desc-item { + color: var(--text-color); + flex: none; + opacity: 0; + width: 100%; + transition: opacity var(--duration-small) var(--easing-exit), + transform 1s var(--easing-entrance); + transform: translateX(-50px); +} + +.v2-tabbed-carousel__desc-item.active { + opacity: 1; + transform: translateX(0); + transition: opacity var(--duration-large) var(--easing-entrance), + transform 1s var(--easing-entrance); +} + +.v2-tabbed-carousel__text { + text-wrap: balance; + max-width: 400px; + margin: 0 auto; +} + +.v2-tabbed-carousel__title { + margin-right: 5px; +} + +.v2-tabbed-carousel__title, +p.v2-tabbed-carousel__description { + display: inline; + color: var(--c-grey-800); + font-size: var(--f-heading-5-font-size); + line-height: var(--f-heading-5-line-height); +} + +.v2-tabbed-carousel__buttons-container { + margin-top: 32px; +} + +.v2-tabbed-carousel__buttons-container .button-container { + margin: 10px 0; +} + +/* Navigation */ +ul.v2-tabbed-carousel__navigation { + display: flex; + flex-flow: row nowrap; + overflow: auto; + list-style: none; + margin: 32px 0; + padding: 2px 32px; + order: 0; + position: relative; +} + +.v2-tabbed-carousel__navigation-line { + position: absolute; + bottom: 3px; + left: 0; + width: 0; + height: 3px; + background: var(--c-primary-black); + transition: all var(--duration-small) var(--easing-standard); + margin: 0; +} + +.v2-tabbed-carousel__navigation::before, +.v2-tabbed-carousel__navigation::after { + content: ''; + margin: auto; +} + +.v2-tabbed-carousel__navigation-item { + /* padding: 0 16px; */ + border-bottom: 1px solid var(--c-primary-black); +} + +.v2-tabbed-carousel__navigation-item.active button, +.v2-tabbed-carousel__navigation-item button:hover { + color: var(--c-primary-black); +} + +.v2-tabbed-carousel__navigation-item button { + background: 0 0; + border: none; + color: var(--c-secondary-steel); + font-size: var(--f-button-font-size); + letter-spacing: var(--f-button-letter-spacing); + line-height: 100%; + margin: 0 0 0 20px; + padding: 14px 0; + text-transform: uppercase; +} + +.v2-tabbed-carousel__navigation-item:first-child button { + margin-left: 0; +} + +/* Arrow controls */ +.v2-tabbed-carousel__slider-wrapper { + position: relative; + order: 1; +} + +.v2-tabbed-carousel__arrow-controls { + /* display: none; */ + margin: 0; + /* opacity: 0; */ + transition: opacity var(--duration-small) var(--easing-standard); +} + +.v2-tabbed-carousel__slider-wrapper:hover .v2-tabbed-carousel__arrow-controls { + opacity: 1; +} + +.v2-tabbed-carousel__arrow-controls li { + align-items: center; + display: flex; + height: 100%; + left: 10%; + position: absolute; + top: 0; +} + +.v2-tabbed-carousel__arrow-controls li:last-child { + left: auto; + right: 10%; +} + +.v2-tabbed-carousel__arrow-controls button { + background-color: var(--c-primary-white); + border: 1px solid var(--c-primary-black); + color: var(--c-primary-black); + font-size: 0; + line-height: 0; + margin: 0; + padding: 16px; + position: relative; +} + +.v2-tabbed-carousel__arrow-controls button:hover { + background-color: #F1F1F1; + border-color: #A7A8A9; +} + +.v2-tabbed-carousel__arrow-controls button:active { + background-color: #E1DFDD; + border-color: #D9D9D9; +} + +.v2-tabbed-carousel__arrow-controls button:focus { + outline: 2px solid var(--border-focus); +} + +@media screen and (min-width: 744px) { + .v2-tabbed-carousel__arrow-controls { + display: block; + } + + .v2-tabbed-carousel__buttons-container { + display: flex; + justify-content: center; + } + + .v2-tabbed-carousel__buttons-container .button-container { + margin-left: 16px; + } +} + +@media screen and (min-width: 1200px) { + :root { + --tabbed-carousel-img-width: 60%; + } + + .v2-tabbed-carousel__image-item img { + max-height: none; + } + + .v2-tabbed-carousel__content { + max-width: 1000px; + } + + .v2-tabbed-carousel__content .default-content-wrapper { + display: flex; + justify-content: space-between; + align-items: flex-start; + } + + .v2-tabbed-carousel__text { + flex-basis: 60%; + text-align: left; + margin: 0; + } + + .v2-tabbed-carousel__buttons-container { + flex-direction: row; + margin-top: 0; + } +} \ No newline at end of file diff --git a/blocks/v2-tabbed-carousel/v2-tabbed-carousel.js b/blocks/v2-tabbed-carousel/v2-tabbed-carousel.js new file mode 100644 index 000000000..eda1e554f --- /dev/null +++ b/blocks/v2-tabbed-carousel/v2-tabbed-carousel.js @@ -0,0 +1,256 @@ +import { createElement } from '../../scripts/scripts.js'; + +const blockName = 'v2-tabbed-carousel'; + +function stripEmptyTags(main, child) { + if (child !== main && child.innerHTML.trim() === '') { + const parent = child.parentNode; + child.remove(); + stripEmptyTags(main, parent); + } +} + +function buildTabNavigation(tabItems, clickHandler) { + const tabNavigation = createElement('ul', `${blockName}__navigation`); + const navigationLine = createElement('li', `${blockName}__navigation-line`); + let timeout; + + [...tabItems].forEach((tabItem, i) => { + const listItem = createElement('li', `${blockName}__navigation-item`); + const button = createElement('button'); + button.addEventListener('click', () => clickHandler(i)); + if (navigationLine) { + button.addEventListener('mouseover', (e) => { + clearTimeout(timeout); + const { x, width } = e.currentTarget.getBoundingClientRect(); + Object.assign(navigationLine.style, { + left: `${x + tabNavigation.scrollLeft}px`, + width: `${width}px`, + }); + }); + + button.addEventListener('mouseout', () => { + timeout = setTimeout(() => { + const activeItem = document.querySelector(`.${blockName}__navigation-item.active button`); + const { x, width } = activeItem.getBoundingClientRect(); + Object.assign(navigationLine.style, { + left: `${x + tabNavigation.scrollLeft}px`, + width: `${width}px`, + }); + }, 600); + }); + } + + const tabContent = tabItem.querySelector(':scope > div'); + button.innerHTML = tabContent.dataset.truckCarousel; + listItem.append(button); + tabNavigation.append(listItem); + }); + + tabNavigation.append(navigationLine); + + return tabNavigation; +} + +const updateActiveItem = (index) => { + const images = document.querySelector(`.${blockName}__images-container`); + const descriptions = document.querySelector(`.${blockName}__description-container`); + const navigation = document.querySelector(`.${blockName}__navigation`); + const navigationLine = document.querySelector(`.${blockName}__navigation-line`); + + [images, descriptions, navigation].forEach((c) => c.querySelectorAll('.active').forEach((i) => i.classList.remove('active'))); + images.children[index].classList.add('active'); + descriptions.children[index].classList.add('active'); + navigation.children[index].classList.add('active'); + + if (navigationLine) { + const activeNavigationItem = navigation.children[index].querySelector('button'); + const { x, width } = activeNavigationItem.getBoundingClientRect(); + Object.assign(navigationLine.style, { + left: `${x + navigation.scrollLeft}px`, + width: `${width}px`, + }); + } +}; + +const listenScroll = (carousel) => { + const elements = carousel.querySelectorAll(':scope > *'); + + const io = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if ( + entry.isIntersecting + && entry.intersectionRatio >= 0.75 + ) { + const activeItem = entry.target; + const currentIndex = [...activeItem.parentNode.children].indexOf(activeItem); + updateActiveItem(currentIndex); + } + }); + }, { + root: carousel, + threshold: 0.75, + }); + + elements.forEach((el) => { + io.observe(el); + }); +}; + +const setCarouselPosition = (carousel, index) => { + const firstEl = carousel.firstElementChild; + const scrollOffset = firstEl.getBoundingClientRect().width; + const style = window.getComputedStyle(firstEl); + const marginleft = parseFloat(style.marginLeft); + + carousel.scrollTo({ + left: index * scrollOffset + marginleft, + behavior: 'smooth', + }); +}; + +const createArrowControls = (imagesContainer) => { + function scroll(direction) { + const activeItem = imagesContainer.querySelector(`.${blockName}__image-item.active`); + let index = [...activeItem.parentNode.children].indexOf(activeItem); + if (direction === 'left') { + index -= 1; + if (index === -1) { + index = imagesContainer.childElementCount; + } + } else { + index += 1; + if (index > imagesContainer.childElementCount - 1) { + index = 0; + } + } + + const firstEl = imagesContainer.firstElementChild; + const scrollOffset = firstEl.getBoundingClientRect().width; + const style = window.getComputedStyle(firstEl); + const marginleft = parseFloat(style.marginLeft); + + imagesContainer.scrollTo({ + left: index * scrollOffset + marginleft, + behavior: 'smooth', + }); + } + + const arrowControls = createElement('ul', [`${blockName}__arrow-controls`]); + const arrows = document.createRange().createContextualFragment(` +
  • + +
  • +
  • + +
  • + `); + arrowControls.append(...arrows.children); + imagesContainer.insertAdjacentElement('beforebegin', arrowControls); + const [prevButton, nextButton] = arrowControls.querySelectorAll(':scope button'); + prevButton.addEventListener('click', () => scroll('left')); + nextButton.addEventListener('click', () => scroll('right')); +}; + +export default function decorate(block) { + const descriptionContainer = block.querySelector(':scope > div'); + descriptionContainer.classList.add(`${blockName}__description-container`); + + const tabItems = block.querySelectorAll(':scope > div > div'); + + const imagesWrapper = createElement('div', `${blockName}__slider-wrapper`); + const imagesContainer = createElement('div', `${blockName}__images-container`); + descriptionContainer.parentNode.prepend(imagesWrapper); + imagesWrapper.appendChild(imagesContainer); + + const tabNavigation = buildTabNavigation(tabItems, (index) => { + setCarouselPosition(imagesContainer, index); + }); + + // Arrows + createArrowControls(imagesContainer); + + descriptionContainer.parentNode.append(tabNavigation); + + tabItems.forEach((tabItem) => { + tabItem.classList.add(`${blockName}__desc-item`); + const tabContent = tabItem.querySelector(':scope > div'); + const headings = tabContent.querySelectorAll('h1, h2, h3, h4, h5, h6'); + [...headings].forEach((heading) => heading.classList.add(`${blockName}__title`)); + + // create div for image and append inside image div container + const picture = tabItem.querySelector('picture'); + const imageItem = createElement('div', `${blockName}__image-item`); + imageItem.appendChild(picture); + imagesContainer.appendChild(imageItem); + + // remove empty tags + tabContent.querySelectorAll('p, div').forEach((item) => { + stripEmptyTags(tabContent, item); + }); + + const descriptions = tabContent.querySelectorAll('p:not(.button-container)'); + [...descriptions].forEach((description) => description.classList.add(`${blockName}__description`)); + + // Wrap text in container + const textContainer = createElement('div', `${blockName}__text`); + const text = tabContent.querySelector('.default-content-wrapper')?.querySelectorAll(':scope > *:not(.button-container)'); + if (text) { + const parentTextContainer = text[0].parentNode; + textContainer.append(...text); + parentTextContainer.appendChild(textContainer); + } + + // Wrap links in container + const buttonContainer = createElement('div', `${blockName}__buttons-container`); + const buttons = tabContent.querySelectorAll('.button-container'); + + buttons.forEach((bt, i) => { + const buttonLink = bt.firstElementChild; + + if (i > 0) { + buttonLink.classList.remove('primary'); + buttonLink.classList.add('tertiary'); + } + }); + + if (buttons.length) { + const parentButtonContainer = buttons[0].parentNode; + buttonContainer.append(...buttons); + parentButtonContainer.appendChild(buttonContainer); + } + }); + + // update the button indicator on scroll + listenScroll(imagesContainer); + + // Update description position to be equal to image position + imagesContainer.addEventListener('scroll', () => { + const itemWidth = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--tabbed-carousel-img-width')); + + const firstCarouselItemWidth = imagesContainer.offsetWidth * (itemWidth / 100); + const secondCarouselItemWidth = descriptionContainer.offsetWidth; + + // Determine the number of items in the second carousel + // that correspond to one item in the first carousel + const itemsInSecondCarouselPerItemInFirst = Math.ceil( + firstCarouselItemWidth / secondCarouselItemWidth, + ); + + // Calculate the scrollLeft position of the second carousel + const firstCarouselScrollLeft = imagesContainer.scrollLeft; + const secondCarouselScrollLeft = Math.floor(firstCarouselScrollLeft / firstCarouselItemWidth) + * (secondCarouselItemWidth * itemsInSecondCarouselPerItemInFirst); + + // Apply the calculated scrollLeft position to the second carousel + descriptionContainer.scrollLeft = secondCarouselScrollLeft; + }); +} diff --git a/scripts/scripts.js b/scripts/scripts.js index 082ce419f..bfa61e977 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -16,6 +16,7 @@ import { toCamelCase, toClassName, loadScript, + getHref, } from './lib-franklin.js'; /** @@ -174,6 +175,51 @@ export function createElement(tagName, classes = [], props = {}) { return elem; } +/** + * Returns a picture element with webp and fallbacks / allow multiple src paths for every breakpoint + * @param {string} src Default image URL (if no src is passed to breakpoints object) + * @param {boolean} eager load image eager + * @param {Array} breakpoints breakpoints and corresponding params (eg. src, width, media) + */ +export function createCustomOptimizedPicture(src, alt = '', eager = false, breakpoints = [{ media: '(min-width: 400px)', width: '2000' }, { width: '750' }]) { + const url = new URL(src, getHref()); + const picture = document.createElement('picture'); + let { pathname } = url; + const ext = pathname.substring(pathname.lastIndexOf('.') + 1); + + breakpoints.forEach((br) => { + // custom src path in breakpoint + if (br.src) { + const customUrl = new URL(br.src, getHref()); + pathname = customUrl.pathname; + } + + const source = document.createElement('source'); + if (br.media) source.setAttribute('media', br.media); + source.setAttribute('type', 'image/webp'); + source.setAttribute('srcset', `${pathname}?width=${br.width}&format=webply&optimize=medium`); + picture.appendChild(source); + }); + + // fallback + breakpoints.forEach((br, j) => { + if (j < breakpoints.length - 1) { + const source = document.createElement('source'); + if (br.media) source.setAttribute('media', br.media); + source.setAttribute('srcset', `${pathname}?width=${br.width}&format=${ext}&optimize=medium`); + picture.appendChild(source); + } else { + const image = document.createElement('img'); + image.setAttribute('loading', eager ? 'eager' : 'lazy'); + image.setAttribute('alt', alt); + picture.appendChild(image); + image.setAttribute('src', `${pathname}?width=${br.width}&format=${ext}&optimize=medium`); + } + }); + + return picture; +} + /** * Builds hero block and prepends to main in a new section. * @param {Element} main The container element @@ -266,6 +312,9 @@ export function decorateMain(main, head) { decorateSections(main); decorateBlocks(main); decorateLinks(main); + + // redesign + buildTruckCarouselBlock(main); } async function loadTemplate(doc, templateName) { @@ -626,6 +675,81 @@ allLinks.forEach((link) => { link.innerText = selectedText; }); +function createTabbedTruckSection(tabItems) { + const tabSection = createElement('div', ['section']); + tabSection.dataset.sectionStatus = 'initialized'; + const wrapper = createElement('div'); + tabSection.append(wrapper); + const tabBlock = buildBlock('v2-tabbed-carousel', [tabItems]); + wrapper.append(tabBlock); + return tabSection; +} + +function buildTruckCarouselBlock(main) { + const tabItems = []; + let nextElement; + const BREAKPOINTS = { + 0: '(min-width: 400px)', + 1: '(min-width: 1200px)', + }; + + const mainChildren = [...main.querySelectorAll(':scope > div')]; + mainChildren.forEach((section, i) => { + const isTruckCarousel = section.dataset.truckCarousel; + if (!isTruckCarousel) return; + + // save carousel position + nextElement = mainChildren[i + 1]; + const sectionMeta = section.dataset.truckCarousel; + + const tabContent = createElement('div', 'v2-tabbed-carousel__content'); + tabContent.dataset.truckCarousel = sectionMeta; + tabContent.innerHTML = section.innerHTML; + const images = tabContent.querySelectorAll('p > picture'); + + const imageBreakpoints = []; + const firstImage = images[0]?.lastElementChild; + const baseImageObj = { + src: firstImage?.src, + alt: firstImage?.alt, + }; + + images.forEach((pic, j) => { + const img = pic.lastElementChild; + imageBreakpoints.push({ + src: img.src, + width: 2000, + media: BREAKPOINTS[j], + }); + + pic.parentNode.remove(); + }); + imageBreakpoints.reverse(); // order first big and then small version + const newPicture = createCustomOptimizedPicture( + baseImageObj.src, + baseImageObj.alt, + true, + imageBreakpoints, + ); + + tabContent.prepend(newPicture); + + tabItems.push(tabContent); + section.remove(); + }); + + if (tabItems.length > 0) { + const tabbedCarouselSection = createTabbedTruckSection(tabItems); + if (nextElement) { // if we saved a position push the carousel in that position if not + main.insertBefore(tabbedCarouselSection, nextElement); + } else { + main.append(tabbedCarouselSection); + } + decorateIcons(tabbedCarouselSection); + decorateBlock(tabbedCarouselSection.querySelector('.v2-tabbed-carousel')); + } +} + /* REDESING CLASS CHECK */ if (getMetadata('style') === 'redesign-v2') { document.querySelector('html').classList.add('redesign-v2'); diff --git a/styles/styles.css b/styles/styles.css index e712a223f..39d54a3ec 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -62,6 +62,8 @@ --button-tertiary-white-pressed: #e1dfdd; --button-tertiary-white-disabled: var(--c-primary-white); + --border-focus: red; + /* Font family */ /* Alternate fonts */ From 4c04ec54c0257d200434a9249b88b515f5f5409d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 13:13:24 +0200 Subject: [PATCH 02/15] truck carousel --- blocks/v2-truck-lineup/v2-truck-lineup.css | 148 ++++++++++----------- blocks/v2-truck-lineup/v2-truck-lineup.js | 134 ++++++++----------- scripts/scripts.js | 4 +- 3 files changed, 134 insertions(+), 152 deletions(-) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 47e931501..b9aa41126 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -1,6 +1,5 @@ -/* stylelint-disable selector-class-pattern */ :root { - --tabbed-carousel-img-width: 60%; + --truck-lineup-img-width: 60%; } @keyframes truck-entry { @@ -19,38 +18,38 @@ } /* Full width block */ -main .section.v2-truck-carousel-container .v2-truck-carousel-wrapper { +main .section.v2-truck-lineup-container .v2-truck-lineup-wrapper { margin: 0; padding: 0; width: 100%; max-width: none; } -main .section.v2-truck-carousel-container { +main .section.v2-truck-lineup-container { padding: 0; } /* End Full width block */ -.v2-truck-carousel { +.v2-truck-lineup { display: flex; flex-direction: column; } -.v2-truck-carousel__images-container, -.v2-truck-carousel__description-container { +.v2-truck-lineup__images-container, +.v2-truck-lineup__description-container { display: flex; flex-flow: row nowrap; padding-left: 0; margin: 0; } -.v2-truck-carousel__description-container { +.v2-truck-lineup__description-container { order: 2; overflow: hidden; } -.v2-truck-carousel__images-container { +.v2-truck-lineup__images-container { align-items: flex-end; overflow: scroll hidden; scroll-behavior: smooth; @@ -58,18 +57,18 @@ main .section.v2-truck-carousel-container { scrollbar-width: none; } -.v2-truck-carousel__images-container::-webkit-scrollbar { +.v2-truck-lineup__images-container::-webkit-scrollbar { display: none; } -.v2-truck-carousel__image-item { +.v2-truck-lineup__image-item { flex: none; scroll-snap-align: center; text-align: center; - width: var(--tabbed-carousel-img-width); + width: var(--truck-lineup-img-width); } -.v2-truck-carousel__image-item picture { +.v2-truck-lineup__image-item picture { display: block; animation-duration: 1s; animation-delay: 0.5s; @@ -77,73 +76,79 @@ main .section.v2-truck-carousel-container { animation-timing-function: ease-in; } -.v2-truck-carousel__image-item:first-child { - margin-left: calc(100% - var(--tabbed-carousel-img-width)/2); +.v2-truck-lineup__image-item:first-child { + margin-left: calc(100% - var(--truck-lineup-img-width)/2); } -.v2-truck-carousel__images-container::after { +.v2-truck-lineup__images-container::after { content: ''; display: block; - flex: 0 0 calc(100% - var(--tabbed-carousel-img-width)/2); + flex: 0 0 calc(100% - var(--truck-lineup-img-width)/2); } -.v2-truck-carousel__image-item img { +.v2-truck-lineup__image-item img { width: 100%; object-fit: contain; max-height: 570px; } -.v2-truck-carousel__content { +.v2-truck-lineup__content { width: 90%; margin: 0 auto 32px; text-align: center; } -.v2-truck-carousel__desc-item { +.v2-truck-lineup__desc-item { color: var(--text-color); flex: none; opacity: 0; width: 100%; - transition: opacity var(--duration-small) var(--easing-exit), - transform 1s var(--easing-entrance); - transform: translateX(-50px); + + /* fadeout */ + transition: opacity 0.3s cubic-bezier(0, 0, 0, 1); } -.v2-truck-carousel__desc-item.active { +.v2-truck-lineup__desc-item.active { opacity: 1; - transform: translateX(0); - transition: opacity var(--duration-large) var(--easing-entrance), - transform 1s var(--easing-entrance); + transition: opacity 0.5s cubic-bezier(0, 0, 0, 1) 0.5s; } -.v2-truck-carousel__text { - text-wrap: balance; - max-width: 400px; +.v2-truck-lineup__text { margin: 0 auto; + max-width: 400px; + text-wrap: balance; } -.v2-truck-carousel__title { +.v2-truck-lineup__title { margin-right: 5px; } -.v2-truck-carousel__title, -p.v2-truck-carousel__description { +.v2-truck-lineup__title, +p.v2-truck-lineup__description { display: inline; color: var(--c-grey-800); font-size: var(--f-heading-5-font-size); line-height: var(--f-heading-5-line-height); } -.v2-truck-carousel__buttons-container { +.v2-truck-lineup__buttons-container { margin-top: 32px; + display: flex; + justify-content: center; + gap: 16px; } -.v2-truck-carousel__buttons-container .button-container { - margin: 10px 0; +.v2-truck-lineup__buttons-container a.button, +.v2-truck-lineup__buttons-container .button-container { + margin: 0; } /* Navigation */ -ul.v2-truck-carousel__navigation { +.v2-truck-lineup__navigation::-webkit-scrollbar { + display: none; +} + +ul.v2-truck-lineup__navigation { display: flex; flex-flow: row nowrap; overflow: auto; @@ -154,7 +159,7 @@ ul.v2-truck-carousel__navigation { position: relative; } -.v2-truck-carousel__navigation-line { +.v2-truck-lineup__navigation-line { position: absolute; bottom: 3px; left: 0; @@ -165,23 +170,23 @@ ul.v2-truck-carousel__navigation { margin: 0; } -.v2-truck-carousel__navigation::before, -.v2-truck-carousel__navigation::after { +.v2-truck-lineup__navigation::before, +.v2-truck-lineup__navigation::after { content: ''; margin: auto; } -.v2-truck-carousel__navigation-item { - /* padding: 0 16px; */ +.v2-truck-lineup__navigation-item { border-bottom: 1px solid var(--c-primary-black); } -.v2-truck-carousel__navigation-item.active button, -.v2-truck-carousel__navigation-item button:hover { +.v2-truck-lineup__navigation-item.active button, +.v2-truck-lineup__navigation-item button:hover { color: var(--c-primary-black); } -.v2-truck-carousel__navigation-item button { +/* stylelint-disable-next-line no-descending-specificity */ +.v2-truck-lineup__navigation-item button { background: 0 0; border: none; color: var(--c-secondary-steel); @@ -191,30 +196,31 @@ ul.v2-truck-carousel__navigation { margin: 0 0 0 20px; padding: 14px 0; text-transform: uppercase; + text-wrap: nowrap; } -.v2-truck-carousel__navigation-item:first-child button { +.v2-truck-lineup__navigation-item:first-child button { margin-left: 0; } /* Arrow controls */ -.v2-truck-carousel__slider-wrapper { +.v2-truck-lineup__slider-wrapper { position: relative; order: 1; } -.v2-truck-carousel__arrow-controls { - /* display: none; */ +.v2-truck-lineup__arrow-controls { + display: none; margin: 0; - /* opacity: 0; */ + opacity: 0; transition: opacity var(--duration-small) var(--easing-standard); } -.v2-truck-carousel__slider-wrapper:hover .v2-truck-carousel__arrow-controls { +.v2-truck-lineup__slider-wrapper:hover .v2-truck-lineup__arrow-controls { opacity: 1; } -.v2-truck-carousel__arrow-controls li { +.v2-truck-lineup__arrow-controls li { align-items: center; display: flex; height: 100%; @@ -223,12 +229,13 @@ ul.v2-truck-carousel__navigation { top: 0; } -.v2-truck-carousel__arrow-controls li:last-child { +.v2-truck-lineup__arrow-controls li:last-child { left: auto; right: 10%; } -.v2-truck-carousel__arrow-controls button { +/* stylelint-disable-next-line no-descending-specificity */ +.v2-truck-lineup__arrow-controls button { background-color: var(--c-primary-white); border: 1px solid var(--c-primary-black); color: var(--c-primary-black); @@ -239,62 +246,53 @@ ul.v2-truck-carousel__navigation { position: relative; } -.v2-truck-carousel__arrow-controls button:hover { +.v2-truck-lineup__arrow-controls button:hover { background-color: #F1F1F1; border-color: #A7A8A9; } -.v2-truck-carousel__arrow-controls button:active { +.v2-truck-lineup__arrow-controls button:active { background-color: #E1DFDD; border-color: #D9D9D9; } -.v2-truck-carousel__arrow-controls button:focus { +.v2-truck-lineup__arrow-controls button:focus { outline: 2px solid var(--border-focus); } @media screen and (min-width: 744px) { - .v2-truck-carousel__arrow-controls { + .v2-truck-lineup__arrow-controls { display: block; } - - .v2-truck-carousel__buttons-container { - display: flex; - justify-content: center; - } - - .v2-truck-carousel__buttons-container .button-container { - margin-left: 16px; - } } @media screen and (min-width: 1200px) { :root { - --tabbed-carousel-img-width: 60%; + --truck-lineup-img-width: 60%; } - .v2-truck-carousel__image-item img { + .v2-truck-lineup__image-item img { max-height: none; } - .v2-truck-carousel__content { - max-width: 1000px; + .v2-truck-lineup__content { + max-width: var(--truck-lineup-img-width); } - .v2-truck-carousel__content .default-content-wrapper { + .v2-truck-lineup__content .default-content-wrapper { display: flex; justify-content: space-between; align-items: flex-start; } - .v2-truck-carousel__text { + .v2-truck-lineup__text { flex-basis: 60%; text-align: left; margin: 0; } - .v2-truck-carousel__buttons-container { + .v2-truck-lineup__buttons-container { flex-direction: row; margin-top: 0; } -} \ No newline at end of file +} diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.js b/blocks/v2-truck-lineup/v2-truck-lineup.js index 753a75d7c..877570f97 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.js +++ b/blocks/v2-truck-lineup/v2-truck-lineup.js @@ -1,6 +1,6 @@ import { createElement } from '../../scripts/common.js'; -const blockName = 'v2-truck-carousel'; +const blockName = 'v2-truck-lineup'; function stripEmptyTags(main, child) { if (child !== main && child.innerHTML.trim() === '') { @@ -10,36 +10,35 @@ function stripEmptyTags(main, child) { } } +const moveNavigationLine = (navigationLine, activeTab, tabNavigation) => { + const { x: navigationX } = tabNavigation.getBoundingClientRect(); + const { x, width } = activeTab.getBoundingClientRect(); + Object.assign(navigationLine.style, { + left: `${x + tabNavigation.scrollLeft - navigationX}px`, + width: `${width}px`, + }); +}; + function buildTabNavigation(tabItems, clickHandler) { - const tabNavigation = createElement('ul', `${blockName}__navigation`); - const navigationLine = createElement('li', `${blockName}__navigation-line`); + const tabNavigation = createElement('ul', { classes: `${blockName}__navigation` }); + const navigationLine = createElement('li', { classes: `${blockName}__navigation-line` }); let timeout; [...tabItems].forEach((tabItem, i) => { - const listItem = createElement('li', `${blockName}__navigation-item`); + const listItem = createElement('li', { classes: `${blockName}__navigation-item` }); const button = createElement('button'); button.addEventListener('click', () => clickHandler(i)); - if (navigationLine) { - button.addEventListener('mouseover', (e) => { - clearTimeout(timeout); - const { x, width } = e.currentTarget.getBoundingClientRect(); - Object.assign(navigationLine.style, { - left: `${x + tabNavigation.scrollLeft}px`, - width: `${width}px`, - }); - }); - - button.addEventListener('mouseout', () => { - timeout = setTimeout(() => { - const activeItem = document.querySelector(`.${blockName}__navigation-item.active button`); - const { x, width } = activeItem.getBoundingClientRect(); - Object.assign(navigationLine.style, { - left: `${x + tabNavigation.scrollLeft}px`, - width: `${width}px`, - }); - }, 600); - }); - } + button.addEventListener('mouseover', (e) => { + clearTimeout(timeout); + moveNavigationLine(navigationLine, e.currentTarget, tabNavigation); + }); + + button.addEventListener('mouseout', () => { + timeout = setTimeout(() => { + const activeItem = document.querySelector(`.${blockName}__navigation-item.active button`); + moveNavigationLine(navigationLine, activeItem, tabNavigation); + }, 600); + }); const tabContent = tabItem.querySelector(':scope > div'); button.innerHTML = tabContent.dataset.truckCarousel; @@ -63,14 +62,29 @@ const updateActiveItem = (index) => { descriptions.children[index].classList.add('active'); navigation.children[index].classList.add('active'); - if (navigationLine) { - const activeNavigationItem = navigation.children[index].querySelector('button'); - const { x, width } = activeNavigationItem.getBoundingClientRect(); - Object.assign(navigationLine.style, { - left: `${x + navigation.scrollLeft}px`, - width: `${width}px`, + const activeNavigationItem = navigation.children[index].querySelector('button'); + moveNavigationLine(navigationLine, activeNavigationItem, navigation); + + // Center navigation item + const navigationActiveItem = navigation.querySelector('.active'); + + if (navigation && navigationActiveItem) { + const { clientWidth: itemWidth, offsetLeft } = navigationActiveItem; + // Calculate the scroll position to center the active item + const scrollPosition = offsetLeft - (navigation.clientWidth - itemWidth) / 2; + navigation.scrollTo({ + left: scrollPosition, + behavior: 'smooth', }); } + + // Update description position + const descriptionWidth = descriptions.offsetWidth; + + descriptions.scrollTo({ + left: descriptionWidth * index, + behavior: 'smooth', + }); }; const listenScroll = (carousel) => { @@ -109,34 +123,26 @@ const setCarouselPosition = (carousel, index) => { }); }; -const createArrowControls = (imagesContainer) => { +const createArrowControls = (carousel) => { function scroll(direction) { - const activeItem = imagesContainer.querySelector(`.${blockName}__image-item.active`); + const activeItem = carousel.querySelector(`.${blockName}__image-item.active`); let index = [...activeItem.parentNode.children].indexOf(activeItem); if (direction === 'left') { index -= 1; if (index === -1) { - index = imagesContainer.childElementCount; + index = carousel.childElementCount; } } else { index += 1; - if (index > imagesContainer.childElementCount - 1) { + if (index > carousel.childElementCount - 1) { index = 0; } } - const firstEl = imagesContainer.firstElementChild; - const scrollOffset = firstEl.getBoundingClientRect().width; - const style = window.getComputedStyle(firstEl); - const marginleft = parseFloat(style.marginLeft); - - imagesContainer.scrollTo({ - left: index * scrollOffset + marginleft, - behavior: 'smooth', - }); + setCarouselPosition(carousel, index); } - const arrowControls = createElement('ul', [`${blockName}__arrow-controls`]); + const arrowControls = createElement('ul', { classes: [`${blockName}__arrow-controls`] }); const arrows = document.createRange().createContextualFragment(`
  • `); arrowControls.append(...arrows.children); - imagesContainer.insertAdjacentElement('beforebegin', arrowControls); + carousel.insertAdjacentElement('beforebegin', arrowControls); const [prevButton, nextButton] = arrowControls.querySelectorAll(':scope button'); prevButton.addEventListener('click', () => scroll('left')); nextButton.addEventListener('click', () => scroll('right')); @@ -166,8 +172,8 @@ export default function decorate(block) { const tabItems = block.querySelectorAll(':scope > div > div'); - const imagesWrapper = createElement('div', `${blockName}__slider-wrapper`); - const imagesContainer = createElement('div', `${blockName}__images-container`); + const imagesWrapper = createElement('div', { classes: `${blockName}__slider-wrapper` }); + const imagesContainer = createElement('div', { classes: `${blockName}__images-container` }); descriptionContainer.parentNode.prepend(imagesWrapper); imagesWrapper.appendChild(imagesContainer); @@ -188,7 +194,7 @@ export default function decorate(block) { // create div for image and append inside image div container const picture = tabItem.querySelector('picture'); - const imageItem = createElement('div', `${blockName}__image-item`); + const imageItem = createElement('div', { classes: `${blockName}__image-item` }); imageItem.appendChild(picture); imagesContainer.appendChild(imageItem); @@ -201,7 +207,7 @@ export default function decorate(block) { [...descriptions].forEach((description) => description.classList.add(`${blockName}__description`)); // Wrap text in container - const textContainer = createElement('div', `${blockName}__text`); + const textContainer = createElement('div', { classes: `${blockName}__text` }); const text = tabContent.querySelector('.default-content-wrapper')?.querySelectorAll(':scope > *:not(.button-container)'); if (text) { const parentTextContainer = text[0].parentNode; @@ -210,15 +216,15 @@ export default function decorate(block) { } // Wrap links in container - const buttonContainer = createElement('div', `${blockName}__buttons-container`); + const buttonContainer = createElement('div', { classes: `${blockName}__buttons-container` }); const buttons = tabContent.querySelectorAll('.button-container'); buttons.forEach((bt, i) => { const buttonLink = bt.firstElementChild; if (i > 0) { - buttonLink.classList.remove('primary'); - buttonLink.classList.add('tertiary'); + buttonLink.classList.remove('button--primary'); + buttonLink.classList.add('button--secondary'); } }); @@ -231,26 +237,4 @@ export default function decorate(block) { // update the button indicator on scroll listenScroll(imagesContainer); - - // Update description position to be equal to image position - imagesContainer.addEventListener('scroll', () => { - const itemWidth = parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--tabbed-carousel-img-width')); - - const firstCarouselItemWidth = imagesContainer.offsetWidth * (itemWidth / 100); - const secondCarouselItemWidth = descriptionContainer.offsetWidth; - - // Determine the number of items in the second carousel - // that correspond to one item in the first carousel - const itemsInSecondCarouselPerItemInFirst = Math.ceil( - firstCarouselItemWidth / secondCarouselItemWidth, - ); - - // Calculate the scrollLeft position of the second carousel - const firstCarouselScrollLeft = imagesContainer.scrollLeft; - const secondCarouselScrollLeft = Math.floor(firstCarouselScrollLeft / firstCarouselItemWidth) - * (secondCarouselItemWidth * itemsInSecondCarouselPerItemInFirst); - - // Apply the calculated scrollLeft position to the second carousel - descriptionContainer.scrollLeft = secondCarouselScrollLeft; - }); } diff --git a/scripts/scripts.js b/scripts/scripts.js index c7836b8e1..e3fb98df9 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -654,7 +654,7 @@ allLinks.forEach((link) => { }); function createTabbedTruckSection(tabItems) { - const tabSection = createElement('div', ['section']); + const tabSection = createElement('div', { classes: 'section' }); tabSection.dataset.sectionStatus = 'initialized'; const wrapper = createElement('div'); tabSection.append(wrapper); @@ -680,7 +680,7 @@ function buildTruckCarouselBlock(main) { nextElement = mainChildren[i2 + 1]; const sectionMeta = section.dataset.truckCarousel; - const tabContent = createElement('div', 'v2-truck-lineup__content'); + const tabContent = createElement('div', { classes: 'v2-truck-lineup__content' }); tabContent.dataset.truckCarousel = sectionMeta; tabContent.innerHTML = section.innerHTML; const images = tabContent.querySelectorAll('p > picture'); From 97c9ecb4d0b64cb43207ca1dab0c1cc7de0f396d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 13:50:12 +0200 Subject: [PATCH 03/15] styles --- blocks/v2-truck-lineup/v2-truck-lineup.css | 17 ++++++++--------- styles/styles.css | 5 +++++ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index b9aa41126..1da300158 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -120,15 +120,15 @@ main .section.v2-truck-lineup-container { } .v2-truck-lineup__title { - margin-right: 5px; + font-family: var(--ff-headline-medium); + font-size: 45px; + line-height: 117%; + letter-spacing: -0.9px; + margin: 0; } -.v2-truck-lineup__title, p.v2-truck-lineup__description { - display: inline; color: var(--c-grey-800); - font-size: var(--f-heading-5-font-size); - line-height: var(--f-heading-5-line-height); } .v2-truck-lineup__buttons-container { @@ -190,12 +190,11 @@ ul.v2-truck-lineup__navigation { background: 0 0; border: none; color: var(--c-secondary-steel); - font-size: var(--f-button-font-size); - letter-spacing: var(--f-button-letter-spacing); - line-height: 100%; + font-family: var(--ff-subheadings-medium); + font-size: var(--headline-4-font-size); + line-height: var(--headline-4-line-height); margin: 0 0 0 20px; padding: 14px 0; - text-transform: uppercase; text-wrap: nowrap; } diff --git a/styles/styles.css b/styles/styles.css index abb86d4d8..b4211ea5f 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -847,6 +847,11 @@ main .section.responsive-title h1 { font-size: 16px; } +.redesign-v2 body { + font-size: var(--body-1-font-size); + line-height: var(--body-1-line-height); +} + /* ICONS STYLES */ /* icons on gold background */ From 14368a6298a97309354d5e876ce3507f2843db5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 18:22:22 +0200 Subject: [PATCH 04/15] allow icon in navigation tab --- blocks/v2-truck-lineup/v2-truck-lineup.css | 25 ++++++++++++++++------ blocks/v2-truck-lineup/v2-truck-lineup.js | 8 +++++-- icons/flash.svg | 3 +++ scripts/scripts.js | 4 ++++ 4 files changed, 32 insertions(+), 8 deletions(-) create mode 100644 icons/flash.svg diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 1da300158..cabe59bf1 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -127,15 +127,12 @@ main .section.v2-truck-lineup-container { margin: 0; } -p.v2-truck-lineup__description { - color: var(--c-grey-800); -} - .v2-truck-lineup__buttons-container { margin-top: 32px; display: flex; justify-content: center; gap: 16px; + flex-wrap: wrap; } .v2-truck-lineup__buttons-container a.button, @@ -182,17 +179,22 @@ ul.v2-truck-lineup__navigation { .v2-truck-lineup__navigation-item.active button, .v2-truck-lineup__navigation-item button:hover { + --color-icon: var(--c-primary-black); + color: var(--c-primary-black); } /* stylelint-disable-next-line no-descending-specificity */ .v2-truck-lineup__navigation-item button { + --color-icon: var(--c-secondary-steel); + background: 0 0; border: none; color: var(--c-secondary-steel); + display: flex; font-family: var(--ff-subheadings-medium); - font-size: var(--headline-4-font-size); - line-height: var(--headline-4-line-height); + font-size: var(--headline-5-font-size); + line-height: var(--headline-5-line-height); margin: 0 0 0 20px; padding: 14px 0; text-wrap: nowrap; @@ -202,6 +204,12 @@ ul.v2-truck-lineup__navigation { margin-left: 0; } +.v2-truck-lineup__navigation-item .icon { + display: inline-flex; + height: 15px; + width: 15px; +} + /* Arrow controls */ .v2-truck-lineup__slider-wrapper { position: relative; @@ -260,6 +268,11 @@ ul.v2-truck-lineup__navigation { } @media screen and (min-width: 744px) { + .v2-truck-lineup__navigation-item button { + font-size: var(--headline-4-font-size); + line-height: var(--headline-4-line-height); + } + .v2-truck-lineup__arrow-controls { display: block; } diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.js b/blocks/v2-truck-lineup/v2-truck-lineup.js index 877570f97..378b45206 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.js +++ b/blocks/v2-truck-lineup/v2-truck-lineup.js @@ -1,3 +1,4 @@ +import { decorateIcons } from '../../scripts/lib-franklin.js'; import { createElement } from '../../scripts/common.js'; const blockName = 'v2-truck-lineup'; @@ -41,7 +42,9 @@ function buildTabNavigation(tabItems, clickHandler) { }); const tabContent = tabItem.querySelector(':scope > div'); - button.innerHTML = tabContent.dataset.truckCarousel; + const icon = tabContent.dataset.truckCarouselIcon; + const svgIcon = icon ? `` : ''; + button.innerHTML = `${tabContent.dataset.truckCarousel}${svgIcon}`; listItem.append(button); tabNavigation.append(listItem); }); @@ -166,7 +169,7 @@ const createArrowControls = (carousel) => { nextButton.addEventListener('click', () => scroll('right')); }; -export default function decorate(block) { +export default async function decorate(block) { const descriptionContainer = block.querySelector(':scope > div'); descriptionContainer.classList.add(`${blockName}__description-container`); @@ -180,6 +183,7 @@ export default function decorate(block) { const tabNavigation = buildTabNavigation(tabItems, (index) => { setCarouselPosition(imagesContainer, index); }); + await decorateIcons(tabNavigation); // Arrows createArrowControls(imagesContainer); diff --git a/icons/flash.svg b/icons/flash.svg new file mode 100644 index 000000000..843f9bbf8 --- /dev/null +++ b/icons/flash.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/scripts/scripts.js b/scripts/scripts.js index e3fb98df9..9eb9a4a96 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -682,6 +682,10 @@ function buildTruckCarouselBlock(main) { const tabContent = createElement('div', { classes: 'v2-truck-lineup__content' }); tabContent.dataset.truckCarousel = sectionMeta; + if (section.dataset.truckCarouselIcon) { + tabContent.dataset.truckCarouselIcon = section.dataset.truckCarouselIcon; + } + tabContent.innerHTML = section.innerHTML; const images = tabContent.querySelectorAll('p > picture'); From 4b3ff6d6effdf800e3d846b7f078a5b6c58bf826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 18:25:29 +0200 Subject: [PATCH 05/15] adjust image size --- blocks/v2-truck-lineup/v2-truck-lineup.css | 13 ++++--------- blocks/v2-truck-lineup/v2-truck-lineup.js | 4 ++-- 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index cabe59bf1..70f97625f 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -77,19 +77,18 @@ main .section.v2-truck-lineup-container { } .v2-truck-lineup__image-item:first-child { - margin-left: calc(100% - var(--truck-lineup-img-width)/2); + margin-left: 50vw; } .v2-truck-lineup__images-container::after { content: ''; display: block; - flex: 0 0 calc(100% - var(--truck-lineup-img-width)/2); + flex: 0 0 50vw; } .v2-truck-lineup__image-item img { - width: 100%; - object-fit: contain; - max-height: 570px; + aspect-ratio: 16 / 9; + max-height: 80vh; } .v2-truck-lineup__content { @@ -283,10 +282,6 @@ ul.v2-truck-lineup__navigation { --truck-lineup-img-width: 60%; } - .v2-truck-lineup__image-item img { - max-height: none; - } - .v2-truck-lineup__content { max-width: var(--truck-lineup-img-width); } diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.js b/blocks/v2-truck-lineup/v2-truck-lineup.js index 378b45206..c9197b0aa 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.js +++ b/blocks/v2-truck-lineup/v2-truck-lineup.js @@ -97,7 +97,7 @@ const listenScroll = (carousel) => { entries.forEach((entry) => { if ( entry.isIntersecting - && entry.intersectionRatio >= 0.75 + && entry.intersectionRatio >= 0.9 ) { const activeItem = entry.target; const currentIndex = [...activeItem.parentNode.children].indexOf(activeItem); @@ -106,7 +106,7 @@ const listenScroll = (carousel) => { }); }, { root: carousel, - threshold: 0.75, + threshold: 0.9, }); elements.forEach((el) => { From 09de956075cd6a9797521fcb0a3483577174e028 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 18:25:45 +0200 Subject: [PATCH 06/15] rename function name --- scripts/scripts.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/scripts.js b/scripts/scripts.js index 9eb9a4a96..5b72c520c 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -406,7 +406,7 @@ export function decorateMain(main, head) { decorateLinks(main); // Truck carousel - buildTruckCarouselBlock(main); + buildTruckLineupBlock(main); // Inpage navigation buildInpageNavigationBlock(main); // V2 tabbed carousel @@ -653,7 +653,7 @@ allLinks.forEach((link) => { link.innerText = selectedText; }); -function createTabbedTruckSection(tabItems) { +function createTruckLineupSection(tabItems) { const tabSection = createElement('div', { classes: 'section' }); tabSection.dataset.sectionStatus = 'initialized'; const wrapper = createElement('div'); @@ -663,7 +663,7 @@ function createTabbedTruckSection(tabItems) { return tabSection; } -function buildTruckCarouselBlock(main) { +function buildTruckLineupBlock(main) { const tabItems = []; let nextElement; const BREAKPOINTS = { @@ -721,7 +721,7 @@ function buildTruckCarouselBlock(main) { }); if (tabItems.length > 0) { - const tabbedCarouselSection = createTabbedTruckSection(tabItems); + const tabbedCarouselSection = createTruckLineupSection(tabItems); if (nextElement) { // if we saved a position push the carousel in that position if not main.insertBefore(tabbedCarouselSection, nextElement); } else { From 9e887db8116a9eb0f7853662c41f1e6c4db875b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 18:30:18 +0200 Subject: [PATCH 07/15] allow one image only for mobile & desktop --- scripts/scripts.js | 34 ++-------------------------------- 1 file changed, 2 insertions(+), 32 deletions(-) diff --git a/scripts/scripts.js b/scripts/scripts.js index 5b72c520c..621434c40 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -666,10 +666,6 @@ function createTruckLineupSection(tabItems) { function buildTruckLineupBlock(main) { const tabItems = []; let nextElement; - const BREAKPOINTS = { - 0: '(min-width: 400px)', - 1: '(min-width: 1200px)', - }; const mainChildren = [...main.querySelectorAll(':scope > div')]; mainChildren.forEach((section, i2) => { @@ -687,34 +683,8 @@ function buildTruckLineupBlock(main) { } tabContent.innerHTML = section.innerHTML; - const images = tabContent.querySelectorAll('p > picture'); - - const imageBreakpoints = []; - const firstImage = images[0]?.lastElementChild; - const baseImageObj = { - src: firstImage?.src, - alt: firstImage?.alt, - }; - - images.forEach((pic, j) => { - const img2 = pic.lastElementChild; - imageBreakpoints.push({ - src: img2.src, - width: 2000, - media: BREAKPOINTS[j], - }); - - pic.parentNode.remove(); - }); - imageBreakpoints.reverse(); // order first big and then small version - const newPicture = createCustomOptimizedPicture( - baseImageObj.src, - baseImageObj.alt, - true, - imageBreakpoints, - ); - - tabContent.prepend(newPicture); + const image = tabContent.querySelector('p > picture'); + tabContent.prepend(image); tabItems.push(tabContent); section.remove(); From 0431deb576750e3c46484b0b02dc2a1d3a4305d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 18:37:26 +0200 Subject: [PATCH 08/15] order css --- blocks/v2-truck-lineup/v2-truck-lineup.css | 30 +++++++++++----------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 70f97625f..21c06b810 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -92,9 +92,9 @@ main .section.v2-truck-lineup-container { } .v2-truck-lineup__content { - width: 90%; margin: 0 auto 32px; text-align: center; + width: 90%; } .v2-truck-lineup__desc-item { @@ -121,17 +121,17 @@ main .section.v2-truck-lineup-container { .v2-truck-lineup__title { font-family: var(--ff-headline-medium); font-size: 45px; - line-height: 117%; letter-spacing: -0.9px; + line-height: 117%; margin: 0; } .v2-truck-lineup__buttons-container { - margin-top: 32px; display: flex; - justify-content: center; - gap: 16px; flex-wrap: wrap; + gap: 16px; + justify-content: center; + margin-top: 32px; } .v2-truck-lineup__buttons-container a.button, @@ -147,23 +147,23 @@ main .section.v2-truck-lineup-container { ul.v2-truck-lineup__navigation { display: flex; flex-flow: row nowrap; - overflow: auto; list-style: none; margin: 32px 0; - padding: 2px 32px; order: 0; + overflow: auto; + padding: 2px 32px; position: relative; } .v2-truck-lineup__navigation-line { - position: absolute; + background: var(--c-primary-black); bottom: 3px; - left: 0; - width: 0; height: 3px; - background: var(--c-primary-black); - transition: all var(--duration-small) var(--easing-standard); + left: 0; margin: 0; + position: absolute; + transition: all var(--duration-small) var(--easing-standard); + width: 0; } .v2-truck-lineup__navigation::before, @@ -211,8 +211,8 @@ ul.v2-truck-lineup__navigation { /* Arrow controls */ .v2-truck-lineup__slider-wrapper { - position: relative; order: 1; + position: relative; } .v2-truck-lineup__arrow-controls { @@ -287,15 +287,15 @@ ul.v2-truck-lineup__navigation { } .v2-truck-lineup__content .default-content-wrapper { + align-items: flex-start; display: flex; justify-content: space-between; - align-items: flex-start; } .v2-truck-lineup__text { flex-basis: 60%; - text-align: left; margin: 0; + text-align: left; } .v2-truck-lineup__buttons-container { From 9e2d8b53718ff3497fc8e5fb359555527c49c691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 18:45:36 +0200 Subject: [PATCH 09/15] icon size --- blocks/v2-truck-lineup/v2-truck-lineup.css | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 21c06b810..0c555cbe7 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -1,5 +1,6 @@ :root { --truck-lineup-img-width: 60%; + --truck-lineup-navigation-icon: 10px; } @keyframes truck-entry { @@ -205,8 +206,8 @@ ul.v2-truck-lineup__navigation { .v2-truck-lineup__navigation-item .icon { display: inline-flex; - height: 15px; - width: 15px; + height: var(--truck-lineup-navigation-icon); + width: var(--truck-lineup-navigation-icon); } /* Arrow controls */ @@ -280,6 +281,7 @@ ul.v2-truck-lineup__navigation { @media screen and (min-width: 1200px) { :root { --truck-lineup-img-width: 60%; + --truck-lineup-navigation-icon: 15px; } .v2-truck-lineup__content { From 18313f8d925d7ba19649387e01a01839f7f6d5be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Wed, 20 Sep 2023 18:54:54 +0200 Subject: [PATCH 10/15] fix resize --- blocks/v2-truck-lineup/v2-truck-lineup.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.js b/blocks/v2-truck-lineup/v2-truck-lineup.js index c9197b0aa..a2f974800 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.js +++ b/blocks/v2-truck-lineup/v2-truck-lineup.js @@ -241,4 +241,11 @@ export default async function decorate(block) { // update the button indicator on scroll listenScroll(imagesContainer); + + // Update text position + navigation line when page is resized + window.addEventListener('resize', () => { + const activeItem = imagesContainer.querySelector(`.${blockName}__image-item.active`); + const index = [...activeItem.parentNode.children].indexOf(activeItem); + updateActiveItem(index); + }); } From 6dd0704fec47270b3544b9e2d78ee2f40a57f65b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Thu, 21 Sep 2023 09:22:49 +0200 Subject: [PATCH 11/15] adjust width --- blocks/v2-truck-lineup/v2-truck-lineup.css | 1 + 1 file changed, 1 insertion(+) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 0c555cbe7..0ab4612dc 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -90,6 +90,7 @@ main .section.v2-truck-lineup-container { .v2-truck-lineup__image-item img { aspect-ratio: 16 / 9; max-height: 80vh; + width: auto; } .v2-truck-lineup__content { From 570751684d710100a28fd6b7c3b1fd650e64ab2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Fri, 22 Sep 2023 07:45:39 +0200 Subject: [PATCH 12/15] fix based on comments --- blocks/v2-truck-lineup/v2-truck-lineup.css | 2 +- styles/styles.css | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 0ab4612dc..0548be673 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -180,7 +180,7 @@ ul.v2-truck-lineup__navigation { .v2-truck-lineup__navigation-item.active button, .v2-truck-lineup__navigation-item button:hover { - --color-icon: var(--c-primary-black); + --color-icon: var(--c-primary-gold); color: var(--c-primary-black); } diff --git a/styles/styles.css b/styles/styles.css index b388f53c3..6f9f07f3c 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -268,7 +268,7 @@ a.button:any-link, button { padding: 5px 30px; text-align: center; font-style: normal; - font-weight: 600; + font-weight: normal; cursor: pointer; color: var(--background-color); background-color: var(--link-color); From 67a62024297383af35982566b7c1ed6346672a75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Fri, 22 Sep 2023 07:47:06 +0200 Subject: [PATCH 13/15] change icon color --- blocks/v2-truck-lineup/v2-truck-lineup.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks/v2-truck-lineup/v2-truck-lineup.css b/blocks/v2-truck-lineup/v2-truck-lineup.css index 0548be673..646420cd1 100644 --- a/blocks/v2-truck-lineup/v2-truck-lineup.css +++ b/blocks/v2-truck-lineup/v2-truck-lineup.css @@ -180,7 +180,7 @@ ul.v2-truck-lineup__navigation { .v2-truck-lineup__navigation-item.active button, .v2-truck-lineup__navigation-item button:hover { - --color-icon: var(--c-primary-gold); + --color-icon: var(--c-accent-copper); color: var(--c-primary-black); } From d4f36aa518f6a9edb1c5ba1c16e7904e8f15de24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Fri, 22 Sep 2023 07:59:44 +0200 Subject: [PATCH 14/15] improve code --- scripts/scripts.js | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/scripts/scripts.js b/scripts/scripts.js index 621434c40..7106cf9d8 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -331,9 +331,7 @@ const createInpageNavigation = (main) => { return navItems; }; -function buildInpageNavigationBlock(main) { - const inapgeClassName = 'v2-inpage-navigation'; - +function buildInpageNavigationBlock(main, classname) { const items = createInpageNavigation(main); if (items.length > 0) { @@ -343,11 +341,11 @@ function buildInpageNavigationBlock(main) { overflow: 'hidden', }); - section.append(buildBlock(inapgeClassName, { elems: items })); + section.append(buildBlock(classname, { elems: items })); // insert in second position, assumption is that Hero should be first main.insertBefore(section, main.children[1]); - decorateBlock(section.querySelector(`.${inapgeClassName}`)); + decorateBlock(section.querySelector(`.${classname}`)); } } @@ -406,9 +404,9 @@ export function decorateMain(main, head) { decorateLinks(main); // Truck carousel - buildTruckLineupBlock(main); + buildTruckLineupBlock(main, 'v2-truck-lineup'); // Inpage navigation - buildInpageNavigationBlock(main); + buildInpageNavigationBlock(main, 'v2-inpage-navigation'); // V2 tabbed carousel buildTabbedBlock(main, 'v2-tabbed-carousel'); } @@ -456,6 +454,7 @@ async function loadEager(doc) { await getPlaceholders(); } + /** * Loads everything that doesn't need to be delayed. * @param {Element} doc The container element @@ -653,17 +652,17 @@ allLinks.forEach((link) => { link.innerText = selectedText; }); -function createTruckLineupSection(tabItems) { +function createTruckLineupSection(tabItems, classname) { const tabSection = createElement('div', { classes: 'section' }); tabSection.dataset.sectionStatus = 'initialized'; const wrapper = createElement('div'); tabSection.append(wrapper); - const tabBlock = buildBlock('v2-truck-lineup', [tabItems]); + const tabBlock = buildBlock(classname, [tabItems]); wrapper.append(tabBlock); return tabSection; } -function buildTruckLineupBlock(main) { +function buildTruckLineupBlock(main, classname) { const tabItems = []; let nextElement; @@ -676,7 +675,7 @@ function buildTruckLineupBlock(main) { nextElement = mainChildren[i2 + 1]; const sectionMeta = section.dataset.truckCarousel; - const tabContent = createElement('div', { classes: 'v2-truck-lineup__content' }); + const tabContent = createElement('div', { classes: `${classname}__content` }); tabContent.dataset.truckCarousel = sectionMeta; if (section.dataset.truckCarouselIcon) { tabContent.dataset.truckCarouselIcon = section.dataset.truckCarouselIcon; @@ -691,14 +690,14 @@ function buildTruckLineupBlock(main) { }); if (tabItems.length > 0) { - const tabbedCarouselSection = createTruckLineupSection(tabItems); + const tabbedCarouselSection = createTruckLineupSection(tabItems, classname); if (nextElement) { // if we saved a position push the carousel in that position if not main.insertBefore(tabbedCarouselSection, nextElement); } else { main.append(tabbedCarouselSection); } decorateIcons(tabbedCarouselSection); - decorateBlock(tabbedCarouselSection.querySelector('.v2-truck-lineup')); + decorateBlock(tabbedCarouselSection.querySelector(`.${classname}`)); } } From fb3cdac74ada44df8db0f20520c58a9b53741b74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Jove=CC=81?= Date: Fri, 22 Sep 2023 17:13:23 +0200 Subject: [PATCH 15/15] fix bold button --- styles/styles.css | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/styles/styles.css b/styles/styles.css index 93815ccc6..916fa868b 100644 --- a/styles/styles.css +++ b/styles/styles.css @@ -272,7 +272,7 @@ a.button:any-link, button { padding: 5px 30px; text-align: center; font-style: normal; - font-weight: normal; + font-weight: 600; cursor: pointer; color: var(--background-color); background-color: var(--link-color); @@ -910,6 +910,13 @@ main .section.responsive-title h1 { /* ICONS STYLES - END */ /* REDESIGN Buttons */ +/* stylelint-disable-next-line no-descending-specificity */ +.redesign-v2 a.button:any-link, +.redesign-v2 button { + font-weight: normal; +} + +/* stylelint-disable-next-line no-descending-specificity */ .redesign-v2 a.button, .redesign-v2 button.button { align-items: center;