# Excalidraw — Product app (editor) Design Spec

> Source: excalidraw.com (live DOM, computed styles + the app's own published CSS custom properties), 2026-07-21.
> Coverage: editor shell — welcome screen, top toolbar, left properties island, zoom/undo bar, colour pickers. Light theme only; dark theme ships (`--theme-filter`) but was not captured.
> No login required — this is the real product surface, not a mock.

## 1. Visual Theme & Atmosphere

The editor is **chrome floating on an infinite white canvas**: there is no app frame, no sidebar, no header bar. Every control lives inside an "Island" — a white 8px-radius panel with a soft three-layer shadow — that hovers over the drawing surface with the canvas visible all around it. Chrome is deliberately quiet and monochrome so the drawing is the only saturated thing on screen; the single violet `#6965db` marks selection and the primary action, and the entire hand-drawn character is confined to the *canvas contents* and to the greyed Excalifont hints on the empty welcome screen. It avoids the usual design-tool look: no dark shell, no dense inspector, no icon rail.

Feels: light, floating, quiet, unintimidating.

## 2. Color Palette & Roles

**Text & icons**

| Token | Hex | Role / usage |
|---|---|---|
| `--color-on-surface` / `--text-primary-color` / `--icon-fill-color` | `#1b1b1f` | All primary text and icons |
| `--color-logo-text` | `#190064` | Wordmark |
| `--color-on-primary-container` | `#030064` | Text on violet containers |
| `--color-disabled` / `--keybinding-color` | `#b8b8b8` | Disabled labels, shortcut hints, welcome hints |
| `--ExcTextField--placeholder` | `#c5c5d0` | Placeholders |
| `--input-label-color` | `#495057` | Field labels |

**Surfaces & borders**

| Token | Hex | Role / usage |
|---|---|---|
| `--island-bg-color` / `--color-surface-lowest` / `--popup-bg-color` / `--sidebar-bg-color` | `#ffffff` | Every floating panel |
| `--color-surface-mid` | `#f6f6f9` | Faint fill |
| `--color-surface-low` / `--ExcTextField--background` | `#ececf4` | Inset fields, segmented tracks |
| `--color-surface-high` / `--button-hover-bg` / `--button-active-bg` / `--default-border-color` / `--sidebar-border-color` | `#f1f0ff` | Hover + active fills (a violet-tinted grey) |
| `--color-surface-primary-container` | `#e0dfff` | Selected tool container |
| `--popup-secondary-bg-color` / `--input-hover-bg-color` | `#f1f3f5` | Popup secondary ground |
| `--dialog-border-color` / `--list-border-color` / `--avatar-border-color` | `#ebebeb` | Hairlines |
| `--input-border-color` / `--scrollbar-thumb` | `#ced4da` | Input border, scrollbar |
| `--color-border-outline` | `#767680` | Strong outline |
| `--color-border-outline-variant` | `#c5c5d0` | Soft outline |
| `--overlay-bg-color` | `rgba(255,255,255,.88)` | Modal scrim — white, not black |

Grey ramp (published): `--color-gray-10 #f5f5f5` · `20 #ebebeb` · `30 #d6d6d6` · `40 #b8b8b8` · `50 #999999` · `60 #7a7a7a` · `70 #5c5c5c` · `80 #3d3d3d` · `85 #242424` · `90 #1e1e1e` · `100 #121212`.

**Accent**

| Token | Hex | Role / usage |
|---|---|---|
| `--color-primary` / `--color-selection` / `--color-logo-icon` / `--color-promo` | `#6965db` | Selection, active tool, primary button |
| `--color-primary-hover` / `--color-brand-hover` | `#5753d0` | Hover |
| `--color-primary-darker` | `#5b57d1` | — |
| `--color-brand-active` / `--color-primary-darkest`… | `#4440bf` | Active / pressed, field focus border |
| `--color-primary-light` | `#e3e2fe` | Light violet fill |
| `--color-primary-light-darker` | `#d7d5ff` | Light violet fill, pressed |

**Semantic**

| Token | Hex | Role / usage |
|---|---|---|
| `--color-danger` | `#db6965` | Danger |
| `--color-danger-background` / `--button-destructive-bg-color` | `#fff0f0` / `#ffe3e3` | Danger grounds |
| `--color-danger-color` / `--button-destructive-color` | `#700000` / `#c92a2a` | Danger text |
| `--color-warning` | `#fceeca` | Warning ground (text stays `#1b1b1f`) |
| `--color-warning-dark` → `-darkest` | `#f5c354` → `#f3ab2c` → `#ec8b14` | Warning ramp |
| `--color-success` / `-contrast` / `-text` | `#cafccc` / `#65bb6a` / `#268029` | Success |
| `--background-color-badge` / `--color-badge` | `#d3ffd2` / `#0b6513` | Badge |
| `--link-color` / hover / active | `#1c7ed6` / `#1971c2` / `#a5d8ff` | Links |
| `--focus-highlight-color` | `#a5d8ff` | Focus highlight |
| `--select-highlight-color` | `#339af0` | Text selection |

**Canvas element palette** (drawing content only — never chrome)

| Stroke top-picks | `#1e1e1e` · `#e03131` · `#2f9e44` · `#1971c2` · `#f08c00` |
|---|---|
| Fill top-picks | transparent · `#ffc9c9` · `#b2f2bb` · `#a5d8ff` · `#ffec99` |

Rules: **chrome is monochrome plus one violet.** The five-stroke / five-fill palette is the *drawing's* colour and must never appear on a button, panel or border. Hover and active fills are the violet-tinted `#f1f0ff`, not a neutral grey.

## 3. Typography Rules

- UI: `Assistant, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif` (`--ui-font`).
- Hand-drawn: `Excalifont, Xiaolai` — the canvas face, also used for the empty-state hints.

| Role | Size/Line | Weight | Tracking |
|---|---|---|---|
| Base | 16 | 400 | normal |
| Panel legend / label | 12 | 400 | normal |
| Menu item | 14 | 400 | normal |
| Menu shortcut | 12 | 400 | normal |
| Button label | 13.33 | 400 | normal |
| Welcome heading (Excalifont) | 18 / 21.6 | 400 | normal |
| Welcome decor / arrows (Excalifont) | 36 | 400 | normal |

Patterns: **nothing in the chrome is bold, uppercase or tracked.** The whole UI sits between 12 and 16px at weight 400 — hierarchy comes entirely from colour steps (`#1b1b1f` → `#999999` → `#b8b8b8`) and from panel grouping. The hand-drawn face appears in chrome *only* as `#b8b8b8` empty-state hints.

## 4. Component Stylings

- **Island (the universal panel)** — `background #ffffff`, `border-radius 8px`, `padding 4px`, **no border**, shadow below. Everything — toolbar, properties panel, zoom bar, popups — is an Island.
- **Top toolbar** — an Island, height `44px`, holding 36px tool buttons; the active tool fills `#e0dfff` with the icon in `#6965db`, and each tool shows a subscript numeric shortcut in `#b8b8b8`.
- **Tool button** — 36 × 36px, radius `8px`, transparent at rest, `#f1f0ff` on hover/active.
- **Primary button (Share / collab)** — `#6965db` fill, `#ffffff` label, radius `8px`, height 36px, padding 10px, 13.33px / 400, with a `#ffffff 0 0 0 1px` ring so it reads clearly against the canvas.
- **Segmented control (stroke width, sloppiness, edges…)** — a row of 32px square-ish buttons at radius `8px` on `#ececf4`; the selected one takes `#e0dfff`.
- **Colour swatch row** — five square top-pick swatches plus a divider and the current-colour well; the first background pick is a checkerboard "transparent".
- **Text field** — background `#ececf4`, border `#ebebeb`, hover border `#5753d0`, **focus border `#4440bf`**, placeholder `#c5c5d0`; read-only variant swaps to `#f1f0ff` ground with a `#c5c5d0` border.
- **Menu item** — height 42px, radius `6px`, padding 12px, label 14px in `#999999`, shortcut 12px in `#b8b8b8`.
- **Slider** — track `hsl(240,100%,90%)`, thumb `#3d3d3d`, thumb size 16px.
- **Radio group** — off `#ffffff` with `#f1f0ff` border and `#6965db` label; on `#6965db` with white label, hover `#5753d0`, active `#4440bf`.
- **Switch** — track `#ffffff`, thumb `#1b1b1f`, hover `#5753d0`, active `#4440bf`, disabled border/thumb `#c5c5d0` with `#767680` label.

## 5. Layout Principles

There is no page layout — the canvas is full-viewport and the chrome is positioned into its corners: menu top-left, toolbar top-centre, account/Share top-right, properties panel left (appears only when a tool or element is active), zoom + undo bottom-left, help bottom-right. Spacing is driven by `--space-factor: .25rem` with `--container-padding-y: 1rem` / `--container-padding-x: .75rem` and `--editor-container-padding: 1rem`. Control sizes are tokenised: `--default-button-size 2rem`, `--lg-button-size 2.25rem`, `--default-icon-size 1rem`, `--avatar-size 1.75rem`.

## 6. Depth & Elevation

Radii: `--border-radius-lg .5rem` (8px) and `--border-radius-md .375rem` (6px). Nothing larger — no pills in the editor chrome.

One recipe, used by every floating surface:

```css
box-shadow:
  rgba(0,0,0,.17) 0 0 1px 0,
  rgba(0,0,0,.08) 0 0 3px 0,
  rgba(0,0,0,.05) 0 7px 14px 0;
```

Philosophy: **shadow instead of border.** Islands have `border: none`; the three-layer stack (a tight contact line, a close ambient, a wide soft lift) is what separates chrome from canvas. Borders appear only inside panels as `#ebebeb` list hairlines.

## 7. Motion

Not richly captured. Transitions on chrome are short colour/background fades on hover and active; the canvas itself is redrawn imperatively (rough.js hand-drawn rendering) rather than CSS-animated. No keyframe animations, scroll effects or animation library detected in the shell.

## 8. Backgrounds, Effects & WebGL

No gradients, no `backdrop-filter`, no blend modes, no grain. The drawing surface is a `<canvas>` rendered by **rough.js**, which is what produces the sketchy stroke quality (the "Sloppiness" control is literally rough.js roughness). Modal scrims are a white `rgba(255,255,255,.88)` overlay rather than a dark one. Dark theme is implemented as a global CSS `--theme-filter` inversion rather than a second palette.

## 9. Do's and Don'ts

### Do
- Float every control inside a white 8px Island with the three-layer shadow and no border.
- Keep all chrome text at 12–16px, weight 400, `normal` tracking, in the `#1b1b1f` → `#999999` → `#b8b8b8` ladder.
- Use `#f1f0ff` (violet-tinted) as the hover/active fill and `#e0dfff` for the selected state.
- Show a keyboard shortcut beside every menu item and a subscript number on every tool.
- Let the canvas show through — corner-anchored chrome, no frame, no rail, no header.
- Confine the five stroke / five fill colours to drawn elements.

### Don't
- Don't use bold, uppercase or tracked chrome text.
- Don't put a border on an Island — depth is the shadow.
- Don't use radii above 8px in the editor chrome.
- Don't paint chrome with the canvas palette, or introduce a second accent beside `#6965db`.
- Don't use a dark or neutral-grey scrim — the overlay is white at 88%.
- Don't set the hand-drawn face above `#b8b8b8` in the chrome; it is a hint, not a UI face.

## 10. Responsive Behavior

Not captured on device widths, but the token set anticipates it: `--mobile-action-button-bg: rgba(255,255,255,.35)`, `--mobile-action-button-size: 2rem`, `--mobile-color-border: #f1f0ff`, and safe-area tokens `--sat/--sar/--sab/--sal`, implying a translucent bottom action bar on mobile.

## 11. Agent Prompt Guide

- Canvas `#ffffff` · Text `#1b1b1f` · Muted `#999999` · Border `#ebebeb` · Accent `#6965db`
- Font: Assistant / system-ui · Body: 14px UI (16px base) · Radius: 8px panel & control / 6px menu item · Control height: 32–36px
- Focus: field border → `#4440bf` · Hover fill: `#f1f0ff` · Selected fill: `#e0dfff`

<excalidraw_app_design_language>
An editor with no frame: white chrome floating in the corners of an infinite white canvas. Every control lives in an "Island" — background #ffffff, border-radius 8px, padding 4px, no border, lifted by rgba(0,0,0,.17) 0 0 1px, rgba(0,0,0,.08) 0 0 3px, rgba(0,0,0,.05) 0 7px 14px. Text is Assistant at 12-16px, weight 400, normal tracking, nothing bold or uppercase, stepping #1b1b1f to #999999 to #b8b8b8 for hierarchy. One accent violet #6965db (hover #5753d0, active/focus #4440bf); hover and active fills are the violet-tinted #f1f0ff and the selected state is #e0dfff, never a neutral grey. Tool buttons are 36px at 8px radius with a subscript number shortcut; the toolbar Island is 44px tall; text fields sit on #ececf4 with an #ebebeb border that turns #4440bf on focus. Scrims are white at rgba(255,255,255,.88). The five stroke colours (#1e1e1e #e03131 #2f9e44 #1971c2 #f08c00) and five fills (transparent #ffc9c9 #b2f2bb #a5d8ff #ffec99) belong to drawn shapes only. Feels: light, floating, quiet, unintimidating. Avoid: borders on panels, radii above 8px, bold or tracked chrome text, dark scrims, a second accent, or painting the UI with the canvas palette.
</excalidraw_app_design_language>
