Skip to content

Commit

Permalink
How To Steps Accordion (#1324)
Browse files Browse the repository at this point in the history
* how-to-steps-accordion.js

* how-to-steps-accordion.css

* remove $ from js code

* mobile image aspect ratio maintained between screen sizes, max-width set to 400

* wip - mobile and tablet container sizing complete

* all layout / positioning from mobile to desktop complete for image and video

* ensures video is sized and centered for mobile, tablet and desktop

* ensure hover only applies to header and not sub-text

* set background from mobile to desktop

* ensure background image looks consistent

* use vw for top value for background to allow a dynamic position on window re-size - this prevents the bgImage from moving too low into the steps

* rename to how-to-v2

* remove steps from name

* update CSS classes to use new block name

* adds margin-top to header of video container
  • Loading branch information
jsandland authored Dec 18, 2024
1 parent 208202e commit d802dec
Show file tree
Hide file tree
Showing 2 changed files with 345 additions and 0 deletions.
216 changes: 216 additions & 0 deletions express/blocks/how-to-v2/how-to-v2.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
main .how-to-v2 h2 {
font-size: 22px;
text-align: center;
padding: 0 24px;
}

main .how-to-v2 ol {
list-style-type: none;
font-size: 21px;
font-weight: 900;
}

main .how-to-v2 ol li {
padding: 8px 0;
cursor: pointer;
transition: all 0.21s;
display: flex;
}

main .how-to-v2 ol li .step-indicator {
width: 5px;
min-width: 5px;
background: linear-gradient(-96.68deg, #FF4885 5.24%, #FC7D00 94.76%), #686DF4;
border-radius: 2.5px;
}

main .how-to-v2 ol li .step-content {
padding: 8px 16px;
margin: 0;
}

main .how-to-v2 ol li .step-indicator:has(+ div .closed) {
background: linear-gradient(95.55deg, rgba(255, 255, 255, 0.5) 4.43%, rgba(255, 255, 255, 0.3) 93.65%), #8F8F8F;
}

main .how-to-v2 ol li h3 {
text-align: left;
font-size: 18px;
line-height: 23.4px;
font-weight: 700;
}

main .how-to-v2 ol li .detail-container {
font-size: 16px;
line-height: 20.8px;
font-weight: 400;
}

main .how-to-v2 ol li .detail-container {
max-height: 0;
overflow: hidden;
transition: max-height 0.21s ease-out;
}

main .how-to-v2 ol li .detail-text {
padding-top: 10px;
}

/* so that there is no initial re-paint */
main .how-to-v2 ol li:first-child .detail-container {
max-height: none;
}

main .how-to-v2.video lite-youtube,
main .how-to-v2.image picture {
border-radius: 16px;
overflow: hidden;
}

main .how-to-v2 em {
font-style: normal;
background: linear-gradient(140.08deg, #FF4DD2 67.54%, #FF993B 76.42%);
background-size: 108% 108%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
font-weight: 900;
}

main .how-to-v2 .steps-content {
display: flex;
flex-direction: column;
padding: 0 24px;
position: relative;
}

main .how-to-v2 .steps-content .steps-content-backg {
position: absolute;
width: 100%;
top: -24vw;
z-index: -1;
height: 100%;
transform: scale(0.9);
}

main .how-to-v2 ol.steps {
margin: 6px 0 0;
padding: 0;
}

main .how-to-v2 ol.steps .step .step-content h3:hover {
background-color: var( --color-gray-100);
margin: -8px 0 -8px -16px;
padding: 8px 0 8px 16px;
}

main .how-to-v2.video .video-container {
margin-top: 20px;
}

main .how-to-v2.image .image-container {
margin-top: 20px;
width: 100%;
overflow: hidden;
}

main .how-to-v2.image .image-container img,
main .how-to-v2.video .video-container > * {
border-radius: 16px;
max-width: 400px;
width: 100%;
height: 100%;
object-fit: cover;
}

main .how-to-v2.video .video-container > * {
margin-left: auto;
margin-right: auto;
height: unset;
}

main .how-to-v2.video,
main .how-to-v2.image {
/* note we don't set margin here because background gradient has to go past into this container */
margin: 0;
max-width: unset;
}

@media (min-width: 768px) {

main .how-to-v2 h2 {
font-size: 28px;
}

main .how-to-v2.video,
main .how-to-v2.image {
max-width: 950px;
margin: auto;
}

main .how-to-v2 .steps-content {
display: flex;
flex-direction: row;
padding: 0 24px;
}

main .how-to-v2 .steps-content > * {
flex: 1 1 50%;
box-sizing: border-box;
}

main .how-to-v2.image .image-container,
main .how-to-v2.video .video-container {
align-items: center;
display: inline-grid;
}

main .how-to-v2.image .image-container img {
height: unset;
}

main .how-to-v2 .steps-content .steps {
padding-left: 20px;
}

main .how-to-v2 .steps-content .steps-content-backg {
left: -90px;
width: 67%;
transform: scale(0.9);
top: -68px;
}
}

@media (min-width: 1280px) {

main .how-to-v2 h2 {
font-size: 36px;
}

main .how-to-v2.video,
main .how-to-v2.image {
max-width: 1300px;
margin: auto;
}

main .how-to-v2.image .image-container img,
main .how-to-v2.video .video-container > * {
max-height: 300px;
max-width: 533px;
}

main .how-to-v2.image .image-container {
display: inline-block;
}

main .how-to-v2 .steps-content {
margin-top: 30px;
}

main .how-to-v2 ol li h3 {
text-align: left;
font-size: 22px;
line-height: 28.6px;
font-weight: 700;
}
}
129 changes: 129 additions & 0 deletions express/blocks/how-to-v2/how-to-v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/* eslint-disable import/named, import/extensions */
import { createTag } from '../../scripts/utils.js';
import { embedYoutube } from '../../scripts/embed-videos.js';

function setStepDetails(block, indexOpenedStep) {
const listItems = block.querySelectorAll(':scope li');

listItems.forEach((item, i) => {
const detail = item.querySelector('.detail-container');

if (i === indexOpenedStep) {
detail.classList.remove('closed');
detail.style.maxHeight = `${detail.scrollHeight}px`;
} else {
detail.classList.add('closed');
detail.style.maxHeight = '0';
}
});
}

function buildAccordion(block, rows, stepsContent) {
let indexOpenedStep = 0;
const list = createTag('OL', { class: 'steps' });

rows.forEach((row, i) => {
const [stepTitle, stepDetail] = row.querySelectorAll(':scope div');

const newStepTitle = createTag('h3');
newStepTitle.replaceChildren(...stepTitle.childNodes);

const listItem = createTag('LI', { class: 'step', tabindex: '0' });
list.append(listItem);

const listItemIndicator = createTag('div', { class: 'step-indicator' });
const listItemContent = createTag('div', { class: 'step-content' });

const detailText = stepDetail;
detailText.classList.add('detail-text');

const detailContainer = createTag('div', { class: 'detail-container' });

if (i !== 0) {
detailContainer.classList.add('closed');
}

detailContainer.append(detailText);

listItem.append(listItemIndicator);
listItem.append(listItemContent);

listItemContent.append(newStepTitle);
listItemContent.append(detailContainer);

const handleOpenDetails = (ev) => {
indexOpenedStep = i;
setStepDetails(block, indexOpenedStep);
ev.preventDefault();
};

newStepTitle.addEventListener('click', handleOpenDetails);
listItem.addEventListener('keyup', (ev) => ev.which === 13 && handleOpenDetails(ev));
});

stepsContent.append(list);

// set this in the next event cycle when scrollHeight has been established
setTimeout(() => {
setStepDetails(block, indexOpenedStep);
}, 0);
}

export default async function decorate(block) {
const isVideoVariant = block.classList.contains('video');
const isImageVariant = block.classList.contains('image');

const section = block.closest('.section');
const rows = Array.from(block.children);

const backgroundRow = block.children[0];
const backgroundImage = backgroundRow.querySelector('img');
const backgroundURL = backgroundImage?.src;
const hasBackground = !!backgroundURL;

if (hasBackground) {
rows.shift();
}

if (isVideoVariant || isImageVariant) {
const stepsContent = createTag('div', { class: 'steps-content' });

if (hasBackground) {
// So that background image goes beyond container
const stepsContentBackground = createTag('div', { class: 'steps-content-backg' });
const stepsContentBackgroundImg = createTag('img', { class: 'steps-content-backg-image' });
stepsContent.append(stepsContentBackground);
stepsContentBackground.append(stepsContentBackgroundImg);
stepsContentBackgroundImg.src = backgroundURL;
}

if (isVideoVariant) {
const videoData = rows.shift();

// remove the added social link from the block DOM
block.removeChild(block.children[0]);

const videoLink = videoData.querySelector('a');
const youtubeURL = videoLink?.href;
const url = new URL(youtubeURL);

const videoContainerEl = createTag('div', { class: 'video-container' });

const videoEl = embedYoutube(url);
videoEl.classList.add('video-how-to-steps-accordion');

videoContainerEl.append(videoEl);
stepsContent.append(videoContainerEl);
} else {
const imageData = rows.shift();
const imageEl = imageData.querySelector('picture');
const imageContainerEl = createTag('div', { class: 'image-container' });
imageContainerEl.append(imageEl);
stepsContent.append(imageContainerEl);
}

const heading = section.querySelector('h2, h3, h4');
block.replaceChildren(heading, stepsContent);
buildAccordion(block, rows, stepsContent);
}
}

0 comments on commit d802dec

Please sign in to comment.