homepage copy
This commit is contained in:
parent
8fadc27c75
commit
654edbf271
1 changed files with 36 additions and 13 deletions
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { Link } from "react-router";
|
||||
import { Users, Shuffle, Trophy } from "lucide-react";
|
||||
import { Users, Shuffle, Trophy, ChevronDown } from "lucide-react";
|
||||
import { Button } from "~/components/ui/button";
|
||||
import { Card, CardContent } from "~/components/ui/card";
|
||||
import { SectionCardHeader } from "~/components/ui/SectionCardHeader";
|
||||
|
|
@ -21,15 +21,17 @@ function shuffle<T>(arr: T[]): T[] {
|
|||
return a;
|
||||
}
|
||||
|
||||
const TOTAL_TICKS = 36;
|
||||
const TOTAL_TICKS = 12;
|
||||
|
||||
// WCAG 2.3.1: no more than 3 flashes/second → minimum interval 334 ms.
|
||||
// Curve runs 800 ms → 340 ms over 12 ticks (ramps to full speed at tick 8).
|
||||
function getInterval(tick: number): number {
|
||||
const pct = tick / TOTAL_TICKS;
|
||||
return Math.round(130 - (130 - 38) * (pct * pct));
|
||||
const pct = Math.min((tick / TOTAL_TICKS) * 1.5, 1);
|
||||
return Math.round(800 - (800 - 340) * (pct * pct));
|
||||
}
|
||||
|
||||
function SlotMachineHeadline() {
|
||||
const [text, setText] = useState("");
|
||||
const [text, setText] = useState("\u00A0");
|
||||
const [landed, setLanded] = useState(false);
|
||||
const [animKey, setAnimKey] = useState(0);
|
||||
const tickRef = useRef(0);
|
||||
|
|
@ -54,7 +56,7 @@ function SlotMachineHeadline() {
|
|||
timerRef.current = setTimeout(tick, getInterval(tickRef.current));
|
||||
}
|
||||
|
||||
timerRef.current = setTimeout(tick, 130);
|
||||
timerRef.current = setTimeout(tick, 800);
|
||||
return () => {
|
||||
if (timerRef.current) clearTimeout(timerRef.current);
|
||||
};
|
||||
|
|
@ -207,6 +209,25 @@ function BracketDecor({ flip = false }: { flip?: boolean }) {
|
|||
);
|
||||
}
|
||||
|
||||
// ─── Scroll Hint ─────────────────────────────────────────────────────────────
|
||||
|
||||
function ScrollHint() {
|
||||
const [visible, setVisible] = useState(true);
|
||||
useEffect(() => {
|
||||
const onScroll = () => { if (window.scrollY > 50) setVisible(false); };
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
return (
|
||||
<div
|
||||
className="pointer-events-none absolute bottom-8 left-1/2 -translate-x-1/2 animate-bounce transition-opacity duration-500"
|
||||
style={{ opacity: visible ? 0.35 : 0 }}
|
||||
>
|
||||
<ChevronDown className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Sport Ticker ─────────────────────────────────────────────────────────────
|
||||
|
||||
const TICKER_ITEMS = [
|
||||
|
|
@ -245,17 +266,17 @@ const HOW_IT_WORKS_STEPS = [
|
|||
{
|
||||
icon: Users,
|
||||
title: "Create a League",
|
||||
body: "Name it, pick your sports, invite your friends with a link. Takes 30 seconds.",
|
||||
body: "Start a league, pick your sports (we can help), and invite your friends. It takes 30 seconds to set the stage.",
|
||||
},
|
||||
{
|
||||
icon: Shuffle,
|
||||
title: "Draft Your Teams",
|
||||
body: "Snake draft — everyone picks teams from any sport on the calendar. NHL, NBA, World Cup, whatever is running.",
|
||||
body: "Pick teams from all the sports in order to build a superteam to win you bragging rights! Draft your way, slow or fast, with standard timers or high-pressure chess clocks.",
|
||||
},
|
||||
{
|
||||
icon: Trophy,
|
||||
title: "Watch & Win",
|
||||
body: "Your teams earn points as they win real games. The bracket updates live. Last one standing takes it.",
|
||||
body: "Your teams earn points for how well they perform in the playoffs. Follow your rooting interests along the way, and gloat over your leaguemates when they fall.",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -301,8 +322,8 @@ export function LandingPage() {
|
|||
if (i < HOW_IT_WORKS_STEPS.length - 1) {
|
||||
// Triangles reference #brackt-primary-gradient defined in <BracktGradients /> (mounted in root.tsx)
|
||||
howItWorksItems.push(
|
||||
<div key={`arrow-${step.title}`} className="hidden shrink-0 md:flex md:items-center md:justify-center md:px-1">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20">
|
||||
<div key={`arrow-${step.title}`} className="flex shrink-0 items-center justify-center py-1 md:px-1 md:py-0">
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" className="rotate-90 md:rotate-0">
|
||||
<polygon
|
||||
points="3,2 18,10 3,18"
|
||||
fill="url(#brackt-primary-gradient)"
|
||||
|
|
@ -329,11 +350,13 @@ export function LandingPage() {
|
|||
<BracketDecor flip />
|
||||
</div>
|
||||
|
||||
<ScrollHint />
|
||||
|
||||
{/* Center content */}
|
||||
<div className="relative z-10 w-full max-w-[680px] text-center">
|
||||
<h1
|
||||
className="mb-0 leading-[0.95] tracking-[-0.04em]"
|
||||
style={{ fontSize: "clamp(52px, 8vw, 88px)", fontWeight: 900 }}
|
||||
style={{ fontSize: "clamp(38px, 8vw, 88px)", fontWeight: 900 }}
|
||||
>
|
||||
<span className="block text-white">Fantasy</span>
|
||||
<SlotMachineHeadline />
|
||||
|
|
@ -347,7 +370,7 @@ export function LandingPage() {
|
|||
Compete with your friends in a league all season long.
|
||||
</p>
|
||||
|
||||
<div className="mt-8 flex items-center justify-center gap-2.5">
|
||||
<div className="mt-8 flex flex-col items-center justify-center gap-2.5 sm:flex-row">
|
||||
<Button asChild size="lg" className="text-sm font-extrabold">
|
||||
<Link to="/leagues/new">Create a League</Link>
|
||||
</Button>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue