---
version: alpha
name: Twitch
description: Live-streaming's signature purple, weaponized. Twitch builds its whole identity on one electric violet (#9147ff) that fills the primary button, tints every link, and washes the footer banner, set against a near-black app canvas and crisp white marketing pages with a tight Roobert-grotesque headline and Inter UI labels.
colors:
  # Surface / canvas
  background: "#ffffff"          # marketing / account canvas
  surface: "#efeff1"             # muted section bands, resting card chrome
  surface-dark: "#0e0e10"        # the app canvas — near-black streaming UI
  surface-elevated: "#18181b"    # raised dark panels, side rail

  # Ink / text
  ink: "#0e0e10"                 # near-black headings and body on light
  ink-secondary: "#3b3b44"       # supporting copy, muted nav labels
  ink-muted: "#adadb8"           # captions, metadata, placeholder
  on-dark: "#ffffff"             # text on dark surfaces and the purple CTA

  # Brand accent — the singular Twitch purple
  primary: "#9147ff"             # primary CTA, the brand violet
  primary-hover: "#772ce8"       # button hover / pressed
  primary-strong: "#5c16c5"      # deep purple links, logo, footer banner
  primary-soft: "#bf94ff"        # light purple tint, dark-mode accents
  on-primary: "#ffffff"

  # Secondary accents
  link-blue: "#1f69ff"           # informational links, secondary action
  live-red: "#eb0400"            # the LIVE indicator dot and badge

  # Borders
  border: "#dedee3"              # hairline dividers on light
  border-strong: "#adadb8"       # input outlines, stronger separators

  # Shadow tint (opaque approximation for the elevation table)
  shadow-ambient: "#000000"      # was rgba(0,0,0,0.22) — Google format requires hex

typography:
  display:
    fontFamily: "Roobert, Inter, Helvetica Neue, Arial, sans-serif"
    fontSize: 48px
    fontWeight: 700
    lineHeight: 1.05
    letterSpacing: -0.96px
  heading:
    fontFamily: "Roobert, Inter, Helvetica Neue, Arial, sans-serif"
    fontSize: 28px
    fontWeight: 600
    lineHeight: 1.15
    letterSpacing: -0.28px
  heading-sub:
    fontFamily: "Roobert, Inter, Helvetica Neue, Arial, sans-serif"
    fontSize: 18px
    fontWeight: 500
    lineHeight: 1.1
    letterSpacing: -0.18px
  body-large:
    fontFamily: "Inter, Roobert, Helvetica Neue, Arial, sans-serif"
    fontSize: 18px
    fontWeight: 400
    lineHeight: 1.5
    letterSpacing: 0px
  body:
    fontFamily: "Inter, Roobert, Helvetica Neue, Arial, sans-serif"
    fontSize: 14px
    fontWeight: 400
    lineHeight: 1.4
    letterSpacing: 0px
  label:
    fontFamily: "Inter, Roobert, Helvetica Neue, Arial, sans-serif"
    fontSize: 14px
    fontWeight: 600
    lineHeight: 1.4
    letterSpacing: 0px
  caption:
    fontFamily: "Inter, Roobert, Helvetica Neue, Arial, sans-serif"
    fontSize: 12px
    fontWeight: 400
    lineHeight: 1.4
    letterSpacing: 0px
  overline:
    fontFamily: "Inter, Roobert, Helvetica Neue, Arial, sans-serif"
    fontSize: 12px
    fontWeight: 600
    lineHeight: 1.4
    letterSpacing: 0.5px
  micro:
    fontFamily: "Inter, Roobert, Helvetica Neue, Arial, sans-serif"
    fontSize: 10.5px
    fontWeight: 600
    lineHeight: 1.3
    letterSpacing: 0.5px

spacing:
  xs: 4px
  sm: 8px
  md: 10px
  lg: 16px
  xl: 20px
  2xl: 24px
  3xl: 40px
  4xl: 55px

rounded:
  sm: 2px
  md: 4px
  lg: 6px
  xl: 8px
  pill: 9999px

