# tldraw — Marketing site Design Spec

> Source: tldraw.dev (live DOM, computed styles + the published shadcn-style `--background` / `--primary` / `--alt-*` token set), 2026-07-19.
> Coverage: SDK marketing / developer landing. The editor product itself is embedded live on the page; a separate `app/` capture is not required.
> Note: the site is **light and restrained**, not the playful hand-drawn chalk look the category hint implied — the playfulness lives inside the embedded canvas, not in the chrome.

## 1. Visual Theme & Atmosphere

tldraw's developer site is deliberately quiet so the live canvas can be the loud thing. The chrome is a near-white `#fefefe` with pure-black headings, `#404047` body text, `#e3e3e3` hairlines and one blue `#155dfc` — an ordinary, well-built SaaS surface. What makes it distinctive is the split: an **`alt-*` token family** (`#0d0d0d` surface, `#fff` foreground) gives the page a fully-specified inverted mode used for dark panels and the secondary CTA, and a separate **`code-chrome-*` family** styles code frames independently of both. Type is Archivo throughout at a very compressed scale — headings are 48px and 32px with **line-height *below* the font size** (48/45.12, 32/30.08) and -0.96px tracking, while everything else, including h3 subheadings, sits at just 14px. Colour appears only in the embedded editor's shape palette. Feels: restrained, developer-first, tightly set, canvas-forward.

## 2. Color Palette & Roles

### Core semantic tokens (published)

| Token | Hex | Role / usage |
|---|---|---|
| `background` | `#fefefe` | Page canvas |
| `foreground` | `#1d1d1d` | Default foreground |
| `card` / `popover` | `#fff` | Card and popover fill |
| `card-foreground` / `popover-foreground` | `#1d1d1d` | Text on card/popover |
| `primary` | `#155dfc` | THE accent — primary button |
| `primary-hover` | `#2b7fff` | Primary hover |
| `primary-foreground` | `#fff` | Text on primary |
| `secondary` | `#fafafa` | Secondary fill |
| `secondary-foreground` | `#262626` | Text on secondary |
| `muted` | `#fafafa` | Muted fill |
| `muted-background` | `#f4f4f5` | Muted ground |
| `muted-foreground` | `#aaa` | Muted text |
| `text` | `#404047` | Body copy |
| `emphasis` / `heading-foreground` | `#000` | Headings |
| `border` / `input` | `#e3e3e3` | Hairlines and field borders |
| `overlay` | `#000` | Modal overlay |
| `highlight` | `#fafa98` | Highlighter yellow |
| `star-accent` | `#fe9a00` | GitHub-star accent |

### Inverted (`alt-*`) family

| Token | Hex |
|---|---|
| `alt-surface` | `#0d0d0d` |
| `alt-surface-hover` | `#18181b` |
| `alt-foreground` | `#fff` |
| `alt-foreground-hover` | `#fefefe` |
| `alt-muted-foreground` | `#ccc` |
| `alt-muted` | `#ccc` |

### Code-frame (`code-chrome-*`) family

| Token | Hex |
|---|---|
| `code-background` | `#ebeded` |
| `code-chrome-surface` | `#18181b` |
| `code-chrome-foreground` | `#fafafa` |
| `code-chrome-muted-foreground` | `#a1a1aa` |
| `code-chrome-border` | `#27272a` |
| `code-chrome-frame` | `#e4e4e7` |

### Editor shape palette (observed in the embedded canvas)

`violet #ae3ec9` &middot; `light-violet #e085f4` &middot; `blue #4465e9` &middot; `light-blue #4ba1f1` &middot; `orange #f1ac4b` &middot; `grey #9fa8b2` &middot; `grey-2 #888d91` &middot; ink `#2e2e2e`

### Code syntax (VS Code dark, inside code frames)

`#c586c0` keyword &middot; `#569cd6` type &middot; `#9cdcfe` identifier &middot; `#ce9178` string &middot; `#d4d4d4` default &middot; `#808080` comment &middot; `#cccccc` chrome

**Rules.** Chrome is white, black and one blue. The shape palette belongs to the *canvas* and must not leak into buttons or headings. The `alt-*` family is a complete inverted set — use it wholesale for dark panels rather than hand-mixing darks.

