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
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
| Ramp | Role | Key value |
|---|---|---|
aVoid | Dark surfaces, borders | 950 #0B0E1A app background |
ghost | Text and neutrals | 50 #FFFFFF |
electricViolet | Primary brand | 500 #7C4DFF |
neonCyan | Accent | 500 #00BCD4 |
flame | Warm accent | 500 #FF6B1A |
gold | Highlights, rewards | 500 #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 dividersSemantic tokens
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 // #EF4444status.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:
| Family | Token | Used for |
|---|---|---|
| Chakra Petch | fonts.display | Display, headings, stats, nav labels, branding |
| Outfit | fonts.body | Body 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
| Token | Size |
|---|---|
xs | 11 |
sm | 13 |
base | 15 |
md | 17 |
lg | 20 |
xl | 24 |
2xl | 30 |
3xl | 40 |
4xl | 52 |
5xl | 72 |
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:
space = { 1: 4, 2: 8, 3: 12, 4: 16, 6: 24, 8: 32, 12: 48, 16: 64, 24: 96 }style={{ padding: tokens.space[4], gap: tokens.space[2] }}Radius
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:
glow.violet // electricViolet[500], opacity 0.35
glow.cyan
glow.flame
glow.goldUse 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
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
- Never hard-code a hex value. If a colour is missing, add it to the ramp.
- Never hard-code spacing. Use
tokens.space. - Use semantic colours in components. Raw ramps are for exceptions.
- Respect the font split. Chakra Petch displays, Outfit reads.
- Check the HTML spec first —
dev-notes/playpalz-design-system.htmlshows the intended component treatments, not just the values.