components:
  button-primary:
    backgroundColor: "{colors.primary}"
    textColor: "{colors.on-primary}"
    typography: "{typography.label}"
    rounded: "{rounded.md}"
    padding: 8px 16px
  button-primary-hover:
    backgroundColor: "{colors.primary-hover}"
    textColor: "{colors.on-primary}"

  button-secondary:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink}"
    typography: "{typography.label}"
    rounded: "{rounded.md}"
    padding: 8px 16px
  button-secondary-hover:
    backgroundColor: "{colors.border}"
    textColor: "{colors.ink}"

  button-dark:
    backgroundColor: "{colors.surface-dark}"
    textColor: "{colors.on-dark}"
    typography: "{typography.label}"
    rounded: "{rounded.md}"
    padding: 8px 16px

  card:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"
    rounded: "{rounded.lg}"
    padding: 16px
  card-dark:
    backgroundColor: "{colors.surface-elevated}"
    textColor: "{colors.on-dark}"
    rounded: "{rounded.lg}"
    padding: 16px

  input:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"
    typography: "{typography.body}"
    rounded: "{rounded.xl}"
    padding: 4px 12px
  input-focus:
    backgroundColor: "{colors.background}"
    textColor: "{colors.ink}"

  nav-bar:
    backgroundColor: "{colors.surface-dark}"
    textColor: "{colors.on-dark}"
    typography: "{typography.label}"
    padding: 10px 16px

  link:
    textColor: "{colors.primary-strong}"
    typography: "{typography.body}"
    padding: 0px
  link-hover:
    textColor: "{colors.primary}"
    typography: "{typography.body}"

  badge-live:
    backgroundColor: "{colors.live-red}"
    textColor: "{colors.on-dark}"
    typography: "{typography.micro}"
    rounded: "{rounded.sm}"
    padding: 2px 6px

  tag:
    backgroundColor: "{colors.surface}"
    textColor: "{colors.ink-secondary}"
    typography: "{typography.caption}"
    rounded: "{rounded.pill}"
    padding: 4px 10px
---

# Twitch Design System

## Overview

Twitch runs its entire visual identity on a single, unmissable note: an electric purple (`{colors.primary}`) that has become as synonymous with live streaming as the play triangle is with video. The whole system is built to make that violet land hard. On the white marketing and account surfaces it is the only saturated color in the room — every other element is grayscale — so the eye snaps straight to the purple "Sign Up" button and the deep-violet footer banner. Inside the app, the canvas flips to a near-black `{colors.surface-dark}` and the purple becomes the glow that picks out follows, badges, and the live channels you care about. Two backdrops, one accent, zero ambiguity about who you're looking at.

The typographic pairing is functional rather than expressive. Marquee headlines run in **Roobert**, a slightly quirky grotesque with subtly humanist terminals, set tight and heavy (`{typography.display}`, weight 700, negative tracking). Everything operational — buttons, captions, channel names, viewer counts, category tags — drops to **Inter** at small sizes, frequently at 14px/600 for labels and 12px uppercase for the metadata that blankets a streaming dashboard. It's a UI built to survive density: hundreds of thumbnails, viewer counts, and tags on a single screen, all legible because the type is small, even-weighted, and never competes with the purple.

Geometry is tight and utilitarian. Radii top out small — 4px on buttons (`{rounded.md}`), 2px on cards and tag chips (`{rounded.sm}`), with full pills (`{rounded.pill}`) reserved for status tags and avatars. There is no decorative roundness, no soft consumer bubbliness; the corners read as software chrome. Elevation is sparing and dark-tinted: a layered ambient shadow lifts hover cards and popovers off the canvas, but most of the interface separates with flat fills and hairline borders. The one piece of unmissable visual urgency is the `{colors.live-red}` LIVE dot — the only red on the page, and it means exactly one thing.

**Key Characteristics:**
- One signature accent: electric Twitch purple (`{colors.primary}`) fills the primary CTA, the logo, and the footer banner — the brand's entire color identity
- Dual canvas: crisp white (`{colors.background}`) for marketing/account, near-black (`{colors.surface-dark}`) for the streaming app
- **Roobert** grotesque for headlines (tight, heavy, slightly negative tracking); **Inter** for all UI labels and metadata
- Dense, small-type UI — 14px/600 labels, 12px uppercase overlines — built to carry hundreds of tiles per screen
- Tight, software-grade radii: 4px buttons (`{rounded.md}`), 2px cards (`{rounded.sm}`), pills only for status tags
- The `{colors.live-red}` LIVE indicator is the only red — pure functional urgency, never decoration
- Grayscale everywhere except the purple: the accent does all the signaling work
- Layered ambient shadows reserved for hover cards and popovers; the rest is flat fills + hairlines
- Secondary `{colors.link-blue}` for informational links keeps the purple exclusively for brand and primary action

