From bcb4f1688ea23f51abcc17b9a6f91bd8e9e55e3d Mon Sep 17 00:00:00 2001 From: Senior Web Designer Date: Fri, 26 Jun 2026 19:30:57 +0200 Subject: [PATCH] ELF-457: add slot 3 rotating tagline above hero sub Three taglines rotate every 3.5s with a subtle 700ms opacity crossfade: 1. Inspect any request. Mock any response. (default) 2. Your HTTP bin, on your machine. 3. Send. Capture. Mock. No signup. Implementation notes: - Client component, default tagline in initial SSR HTML for SEO/no-JS - ARIA: aria-live=polite on container, aria-hidden on inactive items, aria-atomic=true; prefers-reduced-motion suppresses both the timer and the fade (via motion-reduce:transition-none + early-return guard) - No CLS: container has fixed h-7 md:h-8 height; absolute children crossfade in place - Hierarchy: tagline below H1 (mb-6), above hero sub (mb-6 from tagline container, mb-10 from sub to CTAs) Tailwind v4 fixes that were needed to make this visible: - app/globals.css: switch to @import 'tailwindcss' with explicit @source globs (auto-detection misses components when Turbopack workspace root is ambiguous) - app/globals.css: drop redundant reset; Tailwind v4 Preflight handles it, and an unlayered selector beats @layer utilities so it was silently killing mb-*, mx-*, etc. - app/globals.css: wrap in @layer base so text-white / text-zinc-* utilities can override it - next.config.ts: pin turbopack.root to project dir so the build works regardless of shell cwd (was running into the paperclip workspace root during agent sessions) --- app/globals.css | 39 ++++++++++++++++++++------------- components/Hero.tsx | 53 +++++++++++++++++++++++++++++++++++++++++++-- next.config.ts | 8 +++++++ 3 files changed, 83 insertions(+), 17 deletions(-) diff --git a/app/globals.css b/app/globals.css index 44371d21..e2362483 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,6 +1,10 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; + +/* Explicit source roots for Tailwind v4. Auto-detection misses files when + Turbopack's workspace root is ambiguous (e.g. shell cwd differs from the + web project root), so we pin the scan paths here. */ +@source "../components/**/*.{ts,tsx}"; +@source "../app/**/*.{ts,tsx}"; :root { --surface-0: #09090b; @@ -17,11 +21,11 @@ --code-border: #27272a; } -* { - margin: 0; - padding: 0; - box-sizing: border-box; -} +/* Note: no `* { margin: 0; padding: 0; box-sizing: border-box; }` here. + Tailwind v4's Preflight (included by `@import "tailwindcss"`) already + resets margins on block elements and sets border-box globally. An + unlayered `*` selector would beat Tailwind's `@layer utilities` and + silently break every margin utility (mb-*, mt-*, mx-*, etc.). */ html { font-size: 16px; @@ -37,14 +41,19 @@ body { overflow-x: hidden; } -a { - color: var(--brand); - text-decoration: none; - transition: color 150ms cubic-bezier(0.16, 1, 0.3, 1); -} +/* Default link color. Wrapped in @layer base so Tailwind utilities + (text-white, text-zinc-*, etc.) in @layer utilities still win. An + unlayered `a { color: ... }` would silently override utilities. */ +@layer base { + a { + color: var(--brand); + text-decoration: none; + transition: color 150ms cubic-bezier(0.16, 1, 0.3, 1); + } -a:hover { - color: var(--text-primary); + a:hover { + color: var(--text-primary); + } } code { diff --git a/components/Hero.tsx b/components/Hero.tsx index d5281e3f..039f091c 100644 --- a/components/Hero.tsx +++ b/components/Hero.tsx @@ -1,16 +1,65 @@ +"use client"; + +import { useEffect, useState } from "react"; + +// Rotating taglines — Slot 3 above the hero sub (ELF-457, brief ELF-456). +// Order matters: index 0 is the default and the initial server-rendered string. +const TAGLINES = [ + "Inspect any request. Mock any response.", + "Your HTTP bin, on your machine.", + "Send. Capture. Mock. No signup.", +] as const; + +// Per-tagline dwell. 3.5s gives ~2.8s solid visibility after the 700ms crossfade. +const ROTATE_MS = 3500; + export default function Hero() { + const [index, setIndex] = useState(0); + + useEffect(() => { + // Respect prefers-reduced-motion at the JS layer too: don't start a timer + // for users who opt out. The fade is independently suppressed via the + // motion-reduce: variant on the

class below. + if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; + const id = window.setInterval(() => { + setIndex((i) => (i + 1) % TAGLINES.length); + }, ROTATE_MS); + return () => window.clearInterval(id); + }, []); + return (

Open source · MIT licensed
- +

Self-hostable HTTP request
inspector + mocker

- + + {/* Slot 3: rotating tagline. Fixed-height container prevents CLS; subtle + opacity crossfade so it doesn't compete with the terminal CTA below. + a11y: aria-live polite + aria-hidden on inactive items. */} +
+ {TAGLINES.map((t, i) => ( +

+ {t} +

+ ))} +
+

One Go binary. SQLite under the hood. diff --git a/next.config.ts b/next.config.ts index 1f6f6ced..519c8e35 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,9 +1,17 @@ import type { NextConfig } from "next"; +import path from "node:path"; const nextConfig: NextConfig = { images: { unoptimized: true, }, + // Pin the Turbopack workspace root to this directory. Without this, Turbopack + // infers it from the shell's cwd, which during Paperclip agent runs is the + // paperclip project root — that misroutes CSS/source scanning and silently + // strips Tailwind utility classes from the production bundle. + turbopack: { + root: path.resolve(import.meta.dirname), + }, }; export default nextConfig;