From 6f8b8da15109f1937087c64a96d6e78eeaf725a8 Mon Sep 17 00:00:00 2001 From: Marco Sadjadi Date: Wed, 27 May 2026 12:20:25 +0200 Subject: [PATCH] feat(web): glow-pulse on primary CTAs + hero fills full first viewport MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two coordinated polish moves: 1. ** / ** — new `apps/web/components/pulse.tsx`. Click anywhere on a wrapped link or button and a small indigo dot detonates from the click point, scaling 1x→80x over 650ms before fading to transparent. Same visual language as the hero load-in glow — the click effectively says "this is the brand reaching back." The dot lives in a `pointer-events: none` overlay, so it never blocks the underlying navigation. `overflow-hidden + relative` are added to the host so the bloom stays inside the rounded shape. `glow-pulse` keyframe sits in globals.css next to the existing `pulse-dot` / `shimmer` / `fade-in` definitions; reduced-motion suppresses the animation to instant-opacity-0 so the click flow is preserved without the bloom. Wired into the highest-conversion CTAs only — the user explicitly asked "wo's Sinn macht": - Hero "Start building free" + "Read the docs" - Marketing header Login / Dashboard button - Dashboard header "+ New server" pill Deliberately NOT applied to dashboard nav links, logout, destructive buttons, form internals, carousel dots — pulse on every click would be noise. 2. **Hero fills 100svh − nav** (`min-height: calc(100svh - 3rem)`). `svh` (small viewport height) instead of `vh` so the hero doesn't jump when the mobile address bar hides/shows. The 3rem subtracts the sticky marketing nav (h-12 = 48px), so the hero ends right at the loadscreen's natural bottom edge. `flex items-center` plus the inner grid's existing `md:items-center` keep the content vertically centred inside the tall section. The ParticleHero background now has cinematic-scale room and the indigo radial-glow + dot-mask read as the dominant background motif — which is the effect the user loved at load-in. Co-Authored-By: Claude Opus 4.7 (1M context) --- apps/web/app/(dashboard)/layout.tsx | 5 +- apps/web/app/(marketing)/page.tsx | 21 ++- apps/web/app/globals.css | 26 ++++ .../web/components/marketing-auth-buttons.tsx | 6 +- apps/web/components/pulse.tsx | 121 ++++++++++++++++++ 5 files changed, 167 insertions(+), 12 deletions(-) create mode 100644 apps/web/components/pulse.tsx diff --git a/apps/web/app/(dashboard)/layout.tsx b/apps/web/app/(dashboard)/layout.tsx index da4002d..8afb423 100644 --- a/apps/web/app/(dashboard)/layout.tsx +++ b/apps/web/app/(dashboard)/layout.tsx @@ -1,6 +1,7 @@ import { CookieBanner } from '@/components/cookie-banner'; import { Logo } from '@/components/logo'; import { MobileActionBar } from '@/components/mobile-action-bar'; +import { PulseLink } from '@/components/pulse'; import { UserMenu } from '@/components/user-menu'; import { FileClock, LayoutGrid, Package, Server, Settings } from 'lucide-react'; import Link from 'next/link'; @@ -33,12 +34,12 @@ export default function DashboardLayout({ children }: { children: React.ReactNod
- + New server - +
diff --git a/apps/web/app/(marketing)/page.tsx b/apps/web/app/(marketing)/page.tsx index 734a5de..6438d07 100644 --- a/apps/web/app/(marketing)/page.tsx +++ b/apps/web/app/(marketing)/page.tsx @@ -1,6 +1,7 @@ import { HeroStepRotator } from '@/components/hero-step-rotator'; import { JsonLd } from '@/components/json-ld'; import { ParticleHero } from '@/components/particle-hero'; +import { PulseLink } from '@/components/pulse'; import { ScrollCue } from '@/components/scroll-cue'; import { StaticCodeBlock } from '@/components/static-code-block'; import { FAQ, faqJsonLd } from '@/lib/seo'; @@ -92,14 +93,20 @@ export default function Landing() { three artifacts (prompt → build.log → claude config) with a mouse-reactive 3D tilt and a step indicator. Shorter overall so the video section below is teased above the fold. */} -
+
{/* WebGL particle field — capability-detected client component. Sits behind the hero content at z-0 with pointer-events:none so the CTAs above remain fully interactive. The canvas listens for pointermove on window itself, so the ring still tracks - the cursor through the content above. */} + the cursor through the content above. With the hero now + filling the full first-viewport (minus the 48px sticky nav), + the field has cinematic-scale room and the indigo radial + glow + dot mask read as the dominant background motif. */} -
+
v0.1 — updated 2026-05-20 @@ -116,18 +123,18 @@ export default function Landing() { for Claude, Cursor and ChatGPT.

- Start building free - - + Read the docs - +
diff --git a/apps/web/app/globals.css b/apps/web/app/globals.css index 01f4dfc..ecec493 100644 --- a/apps/web/app/globals.css +++ b/apps/web/app/globals.css @@ -168,3 +168,29 @@ transform: translateY(0); } } + +/* Click-pulse for primary actions (see components/pulse.tsx). + Scales a small indigo dot from 1x to 80x over 650ms — on a 2px base + that's ~160px max radius, comfortably larger than any button on the + site. The translate(-50%, -50%) keeps the dot anchored to the click + point as it grows. Reduced-motion users get an instant fade-out so + the click action still proceeds without the bloom. */ +@keyframes glow-pulse { + 0% { + transform: translate(-50%, -50%) scale(1); + opacity: 0.85; + } + 100% { + transform: translate(-50%, -50%) scale(80); + opacity: 0; + } +} +@media (prefers-reduced-motion: reduce) { + @keyframes glow-pulse { + 0%, + 100% { + transform: translate(-50%, -50%) scale(1); + opacity: 0; + } + } +} diff --git a/apps/web/components/marketing-auth-buttons.tsx b/apps/web/components/marketing-auth-buttons.tsx index a126603..6394d53 100644 --- a/apps/web/components/marketing-auth-buttons.tsx +++ b/apps/web/components/marketing-auth-buttons.tsx @@ -1,7 +1,7 @@ 'use client'; +import { PulseLink } from '@/components/pulse'; import { apiFetch } from '@/lib/api'; -import Link from 'next/link'; import { useEffect, useState } from 'react'; /** @@ -26,11 +26,11 @@ export function MarketingAuthButtons() { const showDashboard = authed === true; return ( - {showDashboard ? 'Dashboard' : 'Login'} - + ); } diff --git a/apps/web/components/pulse.tsx b/apps/web/components/pulse.tsx new file mode 100644 index 0000000..6511b35 --- /dev/null +++ b/apps/web/components/pulse.tsx @@ -0,0 +1,121 @@ +'use client'; + +import Link from 'next/link'; +import { + type ComponentProps, + type MouseEvent as ReactMouseEvent, + useRef, + useState, +} from 'react'; + +interface Pulse { + id: number; + x: number; // percentage 0-100 + y: number; +} + +/** + * Shared indigo glow-pulse-on-click effect used by hero CTAs and other + * primary actions across the site. Same visual language as the hero + * load-in glow — a small bright dot detonates from the click point and + * scales out fast, fading to transparent. + * + * Two variants exposed: + * - wraps next/link + * - wraps a native + ); +}