## Colors

### Surface & Canvas
- **White** (`{colors.background}`): The marketing and account canvas — the surface where the purple CTA pops hardest.
- **Whisper Gray** (`{colors.surface}`): Muted section bands and resting secondary-button / tag fills.
- **App Black** (`{colors.surface-dark}`): The near-black streaming-app canvas and the global nav bar.
- **Elevated Black** (`{colors.surface-elevated}`): Raised dark panels and the side channel rail.

### Ink / Text
- **Near-Black** (`{colors.ink}`): Headings and body on light surfaces. Not pure black.
- **Slate** (`{colors.ink-secondary}`): Supporting copy and muted navigation labels.
- **Muted Gray** (`{colors.ink-muted}`): Captions, metadata, and input placeholders.

### Brand Accent
- **Twitch Purple** (`{colors.primary}`): The brand. Primary CTA fill, badges, active states.
- **Purple Hover** (`{colors.primary-hover}`): The button hover and pressed state.
- **Deep Purple** (`{colors.primary-strong}`): Logo, text links, and the saturated footer banner.
- **Soft Purple** (`{colors.primary-soft}`): Light tints and dark-mode accent text.

### Secondary & Status
- **Link Blue** (`{colors.link-blue}`): Informational links and secondary actions — keeps purple reserved.
- **Live Red** (`{colors.live-red}`): The LIVE dot and badge — the only red, strictly functional.

### Borders & Shadow Tint
- **Hairline** (`{colors.border}`) / **Strong Border** (`{colors.border-strong}`): Dividers and input outlines.
- **Ambient** (`{colors.shadow-ambient}`): Opaque stand-in for the layered hover-card shadow (originals were rgba — flattened for the Google spec).

## Typography

### Font Family
- **Display**: `Roobert` — a grotesque with subtly humanist terminals, set heavy and tight for headlines. Fallbacks `Inter, Helvetica Neue, Arial`.
- **UI / Body**: `Inter` — carries every button, caption, channel name, viewer count, and tag. Even-weighted and legible at very small sizes.
- **Tracking**: negative on display (-0.96px at 48px); near-zero on body; slight positive (0.5px) on uppercase overlines.

### Hierarchy

The full type scale lives in the `typography:` token block. Reference tokens directly.

| Token | Use |
|---|---|
| `display` | 48px / 700 / -0.96px — Roobert marquee headline |
| `heading` | 28px / 600 — section titles |
| `heading-sub` | 18px / 500 — Roobert sub-headings, large channel names |
| `body-large` | 18px / 400 — lead paragraphs |
| `body` | 14px / 400 — standard UI body and inputs |
| `label` | 14px / 600 — button text, nav labels |
| `caption` | 12px / 400 — metadata, helper text |
| `overline` | 12px / 600 / uppercase / 0.5px — category and section markers |
| `micro` | 10.5px / 600 / uppercase — the densest tags and the LIVE badge |

### Principles
- **Roobert displays, Inter operates**: the grotesque carries brand moments; Inter handles the dense functional layer.
- **Small and even**: a streaming UI is type-dense by nature — keep weights even and sizes compact so nothing fights the purple.
- **Uppercase for status**: tags, LIVE badges, and overlines uppercase at 12px/10.5px to read as system chrome.

## Layout

### Spacing System
The full scale is in the `spacing:` token block. Base unit: 8px with a 4px sub-step and a frequent 10px increment for component internals. Tile grids pack tightly (`{spacing.sm}`–`{spacing.lg}` gutters); marketing sections breathe wider (`{spacing.3xl}`–`{spacing.4xl}`).

### Grid & Container
- Responsive thumbnail grid: live channels, categories, and recommended streams in dense repeating tiles
- Persistent left rail of followed/recommended channels in the app; collapses on narrow widths
- Marketing pages center a single text column over a near-full-width hero

