feat(web): restore tall hero + carousel slide + viewport-fixed scroll cue
All checks were successful
Deploy to Production / deploy (push) Successful in 1m0s
All checks were successful
Deploy to Production / deploy (push) Successful in 1m0s
Three coordinated tweaks to the landing-page above-the-fold: 1. **Hero padding restored to py-14/sm:py-20/md:py-28** (was py-12/14/16). Compressing it for the scroll-cue position fight made the hero feel cramped and gave the ParticleHero background less room to breathe. With the cue moved out (see #3), there's no reason to shrink the hero. 2. **Step rotator switches to carousel-style horizontal slide.** The AnimatePresence transition was a fade+y-shift cross-fade — clean but sequential. Now the leaving card slides left out (x:-220) while the entering card slides right in (x:220→0), both coexisting in the same 3D-space and inheriting the same mouse-tilt. The container gets `min-h-[240px]` so the absolutely-positioned cards have layout to anchor to (claude_desktop_config.json is the tallest at 7 lines). Reduced-motion still gets the opacity-only cross-fade — sliding content sideways is exactly the kind of motion that preference is meant to suppress. 3. **`<ScrollCue>` extracted into its own client component**, fixed- positioned at viewport bottom (bottom-5) with a frosted pill style. Fades to opacity:0 once `window.scrollY > 80`, so it doesn't shadow the rest of the page. Lives next to `<section>` in page.tsx rather than inside the hero — that way it anchors to the loadscreen's natural bottom edge whether the hero is short or tall. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e4e437c44c
commit
0cf9c66b6b
@ -1,9 +1,9 @@
|
||||
import { HeroStepRotator } from '@/components/hero-step-rotator';
|
||||
import { JsonLd } from '@/components/json-ld';
|
||||
import { ParticleHero } from '@/components/particle-hero';
|
||||
import { ScrollCue } from '@/components/scroll-cue';
|
||||
import { StaticCodeBlock } from '@/components/static-code-block';
|
||||
import { FAQ, faqJsonLd } from '@/lib/seo';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import Link from 'next/link';
|
||||
|
||||
const PROMPT_EXAMPLE = `Create an MCP server that searches our Notion workspace.
|
||||
@ -99,7 +99,7 @@ export default function Landing() {
|
||||
for pointermove on window itself, so the ring still tracks
|
||||
the cursor through the content above. */}
|
||||
<ParticleHero />
|
||||
<div className="relative z-10 mx-auto grid max-w-6xl gap-10 px-6 py-12 sm:py-14 md:grid-cols-[1.05fr_1fr] md:items-center md:gap-12 md:py-16">
|
||||
<div className="relative z-10 mx-auto grid max-w-6xl gap-10 px-6 py-14 sm:py-20 md:grid-cols-[1.05fr_1fr] md:items-center md:gap-12 md:py-28">
|
||||
<div className="min-w-0">
|
||||
<span className="mono inline-block rounded-full border border-[--color-border] bg-[--color-bg-elevated] px-2.5 py-0.5 text-[11px] tracking-wide text-[--color-fg-muted]">
|
||||
v0.1 — updated 2026-05-20
|
||||
@ -149,16 +149,12 @@ export default function Landing() {
|
||||
<HeroStepRotator />
|
||||
</div>
|
||||
</div>
|
||||
{/* Scroll cue — hints the video section sits directly below. */}
|
||||
<a
|
||||
href="#flow"
|
||||
aria-label="See the flow in action"
|
||||
className="absolute inset-x-0 bottom-2 z-10 mx-auto flex w-fit items-center gap-1 text-[11px] uppercase tracking-[0.18em] text-[--color-fg-subtle] transition-colors hover:text-[--color-fg-muted]"
|
||||
>
|
||||
<span>see it run</span>
|
||||
<ChevronDown size={12} className="animate-bounce" />
|
||||
</a>
|
||||
</section>
|
||||
{/* Scroll cue — fixed at the bottom of the loadscreen rather than
|
||||
inside the hero, so it sits at the natural lower edge of the
|
||||
first viewport regardless of how tall the hero ends up. Fades
|
||||
out once the user has scrolled past the loadscreen. */}
|
||||
<ScrollCue targetId="flow" />
|
||||
|
||||
{/* Flow video — full-width edge-to-edge under the hero. The clip
|
||||
shows the real flow (prompt → server schematic → live connection
|
||||
|
||||
@ -109,23 +109,38 @@ export function HeroStepRotator() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-center gap-5">
|
||||
{/* Container is relative + has a min-height so the absolutely-
|
||||
positioned cards inside (during the slide) overlap cleanly
|
||||
without collapsing the layout. min-h is sized for the tallest
|
||||
card (claude_desktop_config.json at 7 lines). */}
|
||||
<div
|
||||
ref={containerRef}
|
||||
className="relative w-full max-w-md"
|
||||
className="relative w-full max-w-md min-h-[240px]"
|
||||
style={{ perspective: 1200 }}
|
||||
onMouseEnter={onEnter}
|
||||
onMouseLeave={onLeave}
|
||||
onMouseMove={onMove}
|
||||
>
|
||||
<AnimatePresence mode="wait" initial={false}>
|
||||
<AnimatePresence initial={false}>
|
||||
<motion.div
|
||||
key={step}
|
||||
initial={reduced ? { opacity: 0 } : { opacity: 0, scale: 0.96, y: 12 }}
|
||||
animate={reduced ? { opacity: 1 } : { opacity: 1, scale: 1, y: 0 }}
|
||||
exit={reduced ? { opacity: 0 } : { opacity: 0, scale: 0.97, y: -8 }}
|
||||
transition={{ duration: reduced ? 0.15 : 0.5, ease: [0.16, 1, 0.3, 1] }}
|
||||
style={{ rotateX, rotateY, transformStyle: 'preserve-3d' }}
|
||||
className="relative overflow-hidden rounded-lg border border-[--color-border-strong] bg-[--color-bg-elevated] shadow-2xl shadow-black/50"
|
||||
// Carousel: current card slides left-out, next slides right-in.
|
||||
// Both cards coexist briefly in the same 3D-space and inherit
|
||||
// the same tilt — reads as a single tile that's swapping its
|
||||
// contents, not two discrete tiles. Reduced-motion collapses
|
||||
// the slide to a plain opacity cross-fade.
|
||||
initial={reduced ? { opacity: 0 } : { x: 220, opacity: 0 }}
|
||||
animate={reduced ? { opacity: 1 } : { x: 0, opacity: 1 }}
|
||||
exit={reduced ? { opacity: 0 } : { x: -220, opacity: 0 }}
|
||||
transition={{ duration: reduced ? 0.15 : 0.55, ease: [0.16, 1, 0.3, 1] }}
|
||||
style={{
|
||||
rotateX,
|
||||
rotateY,
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
transformStyle: 'preserve-3d',
|
||||
}}
|
||||
className="overflow-hidden rounded-lg border border-[--color-border-strong] bg-[--color-bg-elevated] shadow-2xl shadow-black/50"
|
||||
>
|
||||
{/* Cursor-following glow — sits behind the content, additive. */}
|
||||
<motion.div
|
||||
|
||||
43
apps/web/components/scroll-cue.tsx
Normal file
43
apps/web/components/scroll-cue.tsx
Normal file
@ -0,0 +1,43 @@
|
||||
'use client';
|
||||
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
/**
|
||||
* Scroll cue — a fixed pill anchored to the bottom of the viewport that
|
||||
* points the visitor down to the flow video below the hero. Fades out
|
||||
* once the user has scrolled past the loadscreen so it doesn't follow
|
||||
* them around the page.
|
||||
*
|
||||
* Lives at z-30 so it sits above the hero content but below modals.
|
||||
* The frosted pill (backdrop-blur + border) reads clearly against the
|
||||
* particle background without stealing focus from the H1.
|
||||
*/
|
||||
export function ScrollCue({ targetId }: { targetId: string }) {
|
||||
const [visible, setVisible] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
function onScroll() {
|
||||
setVisible(window.scrollY < 80);
|
||||
}
|
||||
onScroll();
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<a
|
||||
href={`#${targetId}`}
|
||||
aria-label="See the flow in action"
|
||||
className={`fixed inset-x-0 bottom-5 z-30 mx-auto flex w-fit items-center gap-1.5 rounded-full border border-[--color-border] px-3 py-1.5 text-[10.5px] uppercase tracking-[0.18em] text-[--color-fg-subtle] backdrop-blur transition-opacity duration-500 hover:text-[--color-fg] ${
|
||||
visible ? 'opacity-100' : 'pointer-events-none opacity-0'
|
||||
}`}
|
||||
style={{
|
||||
backgroundColor: 'color-mix(in oklab, var(--color-bg-elevated) 75%, transparent)',
|
||||
}}
|
||||
>
|
||||
<span>see it run</span>
|
||||
<ChevronDown size={12} className="animate-bounce" />
|
||||
</a>
|
||||
);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user