## 3. Typography Rules

- One family: `Archivo, "Archivo Fallback"` for all text
- Code: `"Geist Mono", "Geist Mono Fallback"`

| Role | Size/Line | Weight | Tracking |
|---|---|---|---|
| Hero h1 | 48/**45.12** | 600 | -0.96px |
| Section h2 | 32/**30.08** | 600 | -0.96px |
| Sub-heading h3 | 14/19.25 | 500 | normal |
| Body | 14/21 | 400 | normal (`#404047`) |
| Button label | 14 | 400 (500 on tonal) | normal |
| Small button | 12 | 500 | normal |

Patterns: both display roles run a **line-height below the font size** (48/45.12 and 32/30.08 — a 0.94 ratio) with -0.96px tracking held constant at both sizes rather than scaled. Below the two display roles the scale collapses hard: h3 subheadings and body copy are both **14px**, so hierarchy comes almost entirely from weight (600 / 500 / 400) and colour (black / `#404047`). Sentence case throughout.

## 4. Component Stylings

**Nav** — 112px, `#fefefe`, no blur and no bottom border colour resolved.

**Buttons** — small and tight at 40px on a **6px** radius:

| Variant | Fill | Text | Border | Radius | Height | Padding | Type |
|---|---|---|---|---|---|---|---|
| Primary | `#155dfc` | `#ffffff` | none | 6px | 40px | 0 16px / 0 14px | 14/400 |
| Alt / dark | `#0d0d0d` | `#ffffff` | none | 6px | 40px | 0 16px / 0 14px | 14/400 |
| Tonal | `#edf0f2` | `#2e2e2e` | none | **9px** | 40px | 8px 16px | 12/500 |
| Ghost (on dark) | transparent | `#ffffff` | 1px | 6px | 32px | 0 14px | 12/500 |

Primary hover steps `#155dfc` &rarr; `#2b7fff` (a published pair).

**Embedded editor** — 2 `<canvas>` elements and 58 `<svg>` nodes carry the live tldraw editor: a toolbar of shape tools, the shape palette above, and a `rgb(46,46,46) 0 0 0 2px inset` selection ring — the ring is drawn as an **inset shadow**, not a border, so it never shifts layout.

**Code frame** — `#ebeded` outer ground with a `#18181b` chrome bar, `#27272a` border and `#e4e4e7` frame, holding VS Code-dark syntax in Geist Mono. The frame is styled by its own token family, independent of both light chrome and the `alt-*` set.

**Inputs** — `input` shares the `#e3e3e3` border token; full field states were **not captured**.

## 5. Layout Principles

Light page, 112px nav, a hero pairing a compressed 48px headline with a 14px sub and two 40px buttons, then the live editor embed, a logo wall, and 14px feature columns. Radius scale: `4 / 4.8 / 6 / 9 / 10 / 11 / 14 / 999px`, plus `4px 0 0 4px` and `11px 11px 0 0` for grouped and tab-capped elements.

## 6. Depth &amp; Elevation

Radius scale: `4px` / `4.8px` / `6px` (buttons) / `9px` (tonal) / `10px` / `11px` / `11px 11px 0 0` / `14px` / `999px` / `4px 0 0 4px`.

Shadow recipes:

- `rgba(0,0,0,0.16) 0 0 2px 0, rgba(0,0,0,0.24) 0 2px 3px 0, rgba(0,0,0,0.1) 0 2px …` — the layered editor-panel shadow
- `rgb(46, 46, 46) 0px 0px 0px 2px inset` — the canvas selection ring
- transparent multi-layer placeholder rings

Chrome is otherwise flat, separated by `#e3e3e3` hairlines; the layered shadow belongs to the floating editor UI.

## 7. Motion

- `0.15s cubic-bezier(0.4, 0, 0.2, 1)` — the base transition
- `color 0.15s cubic-bezier(0.4, 0, 0.2, 1), background-color …`
- `color 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color …` — the slower variant

One easing curve, `cubic-bezier(0.4, 0, 0.2, 1)`, at two speeds (0.15s and 0.3s).

## 8. Backgrounds, Effects &amp; WebGL

No decorative effects on the chrome — no gradients, backdrop-filter, grain or WebGL. The visual interest is the **live embedded editor**: 2 `<canvas>` elements plus 58 `<svg>` nodes rendering real shapes in the palette above.

## 9. Do's and Don'ts

### Do
- Keep chrome near-white `#fefefe` with black headings, `#404047` body and `#e3e3e3` hairlines.
- Set both display roles with a line-height *below* the font size (48/45.12, 32/30.08) and hold -0.96px tracking constant.
- Collapse everything below the display roles to 14px and build hierarchy from weight and colour.
- Use the `alt-*` family wholesale for inverted panels instead of mixing your own darks.
- Style code frames from the `code-chrome-*` family, separately from both light and alt chrome.
- Keep buttons small — 40px at a 6px radius — with `#155dfc` hovering to `#2b7fff`.
- Draw selection as a `0 0 0 2px inset` ring so it never shifts layout.

### Don't
- Don't render this brand as playful or hand-drawn — the chrome is restrained; the play is inside the canvas.
- Don't let the shape palette (violet, blue, orange) into buttons or headings.
- Don't inflate the type scale; h3 and body are both 14px.
- Don't scale the display tracking with size — it stays at -0.96px.
- Don't add gradients or decorative effects to chrome.
- Don't use borders for selection states; use the inset ring.

## 10. Responsive Behavior

Not captured — no media-query census was resolved from the accessible stylesheets.

## 11. Agent Prompt Guide

- Canvas `#fefefe` · Card `#fff` · Heading `#000` · Body `#404047` · Muted `#aaa` · Border `#e3e3e3` · Accent `#155dfc` (hover `#2b7fff`) · Alt surface `#0d0d0d`
- Font: Archivo (all), Geist Mono (code) · Body: 14/21 · Radius: 6px button / 9px tonal / 11px panel · Control height: 40px (32px small)
- Focus: not captured · Selection: `rgb(46,46,46) 0 0 0 2px inset` · Motion `0.15s` / `0.3s` on `cubic-bezier(0.4, 0, 0.2, 1)`

<tldraw_design_language>
A deliberately quiet developer surface built so the live canvas can be the loud thing — restrained and light, NOT the playful hand-drawn look the category suggests. Chrome is near-white #fefefe with #fff cards, pure-black headings, #404047 body copy, #aaa muted text and #e3e3e3 hairlines, plus one blue #155dfc that hovers to a published #2b7fff. Two further token families make the system distinctive: a complete inverted alt-* set (#0d0d0d surface, #18181b hover, #fff foreground, #ccc muted) used wholesale for dark panels and the secondary CTA, and a separate code-chrome-* family (#ebeded ground, #18181b bar, #27272a border, #e4e4e7 frame) that styles code frames independently of both. Type is Archivo throughout on a deliberately compressed scale: display roles run a LINE-HEIGHT BELOW THE FONT SIZE — 48/45.12 and 32/30.08, a 0.94 ratio — with -0.96px tracking held CONSTANT at both sizes rather than scaled, and below them the scale collapses so h3 subheadings and body copy are both 14px, leaving hierarchy to weight (600/500/400) and colour. Buttons are small and tight: 40px at a 6px radius in blue or #0d0d0d, plus a 9px tonal variant on #edf0f2. The shape palette (violet #ae3ec9, light-violet #e085f4, blue #4465e9, light-blue #4ba1f1, orange #f1ac4b, greys #9fa8b2/#888d91 over #2e2e2e ink) belongs to the embedded editor and must never leak into chrome. Selection is drawn as rgb(46,46,46) 0 0 0 2px INSET so it never shifts layout, and the only real elevation is a layered rgba(0,0,0,.16)/.24/.1 shadow on floating editor panels; chrome is otherwise flat with no gradients, backdrop-filter, grain or decorative effects. Motion is one curve, cubic-bezier(0.4, 0, 0.2, 1), at 0.15s and 0.3s. Feels: restrained, developer-first, tightly set, canvas-forward. Avoid: a playful or hand-drawn chrome treatment, shape-palette colours in buttons or headings, an inflated type scale, tracking that scales with size, gradients in chrome, and borders for selection states.
</tldraw_design_language>