### Whitespace Philosophy
- **Density is the product**: the app deliberately packs many tiles per screen; whitespace is the gutter between cards, not luxury padding
- **Marketing breathes, app packs**: the two canvases run opposite spacing rhythms
- **The purple needs grayscale around it**: restraint everywhere else is what lets the accent carry

## Elevation & Depth

| Level | Treatment | Use |
|---|---|---|
| Flat (Level 0) | No shadow; flat fill or hairline `{colors.border}` | Tiles, nav, most app chrome |
| Inset ring (Level 1) | `inset 0 0 0 1px` at ~10% ink | Search input containment |
| Subtle (Level 2) | `0 1px 2px` at ~13% black | Resting small cards, dropdowns |
| Floating (Level 3) | `0 6px 16px` (`{colors.shadow-ambient}`) + `0 0 4px` ambient | Hover-preview cards, popovers, menus |

**Shadow Philosophy**: Twitch keeps the interface mostly flat — tiles and panels separate with fills and hairlines, not shadows. Elevation is spent on transient surfaces: the channel preview that pops on hover, dropdown menus, and overlays. Those use a soft, dark, two-part ambient shadow that reads correctly on both the white marketing canvas and the near-black app. Nothing on the resting page floats.

## Shapes

The full radius scale is in the `rounded:` token block.

| Token | Value | Use |
|---|---|---|
| `sm` | 2px | Cards, category tiles, tag chips |
| `md` | 4px | Buttons — the default interactive radius |
| `lg` | 6px | Mid containers, panels |
| `xl` | 8px | Inputs, larger cards |
| `pill` | 9999px | Avatars, status tags, follower counts |

The system is tight and utilitarian: 4px on buttons, 2px on tiles. Roundness reads as software chrome, not consumer softness. Full pills are reserved for circular avatars and status tags.

## Components

The complete component spec lives in the `components:` token block.

### Buttons
- **`button-primary`** — Twitch purple (`{colors.primary}`) fill, white text, 4px radius. The signature "Sign Up" / "Subscribe" CTA. Hover deepens to `{colors.primary-hover}`.
- **`button-secondary`** — Whisper-gray (`{colors.surface}`) fill, near-black text. Low-emphasis actions. Hover fills `{colors.border}`.
- **`button-dark`** — Near-black (`{colors.surface-dark}`) fill, white text. Used on light surfaces for inverted actions.

### Cards
- **`card`** / **`card-dark`** — Light or elevated-black surface, small radius. Channel tiles, category cards. The dark variant is the app default.

### Inputs
- **`input`** — White fill, hairline outline, 8px radius. **`input-focus`** tightens the inset ring; on dark the search rail uses an elevated fill.

### Navigation
- **`nav-bar`** — Near-black (`{colors.surface-dark}`), white Inter labels. The persistent global top bar.

### Accent & Status
- **`link`** — Deep purple (`{colors.primary-strong}`) text; hover shifts to brighter `{colors.primary}` with underline.
- **`badge-live`** — Live-red (`{colors.live-red}`) fill, white uppercase micro text — the LIVE indicator.
- **`tag`** — Whisper-gray fill, slate text, pill radius — category and metadata chips.

## Do's and Don'ts

### Do
- Make Twitch purple (`{colors.primary}`) the primary CTA fill and the brand's only saturated color
- Keep everything around the purple grayscale so the accent does all the signaling
- Use **Roobert** for headlines (tight, heavy) and **Inter** for every UI label, count, and tag
- Run a dense, small-type UI in the app — 14px/600 labels, 12px uppercase overlines
- Reserve `{colors.live-red}` strictly for the LIVE indicator — never as decoration
- Keep radii tight: `{rounded.md}` (4px) on buttons, `{rounded.sm}` (2px) on tiles
- Spend elevation only on transient surfaces (hover previews, dropdowns, overlays)
- Use `{colors.link-blue}` for informational links so purple stays the brand/action color

### Don't
- Don't add a second saturated brand color — the purple is the identity
- Don't make buttons pill-shaped (pills are for avatars and status tags only)
- Don't use red anywhere except the LIVE badge — it carries a specific meaning
- Don't loosen the dense UI into airy consumer spacing — density is the product
- Don't shadow the resting interface; separate with flat fills and hairlines
- Don't set body copy in Roobert — Inter does the operational work
- Don't dilute the purple with gradients or tints in the primary CTA — it stays solid

