// OPENBOX DOCS

Design system

The real CSS variables, typography, layout, and component specs for theme authoring.

OpenBoxGL's base stylesheet uses a dark, warm game-room palette: near-black surfaces, warm off-white text, and brand orange (#f06000) as the focus/selection signal with orange-gold (#e08a3c) for launch actions. This page documents the actual variables shipped in the application's static/app.css (linked from index.html), not a separate token layer. Theme authors override these variables in a CSS file imported through Themes.

Base palette

All colors are dark by default. Neutral surfaces carry most of the screen; the orange family identifies focus, selection, and launch action.

Neutral surfaces

TokenValueUse
--bg#11100ePage canvas, deepest fullscreen surfaces
--topbar#171513Command rail background
--panel#1b1916Sidebar, detail pane, dialogs, lifecycle surfaces
--surface-deep#141311Sticky library header and tools menu background
--surface-card#211e1aDetail cards, emulator items, result rows, history items
--surface-field#27231eForm controls, secondary buttons
--surface-hover#342d23Hovered raised panels
--border-control#4b4338Control borders
--border-card#534a3dCard and dialog borders
--line#3d3932Borders, dividers, separator rules
--text#f4efe6Primary titles, high-priority content
--muted#aaa094Metadata, labels, helper copy, inactive navigation

Accent colors

TokenValueUse
--focus#f06000Focus ring, selected controls, active platform markers
--brand#f06000Brand identity color
--active#f06000Active navigation, selected cover borders, Big Box cover borders
--action#e08a3cLaunch action (Play button), primary dialog confirmations
--action-ink#1c160dDark text inside the orange-gold action surface
--white#fffOccasional pure white highlights
--cyan#72c9d4Teal accent (defined in :root)
--gold#e5b65cAchievement/gold signal, decorative highlights
--rating#ef8c38Rating star signal
--launch-shadow#e08a3c44Shadow tint under the Play button
--danger#743f3fError/destructive state
--empty-action#e08a3cEmpty-state action buttons
--cover-title-start#51412dCover gradient start tone
--achievement#eaa54fRetroAchievements badge, achievement points
--lifecycle-bg#45351dSession lifecycle overlay background
--bigbox-bg#30261aBig Box cover backdrop
--bigbox-copy#d0c0a5Big Box secondary text
--overlay-insight-cell-0#1c1915Play Insights 0-playtime cell background
--overlay-insight-cell-1#4a2c0aPlay Insights level 1 playtime cell background
--overlay-insight-cell-2#8a4f10Play Insights level 2 playtime cell background
--overlay-insight-cell-3#c97316Play Insights level 3 playtime cell background
--overlay-insight-cell-4#f06000Play Insights level 4 (peak) playtime cell background
--border-insight#3d3932Play Insights heatmap and stat card borders
--shadow-insight#00000066Play Insights card shadow
--surface-insight-card#1b1916Play Insights card surface background
--focus-ring#f06000Global accessible focus outline

Typography

The base stack is ui-sans-serif, system-ui, sans-serif (no bundled webfont in the base stylesheet). Font sizes come from --font-* variables.

TokenValueUse
--font-micro10pxTiny counters, badge numbers
--font-label12pxSection labels, field labels, nav categories
--font-meta12pxRelease year, region tags, auxiliary metadata
--font-body-small13pxInline hints, status chips
--font-body14pxDefault application copy, form content
--font-action15pxButton labels, chip text
--font-dialog16pxDialog body
--font-title-large18pxGame card titles
--font-heading24pxSection headings
--font-brand0.9375remApp title strip, breadcrumbs
--font-nav0.8125remTopbar menu items, sidebar row labels
--font-subtitle19pxDialog section headers
--font-bigbox21pxBig Box body
--font-panel-title28pxBig Box panel headers
--font-bigbox-displayclamp(38px,5vw,72px)Big Box headings
--font-screensaverclamp(44px,8vw,110px)Screensaver statements

