Typography
Geist sans for everything that reads, Geist mono for everything that's code. Every role on this page is a copy-pasteable recipe — drop the className verbatim and you get the right size, weight, tracking, and balance behavior.
Headings
One h1 per page. h2 sections within. h3/h4 carry subsections. All headings use tight tracking and the text-balance rule so two-line headings don't break on a lonely word.
h1
Built for the recruiting cycle
export function TypographyH1() {return (<h1 className="scroll-m-20 text-4xl font-bold tracking-tight text-balance">Built for the recruiting cycle</h1>)}
h2
The class of 2027
export function TypographyH2() {return (<h2 className="scroll-m-20 border-b pb-2 text-3xl font-semibold tracking-tight first:mt-0">The class of 2027</h2>)}
h3
Combine scores at LSU's regional camp
export function TypographyH3() {return (<h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">Combine scores at LSU's regional camp</h3>)}
h4
Notes from the head coach
export function TypographyH4() {return (<h4 className="scroll-m-20 text-xl font-semibold tracking-tight">Notes from the head coach</h4>)}
Body
The bulk of every page. Lead for the page intro, p for everything else. Large for emphatic statements; small/muted for metadata and helper text.
p
A recruiter's day starts at five in the morning and ends well after midnight. Most of it is film. The rest of it is phone calls — to coaches, to parents, to anyone who'll pick up. The only way to do this job at scale is to throw away everything that doesn't move a recruit closer to a decision.
export function TypographyP() {return (<p className="leading-7 [&:not(:first-child)]:mt-6">A recruiter's day starts at five in the morning and ends well aftermidnight. Most of it is film. The rest of it is phone calls — tocoaches, to parents, to anyone who'll pick up.</p>)}
Lead
Marcus moved from third on the depth chart to QB1 in week 4 after Jenkins went down with an ACL. Six wins later, LSU pulled the offer off the table.
export function TypographyLead() {return (<p className="text-xl text-body-light">Marcus moved from third on the depth chart to QB1 in week 4 afterJenkins went down with an ACL.</p>)}
Large
export function TypographyLarge() {return <div className="text-lg font-semibold">Built for coaches, not for everyone else.</div>}
Small
export function TypographySmall() {return (<small className="text-sm leading-none font-medium">Updated 3 minutes ago by Coach Patel.</small>)}
Muted
We'll only show this to verified college recruiters.
export function TypographyMuted() {return (<p className="text-sm text-body-light">We'll only show this to verified college recruiters.</p>)}
Inline elements
Things you reach for mid-sentence: code chips, keyboard keys, links. Each one has a specific job — don't substitute.
Inline code
@radix-ui/react-alert-dialogexport function TypographyInlineCode() {return (<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">@radix-ui/react-alert-dialog</code>)}
Inline code (in a paragraph)
Run the audit with yarn upnext:audit before pushing. It flags any hardcoded color values, including ones hiding inside style={...} props.
<p className="leading-7">Run the audit with{" "}<code className="relative rounded bg-muted px-[0.3rem] py-[0.2rem] font-mono text-sm font-semibold">yarn upnext:audit</code>{" "}before pushing.</p>
kbd
Press ⌘K to open the recruit search, then ↵ to commit.
<p className="text-sm leading-7">Press{" "}<kbd className="inline-flex items-center justify-center rounded-sm bg-muted px-1.5 py-0.5 font-sans text-xs text-body">⌘K</kbd>{" "}to open the recruit search.</p>
Link
Read more about how we grade ceiling and floor.
Or jump to the full grading rubric.
import { Link } from "@/components/upnext";<p className="leading-7">Read more about{" "}<Link href="#">how we grade ceiling and floor</Link>.{/* Primary tone for CTA emphasis: */}<Link href="#" tone="primary">View the full rubric</Link></p>
Block elements
Structured content: pulled quotes, lists, tables. Each is intentionally minimal — borders and indentation, no chrome.
blockquote
"You don't recruit a player — you recruit a family. The best programs figure that out by the second phone call."
export function TypographyBlockquote() {return (<blockquote className="mt-6 border-l-2 pl-6 italic">"You don't recruit a player — you recruit a family. The bestprograms figure that out by the second phone call."</blockquote>)}
list
- Read film three times before the first phone call.
- Take notes in pen — phone notes you forget.
- Never promise an offer in writing before the staff meeting.
export function TypographyList() {return (<ul className="my-6 ml-6 list-disc [&>li]:mt-2"><li>Read film three times before the first phone call.</li><li>Take notes in pen — phone notes you forget.</li><li>Never promise an offer in writing before the staff meeting.</li></ul>)}
Ordered list
- Watch film at game speed first; only then go frame by frame.
- Identify three plays that show the ceiling.
- Identify three plays that show the floor.
<ol className="my-6 ml-6 list-decimal marker:text-body [&>li]:mt-2"><li>Watch film at game speed first; only then go frame by frame.</li><li>Identify three plays that show the ceiling.</li><li>Identify three plays that show the floor.</li></ol>
Table
| Recruit | Position | 40-yd | GPA |
|---|---|---|---|
| Marcus Reed | QB | 4.62s | 3.42 |
| Tariq Bell | WR | 4.51s | 3.81 |
export function TypographyTable() {return (<Table><TableHeader><TableRow><TableHead>Recruit</TableHead><TableHead>Position</TableHead><TableHead className="tabular-nums">40-yd</TableHead></TableRow></TableHeader><TableBody><TableRow><TableCell>Marcus Reed</TableCell><TableCell>QB</TableCell><TableCell className="tabular-nums">4.62s</TableCell></TableRow></TableBody></Table>)}
Full type scale
The px reference. Twelve sizes from 60px display to 11px micro. Tracking tightens as size grows; weight relaxes as size shrinks. Every utility above resolves down to one of these.
display-2xl
60px / 1.05 · 600
UpNext — talent, signed.
display-xl
48px / 1.10 · 600
UpNext — talent, signed.
display-lg
36px / 1.15 · 600
UpNext — talent, signed.
h1
30px / 1.20 · 600
UpNext — talent, signed.
h2
24px / 1.25 · 600
UpNext — talent, signed.
h3
20px / 1.30 · 600
UpNext — talent, signed.
h4
18px / 1.35 · 600
UpNext — talent, signed.
body-lg
16px / 1.55 · 400
UpNext — talent, signed.
body
14px / 1.50 · 400
UpNext — talent, signed.
body-sm
13px / 1.45 · 400
UpNext — talent, signed.
caption
12px / 1.40 · 500
UpNext — talent, signed.
micro
11px / 1.35 · 500
UpNext — talent, signed.
When to reach for mono
Geist mono is a precision instrument. Default to sans; bring mono only when the content is literally code or token-shaped.
Code samples, file paths, CSS variable names.
Color values for reference: oklch(0.563 0.149 225).
Keyboard shortcut chips (⌘K, Ctrl+Shift+P).
Section labels in sidebar/UI nav — sans uppercase reads cleaner.
Hex values in a designer-facing readout (use sans medium lowercase, like Figma).
Captions, metadata, body text — mono is functional, not editorial.