* Redesign home page with new layout and component system - Two-column layout (My Leagues 2/3, Upcoming Events 1/3) with mobile stack - LeagueRow: square avatar, gradient draft highlight, rank/points display, progress bar - MyLeaguesCard, CreateLeagueCard with shared SectionCardHeader - UpcomingEventsCard: vertical timeline with grouped multi-league events - Shared gradient system: BracktGradients SVG defs, GradientIcon wrapper, brand.ts constants - Button default variant updated to green→cyan gradient - Navbar: plain nav links with gradient hover, support/admin icon buttons - Accessibility fixes: semantic h2 headings, aria-label on LeagueAvatar and nav elements - Storybook stories for all new components Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Responsive league row layout and mobile polish - League rows stack avatar+name on top, stats full-width below on mobile - Stats spread to right side on sm+ screens with border separator on mobile - Tighter padding on mobile (px-3/py-3), full padding on sm+ - Card headers and content use px-3 sm:px-6 to reduce mobile gutters - Two-column home layout deferred to lg breakpoint (tablet gets stacked) - Active leagues sorted by completion percentage descending - Default rank 1 / 0 points for active leagues with no scoring events yet - Fix ordinal bug for 11th/12th/13th; add aria-labels to rank change indicators - Remove dead StatDivider className prop Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Improve claude file. * Add StandingsPreview card component with podium row styling - New StandingsPreview component with gold/silver/bronze row tints for top 3, team avatar, and LeagueRow-style stat columns (Ranking + Points) with rank and 7-day point change indicators - Fix GradientIcon in Storybook by adding BracktGradients decorator to preview.tsx (renamed from .ts to support JSX) - Fix degenerate SVG gradient on horizontal strokes by switching BracktGradients to gradientUnits="userSpaceOnUse" with Lucide-space coordinates (0→24) - Revert erroneous fill: url(#gradient) from GradientIcon; stroke-only fix was sufficient once gradientUnits was corrected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update components on league homepage. * Finish up league page styling. * Work on standings page. * Add story for RecentScoresCard * Update Point Progression Chart. * Sort point progression legend by ranking and add team links to standings rows * Fix standings discrepancy on change. * Create draft cell component. * Update draft board page * Draft room improvements. * Update some draft room styling. * Fix context menu missing. * Move tab navigation and autodraft to header row, narrow sidebar * Virtualize available participants list, memoize draft room props Adds @tanstack/react-virtual to replace separate mobile/desktop lists with a single unified virtual scroll loop. Also memoizes miniDraftGrid and availableParticipantsSectionProps, and switches pick lookup from Array.find to a Map for O(1) access. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Update draft room UI. * More draft room fixes. * Draft room tweaks. * Fix Rosters page. * Queue Section fixes. * Mobile Draft fixes. * Fix draft board page. * Create bracket look. * Bracket work. * Finish bracket page. * Homepage initial styling * homepage copy * Add privacy policy. Fixes #88. * how to play copy * rules copy * Fix brackets on homepage. * Add footer to website. * Glow on dots. * Landing page copy. * Fix sidebar. --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
347 lines
14 KiB
TypeScript
347 lines
14 KiB
TypeScript
import { Link } from "react-router";
|
||
import { Button } from "app/components/ui/button";
|
||
|
||
export function meta() {
|
||
return [
|
||
{ title: "How To Play - Brackt" },
|
||
{
|
||
name: "description",
|
||
content:
|
||
"Learn how to play Brackt and compete in multi-sport fantasy leagues",
|
||
},
|
||
];
|
||
}
|
||
|
||
export default function HowToPlay() {
|
||
return (
|
||
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
||
<h1 className="text-4xl font-bold mb-2">How To Play</h1>
|
||
<p className="text-lg text-muted-foreground mb-10">
|
||
Brackt is a multi-sport fantasy league where you draft athletes and
|
||
teams across dozens of sports, then watch your roster compete all year
|
||
long. Here's everything you need to know to get started.
|
||
</p>
|
||
|
||
<div className="space-y-10">
|
||
|
||
{/* The Big Picture */}
|
||
<section>
|
||
<h2 className="text-2xl font-semibold mb-3">The Big Picture</h2>
|
||
<p className="text-muted-foreground">
|
||
Before the season starts, everyone in the league participates in a
|
||
draft by picking real teams (and athletes in individual sports) across a set of sports
|
||
chosen by your commissioner. Over the following months, those picks
|
||
compete in their actual seasons. When it's all said and done,
|
||
whoever earned the most points from their picks wins.
|
||
</p>
|
||
</section>
|
||
|
||
{/* Building Your Roster */}
|
||
<section>
|
||
<h2 className="text-2xl font-semibold mb-3">Building Your Roster</h2>
|
||
<p className="text-muted-foreground mb-4">
|
||
Your league includes a set of sports selected by the commissioner, such as
|
||
the NFL, NBA, Golf, Formula 1, Tennis, and more. Your roster has
|
||
one dedicated spot for each sport, so you need to draft at least
|
||
one pick from every sport in the league.
|
||
</p>
|
||
<p className="text-muted-foreground mb-4">
|
||
Extra draft rounds beyond the number of sports create{" "}
|
||
<span className="font-semibold text-foreground">flex spots</span>.
|
||
Flex picks can come from any sport. These are great for grabbing a
|
||
second (or even a third and a fourth!) player in a sport where you
|
||
see value, or doubling down on a safe bet. How you use your flex
|
||
spots can be the difference between winning the league and
|
||
finishing at the bottom of the table.
|
||
</p>
|
||
<div className="bg-muted rounded-lg p-4 text-sm text-muted-foreground">
|
||
<p>
|
||
<span className="font-semibold text-foreground">Example:</span>{" "}
|
||
Your league has 20 sports and 24 draft rounds. That means you
|
||
fill 20 dedicated sport spots and have 4 flex picks to use
|
||
however you like.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
{/* How Points Work */}
|
||
<section>
|
||
<h2 className="text-2xl font-semibold mb-3">How Points Work</h2>
|
||
<p className="text-muted-foreground mb-4">
|
||
Points are awarded based on where your picks finish: 1st through
|
||
8th. The better they finish, the more you earn. Your commissioner
|
||
can adjust point values before the draft, but here's what a typical
|
||
league looks like:
|
||
</p>
|
||
<div className="bg-muted rounded-lg overflow-hidden mb-4">
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="border-b border-border">
|
||
<th className="text-left p-3 font-semibold text-foreground">
|
||
Finish
|
||
</th>
|
||
<th className="text-right p-3 font-semibold text-foreground">
|
||
Points
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-border">
|
||
<tr>
|
||
<td className="p-3">1st Place</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
100
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">2nd Place</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
70
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">3rd Place</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
50
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">4th Place</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
40
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">5th–6th Place</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
25
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">7th–8th Place</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
15
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<p className="text-sm text-muted-foreground">
|
||
If nobody in your league drafted a team that finishes in the top 8,
|
||
those points aren't awarded to anyone in your league. In other
|
||
words, if no one picks Indiana to win the NCAA Football
|
||
championship, the manager who drafted Miami, the team that lost to
|
||
them, still only would receive the 2nd place points.
|
||
</p>
|
||
</section>
|
||
|
||
{/* Major-Based Sports */}
|
||
<section>
|
||
<h2 className="text-2xl font-semibold mb-3">Sports with Majors</h2>
|
||
<p className="text-muted-foreground mb-4">
|
||
Some sports don't have a single championship, they have multiple
|
||
major tournaments spread throughout the year. Classic examples are
|
||
golf and tennis, but other sports may be scored by majors as well.
|
||
You can see on your league pages how each sport is scored. For
|
||
these sports with majors,
|
||
players earn{" "}
|
||
<span className="font-semibold text-foreground">
|
||
qualifying points (QP)
|
||
</span>{" "}
|
||
at each one:
|
||
</p>
|
||
<div className="bg-muted rounded-lg overflow-hidden mb-4">
|
||
<table className="w-full text-sm">
|
||
<thead>
|
||
<tr className="border-b border-border">
|
||
<th className="text-left p-3 font-semibold text-foreground">
|
||
Major Finish
|
||
</th>
|
||
<th className="text-right p-3 font-semibold text-foreground">
|
||
Qualifying Points
|
||
</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody className="divide-y divide-border">
|
||
<tr>
|
||
<td className="p-3">1st</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
20 QP
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">2nd</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
14 QP
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">3rd</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
10 QP
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">4th</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
8 QP
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">5th–6th</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
5 QP
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">7th–8th</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
3 QP
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">9th–12th</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
2 QP
|
||
</td>
|
||
</tr>
|
||
<tr>
|
||
<td className="p-3">13th–16th</td>
|
||
<td className="p-3 text-right font-semibold text-foreground">
|
||
1 QP
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
<p className="text-muted-foreground">
|
||
QP don't go straight to your score — they determine the final
|
||
ranking within that sport. Once all the majors are done, total QP
|
||
are added up, and whoever's ranked 1st through 8th earns the
|
||
standard league points. Consistency across majors is the key.
|
||
</p>
|
||
</section>
|
||
|
||
{/* The Draft Room */}
|
||
<section>
|
||
<h2 className="text-2xl font-semibold mb-3">The Draft Room</h2>
|
||
<p className="text-muted-foreground mb-6">
|
||
Because Brackt is a "draft and done" league, the draft is your only
|
||
opportunity to take action. Once the final pick is made, your roster
|
||
is locked for the entire year. There are no trades, waivers, or
|
||
mid-season pickups. Your success depends entirely on the strategy
|
||
you execute during the draft.
|
||
</p>
|
||
|
||
{/* Timing Mechanics */}
|
||
<div className="space-y-3 mb-6">
|
||
<h3 className="text-lg font-semibold">Timing Mechanics</h3>
|
||
<p className="text-muted-foreground">
|
||
The draft uses a snake format where the pick order reverses every
|
||
round. You can configure the timing of the draft using one of two
|
||
primary mechanics:
|
||
</p>
|
||
<ul className="space-y-2 text-muted-foreground pl-4">
|
||
<li>
|
||
<span className="font-semibold text-foreground">
|
||
Standard Clock:
|
||
</span>{" "}
|
||
A traditional fixed countdown (such as 60 seconds per pick). It
|
||
is a straightforward and consistent pace.
|
||
</li>
|
||
<li>
|
||
<span className="font-semibold text-foreground">
|
||
Chess Clock (Fischer Increment):
|
||
</span>{" "}
|
||
This system uses a Time Bank and an Increment. You start with a
|
||
pool of time (for example, 2 minutes). Each time you make a
|
||
pick, a set bonus like 15 seconds is added back to your bank.
|
||
This allows you to pick quickly in the early rounds to save up
|
||
time for more difficult decisions later on.
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
{/* Flexible Pacing */}
|
||
<div className="space-y-3 mb-6">
|
||
<h3 className="text-lg font-semibold">Flexible Pacing</h3>
|
||
<p className="text-muted-foreground">
|
||
These mechanics can be applied to any draft speed. Whether you
|
||
want to finish in an hour or over the course of a week, the system
|
||
scales to your needs:
|
||
</p>
|
||
<ul className="space-y-2 text-muted-foreground pl-4">
|
||
<li>
|
||
<span className="font-semibold text-foreground">
|
||
Live Drafts:
|
||
</span>{" "}
|
||
Uses short timers (seconds or minutes) for a fast-paced,
|
||
single-session event.
|
||
</li>
|
||
<li>
|
||
<span className="font-semibold text-foreground">
|
||
Slow Drafts:
|
||
</span>{" "}
|
||
Uses extended timers (hours) for leagues that want to draft over
|
||
several days. Slow drafts include a customizable{" "}
|
||
<span className="font-semibold text-foreground">
|
||
Overnight Pause
|
||
</span>
|
||
. The commissioner sets a uniform window (such as 10:00 PM to
|
||
8:00 AM) where the clock stops for everyone. This ensures no one
|
||
is forced to make a pick in the middle of the night.
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
{/* Queue and Autodraft */}
|
||
<div className="space-y-3">
|
||
<h3 className="text-lg font-semibold">Queue and Autodraft</h3>
|
||
<p className="text-muted-foreground">
|
||
To keep the draft moving regardless of the pace, you can use the{" "}
|
||
<span className="font-semibold text-foreground">Draft Queue</span>{" "}
|
||
to pre-rank your targets. If your clock expires, the system
|
||
automatically selects the highest available player from your
|
||
queue. If your queue is empty, the system will select the
|
||
highest-ranked athlete remaining in the pool to ensure the draft
|
||
never stalls.
|
||
</p>
|
||
</div>
|
||
</section>
|
||
|
||
{/* During the Season */}
|
||
<section>
|
||
<h2 className="text-2xl font-semibold mb-3">After the Draft</h2>
|
||
<p className="text-muted-foreground mb-4">
|
||
Once the draft ends, your roster is locked. No trades, no waivers,
|
||
no pickups. Just watch your picks compete across the year and see
|
||
how your draft decisions play out. Points accumulate as sports
|
||
seasons wrap up, and standings update in real time.
|
||
</p>
|
||
<p className="text-muted-foreground">
|
||
If there's a tie in the final standings, it's broken by who has
|
||
more 1st-place finishes. If that's still a tie, it goes to
|
||
2nd-place finishes, then 3rd, and so on.
|
||
</p>
|
||
</section>
|
||
|
||
{/* Links */}
|
||
<section className="flex flex-col sm:flex-row gap-4 items-center justify-between pt-2">
|
||
<p className="text-muted-foreground">
|
||
Want the full breakdown? Read the{" "}
|
||
<Link
|
||
to="/rules"
|
||
className="underline underline-offset-4 hover:text-foreground transition-colors"
|
||
>
|
||
official rules
|
||
</Link>
|
||
.
|
||
</p>
|
||
<Button asChild size="lg">
|
||
<Link to="/leagues/new">Create a League</Link>
|
||
</Button>
|
||
</section>
|
||
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|