The lifecycle overlay uses a hardcoded clamp(34px,6vw,78px) rather than a --font-* variable.

Stock themes override the design tokens in their own :root blocks. Do not assume a bundled webfont or a theme-specific font family; the base stylesheet's available system stack remains the typography fallback unless the running source explicitly declares another stack.

Spacing

The base :root exposes a spacing scale — --space-* (--space-3xs through --space-2xl), --gap-* (--gap-micro, --gap-tight, --gap-default, --gap-loose, --gap-spacious), --stack-* (--stack-xs/sm/md), and --leading-* (--leading-tight/normal/relaxed) — plus --inset-* and --measure-*. Rules consume these tokens (e.g. padding: var(--space-2xs) var(--space-xs)); when you add custom CSS, derive spacing from these scale tokens rather than inventing new units.

Rounded corners

Four --radius-* base tokens exist, alongside *-radius component tokens (e.g. --moments-radius, which aliases --radius-cover). Other radii are hardcoded in the rules that use them.

TokenValueApplies to
--radius-hairline2pxThin decorative lines, narrow separators
--radius-cover5pxGame cover art containers
--radius-panel12pxBig Box panels, large panels
--radius-pill-large28pxBig Box play/pill buttons

Hardcoded values in the base: inputs use 4px (some 3px), cards and detail panels use 6px to 8px, dialogs use 8px, the Play button uses 18px (pill).

Layout

The shell is a three-column workspace with responsive breakpoints:

BreakpointSidebarLibraryDetail Pane
Default190pxminmax(520px, 1fr)410px
≤ 1100px150px1fr340px
≤ 760pxStacked above libraryStackedStacked below grid

Cover grid

Auto-filled columns with a minimum cover width of 132px (105px at ≤ 1100px, 100px at ≤ 760px). Covers keep the aspect ratio of each image: portrait, square, and landscape box art all render uncropped. Games without artwork fall back to a portrait 0.72 box with the title centered. The default grid gap is 20px 16px (row then column); the sticky header keeps collection title, sort, image group, and view actions visible while the grid scrolls.

Big Box compositions

LayoutComposition
StageOne large cover + description + big Play button; controller footer hints
HybridPlatform rail on left + games + scoped search field
CoverFlowHorizontal jewel-case cover strip; active cover straightens and scales

Shadows

Depth comes from tonal layering first, tokens second. The real token values in the base stylesheet (ADR 0004):

TokenValueRole
--shadow-cover#0006Default card and box art elevation shadow
--shadow-cover-strong#0007Hovered and raised card shadow
--shadow-cover-selected#000aSelected and focused card shadow
--shadow-dialog#000cModal dialog backdrop shadow
--shadow-elevated#0009Drawer and floating menu elevation
--shadow-empty#0005Empty-state container shadow
--accent-ghost#f0600055Strong translucent focus halo
--accent-ghost-soft#f0600044Medium focus glow
--accent-ghost-faint#f060002eSubtle focus glow
--overlay-backdrop#050403d9Dialog and modal screen overlay
--overlay-backdrop-strong#05070bd9Darkened backdrop overlay
--overlay-backdrop-soft#05070bccSoft dimmed background overlay
--overlay-screensaver-start#05070bcfScreensaver top gradient
--overlay-screensaver-mid#05070b1fScreensaver middle gradient
--surface-sheet#171513ddTranslucent bottom sheet surface
--border-sheet#ffffff55Sheet border highlight
--hero-scrim#0d0b0815Hero banner gradient tint

Component specs

Buttons

VariantBackgroundText colorRadiusPadding
Launch (Play)var(--action) → #e08a3cvar(--action-ink) → #1c160dPill (18px)10px
Primaryvar(--action) → #e08a3cvar(--action-ink)4px9px 16px
Secondaryvar(--surface-field) → #27231evar(--text)4px(rule-specific)

