Reference
Design system
Every token, component, and motion utility that makes up the VGDB visual layer. The hero switcher below re-themes the entire page through one of seven console-era looks — try it.
Foundations
Colours
Seven base tokens defined in src/styles/tokens.css and overridden per era in src/styles/era-themes.css. Switch eras using the hero control above — every swatch below uses CSS variables, so they re-theme live.
Base palette
Era accent palette
Note: the admin CMS overrides the palette to a light theme via the[data-admin]selector in globals.css. It is not interactively previewable here.
Foundations
Typography
Three primary fonts loaded via next/font/google and bound to CSS variables, plus a fourth pixel font reserved for retro mode.
h1 · Playfair Display · 48px / 600
h2 · Playfair Display · 30px / 600
h3 · Playfair Display · 20px / 600
h4 · Playfair Display · 18px / 600
body · Inter · 16px / 400
small · Inter · 12px / 400
mono · JetBrains Mono · 14px / 400
Font stack
The quick brown fox
Playfair Display
--font-playfair
All h1–h4 headings via globals.css
The quick brown fox
Inter
--font-inter
Body copy, default font on <body>
The quick brown fox
JetBrains Mono
--font-mono
Spec values, code blocks, mono-font chips
The quick brown fox
Press Start 2P
--font-pixel
Retro mode only (Konami code)
Foundations
Spacing & radii
No custom spacing scale yet — the project uses Tailwind defaults. The most-used values, observed across the codebase, are listed below.
Gap: no custom spacing tokens
src/styles/tokens.css defines colour, font, and shadow tokens — but no --space-* scale. If a tighter, project-specific scale becomes desirable (e.g., for the platform-detail tab grids), define it here and migrate the most-used values to named tokens.
Suggested fix:Add a --space-{xs,sm,md,lg,xl} scale to tokens.css under @theme so the values are exposed as Tailwind utilities.
Most-used spacing
| Class | Pixels | Where |
|---|---|---|
| p-3 | 12px | Tight card padding (e.g., game card footer) |
| p-4 | 16px | Standard card padding (e.g., spec card, platform card footer) |
| p-5 | 20px | Roomy card padding (e.g., gap card body) |
| px-6 py-12 | 24px / 48px | Page container padding |
| gap-3 | 12px | Tight grid gap |
| gap-6 | 24px | Standard grid gap |
| mt-8 | 32px | Section content top spacing |
| mt-16 | 64px | Major section separation |
Border radii
| Class | Pixels | Where |
|---|---|---|
| rounded-md | 6px | Code blocks |
| rounded-lg | 8px | All cards (platform, game, spec, gap) |
| rounded-full | 9999px | Chips, badges, circular avatars |
Foundations
Elevation & borders
Two surface-treatment tokens, both defined in tokens.css. Hover lift comes from the .hover-lift animation class — see Motion.
Components
Platform / box-art cards
The hero card pattern for every platform listing. 16:10 image area with a metadata footer; lifts on hover.
<PlatformCard />
Used in /platforms, /generations, /consoles, /timeline.
Stub data — not a real platform.
bg-bg-cardborder-borderrounded-lgshadow-cardhover-liftShow JSX
import { PlatformCard } from "@/components/platform/platform-card";
<PlatformCard
platform={{
name: "Demo Console 3000",
slug: "demo-console-3000",
manufacturer: "Stub Industries",
generation: "Sixth",
release_date: "2003-09-15",
image_url: null,
category: "console",
}}
/>Components
Game cover cards
The 3:4 cover-art tile used across the games catalogue and game lists.
<GameCard />
Used in /games, top-rated carousels, list previews.
Stub data — not a real game.
bg-surface*border-borderrounded-lggrouphover:border-accentShow JSX
import { GameCard } from "@/components/game/game-card";
<GameCard
slug="untitled-demo-game"
title="Untitled Demo Game"
coverImageUrl={null}
developer="Stub Studios"
firstReleaseDate="2024-06-01"
summary="An imaginary stub game used to render the design system docs."
/>Drift: undefined bg-surface / bg-surface-alt classes
GameCard (and four other files —games-list.tsx,game-children-section.tsx,games/page.tsx,top-rated-carousel.tsx) referencebg-surface andbg-surface-alt, but neither class is defined intokens.css, the era themes, or any@themeblock. Tailwind silently emits no rule, so the cards inherit their parent's background instead. Three other files also reference an undefined--color-surface-raised with an inline fallback.
Suggested fix:Add --color-surface and --color-surface-alt tokens to tokens.css (or rename the call sites to use bg-bg-card / bg-bg-primary), and define --color-surface-raised explicitly to remove the inline fallback.
Components
Spec cards
Two-line stat tiles: uppercase tracking label, monospace value. Used across platform detail pages.
<SpecCard />
bg-bg-cardborder-borderrounded-lgp-4font-monoShow JSX
import { SpecCard } from "@/components/ui/spec-card";
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3">
<SpecCard label="CPU" value="MIPS R3000A · 33.87 MHz" />
<SpecCard label="RAM" value="2 MB" />
<SpecCard label="Released" value="1994-12-03" />
<SpecCard label="Generation" value="Fifth" />
</div>Components
Chips & badges
Pill-shaped generation labels with active and inactive states. Hover-bounce on interaction.
<GenerationChip /> — all generations
rounded-fullpx-2.5py-1text-[10px]hover-bounceShow JSX
import { GenerationChip } from "@/components/ui/generation-chip";
<GenerationChip generation="Sixth" />
<GenerationChip generation="Sixth" active /><GenerationChip active />
Active state: amber background, accent text.
Show JSX
<GenerationChip generation="Sixth" active />Components
Page headers
The canonical eyebrow + h1 + subtitle pattern used by every top-level public page.
<PageHeader />
Reference
Section heading
A short paragraph that sets context for the page.
text-accentfont-playfairtext-4xluppercase tracking-[2px]Show JSX
import { PageHeader } from "@/components/ui/page-header";
<PageHeader
eyebrow="Reference"
title="Section heading"
subtitle="A short paragraph that sets context for the page."
/>Components
Empty states
Centred icon + message + submessage placeholder for tab panels and lists with no content.
<EmptyTabState />
No reviews yet
Be the first to share your thoughts.
flexitems-centerjustify-centerpy-16opacity-30Show JSX
import { EmptyTabState } from "@/components/platform/empty-tab-state";
<EmptyTabState
icon={<span aria-hidden="true">📦</span>}
message="No reviews yet"
submessage="Be the first to share your thoughts."
/>Components
Loaders
Era-aware pixel sprite loader. The sprite and accent colour change based on the EraKey passed in — try the era switcher above and watch the sprite transform.
<PixelLoader />
Currently rendering the "modern" sprite (matches the hero switcher).
Show JSX
import { PixelLoader } from "@/components/ui/pixel-loader";
import type { EraKey } from "@/lib/era-theme";
<PixelLoader era="modern" />Components
Icons
Icons live as raw SVG files under src/assets/icons/, organised by category. There is no central <Icon /> component — each icon is imported individually at the use site.
Gap: no central <Icon /> component
Icons are imported per-file as static asset URLs (e.g.,import logo from "@/assets/icons/game_state/ico_state_playing_active.svg"), which works but provides no autocomplete, no centralised sizing, no tree-shaking visibility, and no consistentaria-label story.
Suggested fix:Create src/components/ui/icon.tsx with a typed name prop backed by a single import map. Use SVGR (next/svgr) so icons render as inline SVG and inherit currentColor.
Folder structure under src/assets/icons/:
add/arrow/badges/collections/common/community/editor/filter/followers_game/followers_user/game/game_state/kudos/leaderboard/leaderboard-points/login/media/menu/notification/platforms/play/report/review/screenshot/services/share/social/sso/video/Note: rendering every icon inline would require static imports for hundreds of files and inflate the bundle. The folder list above is the canonical reference; browse the files directly in the repository.
Motion
Animations & motion
Two custom easings and a small set of hover utilities and keyframes. Defined in src/styles/animations.css and respected by the global prefers-reduced-motion media query.
Hover utilities
Hover me
Hover me
Hover me
Easing functions
--ease-spring
cubic-bezier(0.34, 1.56, 0.64, 1)
Snappy overshoot — used by hover-lift, hover-bounce, hover-glow.
--ease-spring-gentle
cubic-bezier(0.22, 1.2, 0.36, 1)
Softer overshoot — used by scroll-reveal entrances.
Keyframes (consumed via inline animation styles)
| Name | Used by |
|---|---|
| @keyframes bounce-in | Scroll-reveal entrance: scale + rotate |
| @keyframes fade-up | Scroll-reveal entrance: translateY + opacity |
| @keyframes bar-grow | Spec bar width animation in compare arena |
| @keyframes glow-pulse | Winner glow on compare arena |
| @keyframes pixel-assemble | Pixel loader sprite assembly |
| @keyframes pixel-pulse | Pixel loader gentle scale |
| @keyframes nav-dropdown-in | Nav dropdown menu entrance |
All hover utilities and entrance animations are disabled inside@media (prefers-reduced-motion: reduce)— see the bottom of src/styles/animations.css.
Theming
Era themes
Seven console-era themes defined in src/styles/era-themes.css. Applied via a data-era attribute on any wrapper element. Each row below is forced into its own era — use the hero switcher to re-theme the entire page above.
1st–2nd
Pre-8-bit
data-era="pre8bit"#b45309Warm amber, off-white card, CRT scanline overlay.
4th
16-bit
data-era="16bit"#7c3aedJewel purple accent, lavender card, saturated palette.
5th
32-bit
data-era="32bit"#c2410cWarm orange, warm grey card, transitional warmth.
6th–7th
128-bit
data-era="128bit"#2563ebCool blue accent, brushed-metal gradient card.
8th–9th
Modern
data-era="modern"#818cf8Indigo, dark glassmorphic card with backdrop blur.
Portable
Handheld
data-era="handheld"#059669Sage green, light card, compact max-width.
Easter egg
Retro mode
A hidden Game Boy palette + Press Start 2P type theme. Activated on the live site by entering the Konami code (↑ ↑ ↓ ↓ ← → ← → B A). The hero retro toggle above flips it for the whole page.
Scoped preview (always-on, regardless of hero toggle)
Demo Console
ALL YOUR BASE
Or toggle retro mode on the whole page using the toggle in the hero ↑.
Appendix
References & next steps
Where to look in the codebase, and what's still missing.
Source files
src/styles/tokens.css— Base design tokens (colours, fonts, shadow)src/styles/era-themes.css— Per-era CSS variable overrides (7 eras)src/styles/animations.css— Hover utilities, easings, keyframessrc/styles/retro-mode.css— Konami easter egg stylessrc/lib/era-theme.ts— EraKey type and getEraKey() helperdocs/frontend/styling-and-theming.md— Long-form styling reference (markdown)
Open gaps (jump to)
Want to add to the design system? Pick a gap above, send a PR that fills it, and update the corresponding section on this page.