GameZav · Design System v1.0

A single source of truth for GameZav.

Today the codebase carries two parallel theme systems — theme/index.ts and hooks/useTheme.js — with different gold/red values, mismatched neutrals, and one-off colors hardcoded across components. This document unifies them into one palette, type scale, and component vocabulary, and lists every rename needed to land it.

StatusProposed · awaiting review
SurfacesMobile app · Landing · Email
ModesLight + Dark
TypeInter · JetBrains Mono
UpdatedApr 25, 2026

01Brand color

Two ramps drive the brand: warm gold for primary actions and confidence, deep red as the secondary accent. Both are extended into 7 steps so we can tint, tone, and surface without inventing one-offs.

Gold ramp — primary
50
gold-50
#FBF3DC
100
gold-100
#F8E6B0
200
gold-200
#F2D27A
300
gold-300
#ECC053
400
gold-400
#E3B23C
500
gold-500
#C89A1F
600
gold-600
#9F7615
700
gold-700
#6B4F0E
Red ramp — accent
50
red-50
#FCE8E8
100
red-100
#F8C2C2
200
red-200
#EE7070
300
red-300
#E53935
400
red-400
#C62828
500
red-500
#8E1B1B
600
red-600
#5F1010
reserved
future
Neutral ramp — single source
0
n-0
#FFFFFF
50
n-50
#F9FAFB
100
n-100
#F3F4F6
200
n-200
#E5E7EB
300
n-300
#D1D5DB
400
n-400
#9CA3AF
500
n-500
#6B7280
600
n-600
#4B5563
700
n-700
#374151
800
n-800
#1F2937
900
n-900
#111827
950
n-950
#0B1220
1000
n-1000
#000000

02Semantic colors

