All designs

Duolingo

Language learning. Feather green, chunky rounded buttons, cartoon confidence.

extracted from https://www.duolingo.com/ on 2026-07-03

Duolingo - The world’s most popular way to learn

light-leaning · 9 stylesheets · 61 native variables

fonts KaTeX_Main · KaTeX_SansSerif · KaTeX_Fraktur · KaTeX_Math · KaTeX_Size1 · duolingo-sans · feather · KaTeX_Caligraphic
---
name: duolingo-com-design-system
source: https://www.duolingo.com/
extracted: 2026-07-03
theme: light  # measured from neutral usage weight
colors:  # roles inferred from usage frequency + luminance; names verified against values
  canvas: "#ffffff"
  ink: "#333333"
  text-muted: "#c1c1c1"
  surface: "#d7d7d7"
  hairline: "#9e9e9e"
  accent: "#1a73e8"  # cyan
  accent-2: "#68b631"  # lime
  accent-3: "#e8eaed"  # pale-blue
fonts:
  primary: "KaTeX_Main"  # medium-confidence: usage counts are close
---

# Duolingo - The world’s most popular way to learn — Design System

Extracted from **https://www.duolingo.com/** on 2026-07-03.

Every value below is mined from the site's live CSS (61 native custom properties, ranked usage counts). Role assignments are inferred from usage and luminance and marked as such — treat them as a strong starting point, not gospel.

## Overview

The system reads as light: measured neutral usage weighs 50 light against 19 dark. The palette is chromatic: 12 saturated values against 10 neutrals, led by cyan (`#1a73e8`). **KaTeX_Main** leads the font stack, though usage counts are close — verify against rendered pages before committing. Geometry mixes sharp corners (2px) with full pills.

## Usage rules

Derived from the measured usage below — each rule cites its evidence.

- Chromatic color carries 39% of measured usage — this palette uses color structurally. Saturated values can hold large surfaces here; timidity would misrepresent the source.
- The theme is light (measured neutral weight 50:19). Don't flip individual sections to the opposite mode.
- `12px` is the workhorse radius (x23); treat the other radii as exceptions, not options.
- The only shadows are blur-less hairline offsets — this system barely uses elevation; don't add soft drop shadows.

## Colors

### Neutrals (light → dark)

| Token | Value | Usage | Inferred role |
| --- | --- | --- | --- |
| `neutral-50` | `#ffffff` | 39 | **canvas** — lightest neutral, matches the measured light-leaning theme |
| `neutral-200` | `#d7d7d7` | 11 | **surface** — neutral closest to canvas luminance |
| `neutral-300` | `#c1c1c1` | 8 | **text-muted** — most-used mid-luminance neutral |
| `neutral-400` | `#9e9e9e` | 2 | **hairline** — next neutral near canvas, hairline-border weight |
| `neutral-500` | `#8e918f` | 2 | — |
| `neutral-600` | `#777777` | 2 | — |
| `neutral-700` | `#5f6368` | 3 | — |
| `neutral-800` | `#4b4b4b` | 9 | — |
| `neutral-900` | `#3c4043` | 1 | — |
| `neutral-950` | `#333333` | 1 | **ink** — darkest neutral, highest contrast against canvas |

### Accents

| Token | Value | Usage | Inferred role |
| --- | --- | --- | --- |
| `cyan` | `#1a73e8` | 15 | **accent** — most-used chromatic color |
| `lime` | `#68b631` | 8 | **accent-2** — supporting chromatic color |
| `pale-blue` | `#e8eaed` | 8 | **accent-3** — supporting chromatic color |
| `cyan-2` | `#1cb0f6` | 4 | — |
| `cyan-3` | `#1f96db` | 3 | — |
| `teal` | `#32ae88` | 2 | — |
| `cyan-4` | `#33c3f0` | 2 | — |
| `cyan-5` | `#1eaedb` | 2 | — |
| `cyan-6` | `#5cadff` | 2 | — |
| `blue` | `#4285f4` | 1 | — |
| `pale-blue-2` | `#d2e3fc` | 1 | — |
| `cyan-7` | `#00639b` | 1 | — |

### Modern color values (kept verbatim)

- `color("eel")`
- `color("snow")`

## Typography

**KaTeX_Main** has the most `font-family` declarations, but the margin over the rest is thin — treat the primary-face call as provisional and verify on rendered pages. Full list by usage:

| Family | Usage |
| --- | --- |
| KaTeX_Main | 8 |
| KaTeX_SansSerif | 7 |
| KaTeX_Fraktur | 4 |
| KaTeX_Math | 4 |
| KaTeX_Size1 | 4 |
| duolingo-sans | 4 |
| feather | 4 |
| KaTeX_Caligraphic | 3 |

**Size scale (px):** `12`, `13`, `13.33`, `14`, `15`, `16`, `17`, `19`, `19.2`, `24`, `25`, `32`

**Weights in use:** `300` (x1), `400` (x19), `500` (x9), `600` (x4), `700` (x90)

**Line-heights (unitless):** `1`, `1.15`, `1.2`, `1.25`, `1.3`, `1.4`, `1.5`, `2`

**Letter-spacing values:** `-.02em`, `.01em`, `.25px`, `.3px`, `.04em`, `.8px`

## Spacing

