From 3df011d33577a68efdfe6c2805291eaa28f08361 Mon Sep 17 00:00:00 2001 From: fkakatie Date: Wed, 24 Jun 2026 17:03:09 -0600 Subject: [PATCH 1/2] feat: auto-decorate video links --- scripts/scripts.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/scripts/scripts.js b/scripts/scripts.js index 6799e90..7ce29a3 100644 --- a/scripts/scripts.js +++ b/scripts/scripts.js @@ -48,6 +48,30 @@ function buildWidgetAutoBlocks(main) { }); } +/** + * Replaces standalone .mp4 links with native video elements. + * @param {Element} main The container element + */ +function decorateVideos(main) { + main.querySelectorAll('a[href$=".mp4"]').forEach((link) => { + const video = document.createElement('video'); + video.src = link.href; + video.controls = true; + video.setAttribute('playsinline', ''); + const p = link.closest('p'); + if ( + p + && p.querySelectorAll('a[href]').length === 1 + && p.querySelector('a[href]') === link + && p.textContent.trim() === link.textContent.trim() + ) { + p.replaceWith(video); + } else { + link.replaceWith(video); + } + }); +} + /** * Builds all synthetic blocks in a container element. * @param {Element} main The container element @@ -145,11 +169,12 @@ export function decorateExternalLinks(container) { */ // eslint-disable-next-line import/prefer-default-export export function decorateMain(main) { - decorateIcons(main); buildAutoBlocks(main); + decorateIcons(main); decorateSections(main); decorateBlocks(main); decorateButtons(main); + decorateVideos(main); } /** From 077b898fe645784336f0c88694c44a02925fd7d7 Mon Sep 17 00:00:00 2001 From: fkakatie Date: Wed, 24 Jun 2026 17:03:55 -0600 Subject: [PATCH 2/2] feat: branded hero block --- blocks/hero/hero.css | 183 ++++++++++++++++++++++++++++++++++++++++--- blocks/hero/hero.js | 42 ++++++++++ 2 files changed, 212 insertions(+), 13 deletions(-) diff --git a/blocks/hero/hero.css b/blocks/hero/hero.css index f98447c..1d93134 100644 --- a/blocks/hero/hero.css +++ b/blocks/hero/hero.css @@ -1,37 +1,194 @@ -.hero-container .hero-wrapper { +div.hero-container:not(.teaser) { + margin-top: 0; +} + +.hero-container:not(.teaser) .hero-wrapper { max-width: unset; padding: 0; } .hero { + display: flex; + align-items: center; position: relative; - min-height: 300px; - padding: var(--space-l) var(--space-m); + overflow: hidden; + min-height: 350px; + border-bottom: 1px solid var(--color-border); } -.hero h1 { +.hero > div { + display: flex; + flex: 1; + flex-direction: column; max-width: var(--site-width); - margin-left: auto; - margin-right: auto; - color: var(--color-bg); + margin: 0 auto; +} + +@media (width >= 900px) { + .hero { + color: var(--color-bg); + } + + .hero::before { + content: ''; + position: absolute; + inset: 0; + z-index: 1; + background: linear-gradient(to right, rgb(0 0 0 / 85%) 7%, rgb(0 0 0 / 0%) 60%); + pointer-events: none; + } + + .hero[data-body="right"]::before { + background: linear-gradient(to left, rgb(0 0 0 / 85%) 7%, rgb(0 0 0 / 0%) 60%); + } } -.hero picture { +.hero .media-wrapper { + flex-shrink: 0; + order: -1; + position: relative; + z-index: 0; + min-height: 200px; +} + +.hero .media-wrapper picture { + display: block; position: absolute; inset: 0; - z-index: -1; - box-sizing: border-box; +} + +.hero .media-wrapper img { + display: block; + height: 100%; + width: 100%; object-fit: cover; } -.hero img { +.hero .media-wrapper video { + position: absolute; + inset: 0; + z-index: 1; height: 100%; width: 100%; object-fit: cover; } +@media (width >= 600px) { + .hero .media-wrapper { + min-height: 285px; + } +} + @media (width >= 900px) { - .hero { - padding: var(--space-l) var(--space-l); + .hero .media-wrapper { + order: 0; + position: absolute; + inset: 0; + min-height: auto; + } +} + +.hero .body-wrapper { + position: relative; + z-index: 2; + padding: var(--space-m); +} + +.hero .body-wrapper h1, +.hero .body-wrapper h2, +.hero .body-wrapper h3, +.hero .body-wrapper h4, +.hero .body-wrapper h5, +.hero .body-wrapper h6, +.hero .body-wrapper p { + color: inherit; +} + +.hero .body-wrapper h1::after, +.hero .body-wrapper h2::after, +.hero .body-wrapper h3::after, +.hero .body-wrapper h4::after, +.hero .body-wrapper h5::after, +.hero .body-wrapper h6::after { + content: ''; + display: block; + height: 5px; + width: 40px; + margin-top: var(--space-s); + background-color: var(--color-brand); +} + +@media (width >= 900px) { + .hero .body-wrapper { + max-width: 55%; + } + + .hero[data-body="right"] .body-wrapper { + margin-left: auto; + } + + .hero .body-wrapper h1 { + margin-bottom: 1em; + font-size: 3.375rem; + text-shadow: 0 1px 5px rgb(0 0 0 / 60%); + } + + .hero .body-wrapper .button { + min-width: 200px; + } +} + +/* TEASER */ + +.hero.teaser { + background-color: var(--color-black); + color: var(--color-bg); +} + +@media (width >= 900px) { + .hero.teaser { + align-items: center; + min-height: 325px; + } + + .hero.teaser::before { + background: linear-gradient(to right, var(--color-black) 7%, rgb(0 0 0 / 0%) 93%); + } + + .hero.teaser[data-body="right"]::before { + background: linear-gradient(to left, var(--color-black) 7%, rgb(0 0 0 / 0%) 93%); + } +} + +.hero.teaser .media-wrapper::before { + content: ''; + position: absolute; + inset: 0; + z-index: 2; + background: linear-gradient(to top, var(--color-black) 7%, rgb(0 0 0 / 0%) 60%); + pointer-events: none; +} + +@media (width >= 900px) { + .hero.teaser .media-wrapper::before { + content: none; + } +} + +.hero.teaser .body-wrapper .button { + width: 100%; +} + +@media (width >= 600px) { + .hero.teaser .body-wrapper .button { + width: unset; + min-width: 200px; + } +} + +@media (width >= 900px) { + .hero.teaser .body-wrapper { + max-width: 360px; + padding: var(--space-m) var(--space-xl); } } diff --git a/blocks/hero/hero.js b/blocks/hero/hero.js index e69de29..d750ed8 100644 --- a/blocks/hero/hero.js +++ b/blocks/hero/hero.js @@ -0,0 +1,42 @@ +/** + * Configures ambient video and unwraps pictures. + * @param {HTMLElement} col + */ +function decorateMedia(col) { + const video = col.querySelector('video'); + if (video) { + video.removeAttribute('controls'); + video.muted = true; + video.autoplay = true; + video.loop = true; + } + + col.querySelectorAll('p > picture').forEach((pic) => { + pic.parentElement.replaceWith(pic); + }); +} + +export default function decorate(block) { + const cols = [...block.querySelectorAll(':scope > div > div')]; + + const mediaCol = cols.find((col) => { + const els = [...col.children]; + return els.length > 0 && els.every((el) => { + if (el.tagName === 'VIDEO' || el.tagName === 'PICTURE') return true; + return el.tagName === 'P' && el.children.length === 1 && el.children[0].tagName === 'PICTURE'; + }); + }); + mediaCol.className = 'media-wrapper'; + decorateMedia(mediaCol); + + const bodyCol = cols.find((col) => col !== mediaCol); + bodyCol.className = 'body-wrapper'; + + if (cols[0] === mediaCol) block.dataset.body = 'right'; + + const heading = block.querySelector('h1, h2, h3, h4, h5, h6'); + if (!heading || heading.tagName !== 'H1') { + block.classList.add('teaser'); + block.closest('.hero-container').classList.add('teaser'); + } +}