Status colors with both a solid (text/icon) and a soft (background) value. Replaces the four success greens (#10B981, #34C759, #22C55E, #4ADE80) and three reds floating in the codebase.

Success
#16A34A
Won · +1.5u
Won picks, completed steps, positive deltas. Replaces the 4 different greens.
Danger
#DC2626
Lost · −1.0u
Lost picks, errors, destructive. Distinct from red-400 brand accent.
Warning
#F59E0B
Pending
Cautions, low-confidence states, bankroll alerts.
Info
#0EA5E9
Tip
Informational banners, help text callouts, neutral notices.
Specialty
On-Fire
#FF6A1A
🔥 92% confidence
Reserved for predictions with confidence > 75. Replaces the hardcoded #FF4500 in MyPickCard.jsx. This is a deliberate brand moment — do not use for anything else.
Brand gradient
linear-gradient(135deg, gold-300 0%, red-400 100%)
GameZav AI · powered prediction
Use sparingly. Hero strokes, premium upgrades, splash. Never on body text or card fills.

03Dark mode surfaces

The app defines dark surfaces inside useTheme.js while the landing hardcodes its own. These five tokens are the canonical dark surfaces.

surface-base
#0B1220
App background
surface-1
#111827
Cards · sheets
surface-2
#1F2937
Raised cards · modals
surface-3
#374151
Inputs · pressed states
comment-bg
#262626
Quoted / nested content
accent-on-dark
gold-400
Primary on dark · keep contrast ≥ 4.5

04Logo & wordmark

The wordmark is the brand. Always render the two halves with exactly the brand gold and red — no shadows, no strokes, no italics. Track tightly (letter-spacing: -0.025em) and keep the two halves on the same line.

GameZav
primary · lightgold-400 / red-400
GameZav
primary · darkgold-300 / red-300
GameZav
mono · on-goldn-900
GZ
app icon · GZ mark32 · 96 · 1024
GZ
app icon · gold varianttinted
GameZav
AI Sports Predictions
lockup · with taglinemarketing only
Clear space & minimums
GameZav
↳ 1× cap-height padding all sides
Minimum size · 64px wide on screen · 16mm wide in print.
Don't · stretch, recolor halves, add effects, place on busy photography without a scrim.

05Typography

Inter for everything. JetBrains Mono for stat readouts, odds, and confidence percentages where columnar alignment matters. Adds display and h1 sizes that the current scale is missing.

displayHero, splash
Beat the bookies.
56 / 1.02
−0.025em
800
h1Onboarding, paywall
Get smarter picks every day
40 / 1.08
−0.02em
800
h2Screen titles
Today's predictions
28 / 1.18
−0.015em
700
h3Section, sheet titles
Tracking · last 7 days
22 / 1.25
−0.01em
700
titleCard titles, subtitle
LeBron James vs LAL
18 / 1.3
0
600
body-lgLead paragraphs
AI-powered predictions with transparent confidence scores you can actually trust.
17 / 1.55
0
400
bodyDefault
Over 27.5 points · Confidence 78% · Game starts in 2h 14m at Crypto.com Arena
15 / 1.55
0
400
body-strongInline emphasis
You've hit 12 of your last 15 picks.
15 / 1.55
0
600
captionMeta, helper
Tap a pick to see the full reasoning behind the AI's confidence score.
13 / 1.5
0
400
labelAll-caps eyebrow
FEATURED PICK · TONIGHT
11 / 1.4
+0.08em
600
monoStats, odds
PTS 27.5 · AST 6.5 · REB 8.5 · O/U −110
13
JetBrains Mono
500

06Spacing, radius, elevation

The current scale stops at xl: 32. Extends to 64px so screens don't reach for hardcoded margins. Numeric tokens (s-1 through s-16) are the new format — easier to scan than t-shirt sizes once you pass xl.

Spacing

s-1
4px
s-2
8px
s-3
12px
s-4
16px
s-5
20px
s-6
24px
s-8
32px
s-10
40px
s-12
48px
s-16
64px

Radius

r-xs
6px · chips, badges sm
r-sm
8px · inputs, btn-sm
r-md
12px · buttons, tabs
r-lg
16px · cards (default)
r-xl
20px · sheets
r-2xl
24px · hero cards
r-pill
pill · badges, avatars
Elevation
shadow-sm
Inputs · low cards
shadow-md
Default cards
shadow-lg
Sheets · popovers
shadow-xl
Modals
shadow-gold
Premium CTAs only

07Components

Reference renders for the components in app/src/components. Anything not shown should compose from these primitives — no new colors, no inline shadows.

Buttons

Primary uses gold on dark text — strongest CTA. Accent (red) is reserved for destructive or "place pick" moments. Avoid stacking primary buttons on the same screen.

Badges

Use soft variants on cards, solid variants in headers and chips. Confidence badges always live on the card body, never the bar.

Featured 78% conf Player prop ✓ Won 🔥 92% Pro

Confidence ring

Color follows confidence: gold-400 baseline, fire above 75%, neutral n-400 below 40%. Replaces the hardcoded #FF415B default in ConfidenceRing.jsx.

78%
baseline
92%
on fire
34%
low

Pick card · light + dark

The 4px top bar matches the confidence band. Avatar tint is red-50 in light, red-900 @ 18% in dark.

LJ
LeBron James vs DEN
Over 27.5 points
Apr 25 · NBA
🔥 92%
JT
Jayson Tatum vs MIA
Over 8.5 rebounds
Apr 24 · NBA
✓ Won

Form field

Focus ring is gold-400 at 18% opacity. Labels use the label type token. Errors swap the border to danger and add helper text below.

Tab selector

The current TabSelector fills the active pill with colors.accent (red). For the Predictions tab specifically, swap to gold-400 on dark text — it reads as "AI-suggested" instead of "destructive."

All
NBA
NFL
MLB
NHL
Active
Won
Lost

Progress bar

Fill is gold-400, track is n-200 (light) / n-700 (dark). The current default of gray700 as a track in light mode is too dark — fix below.

Building step

Pending → current → completed. Currently interpolates pending → accent (red) → success. Recommend pending → gold-400 → success so the brand color reads in the most-seen onboarding moment.

Analyzing matchup history
Calculating confidence scores…
Ranking player props

08Audit · what's broken today

Issues I found while reading the code. Tagged by severity. Severity reflects user-visible inconsistency, not engineering risk.

High · brand drift

Two different brand golds in production

theme/index.ts uses #E3B23C (warm, deeper). useTheme.js uses #F5A623 (oranger). The wordmark renders with the useTheme values everywhere, while the rest of the system uses theme. They visibly mismatch when both render on the same screen.

app/src/theme/index.ts:24 · app/src/hooks/useTheme.js:3
High · brand drift

Two different brand reds, too

#C62828 (theme) vs #FF415B (useTheme). The latter is closer to coral and is what the wordmark, confidence ring default, and accent fills currently render with. Recommend collapsing to red-400 (#C62828) as the canonical brand red and reserving the coral as a one-off if needed.

app/src/theme/index.ts:30 · app/src/hooks/useTheme.js:4 · ConfidenceRing.jsx:17
High · architecture

Two parallel theme systems

Static theme object (no dark mode) and dynamic useTheme() hook (with dark mode) both export overlapping shapes. Components import from whichever was nearest. Recommend merging: one useTheme() hook that returns light or dark variants of the unified token set.

theme/index.ts · hooks/useTheme.js
Medium · one-offs

Hardcoded predictions accent

const PREDICTIONS_ACCENT = "#F5A623" in predictions.jsx. Should be tokens.gold-400 — and once it is, the predictions tab and the wordmark will agree.

app/(tabs)/predictions.jsx:25
Medium · one-offs

Hardcoded fire color

const FIRE_COLOR = "#FF4500" in MyPickCard.jsx. Promote to semantic.fire (#FF6A1A) so the on-fire moment is documented brand territory.

app/src/components/MyPickCard.jsx:35
Medium · semantic drift

Four different success greens

#10B981 (useTheme), #34C759 (palette.green), #22C55E (palette.greenDark), #4ADE80 (palette.greenLight). Pick one: #16A34A, the WCAG-AA-on-white winner.

useTheme.js:54 · theme/index.ts:35-37 · MyPickCard.jsx:71 · StatsChart.jsx:47,69
Medium · scale

Spacing scale stops at xl (32px)

Screen-level layouts reach for 40 / 48 / 64 hardcoded — search the app and you'll find marginBottom: 40 in 18 places. Extend the scale to s-16 (64px).

theme/index.ts:78-84
Medium · scale

Type scale missing display + h1

AppText caps at title (24px). Onboarding hero text duplicates a fontSize: 32+ definition inline in 6+ screens. Add display and h1 variants.

app/src/components/ui/AppText.tsx:14-21
Low · polish

gray100 breaks the Tailwind ramp

Token uses #F4F4F4 while the rest of the ramp tracks Tailwind's gray. Snap to #F3F4F6 for a clean ramp.

theme/index.ts:11
Low · polish

No elevation tokens

Shadows are inlined or absent. Add shadow-sm/md/lg/xl/gold so cards and modals share the same depth language.

across components
Low · polish

Landing dark surface ≠ app dark surface

Landing uses #111827 as page bg; app dark mode uses #121212. Pick n-950 (#0B1220) for app shell base; n-900 (#111827) for cards. Now they match.

landing/app/globals.css · useTheme.js:8
Low · polish

backgroundSecondary is a beige (#D9D6CD) on light

Reads as a different brand temperature from the rest. Replace with n-100 (#F3F4F6) unless the warm beige is intentional — and if it is, make it a documented token.

useTheme.js:28

09Migration map

Drop-in replacements. Keep both exports during one release for safe rollout, then remove the legacy aliases.

TodayTomorrowNote
BRAND_GOLD #F5A623tokens.brand.gold-400 #E3B23CWordmark, predictions accent
BRAND_RED #FF415Btokens.brand.red-400 #C62828Confidence ring, accent fills
PREDICTIONS_ACCENT #F5A623tokens.brand.gold-400Delete the local const
FIRE_COLOR #FF4500tokens.semantic.fire #FF6A1APromote to documented token
colors.success #10B981tokens.semantic.success #16A34AWCAG-AA on white
palette.green #34C759tokens.semantic.success #16A34A
palette.greenDark #22C55Etokens.semantic.success #16A34ARemove
palette.greenLight #4ADE80tokens.semantic.success-soft #DCFCE7Backgrounds only
colors.error #EF4444tokens.semantic.danger #DC2626Distinct from brand red
gray100 #F4F4F4tokens.neutral.100 #F3F4F6Snap to Tailwind ramp
SCREEN_BG_DARK #121212tokens.surface.base #0B1220Match landing
backgroundSecondary #D9D6CDtokens.neutral.100 #F3F4F6Drop the beige
spacing.xl 32 (max)space-{1..16} → 64pxNumeric scale
AppText variant: title (24)+ display (56), h1 (40)Hero + onboarding

10Recommendations

What I'd do next, in order. Each step is independently shippable.

1

Collapse to one token file

Move all tokens into a single app/src/theme/tokens.ts with the structure shown here. Have useTheme() return either tokens.light or tokens.dark based on color scheme. Delete BRAND_GOLD / BRAND_RED exports; everything imports from tokens.

2

Pick one gold and one red

Recommend gold-400 #E3B23C and red-400 #C62828. They're the deeper, more print-stable values and they read as "professional sports media" — which is the right register for a paid prediction app. The orange-leaning #F5A623 reads as alert/warning when paired with red.

3

Codify the on-fire moment

The 75%+ confidence band is a brand moment — make it deliberate. Lift FIRE_COLOR to semantic.fire, document the threshold, and consider a subtle fire-soft gradient on the card top bar.

4

Share tokens with the landing

The landing CSS variables and the app theme drift independently. Generate globals.css from the same tokens file (Style Dictionary, or a hand-rolled build script) so the site and app can never disagree on the brand again.

5

Adopt numeric scales

Switch spacing.xs/sm/md/lg/xlspace-1/2/3/4/5/6/8/10/12/16 and keep aliases for one release. T-shirt sizes break down past xl; numbers don't.

6

Add type variants for hero copy

Onboarding screens (ai-powered, building-system, unlock-full-potential) all redefine 32–40px headlines inline. Add display and h1 to AppText and replace those inline styles.

7

Document the dark surface stack

App, landing, and cards each pick their own dark backgrounds. Lock down n-950 / n-900 / n-800 as the three surfaces — base, card, raised — and refactor.

8

Ship a Storybook (or Expo route) of this

Render every component on this page from the actual app components, not HTML. That's the only way to keep the design system honest as the codebase moves. Could live at /design-system behind a dev flag.