feat(hero): rotate three positioning lines on a 10s cycle

All three lines render in the DOM; CSS drives the fade via data-active.
Reduced-motion users see the first line only (no interval, no fade).
Rotation pauses on hover and keyboard focus. aria-live=off on the
rotator so AT does not announce every swap; aria-hidden flips per-swap
to avoid duplicate heading announcements.

Also set vite.build.assetsInlineLimit=0 in astro.config.mjs so Astro
emits the rotator script as a same-origin external file (dist/assets/)
rather than inlining it — required for CSP script-src 'self' compliance.
This commit is contained in:
hsiegeln
2026-04-24 23:46:21 +02:00
parent 518d7a8afc
commit 77bf0bfa74
2 changed files with 67 additions and 2 deletions

View File

@@ -20,8 +20,14 @@ import TopographicBg from '../TopographicBg.astro';
Your camels called. They want a GPS.
</p>
</div>
<h1 class="text-display font-bold text-text mb-6">
Run Apache Camel without running Apache Camel.
<h1
class="text-display font-bold text-text mb-6 hero-rotator"
aria-live="off"
data-hero-rotator
>
<span class="hero-line" data-active aria-hidden="false">Run Apache Camel without running Apache Camel.</span>
<span class="hero-line" aria-hidden="true">Camel integrations, minus the baggage.</span>
<span class="hero-line" aria-hidden="true">Your camels, our caravan. You just ride.</span>
</h1>
<p class="text-lg md:text-xl text-text-muted max-w-prose leading-relaxed mb-10">
The hosted home for your Camel integrations — with deep tracing, replay, and live control built in. Because you chose Camel to stay free, not to stay up all night.
@@ -33,3 +39,57 @@ import TopographicBg from '../TopographicBg.astro';
</div>
</div>
</section>
<style>
.hero-rotator {
position: relative;
display: block;
/* Reserve height for the tallest line so no layout shift on swap.
Two lines at current H1 size handles all three on most viewports. */
min-height: 2.2em;
}
.hero-line {
display: block;
opacity: 0;
transition: opacity 700ms ease-in-out;
/* Stack all lines on top of each other — only [data-active] is visible. */
position: absolute;
inset: 0;
}
.hero-line[data-active] {
opacity: 1;
position: relative;
}
@media (prefers-reduced-motion: reduce) {
.hero-line {
transition: none;
}
}
</style>
<script>
// Hero rotating headline. Bundled by Astro (CSP: script-src 'self').
const rotator = document.querySelector<HTMLElement>('[data-hero-rotator]');
if (rotator) {
const lines = Array.from(rotator.querySelectorAll<HTMLElement>('.hero-line'));
const reduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (!reduced && lines.length > 1) {
let index = 0;
let paused = false;
const pause = () => { paused = true; };
const resume = () => { paused = false; };
rotator.addEventListener('mouseenter', pause);
rotator.addEventListener('mouseleave', resume);
rotator.addEventListener('focusin', pause);
rotator.addEventListener('focusout', resume);
setInterval(() => {
if (paused) return;
lines[index].removeAttribute('data-active');
lines[index].setAttribute('aria-hidden', 'true');
index = (index + 1) % lines.length;
lines[index].setAttribute('data-active', '');
lines[index].setAttribute('aria-hidden', 'false');
}, 10000);
}
}
</script>