Hover shifts secondary toward a lighter raised surface. Focused controls gain a 2px focus border (var(--focus)) plus a one-pixel ring.

Chips and badges

Rating, status, and ESRB chips use muted text on card surfaces. Achievement signals use --achievement (#eaa54f). Badges stay subordinate to cover art and the launch action.

Cards

ElementRadiusPaddingBackground
Cover cardvar(--radius-cover) (5px)12pxGradient (#51412d → #1b1814) or raised surface
Detail card6px to 8px12pxvar(--surface-card)
Big Box panelvar(--radius-panel) (12px)24px to 26pxvar(--bigbox-bg) / var(--panel)

Inputs / Fields

Fields use --surface-field background, --border-control border, 4px radius, and 7px 8px padding. On focus, the border shifts to --focus (#f06000) and gains a one-pixel ring. Disabled actions reduce opacity and show not-allowed cursor. No separate error palette exists in the base system; errors reuse the standard flow with a textual message rather than a colored field.

Topbar items are borderless and transparent at rest, gaining a raised background on hover. Active platform rows use a darker panel tone with a small orange marker dot. Inactive navigation is muted and transparent. The rail scrolls horizontally; the workspace columns tighten at 1100px and stack below 760px.

Big Box specifics

Big Box reuses the palette and typography tokens, then scales them:

  • Titles use --font-bigbox-display (clamp 38-72px) and the screensaver uses --font-screensaver (clamp 44-110px).
  • Controller footer hints use the --font-label weight mapped to the currently pressed button.
  • Jewel-case perspective on CoverFlow uses CSS transforms, not asset files.
  • Background music (library_music setting path) plays under covers; video_bgm_mix lowers volume when video audio is present.

Light theme note

Harbor Light is the only bundled light theme. It overrides --bg, --panel, --line, --text, --muted, --cyan, --accent, and --danger with paper tones and additionally defines theme-local names like --green that are not part of the base :root contract. Note --accent is part of the base contract: it defaults to var(--active) and is consumed for focus rings, skeleton shimmer, and --mood-secondary. If you author a light theme, ensure --focus, --active, --action, and --action-ink remain legible against your light surfaces and that text contrast stays at least 4.5:1.

Token contract

The :root block in static/app.css is the theme contract. Every stock theme overrides :root and (almost) nothing else:

  • themes/Cinema Marquee.css
  • themes/Harbor Light.css (the only bundled light theme)
  • themes/High Contrast.css (added in 1.15.0; dark, pure-black base)
  • themes/Midnight Circuit.css
  • themes/Nordic Mist.css
  • themes/Phosphor Terminal.css

scripts/check_tokens.py enforces the contract in CI: raw hex outside :root must stay at the ratcheted baseline of 0. A new visual value means a new :root token plus its entry in each of the six theme files. For the full per-token table, read the :root block in static/app.css in the repository you are running — this page documents the palette groups, not a frozen count of names.

Feature token families (introduced in v1.9.0)

  • Mood Match: --mood-primary, --mood-ink, --mood-secondary (aliases --accent), --mood-glow (aliases --accent-ghost), --mood-tint, --mood-transition. Driven live from the selected cover when mood_match_enabled / mood_match_bigbox are on.
  • Constellation: --constellation-edge-series, --constellation-edge-developer, --constellation-edge-publisher, --constellation-edge-genre, --constellation-edge-platform_family, --constellation-edge-co_played.
  • Mastery Map: --mastery-never, --mastery-played, --mastery-beaten, --mastery-completed, --mastery-mastered.
  • --accent: defined by the base stylesheet as var(--active) and consumed for focus rings, skeleton shimmer, and --mood-secondary — themes may override it directly.
  • Themes for the six stock CSS themes and the import workflow
  • How OpenBoxGL works for how tokens render at runtime
  • static/app.css in the application repository for the authoritative :root block