---

## Responsive Behavior

### Breakpoints
*(Twitch exposes several internal breakpoints; the values below are the practical stops the layout behaves to.)*

| Name | Width | Key Changes |
|---|---|---|
| Mobile | <640px | Single-column tile feed; left channel rail collapses to an icon drawer; nav becomes a hamburger; hero text scales 48px → ~30px |
| Tablet | 640–1023px | 2–3 tile columns; rail can be toggled; search collapses to an icon |
| Desktop | 1024–1279px | Full layout: persistent left rail + multi-column tile grid + top nav |
| Large | ≥1280px | Wider tile grid (more columns), max content width with generous gutters |

### Touch Targets
- Buttons run ~32–40px tall with `8px 16px` padding
- Tile tap areas are the whole card; metadata rows keep comfortable vertical padding

### Collapsing Strategy
- **Left rail**: full channel list → icon-only → fully hidden behind a toggle on mobile
- **Tile grid**: column count steps down with width; tiles never shrink below a legible thumbnail
- **Nav**: full horizontal links → hamburger; the purple "Sign Up" CTA persists longest
- **Type**: display scales down while UI labels hold at 14px for legibility

### Image Behavior
- Thumbnails dominate — fixed 16:9 stream/category art that reflows as grid children
- Avatars stay circular (`{rounded.pill}`) at every size

---

## Agent Prompt Guide

### Quick Color Reference
- Background (marketing): White (`{colors.background}`)
- Background (app): App Black (`{colors.surface-dark}`)
- Text: Near-Black (`{colors.ink}`)
- Brand accent / primary CTA: **Twitch Purple** (`{colors.primary}`)
- Secondary text: Slate (`{colors.ink-secondary}`)
- Status: Live Red (`{colors.live-red}`) — LIVE only
- Border: Hairline (`{colors.border}`)

### Example Component Prompts

- "Create a primary button: Twitch purple (`{colors.primary}`) fill, white Inter 14px/600 label, `{rounded.md}` (4px) radius, `8px 16px` padding; hover deepens to `{colors.primary-hover}`"
- "Build a live channel tile: 16:9 thumbnail, `{rounded.sm}` (2px) radius, a `{colors.live-red}` LIVE badge in the top-left (white uppercase 10.5px), channel name in Inter 14px/600, viewer count in 12px muted (`{colors.ink-muted}`)"
- "Lay out the dark app canvas (`{colors.surface-dark}`) with a persistent left rail (`{colors.surface-elevated}`) of circular avatars (`{rounded.pill}`) + channel names, and a dense multi-column tile grid"
- "Set a marquee headline in Roobert at 48px/700, letter-spacing -0.96px, near-black (`{colors.ink}`) on white, with a purple (`{colors.primary}`) Sign Up button beside it"
- "Make a category tag: whisper-gray (`{colors.surface}`) fill, slate (`{colors.ink-secondary}`) Inter 12px text, pill radius (`{rounded.pill}`), `4px 10px` padding"

### Iteration Guide

1. Is there exactly one saturated color on screen, and is it the purple (`{colors.primary}`)? If a second brand color crept in, remove it.
2. Is the primary CTA filled with the purple? If it's gray or outlined, that's the wrong emphasis.
3. Are UI labels in Inter at 14px/600 and headlines in Roobert? Don't set body in Roobert.
4. Did red appear anywhere other than the LIVE badge? If so, recolor it — red means LIVE.
5. Keep the UI dense — small even type, tight gutters. If it reads airy and consumer-soft, tighten it.
6. Radii stay tight: `{rounded.md}` on buttons, `{rounded.sm}` on tiles. Pills only for avatars/status.
7. Resting surfaces are flat. Reserve the ambient shadow for hover previews and overlays.

---

## Attribution

Independent design analysis from [Design Swatches](https://designmd.santiagoalonso.com) by [Santiago Alonso](https://santiagoalonso.com). Based on publicly observable interface patterns. Not affiliated with or endorsed by Twitch. Brand names and trademarks belong to their respective owners.
