Skip to content

Design System

PlayPalz has a real design system with a dark, neon-on-void aesthetic. Use the tokens; do not hard-code values.

Canonical reference: dev-notes/playpalz-design-system.html — open it in a browser for the full visual specification. The tokens in apps/mobile/theme/ are its implementation.

That file is not in the repository

dev-notes/ is listed in .gitignore, so a fresh clone does not include the design system specification. Ask a teammate for a copy, or move it somewhere tracked — this is the kind of document that should reach every new developer automatically.

Importing

ts
import { colors, tokens, typography, useTheme } from "@/theme";

theme/index.ts re-exports everything, so one import is enough.

Colour

Six raw ramps, then a semantic layer built on top of them. Use the semantic names in components; reach for a raw ramp only for a genuine one-off.

Raw ramps

RampRoleKey value
aVoidDark surfaces, borders950 #0B0E1A app background
ghostText and neutrals50 #FFFFFF
electricVioletPrimary brand500 #7C4DFF
neonCyanAccent500 #00BCD4
flameWarm accent500 #FF6B1A
goldHighlights, rewards500 #FFC800

The aVoid ramp is layered by depth, which is what gives the UI its sense of elevation:

950  #0B0E1A   app background
900  #0F1225   cards
800  #141830   inputs
700  #1A1F3D   hover
600  #222849   borders
500  #2D3460   dividers

Semantic tokens

ts
colors.background.default   // aVoid[950]
colors.background.subtle    // aVoid[900]
colors.background.card      // aVoid[800]

colors.text.primary         // ghost[50]
colors.text.secondary       // ghost[200]
colors.text.tertiary        // ghost[300]
colors.text.placeholder     // ghost[500]
colors.text.link            // neonCyan[500]

colors.border.default       // aVoid[600]
colors.border.strong        // aVoid[500]
colors.border.focused       // electricViolet[500]

colors.primary.default      // electricViolet[500]
colors.primary.hover        // electricViolet[400]
colors.primary.pressed      // electricViolet[600]
colors.primary.disabled     // electricViolet[800]

colors.accent.default       // neonCyan[500]

colors.status.success       // #34D399
colors.status.error         // #F87171
colors.status.warning       // #FBBF24
colors.status.live          // #EF4444

status.live is its own colour, distinct from error, precisely because a live badge and an error must never be confused.

Typography

Two fonts, with a clear division of labour:

FamilyTokenUsed for
Chakra Petchfonts.displayDisplay, headings, stats, nav labels, branding
Outfitfonts.bodyBody copy, UI text, form fields, captions

Chakra Petch's angular, technical feel carries the gaming identity; Outfit stays readable at small sizes. Do not swap them.

Scale

TokenSize
xs11
sm13
base15
md17
lg20
xl24
2xl30
3xl40
4xl52
5xl72

Weights light 300 → bold 700. Line heights tight 1.1 → loose 1.7.

Loading

Fonts are loaded in app/_layout.tsx via useFonts(fontLoadMap), and the splash screen is held until they resolve — so text never flashes in a fallback face. Adding a weight means adding it to theme/fontLoadMap.ts.

Spacing

A 4pt base unit:

ts
space = { 1: 4, 2: 8, 3: 12, 4: 16, 6: 24, 8: 32, 12: 48, 16: 64, 24: 96 }
ts
style={{ padding: tokens.space[4], gap: tokens.space[2] }}

Radius

ts
radius = { sm: 6, md: 12, lg: 20, xl: 28, full: 999 }

Shadows and glows

shadow.sm / md / lg are cross-platform (iOS shadow props plus Android elevation).

Glows are the signature of the aesthetic — shadows tinted with a brand colour rather than black:

ts
glow.violet   // electricViolet[500], opacity 0.35
glow.cyan
glow.flame
glow.gold

Use them for emphasis — an active tab, a live badge, a primary CTA — not everywhere. They stop meaning anything if they are on every surface.

Theme context

ts
import { useTheme, useColors, useTokens, useTypography } from "@/theme";

const { colors, tokens } = useTheme();
const colors = useColors();

ThemeProvider wraps the app in app/_layout.tsx. Prefer the hooks inside components so a future light mode works without touching every file.

Two colour modules exist

constants/colors.ts predates theme/colors.ts

Both are in use, sometimes in the same file — app/(app)/(tabs)/_layout.tsx imports Colors from constants and colors from theme. theme/ is the fuller system and the one the design specification maps to. Prefer it, and migrate constants/ usages as you touch files. Do not add new values to constants/.

Rules

  1. Never hard-code a hex value. If a colour is missing, add it to the ramp.
  2. Never hard-code spacing. Use tokens.space.
  3. Use semantic colours in components. Raw ramps are for exceptions.
  4. Respect the font split. Chakra Petch displays, Outfit reads.
  5. Check the HTML spec firstdev-notes/playpalz-design-system.html shows the intended component treatments, not just the values.

Internal documentation — PlayPalz platform