From 76233b930c7147b4df193803b2cea5e3e4bc6242 Mon Sep 17 00:00:00 2001 From: fkakatie Date: Thu, 25 Jun 2026 04:05:51 -0600 Subject: [PATCH 1/2] feat: jump-nav block --- blocks/jump-nav/jump-nav.css | 95 ++++++++++++++++++++++++++++++++++++ blocks/jump-nav/jump-nav.js | 33 +++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 blocks/jump-nav/jump-nav.css create mode 100644 blocks/jump-nav/jump-nav.js diff --git a/blocks/jump-nav/jump-nav.css b/blocks/jump-nav/jump-nav.css new file mode 100644 index 0000000..fa2428d --- /dev/null +++ b/blocks/jump-nav/jump-nav.css @@ -0,0 +1,95 @@ +div.jump-nav-container { + position: sticky; + top: 0; + z-index: 2; + margin: 0; +} + +.jump-nav-container .jump-nav-wrapper { + max-width: unset; + padding: 0; +} + +.jump-nav { + border-bottom: 1px solid var(--color-border); + background-color: var(--color-bg); +} + +.jump-nav nav { + display: flex; + flex-direction: column; + gap: 0 1ch; + max-width: var(--site-width); + margin: 0 auto; +} + +@media (width >= 900px) { + .jump-nav nav { + flex-direction: row; + align-items: stretch; + padding: 0 var(--space-l); + } +} + +.jump-nav ul { + list-style: none; + display: flex; + flex: 1; + align-items: stretch; + justify-content: safe center; + overflow-x: auto; + margin: 0; + padding: 0; +} + +@media (width >= 900px) { + .jump-nav ul { + align-items: center; + } +} + +.jump-nav ul a { + display: flex; + align-items: center; + position: relative; + padding: 0.5em 1.2em; + color: var(--color-text); + font-family: var(--heading-font-family); + text-decoration: none; + text-transform: uppercase; + white-space: nowrap; +} + +.jump-nav ul a:hover { + text-decoration: none; +} + +.jump-nav ul a::after { + content: ''; + position: absolute; + left: 0; + bottom: 0; + right: 0; + height: 5px; + background-color: var(--color-brand); + opacity: 0; + transition: opacity 0.2s; +} + +.jump-nav ul a[aria-current="true"]::after { + opacity: 1; +} + +.jump-nav .button-wrapper { + display: flex; + flex-shrink: 0; + align-items: center; + justify-content: safe center; + order: -1; +} + +@media (width >= 900px) { + .jump-nav .button-wrapper { + order: 0; + } +} diff --git a/blocks/jump-nav/jump-nav.js b/blocks/jump-nav/jump-nav.js new file mode 100644 index 0000000..40e1eb9 --- /dev/null +++ b/blocks/jump-nav/jump-nav.js @@ -0,0 +1,33 @@ +export default function decorate(block) { + const ul = block.querySelector('ul'); + const cta = block.querySelector('p.button-wrapper'); + + const nav = document.createElement('nav'); + nav.setAttribute('aria-label', 'Page Sections'); // TODO: localization + nav.append(ul); + if (cta) nav.append(cta); + + block.replaceChildren(nav); + + const links = [...ul.querySelectorAll('a[href*="#"]')]; + const targets = links + .map((a) => { + try { + return document.getElementById(new URL(a.href).hash.slice(1)); + } catch { + return null; + } + }) + .filter(Boolean); + + if (!targets.length) return; + + const observer = new IntersectionObserver((entries) => { + const visible = entries.find((e) => e.isIntersecting); + if (!visible) return; + const { id } = visible.target; + links.forEach((a) => a.setAttribute('aria-current', a.href.endsWith(`#${id}`) ? 'true' : 'false')); + }, { rootMargin: '-20% 0px -79% 0px' }); + + targets.forEach((t) => observer.observe(t)); +} From 028823356d7babe06ad0007816e782ed23d4049a Mon Sep 17 00:00:00 2001 From: fkakatie Date: Thu, 25 Jun 2026 04:06:10 -0600 Subject: [PATCH 2/2] fix: jump-nav needs to be in its own section --- tools/import/transform-page.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/import/transform-page.js b/tools/import/transform-page.js index b1027f6..436bc90 100644 --- a/tools/import/transform-page.js +++ b/tools/import/transform-page.js @@ -583,7 +583,10 @@ for (const item of comps) { case "list-links": cur().push(listBlock($el, "links")); break; case "list-detailed": cur().push(listBlock($el, "detailed")); break; case "list-product": cur().push(listBlock($el, "product")); break; - case "jump-nav": cur().push(jumpNavBlock($el)); break; + case "jump-nav": + sections.push([jumpNavBlock($el)]); + sections.push([]); + break; case "title": { const c = defaultContent($el, "title"); if (c) cur().push(c); break; } case "text": { const c = defaultContent($el, "text"); if (c) cur().push(c); break; } case "media": { const c = mediaContent($el); if (c) cur().push(c); break; }