| name | SolidStats | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| description | Dark-only "gunmetal" tactical-operations design system for the SolidStats web frontend (TanStack Start + React). Single source of truth for design tokens; the Tailwind v4 @theme is generated from this file, never hand-maintained. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| colors |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typography |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| spacing | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rounded |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elevation |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| motion |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| layout |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| components |
|
SolidStats is the public statistics and moderation site for the SolidGames tactical-milsim community. The UI is a "tactical operations terminal": a gunmetal-ink command surface where data is the hero. It is dense, dark-only, mobile-first, and bilingual (RU + EN). It must feel instant, stable, and trustworthy before it feels decorative — the product reports, it does not sell.
This file is the single source of truth for design tokens. The production Tailwind v4 @theme,
the W3C DTCG export, and any other token artifact are generated from it — never hand-maintained in
parallel. Run the export as an explicit build step on every token change:
node scripts/gen-theme.mjs # generates src/styles/theme.css (interim generator — a dumb copy of this file)
npx @google/design.md lint DESIGN.md # structure + broken {token} refs + WCAG contrast (blocker)
npx @google/design.md diff DESIGN.md # token drift between revisions@google/design.md is used for lint/diff only; the official
export --format css-tailwind is the future migration target, but it currently drops
typography line-height, so theme.css is generated by scripts/gen-theme.mjs until that
exporter is fixed.
Add --*: initial to the emitted @theme so Tailwind's stock palette is removed and only the
SolidStats dark palette exists.
Locked direction (do not regenerate):
- Dark-only layered gunmetal surfaces — no light theme.
- One cyan signal accent, used sparingly so it stays meaningful.
- Four semantics (win / loss / unknown·conflict / info), each with
-weak+-border, never color-alone (always icon and/or label). - Saira display + IBM Plex Sans body + IBM Plex Mono tabular numerals (all carry Cyrillic).
- Sharp technical radii (2–12px), hairline borders as the primary separator, restrained shadows for floating UI only, fast functional motion.
Neutrals are blue-tinted gunmetal, not pure gray. Surfaces step up in lightness as they come forward — depth reads from the surface step + a hairline border, not from shadow.
| Token | Hex | Role |
|---|---|---|
bg-0 |
#0A0D13 |
app backdrop, deepest |
bg-1 |
#0F131C |
raised app background / sticky bars / zebra rows |
surface-1 |
#151A25 |
cards, panels (default content surface) |
surface-2 |
#1B212F |
table header, inputs, raised-in-card |
surface-3 |
#232B3B |
hover / active row |
overlay |
rgba(6,9,14,.66) |
dialog scrim |
Hairline border-1 is the primary structural separator — this is a table/ops product, so
structure comes from 1px lines + surface steps, not heavy shadow. border-2 frames focus-within and
emphasized panels.
| Token | Hex | Role |
|---|---|---|
border-1 |
#262E3D |
hairline separators |
border-2 |
#36415A |
stronger frame / focus-within |
| Token | Hex | Role | Contrast |
|---|---|---|---|
text-primary |
#EAEEF6 |
primary copy, stat readouts | 13.8–16.7:1 — AA everywhere |
text-muted |
#98A2B6 |
secondary / labels / column headers | 6.3–7.6:1 — AA everywhere |
text-subtle |
#616B80 |
tertiary / placeholder / disabled | 3.25–3.63:1 — AA-large/UI/disabled only |
fg-on-accent |
#04141A |
text on any saturated fill | 9.1–9.5:1 on cyan / win — AA |
text-subtleis NOT for body text. At 3.25:1 onsurface-1it passes WCAG 2.2 for large text (≥18.66px / ≥14px bold), UI-component boundaries, and the disabled state (exempt from contrast), but fails the 4.5:1 normal-text rule. Usetext-mutedfor any meaningful sentence; reservetext-subtlefor placeholders, disabled controls, and decorative captions only.
The single accent. It means interactive / active / brand: links, active nav, primary buttons,
focus rings, selected rows, sparkline strokes. Used sparingly so it stays meaningful. #36C5E0
clears AA on every surface (7.8–9.5:1).
| Token | Value | Role |
|---|---|---|
primary |
#36C5E0 |
default interactive |
primary-hover |
#54D3EC |
hover |
primary-active |
#27A8C2 |
press |
primary-weak |
rgba(54,197,224,.13) |
selected-row / badge tint |
primary-border |
rgba(54,197,224,.40) |
tinted frame / input focus |
Green / red / amber / blue carry the palette range so the UI is never one-note. Never color-alone
— every semantic is paired with a Lucide icon and/or a text label. Each token clears AA on dark
(loss is the floor at 5.8:1 on surface-1).
| Token | Hex | Meaning | -weak |
-border |
|---|---|---|---|---|
win |
#3FCF8E |
win / positive delta / approved | rgba(63,207,142,.14) |
rgba(63,207,142,.38) |
loss |
#FF5C6C |
loss / teamkill / danger / rejected | rgba(255,92,108,.14) |
rgba(255,92,108,.38) |
warn |
#F2B33D |
unknown / conflict / warning / stale | rgba(242,179,61,.15) |
rgba(242,179,61,.40) |
info |
#5B9DFF |
info / pending / neutral notice | rgba(91,157,255,.14) |
rgba(91,157,255,.38) |
Data-viz ramp (chart-1…5 + grid-line) is for sparklines and microcharts; chart-1/chart-2
reuse cyan/green so charts stay on-brand. Use grid-line (rgba(255,255,255,.06)) for axes, never
a hard border.
Three families, all carrying full Cyrillic for the RU/EN interface:
- Display — Saira (aerospace/HUD grotesk): headings and big stat readouts, 600/700, tight tracking.
- Body — IBM Plex Sans: engineered, highly legible UI text.
- Mono — IBM Plex Mono with tabular figures: all stats, ranks, IDs, slugs, timers, checksums — anywhere numbers must align.
The scale is px-based and dense (14px is the UI default, not 16px), ranging 2xs 11px →
5xl 64px. Weights 400/500/600/700; line-heights 1.08 (display) / 1.25 (headings) / 1.35 (dense
tables) / 1.5 (body). Uppercase labels get letterSpacing.label (0.06em); brand/overline get
letterSpacing.caps (0.12em).
Semantic roles (typography.roles.*) are the named recipes to apply — overline, label, h1–h4,
body, body-sm, caption, stat, stat-xl, mono. Headings (h1–h3) are Saira; h4, body,
and captions are IBM Plex Sans; stat/stat-xl/mono are tabular. Numbers are right-aligned in
tables, signed for deltas (+12, −3), with explicit units.
- Spacing uses Tailwind's stock 4px scale — no custom keys.
p-0.5=2px,p-1=4px,p-2=8px,p-4=16px,p-6=24px,p-8=32px. Dense defaults: 8/12/16 dominate; 24/32 separate major regions. - Two containers.
container(data/page) = 1760px ceiling, fluid below then centers — tables, leaderboards, stat grids, profiles spend the width.container-prose= 720px — reading content (request flows, moderation comments, about/help), capped for line length. This replaces the seed's old1240pxcap. - Reflow is container-driven (
@container/ the--container-*scale), not viewport — the device frames make viewport media queries lie. Setcontainer-type: inline-sizeon the content container. - Breakpoints (canonical set; defined once in
references/design-system.md, mirrored underlayout.breakpoints): design and review at 360 · 768 · 1024 · 1280 · 1920 · 2560, with 390/414 mobile spot-checks and a 3440 ultrawide cap-check. Tailwind'smd/lg/xl/2xlare kept; add--breakpoint-3xl: 120rem(1920) and--breakpoint-4xl: 160rem(2560). Large screens are first-class: ~54% of the PC-gamer audience sits at 1920 and ~40% are wider, so data surfaces use the width; ultrawide caps at 1760 + centers (turn extra width into rows/columns, not gutter). - Chrome: desktop top nav (
nav-h56px); mobile bottom tab bar (tabbar-h60px). Sticky table headers and filter toolbars. Reserve space for all async content — CLS budget ≤ 0.02.
Depth is communicated by the surface step + hairline border first, shadow second.
- Cards on dark use
border-1+ a surface step — no drop shadow. - Shadows are reserved for things that truly float: menus, popovers (
elevation.md), dialogs and toasts (elevation.lg).elevation.smis a barely-there lift for sticky bars. - Focus is always visible:
elevation.ring(2px offset ring in cyan) on every interactive control;elevation.ring-glowfor inputs. - Transparency is purposeful only:
-weaktints (13–15% alpha) for badge/row fills, theoverlayscrim behind dialogs, and an optional subtle backdrop-blur on the sticky top nav / bottom tab bar. Never blur content regions.
Sharp, technical radii — rounded.xs 2px (chips) · sm 4px (inputs, buttons) · md 6px (small
cards) · lg 8px (cards, panels) · xl 12px (dialogs, sheets). rounded.full (avatars, toggle
pills) is the only pill-soft shape. Borders are 1px. Icons are Lucide outline, 2px stroke,
currentColor, sized 16 (dense/inline) / 18–20 (buttons, nav) / 24 (section headers, empty states).
All recipes live under components.* and reference base tokens via {colors.*} / {rounded.*} /
{typography.*} / {elevation.*}.
- Buttons —
button-primary(cyan fill,fg-on-accenttext),button-secondary(surface + hairline),button-ghost(transparent). All carry hover / active (press =translateY(1px)) / focus-visible (ring) / disabled (text-subtle+ 0.6 opacity). - Badges —
badge-outcome-*(win/loss with trending icon + W/L),badge-status-*(pending/approved/rejected), andbadge-freshnesswith the four-state vocabulary Актуально / Данные устаревают / Связь потеряна / Переподключение. Every badge pairs color with a Lucide icon — never color-alone. - Card —
card(surface-1 +border-1, hover brightens toborder-2for interactive cards);card-prosecaps at the 720px reading width. - Table — sticky uppercase
table-headeronsurface-2;table-rowwithsurface-3hover and aselectedstate that combines aprimary-weakfill with an inset left-edge cyan marker (not fill-only); optionaltable-row-zebraonbg-1; numeric cells are right-aligned tabular mono. - Stat tile — display-font value (tabular),
text-mutedlabel, signed delta colored win/loss and paired with a trending icon. - Data-trust components — the trust layer is systemic, always-present, not a transient badge:
provenance-line:посчитано из N реплеев · <freshness> · Как считается, always under headline stats, with a cyan "how it's computed" link.badge-known/badge-unknown/badge-conflict: the Known / Unknown / Conflict data-trust states.Unknownis the literal amber word with acircle-helpicon — never0or—alone.Conflictis amber +triangle-alert.inline-review-row: a pending SteamID merge is a workflow footnote — a quiet inline amber row (на проверке+ request link) inside the SteamID list it describes, never a filled banner pinned to the bottom of a stretched column.
- Inputs —
surface-2fill, hairline border that brightens on hover and goesprimary-border+ring-glowon focus. - Floating UI —
dialog(rounded.xl+elevation.lg+overlayscrim),popover(rounded.lg+elevation.md).
Do
- Build depth from surface steps + hairline borders; reserve shadow for floating UI.
- Keep cyan rare — links, active state, primary action, focus, selected. If everything is cyan, nothing is.
- Pair every semantic color with a Lucide icon and/or label. Color is never the sole signal.
- Use tabular mono for every number that aligns (stats, ranks, IDs, timers); right-align numeric columns.
- Make data-trust first-class: provenance line, freshness pill, honest Known/Unknown/Conflict states.
- Spend large-screen width on data (rows/columns), keep reading content at the 720px prose width.
- Animate
transform/opacityonly, honorprefers-reduced-motion, hold the CLS ≤ 0.02 budget. - i18n-key every string (RU + EN) and sanity-check the Russian for clipped or awkward wording.
Don't
- Don't add a light theme, decorative gradients, blobs, nested cards, or emoji-as-icons.
- Don't use
text-subtlefor body text (3.25:1 — fails AA for normal text). - Don't render
Unknownas0or—; don't dress a short-lived workflow event up as "the trust layer." - Don't let a data table stretch past the 1760px ceiling on ultrawide — center it, or go master-detail.
- Don't introduce arbitrary Tailwind values or invent a custom spacing scale — stay on the stock 4px grid.
- Don't reach for shadow to separate cards, or animate layout properties (width/height/top/left/margin).
- Don't re-hardcode breakpoints or container widths elsewhere — they live in
references/design-system.md.