Home / Docs / Developer docs / Design system and tokens

Design system and tokens

ModScrim looks like a practice ground, not a gaming app: wall green and chalk for the field, a hand-hung scoreboard for results, and a coach's whistle for the mark. This page explains the tokens, the contrast gate that protects them and how to use the brand kit. The complete rules are brand/DESIGN.md, written for people and for AI coding agents.

Developer docs. You want to read, run, deploy or change the code. Not what you came for? Find the right docs.

The idea

  • The practice field. Wall green is the ground, chalk is the type and the pitch markings.
  • The manual scoreboard. Results sit on dark green boards with hung number plates. Numbers are big and condensed; leading zeros are blank plates.
  • The whistle. The mark is a coach's whistle with chat's typing dots inside. Whistle orange is the one action colour.
  • Practice is always labelled. Card yellow marks anything that is practice.

Colours

RoleTokenHexUse
Wall greenneutral, background-green, text#123D2EHero, nav, closing band; ink on chalk
Dugoutsurface-green#0B2A1FPractice chat and coach's notes panels
Scoreboardscoreboard#082319Scoreboards and linescores
Plateplate#103528Number plates
Chalkbackground, text-on-green#EEF1E8Light ground; text on green
Papersurface#FFFFFFCards on chalk
Whistle orangeprimary#FF6A1FThe mark, the main button, raid numbers, Slipped chips
Orange textprimary-text#B33D00Orange as text on chalk
Card yellowsecondary#FFD23FPractice labels, Foul chips, focus ring on green
Mosstext-muted#4E6358Secondary text on chalk

Outcomes each have one meaning and always carry a word: Caught (green chip), Slipped (orange chip) and Foul (yellow chip, the yellow card). There is no Twitch purple anywhere.

Type

FaceUsed for
Big Shoulders (weight 800)Headlines, scoreboard numbers, drill-plan times
Atkinson Hyperlegible NextBody text, buttons, navigation, labels
Atkinson Hyperlegible MonoThe practice chat and blocked terms only. It keeps look-alike characters such as v1ews and f0ll0w distinct, which is exactly the trick raid spam relies on

All three are self-hosted as variable woff2 in brand/fonts/ (copies in app/public/fonts/ and site/fonts/) with their SIL Open Font Licence files.

Where the tokens live

FileRole
design/tokens.css, tokens.json, palette.jsonGenerated by the bundled colour skill from the brand colours. Do not hand-edit; re-run the skill
design/tokens.override.cssEvery ModScrim deviation from the generated baseline: semantic roles mapped onto the brand primitives, measured hover fills, the action border
site/css/tokens.css, site/css/tokens.override.cssCopies for the website, because Cloudflare Pages publishes only site/
app/src/main.tsxImports the two design/ files for the app

Semantic variables such as --color-bg, --color-text and --color-action are what components use. Colour comes from tokens only.

The contrast gate

Contrast follows WCAG 2.2: body text 4.5:1, large text 3:1 and meaningful non-text 3:1. Measured pairs from the brand file include chalk on wall green at 10.6:1, moss on chalk at 5.7:1, deep green on whistle orange at 5.4:1 (button labels) and deep green on card yellow at 10.7:1.

Whistle orange as a fill on chalk is only 2.5:1, below the 3:1 non-text bar. That is the one documented exemption, and it holds only because an orange control is identified by its label and its border, not by the fill alone. Two scripts protect it:

CommandWhat it enforces
npm run contrast:checkThe generated tokens pass the validator with no exemptions, and the shipped palette fails on exactly the documented pairs (orange on chalk, orange on white), no more and no fewer
npm run lint:tokensAny rule that fills itself with the action colour must also declare --color-action-border or --color-border-strong
npm run site:tokens:checksite/css/tokens*.css still match design/ (run npm run site:tokens to copy)
npm run site:coloursEvery colour hand-written in site/ is a token or listed in brand/DESIGN.md

All four run in CI. To change a colour, change the override file and re-run the gate; do not widen the exemption without recording why.

The brand kit

FolderContents
brand/logo/svg, pngmodscrim-<lockup>-<tone>. Lockups: horizontal, horizontal with tagline, stacked, mark, small mark, wordmark. Tones: color for light grounds, reverse for green grounds, green, white, black. Plus app icons. All SVG text is outlined
brand/socialOpen Graph image, GitHub preview, Devpost thumbnail, YouTube thumbnail, X header, LinkedIn banner, avatars
brand/streamOBS overlays: the practice-drill bar, the scorecard, and a Twitch panel
brand/fontsThe three typefaces and their licences
brand/ModScrim-brand-sheet.pngOne-page overview

The README banner, docs/assets/banner.svg, is built from the horizontal-tagline-reverse lockup and the reverse mark with the brand colours.

Logo rules. Keep at least one mark-height of clear space. Below 48 px use the small cut (no ring, no window); at 16 px drop the dots too.

Do and do not

  • Do label every screen that shows attack text as practice, in card yellow.
  • Do keep one orange button per view; second actions are outline buttons.
  • Do write plain, specific copy from the moderator's side.
  • Do not use Twitch purple, the Twitch logo, or wording that suggests Twitch made or endorses ModScrim.
  • Do not render attack text anywhere outside a labelled practice chat, including marketing screenshots.
  • Do not put orange text on chalk (use #B33D00), add gradients or glitch effects, or fade sections in on scroll. Motion answers actions, and prefers-reduced-motion turns it off.