From 478e2818b994ed1c8c6e6a8b24686b9731ce1306 Mon Sep 17 00:00:00 2001 From: Senior Web Designer Date: Fri, 26 Jun 2026 19:47:22 +0200 Subject: [PATCH] ELF-457: add slot 3 rotating tagline above hero sub on hatch.surf Three taglines rotate every 3.5s with a subtle 700ms opacity crossfade between the H1 and the hero sub on the hatch.surf landing page: 1. Inspect any request. Mock any response. (default, in initial HTML) 2. Your HTTP bin, on your machine. 3. Send. Capture. Mock. No signup. Implementation: - site/index.html:
with three

children stacked; default #1 has is-active class and no aria-hidden (for SEO + no-JS users) - site/style.css: .hero-tagline uses position:relative with fixed height: 1.7em (no CLS during rotation); absolute children crossfade via transition: opacity 700ms var(--ease-out); font: JetBrains Mono weight 500 in var(--text-secondary); clamp(0.85rem, 2.2vw, 1rem) so mobile (375px+) fits the longest tagline on one line - site/main.js: initRotatingTagline() in the existing IIFE; setInterval every 3500ms swaps is-active and aria-hidden between siblings; early-returns if prefers-reduced-motion is set, so the default tagline stays visible without animation - Spacing: H1 mb-sp-6, tagline mb-sp-6, sub mb-sp-8 keeps the existing 24px/24px/32px rhythm unchanged below the headline Note: site/index.html (not the Next.js app under app/ + components/) is what hatch.surf/nginx actually serves. The Next.js work from the earlier failed run was reverted. --- site/index.html | 7 +++++++ site/main.js | 26 ++++++++++++++++++++++++++ site/style.css | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/site/index.html b/site/index.html index ff730b2a..c4b58b2c 100644 --- a/site/index.html +++ b/site/index.html @@ -67,6 +67,13 @@

Self-hostable HTTP request
inspector + mocker

+ +
+

Inspect any request. Mock any response.

+ + +

One Go binary. SQLite under the hood.
docker compose up, and you have an inspection endpoint and a live feed in under 30 seconds. diff --git a/site/main.js b/site/main.js index e2ad715d..f28f9239 100644 --- a/site/main.js +++ b/site/main.js @@ -420,6 +420,32 @@ initEntranceAnimations(animate, stagger); initTerminalGlow(animate); + initRotatingTagline(); + } + + // ============================================================ + // Slot 3 rotating tagline (ELF-457) + // Cycles 3 tagline

s every 3.5s with a 700ms opacity crossfade + // (handled in CSS via .hero-tagline-item transition). Honors + // prefers-reduced-motion: skips the timer entirely so the default + // #1 stays visible without any animation. + // ============================================================ + + function initRotatingTagline() { + var container = document.querySelector('.hero-tagline'); + if (!container) return; + var items = container.querySelectorAll('.hero-tagline-item'); + if (items.length < 2) return; + if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return; + + var idx = 0; + window.setInterval(function () { + items[idx].classList.remove('is-active'); + items[idx].setAttribute('aria-hidden', 'true'); + idx = (idx + 1) % items.length; + items[idx].classList.add('is-active'); + items[idx].removeAttribute('aria-hidden'); + }, 3500); } // Defer boot by one frame so anime.js engine is ready diff --git a/site/style.css b/site/style.css index 464320e1..130f98c7 100644 --- a/site/style.css +++ b/site/style.css @@ -363,6 +363,43 @@ nav { color: var(--text-primary); } +/* Slot 3: rotating tagline (ELF-457). Sits between H1 and hero-sub. + Fixed height prevents CLS during rotation; absolute children crossfade. */ +.hero-tagline { + position: relative; + max-width: 560px; + height: 1.7em; + margin: 0 auto var(--sp-6); + overflow: hidden; + font-family: var(--font-mono); + font-size: clamp(0.85rem, 2.2vw, 1rem); + font-weight: 500; + line-height: 1.7; + color: var(--text-secondary); + text-align: center; +} + +.hero-tagline-item { + position: absolute; + inset: 0; + margin: 0; + opacity: 0; + transition: opacity 700ms var(--ease-out); + display: flex; + align-items: center; + justify-content: center; +} + +.hero-tagline-item.is-active { + opacity: 1; +} + +@media (prefers-reduced-motion: reduce) { + .hero-tagline-item { + transition: none; + } +} + .hero-sub { font-size: clamp(1rem, 2vw, 1.2rem); color: var(--text-secondary);