Add rules page, rewrite how-to-play, and fix scoring display (#20)
- Add new /rules route with official league rules covering rosters,
scoring, major-based QP scoring, tiebreakers, draft, and season rules
- Rewrite how-to-play page with a tutorial/marketing tone, highlighting
the Fischer increment draft clock as a novel feature
- Add Rules link to navbar (desktop and mobile)
- Align QP values in both pages with DEFAULT_QP_VALUES in code
- Fix tiebreaker description to match placement-count logic in code
- Update all fantasy point displays from toFixed(1) to toFixed(2) across
StandingsTable, TeamScoreBreakdown, and PointProgressionChart
- Update tests to match new two-decimal-place point display format
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 21:58:51 -08:00
|
|
|
|
export function meta() {
|
|
|
|
|
|
return [
|
|
|
|
|
|
{ title: "Rules - Brackt" },
|
|
|
|
|
|
{
|
|
|
|
|
|
name: "description",
|
|
|
|
|
|
content: "Official rules for Brackt multi-sport fantasy leagues",
|
|
|
|
|
|
},
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export default function Rules() {
|
|
|
|
|
|
return (
|
|
|
|
|
|
<div className="container mx-auto px-4 py-8 max-w-4xl">
|
|
|
|
|
|
<h1 className="text-4xl font-bold mb-2">Official Rules</h1>
|
|
|
|
|
|
<p className="text-muted-foreground mb-10">
|
|
|
|
|
|
These rules govern all Brackt leagues. Commissioners may adjust scoring
|
|
|
|
|
|
values and draft settings within the options available in league
|
|
|
|
|
|
settings, but the core structure below applies to every league.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="space-y-10">
|
|
|
|
|
|
|
|
|
|
|
|
{/* Object */}
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-1">Object</h2>
|
|
|
|
|
|
<div className="border-b mb-4" />
|
|
|
|
|
|
<p className="text-muted-foreground">
|
|
|
|
|
|
The goal is to draft athletes and teams across multiple sports, then
|
|
|
|
|
|
earn points based on how they finish at the end of their respective
|
|
|
|
|
|
seasons. The participant with the most points at the end of the
|
|
|
|
|
|
league season wins.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Rosters */}
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-1">Rosters</h2>
|
|
|
|
|
|
<div className="border-b mb-4" />
|
|
|
|
|
|
<div className="space-y-3 text-muted-foreground">
|
|
|
|
|
|
<p>
|
|
|
|
|
|
The commissioner sets which sports are included in the league and
|
|
|
|
|
|
how many draft rounds are held. The number of draft rounds must be
|
|
|
|
|
|
at least equal to the number of sports in the league.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Every participant must draft at least one team or individual from
|
|
|
|
|
|
each sport included in the league.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Any draft rounds beyond the number of sports are considered{" "}
|
|
|
|
|
|
<span className="font-semibold text-foreground">flex rounds</span>
|
|
|
|
|
|
. Picks made in flex rounds can be from any sport, including
|
|
|
|
|
|
sports that already have a dedicated roster spot filled.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Scoring */}
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-1">Scoring</h2>
|
|
|
|
|
|
<div className="border-b mb-4" />
|
|
|
|
|
|
<div className="space-y-6 text-muted-foreground">
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="mb-3">
|
|
|
|
|
|
Points are awarded based on where a drafted team or individual
|
|
|
|
|
|
finishes in their sport. The default point values are listed
|
|
|
|
|
|
below. Commissioners may adjust these values before the draft
|
New design (#309)
* 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>
2026-04-23 13:14:55 -07:00
|
|
|
|
begins. To find out how a specific sport determines its final
|
|
|
|
|
|
standings, visit the corresponding sport page.
|
Add rules page, rewrite how-to-play, and fix scoring display (#20)
- Add new /rules route with official league rules covering rosters,
scoring, major-based QP scoring, tiebreakers, draft, and season rules
- Rewrite how-to-play page with a tutorial/marketing tone, highlighting
the Fischer increment draft clock as a novel feature
- Add Rules link to navbar (desktop and mobile)
- Align QP values in both pages with DEFAULT_QP_VALUES in code
- Fix tiebreaker description to match placement-count logic in code
- Update all fantasy point displays from toFixed(1) to toFixed(2) across
StandingsTable, TeamScoreBreakdown, and PointProgressionChart
- Update tests to match new two-decimal-place point display format
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 21:58:51 -08:00
|
|
|
|
</p>
|
|
|
|
|
|
<div className="bg-muted rounded-lg overflow-hidden">
|
|
|
|
|
|
<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>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
|
If a team or individual finishes in a point-scoring position but
|
|
|
|
|
|
was not drafted by anyone, those points are not awarded to anyone.
|
|
|
|
|
|
A participant who drafted the next-highest finisher still earns
|
|
|
|
|
|
zero points for that position.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<p className="mb-3">
|
|
|
|
|
|
When two or more teams or individuals tie for a point-scoring
|
|
|
|
|
|
position within a sport, the points for all tied positions are
|
|
|
|
|
|
combined and split equally among them, rounded to the nearest
|
|
|
|
|
|
whole point.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<div className="bg-muted rounded-lg p-4 text-sm space-y-2">
|
|
|
|
|
|
<p className="font-semibold text-foreground">Examples:</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Tie between 2nd and 3rd: (70 + 50) ÷ 2 ={" "}
|
|
|
|
|
|
<span className="font-semibold text-foreground">
|
|
|
|
|
|
60 points each
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Three-way tie for 6th, 7th, and 8th: (25 + 15 + 15) ÷ 3 ={" "}
|
|
|
|
|
|
<span className="font-semibold text-foreground">
|
|
|
|
|
|
18 points each
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Major-Based Sports Scoring */}
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-1">
|
|
|
|
|
|
Major-Based Sports Scoring
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
<div className="border-b mb-4" />
|
|
|
|
|
|
<div className="space-y-4 text-muted-foreground">
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Some sports — such as golf, tennis, and Counter-Strike — are
|
|
|
|
|
|
scored across multiple major tournaments throughout the year
|
|
|
|
|
|
rather than a single season-ending event. These sports use a
|
|
|
|
|
|
qualifying point (QP) system.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
|
At each major tournament, participants earn qualifying points
|
|
|
|
|
|
based on their finish:
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<div className="bg-muted rounded-lg overflow-hidden">
|
|
|
|
|
|
<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">
|
|
|
|
|
|
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>
|
|
|
|
|
|
Qualifying points are not added to a participant's total league
|
|
|
|
|
|
score directly. They are used only to determine final rankings
|
|
|
|
|
|
within that sport.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Once the final major tournament is complete, qualifying points
|
|
|
|
|
|
from all majors are totaled. Those cumulative totals determine
|
|
|
|
|
|
the sport's final rankings, and the standard point values from
|
|
|
|
|
|
the Scoring section are then awarded accordingly.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Ties in qualifying points are split equally and rounded to the
|
|
|
|
|
|
nearest hundredth of a qualifying point before final rankings are
|
|
|
|
|
|
determined. The same tie-splitting logic from the Scoring section
|
|
|
|
|
|
then applies when awarding league points.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* Season Standings Tiebreakers */}
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-1">
|
|
|
|
|
|
Season Standings Tiebreakers
|
|
|
|
|
|
</h2>
|
|
|
|
|
|
<div className="border-b mb-4" />
|
|
|
|
|
|
<div className="space-y-3 text-muted-foreground">
|
|
|
|
|
|
<p>
|
|
|
|
|
|
If two or more participants finish the season with the same total
|
|
|
|
|
|
score, the following tiebreakers are applied in order:
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<ol className="list-decimal list-inside space-y-2 ml-4">
|
|
|
|
|
|
<li>Most 1st-place finishes across all sports</li>
|
|
|
|
|
|
<li>Most 2nd-place finishes</li>
|
|
|
|
|
|
<li>Most 3rd-place finishes</li>
|
|
|
|
|
|
<li>
|
|
|
|
|
|
This pattern continues down through each place until the tie is
|
|
|
|
|
|
broken
|
|
|
|
|
|
</li>
|
|
|
|
|
|
</ol>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* The Draft */}
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-1">The Draft</h2>
|
|
|
|
|
|
<div className="border-b mb-4" />
|
|
|
|
|
|
<div className="space-y-3 text-muted-foreground">
|
|
|
|
|
|
<p>
|
|
|
|
|
|
The draft is a snake draft. Draft order reverses at the start of
|
|
|
|
|
|
each new round — if you pick last in round 1, you pick first in
|
|
|
|
|
|
round 2, and so on.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
If a participant's time bank runs out, the system will
|
|
|
|
|
|
automatically draft on their behalf. Autodraft picks from the
|
|
|
|
|
|
participant's pre-set draft queue first. If the queue is empty or
|
|
|
|
|
|
all queued picks have already been taken, the system selects from
|
|
|
|
|
|
the top of the available player pool.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>Pick trading is not allowed.</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
{/* During the Season */}
|
|
|
|
|
|
<section>
|
|
|
|
|
|
<h2 className="text-2xl font-semibold mb-1">During the Season</h2>
|
|
|
|
|
|
<div className="border-b mb-4" />
|
|
|
|
|
|
<div className="space-y-3 text-muted-foreground">
|
|
|
|
|
|
<p>
|
|
|
|
|
|
Once the draft is complete, rosters are locked for the entire
|
|
|
|
|
|
season. There is no trading of picks or athletes between
|
|
|
|
|
|
participants.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
<p>
|
|
|
|
|
|
There are no waivers or free-agent pickups. Every participant
|
|
|
|
|
|
competes with the roster they drafted.
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|