Most-used values (px): `5`, `6`, `8`, `10`, `12`, `14`, `15`, `16`, `20`, `24`, `32`, `40`

## Border radius

| Token | Value | Usage |
| --- | --- | --- |
| `radius-sm` | `2px` | 5 |
| `radius-md` | `4px` | 11 |
| `radius-lg` | `6px` | 5 |
| `radius-xl` | `8px` | 8 |
| `radius-2xl` | `10px` | 4 |
| `radius-3xl` | `12px` | 23 |
| `radius-4xl` | `16px` | 13 |
| `radius-full` | `50%` | 12 |

## Shadows (ordered by blur radius)

- `shadow-sm` — `0 0 18px rgba(0,0,0,.2)` (x1)
- `shadow-md` — `0 2px 0` (x1)
- `shadow-lg` — `0 3px 0 1px` (x1)

## Breakpoints

`360px`, `400px`, `410px`, `425px`, `530px`, `550px`, `600px`, `699px`

## Starter recipes

Tokens composed into components. The source site's real components were **not** inspected — these are starting points built from the extracted values, with contrast ratios computed rather than assumed.

```css
.button-primary {
  background: var(--color-accent); /* #1a73e8 */
  color: var(--color-canvas); /* #ffffff — contrast 4.5:1 */
  border-radius: 9999px; /* pill — mined as 50% */
  font-weight: 500;
}

.card {
  background: var(--color-surface); /* #d7d7d7 */
  border: 1px solid var(--color-hairline); /* #9e9e9e */
  border-radius: 12px; /* most-used finite radius */
  box-shadow: var(--shadow-sm);
}

.input {
  background: var(--color-canvas); /* #ffffff */
  color: var(--color-ink); /* #333333 — contrast 12.6:1 */
  border: 1px solid var(--color-hairline); /* #9e9e9e */
  border-radius: 2px;
  /* placeholder color: var(--color-text-muted) #c1c1c1 */
}

.nav {
  background: var(--color-canvas); /* #ffffff */
  border-bottom: 1px solid var(--color-hairline); /* #9e9e9e */
  color: var(--color-ink); /* #333333 */
  /* inactive links: var(--color-text-muted) #c1c1c1 */
  /* active link: var(--color-accent) #1a73e8 — contrast vs canvas 4.5:1 */
}

.modal {
  background: var(--color-surface); /* #d7d7d7 */
  border-radius: 12px;
  box-shadow: var(--shadow-lg); /* largest mined shadow */
}

.modal-backdrop {
  background: color-mix(in srgb, var(--color-ink) 55%, transparent); /* scrim from #333333 */
}

.input-success {
  border-color: #68b631; /* mined token: lime */
}

.input-error {
  /* no red exists in the mined palette — this site does not expose an error color in its CSS. */
  /* Introduce one deliberately rather than borrowing a random red; keep saturation near the accent's. */
}
```

## Observations

- 10 of 22 extracted colors are neutrals.
- 3 distinct shadows; the softest reaches 0px blur.
- 8 breakpoints, from 360px to 699px.

## Native CSS custom properties

First 40 of 61:

```css
:root {
  --viewport-height: 100dvh;
  --web-ui_button-border-radius: 16px;
  --rtl-sign: 1;
  --text-pageTitle-casing-large: none;
  --text-pageTitle-letter-spacing-large: 0;
  --text-pageTitle-line-height-large: 2.75rem;
  --text-pageTitle-size-large: 2.5rem;
  --text-pageTitle-weight-large: 700;
  --text-pageTitle-casing-small: none;
  --text-pageTitle-letter-spacing-small: 0;
  --text-pageTitle-line-height-small: 2.25rem;
  --text-pageTitle-size-small: 2rem;
  --text-pageTitle-weight-small: 700;
  --text-heading-casing-large: none;
  --text-heading-letter-spacing-large: 0;
  --text-heading-line-height-large: 2rem;
  --text-heading-size-large: 1.75rem;
  --text-heading-weight-large: 700;
  --text-heading-casing-medium: none;
  --text-heading-letter-spacing-medium: 0;
  --text-heading-line-height-medium: 2rem;
  --text-heading-size-medium: 1.5rem;
  --text-heading-weight-medium: 700;
  --text-heading-casing-small: none;
  --text-heading-letter-spacing-small: 0;
  --text-heading-line-height-small: 1.5rem;
  --text-heading-size-small: 1.25rem;
  --text-heading-weight-small: 700;
  --text-heading-casing-xsmall: none;
  --text-heading-letter-spacing-xsmall: 0;
  --text-heading-line-height-xsmall: 1.25rem;
  --text-heading-size-xsmall: 1rem;
  --text-heading-weight-xsmall: 700;
  --text-body-casing: none;
  --text-body-letter-spacing: 0;
  --text-body-line-height: 1.75rem;
  --text-body-size: 1.25rem;
  --text-body-weight: 500;
  --text-body-weight-bold: 700;
  --text-body-casing-spacious: none;
}
```

## Files

- `tailwind.css` — Tailwind v4 `@theme` block; tokens become utilities (e.g. `bg-neutral-900`, `text-cyan`).
- `variables.css` — framework-agnostic `:root` variables, same values.
- `tokens.json` — W3C Design Tokens format for Figma plugins, Style Dictionary, etc.