# DESIGN.md: Substack-style design system

Measured from https://substack.com/ on 2026-10-15 by PikSpec (https://www.pikspec.com). An independent measurement of the rendered page, not Substack's official design system, and not affiliated with Substack.

Use it to build your own product in this design language: put this file in your project root and tell your AI tool (Claude Code, Cursor, Codex, Lovable, v0) to follow DESIGN.md. Never use the original's name, logo, copy or images; licensed fonts are replaced with the closest free Google Fonts.

To capture any other page, or get a ready project for your tool: https://www.pikspec.com/build-with-ai

HOW IT SHOULD LOOK
Build it in the design language of substack.com, measured from the live site: it should feel designed by the same team as substack.com — the same temperament, colour, type and density — for a different product. Never use substack.com's name, logo, words or images.

Stack: use the one your project already has. Starting fresh: React + Vite + TypeScript + Tailwind CSS v4, with no UI kit (no shadcn/ui, MUI or Chakra) — their defaults would override this system. In a chat that shows one file (an artifact or canvas), build one HTML file instead, with the tokens below in its <style> block.
Use every value exactly as written. Do not round, rename, approximate or substitute a colour, size, radius, shadow or easing (fonts: see section 1). Anything not listed here does not exist in this system.

THE WEBSITE THIS SYSTEM COMES FROM
- Reference page: https://substack.com/ — "substack.com" (home page).
- Page frame: a top navigation bar and a persistent side panel beside the content. This is a tool- or docs-style layout, not a marketing page of stacked full-width bands.
- Content is grouped in cards (6 on the page). Keep cards for grouped content only, not for every section.
- It uses 495 images including 12 videos, 48 of them large. Large, real product imagery is part of the look — leave clearly labelled image slots at those sizes rather than stock photos or abstract 3D shapes.
- Interaction: 525 buttons and 1 form field on one page.
- The page runs about 41 screens long at 1440px wide.
- Density is low: few elements per screen, generous space around each one.

HOW IT LOOKS (WHAT MAKES IT LOOK LIKE ITSELF, BEYOND THE VALUES)
- Almost entirely neutral: 99% of the page is painted in #FFFFFF and #232525.
- #2F6D5D is an accent: 0.9% of the page, as small fills such as buttons and badges. Keep it to small areas.
- #FF6719 is an accent: 0.3% of the page, as small fills such as buttons and badges. Keep it to small areas.
- The largest type (32px Cahuenga) is medium weight, not bold (500).
- Very airy: text covers 5% of the page.
- Structure comes from thin grid lines, not from shadows or filled cards.
- Share of the painted page: #FFFFFF 90.9% (dominant, backgrounds); #232525 3.5% (detail, backgrounds); #EEEEEE 1.6% (detail, backgrounds); #363737 1.4% (supporting, text); #2F6D5D 0.9% (accent, backgrounds); #000000 0.9% (detail, backgrounds); #777777 0.3% (detail, text); #FF6719 0.3% (accent, backgrounds and text). Keep accents at these proportions; do not promote them to fills.
- Hairline grid: 1px lines in #000000 run both across and down the page (171 across, 54 down), dividing it into cells instead of cards.
- Small marks: 40 small decorative marks (dots, crosses or ticks under 16px) in #FF6719 and #777777, placed at corners and edges.
- Large images: Photographs, video or raster artwork cover about 25% of the page.
- Type as set (size, weight and tracking measured together; use these pairs): 32px weight 500; 24px weight 500; 20px weight 700; 17px weight 400; 15px weight 400; 14px weight 700 tracking -0.014em uppercase.

1. DESIGN TOKENS — PASTE INTO YOUR GLOBAL CSS
Allowed colours, with how the original uses each. Use no other colour, including in hover states:
- #FFFFFF — 90.9% · backgrounds (var(--color-background))
- #232525 — 3.5% · backgrounds (var(--color-232525))
- #EEEEEE — 1.6% · backgrounds (var(--color-surface))
- #363737 — 1.4% · text (var(--color-text))
- #2F6D5D — 0.9% · backgrounds (var(--color-positive))
- #000000 — 0.9% · backgrounds (var(--color-on-action))
- #777777 — 0.3% · text (var(--color-text-muted))
- #FF6719 — 0.3% · backgrounds, text (var(--color-action))
- #C8C8C8 — 0.1% · backgrounds
- #7B61FF — small marks
Fonts — put these first in your global CSS (one import per font, so one that fails cannot stop the others):
```css
@import url("https://fonts.googleapis.com/css2?family=Figtree:wght@500&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Jetbrains+Mono:wght@700&display=swap");
```
Font: substack.com uses Cahuenga (body) — its own licensed face, not available to download. This kit uses Figtree for body, the closest free match, already in the tokens. If you own the original, add it with @font-face and it is used first; to use another font, change the --font-* variables.
Jetbrains Mono is on Google Fonts and loaded from there.
Keep every size, weight, line height and tracking exactly as listed whichever font renders.
```css
:root {
  --color-background: #FFFFFF; /* Background */
  --color-surface: #EEEEEE; /* Surface */
  --color-text: #363737; /* Text */
  --color-text-muted: #777777; /* Text muted */
  --color-action: #FF6719; /* Action */
  --color-on-action: #000000; /* On action */
  --color-positive: #2F6D5D; /* Positive */
  --color-232525: #232525; /* 3.5% of the page, as fills */
  --font-body: "Cahuenga", "Figtree", sans-serif; /* Cahuenga on the site; Figtree renders */
  --font-interface: -apple-system-ui-serif, serif; /* -apple-system-ui-serif */
  --font-code: "Jetbrains Mono", monospace; /* Jetbrains Mono */
  --type-headline-large: 32px; /* Headline Large, weight 500 */
  --type-headline-large-leading: 39.68px;
  --type-headline-small: 24px; /* Headline Small, weight 500 */
  --type-headline-small-leading: 30px;
  --type-title: 20px; /* Title, weight 700 */
  --type-title-leading: 24px;
  --type-body-large: 17px; /* Body Large, weight 400 */
  --type-body-large-leading: 28px;
  --type-body-small: 15px; /* Body Small, weight 400 */
  --type-body-small-leading: 21px;
  --type-label: 13px; /* Label, weight 400 */
  --type-label-leading: 17px;
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-8: 32px;
  --space-10: 40px;
  --space-12: 48px;
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;
  --elevation-1: rgba(255, 255, 255, 0.2) 0px 1px 0px 0px inset, rgba(0, 0, 0, 0.1) 0px -1px 0px 0px inset;
  --elevation-2: rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
  --duration-1: 0.125s;
  --duration-2: 0.25s;
  --ease-1: cubic-bezier(0.19, 1, 0.22, 1);
}
```
Tailwind CSS v4 (the default for new projects): put this after `@import "tailwindcss";`. It removes Tailwind's own palette, so only these colours exist as utilities:
```css
@theme {
  --color-*: initial;
  --color-background: #FFFFFF;
  --color-surface: #EEEEEE;
  --color-text: #363737;
  --color-text-muted: #777777;
  --color-action: #FF6719;
  --color-on-action: #000000;
  --color-positive: #2F6D5D;
  --color-232525: #232525;
  --font-body: "Cahuenga", "Figtree", sans-serif;
  --font-interface: -apple-system-ui-serif, serif;
  --font-code: "Jetbrains Mono", monospace;
  --text-headline-large: 32px;
  --text-headline-large--line-height: 39.68px;
  --text-headline-small: 24px;
  --text-headline-small--line-height: 30px;
  --text-title: 20px;
  --text-title--line-height: 24px;
  --text-body-large: 17px;
  --text-body-large--line-height: 28px;
  --text-body-small: 15px;
  --text-body-small--line-height: 21px;
  --text-label: 13px;
  --text-label--line-height: 17px;
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 12px;
  --radius-full: 9999px;
  --shadow-e1: rgba(255, 255, 255, 0.2) 0px 1px 0px 0px inset, rgba(0, 0, 0, 0.1) 0px -1px 0px 0px inset;
  --shadow-e2: rgba(0, 0, 0, 0.1) 0px 4px 6px -1px, rgba(0, 0, 0, 0.06) 0px 2px 4px -1px;
  --ease-1: cubic-bezier(0.19, 1, 0.22, 1);
}
```
Tailwind CSS v3 only: extend the theme with these instead, and use no other palette:
```js
// tailwind.config — theme.extend
{
  colors: { "background": "var(--color-background)", "surface": "var(--color-surface)", "text": "var(--color-text)", "text-muted": "var(--color-text-muted)", "action": "var(--color-action)", "on-action": "var(--color-on-action)", "positive": "var(--color-positive)" },
  fontFamily: { "body": ["var(--font-body)"], "interface": ["var(--font-interface)"], "code": ["var(--font-code)"] },
  borderRadius: { "sm": "var(--radius-sm)", "md": "var(--radius-md)", "lg": "var(--radius-lg)", "full": "var(--radius-full)" },
  boxShadow: { "e1": "var(--elevation-1)", "e2": "var(--elevation-2)" },
}
```

2. BASE STYLES
```css
html { background: var(--color-background); }
body { margin: 0; font-family: var(--font-body); font-size: var(--type-body-large); line-height: var(--type-body-large-leading); color: var(--color-text); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
h1, h2, h3 { font-family: var(--font-body); margin: 0; }
a { color: inherit; }
:focus-visible { outline: 2px solid var(--color-action); outline-offset: 2px; }
```

3. COMPONENTS — MATCH THESE EXACTLY
```css
.button { /* Primary */
  background: var(--color-action);
  border-radius: var(--radius-md);
  padding: 0px 16px 0px 16px;
  min-height: 40px;
  box-shadow: var(--elevation-1);
  color: var(--color-on-action);
  font-family: "system-ui";
  font-size: var(--type-body-small);
  font-weight: 600;
  line-height: 20px;
}
.button-3 { /* Secondary 2 */
  background: var(--color-background);
  border-radius: var(--radius-lg);
  min-height: 48px;
  color: var(--color-text);
  font-family: "system-ui";
  font-size: 19px;
  font-weight: 400;
}
.card { /* Default */
  background: #000000;
  border-radius: var(--radius-lg);
  min-height: 400px;
  color: var(--color-text);
  font-family: "system-ui";
  font-size: var(--type-body-small);
  font-weight: 400;
  line-height: 20px;
}
.card-2 { /* Alternative 1 */
  background: var(--color-positive);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  min-height: 216px;
  box-shadow: var(--elevation-2);
  color: var(--color-text);
  font-family: "system-ui";
  font-size: var(--type-body-small);
  font-weight: 400;
  line-height: 20px;
}
.card-3 { /* Alternative 2 */
  background: var(--color-background);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  min-height: 252px;
  color: var(--color-text);
  font-family: "system-ui";
  font-size: 19px;
  font-weight: 400;
}
.link { /* Inline */
  background: transparent;
  color: var(--color-text);
  font-family: "system-ui";
  font-size: var(--type-body-small);
  font-weight: 500;
  line-height: 20px;
}
.link-2 { /* Inline 2 */
  background: transparent;
  border-radius: var(--radius-full);
  min-height: 36px;
  color: var(--color-text);
  font-family: "system-ui";
  font-size: var(--type-body-small);
  font-weight: 400;
  line-height: 20px;
}
.link-3 { /* Inline 3 */
  background: transparent;
  color: var(--color-text-muted);
  font-family: "system-ui";
  font-size: var(--type-body-small);
  font-weight: 400;
  line-height: 20px;
}
.input { /* Default */
  background: var(--color-background);
  border: 1px solid #000000;
  border-radius: var(--radius-lg);
  padding: 0px 20px 0px 40px;
  min-height: 40px;
  color: var(--color-text);
  font-family: "system-ui";
  font-size: var(--type-body-small);
  font-weight: 400;
  line-height: 20px;
}
```
Where a component lists recorded states, write those states using only the colours above; the capture saw that the state exists, not its values. Never invent a state colour.

4. PAGE COMPOSITION
- Light interface on #FFFFFF. Keep that ground; do not add a dark mode or alternate section backgrounds the system does not have.
- #FF6719 is the action colour. Fill the primary action with it and little else — at most one #FF6719-filled element per screen; other accents keep to the uses listed above.
- Type: Figtree (standing in for Cahuenga) for body; -apple-system-ui-serif for interface; Jetbrains Mono for code. Hierarchy comes from size and weight, not colour or decoration.
- Everything sits on a 4px grid.
- Corner radii are 4px, 8px, 12px, fully round (pill) — nothing else.
- Body text uses system-ui at around 15px, weights 400, 500, 600, 700.
- Headings use Cahuenga; the largest heading is 32px.
- Primary buttons are #FF6719, 8px radius, weight 600.
- Cards use 12px corners with a soft shadow.
- Corner radii in use: 8px, 9999px (pill), 12px, 4px.

HOW THE ORIGINAL PACES ITS PAGE (BORROW THE RHYTHM, NOT THE LIST)
1. Section, 1208px wide, 36644px tall; H3 centred [content: ~8 words, sentence case]; a strip of 7 small items in one row; 30 small images; 47 controls.
- Content sits in a column about 1208px wide on a 1440px window.
- Most section headings are centred.
- No band uses a gradient background.

COPY SHAPE (MATCH THE LENGTH AND CASE; WRITE YOUR OWN WORDS)
- The page has no single main heading.
- Section headings are medium length: typically 5 words.
- Headings are in sentence case (only the first word capitalised).
- The page describes itself in about 5 words; keep your intro paragraph to about that length.

5. MOTION
Interactive elements change colour, background, border and opacity with the measured timing. Animate nothing else.
```css
a, button, [role="button"], input { transition: color var(--duration-1) var(--ease-1), background-color var(--duration-1) var(--ease-1), border-color var(--duration-1) var(--ease-1), opacity var(--duration-1) var(--ease-1); }
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; scroll-behavior: auto !important; }
}
```
No scroll-triggered fade-ins, parallax, typewriter text or bouncing elements.

6. RESPONSIVE
- The design was measured at 1440px wide. Lay it out for that width first, then let it reflow down to 360px.
- Side padding on small screens: var(--space-4) (16px).
- The largest heading scales fluidly: font-size: clamp(17px, 2.2vw, 32px). Every other size stays fixed.
- Break on composition, not device names: a row of columns drops a column when any column would be narrower than 240px; side-by-side splits stack when either side would be narrower than 320px.
- Spacing steps never change between sizes; use smaller steps from the same list where space is tight.
- On touch screens, every link and button has a hit area of at least 44 × 44px without changing its drawn size.

7. ACCESSIBILITY
- Text pairs that pass for body copy: Text on Background (11.9:1), Text muted on Background (4.5:1), Positive on Background (6.1:1), Text on Surface (10.3:1), Positive on Surface (5.2:1) and On action on Action (7.2:1).
- Only for text 24px and up, or 18.66px bold: Text muted on Surface (3.9:1).
- Use no other text-on-background pairing.
- One h1 per page, headings in order, landmarks (header, nav, main, footer), real buttons and links, a label on every field, alt text on every image, an aria-label on icon-only buttons.
- Every interactive element shows the :focus-visible outline above.

8. DO NOT (THESE ARE WHAT MAKE AI-BUILT SITES LOOK GENERIC)
- Only the two shadows in the tokens, on the components that use them. (2 shadow values were measured; inventing softer ones changes the depth of the page.)
- No gradients: not in backgrounds, text, buttons or borders. (No gradient was measured.)
- Only these colours: #FFFFFF, #232525, #EEEEEE, #363737, #2F6D5D, #000000, #777777, #FF6719, #C8C8C8, #7B61FF. No new tints, no Tailwind defaults (slate, indigo, violet). (They are every colour that covers a measurable part of the page, and the colours its lines and drawings use.)
- #2F6D5D only as small fills (buttons, badges) — never a section background or a large panel. (It covers 0.9% of the page.)
- #FF6719 only as small fills (buttons, badges) — never a section background or a large panel. (It covers 0.3% of the page.)
- Only the fonts in the tokens: Figtree for body; -apple-system-ui-serif for interface; Jetbrains Mono for code. No other typeface, and no Inter, Roboto or system UI font unless it is one of these. (The page is set in system-ui and Cahuenga and -apple-system-ui-serif; the tokens carry what stands in for them.)
- Headings and large text never heavier than weight 500. Do not make headlines bold to add emphasis. (Every size from 24px up is set at weight 500 or lighter.)
- No icon above every feature, no emoji, no decorative blobs, sparkles or glows. (None of these is part of the captured design; they are a coding model's defaults.)
- No glassmorphism, backdrop blur, glowing orbs, noise blobs or neon outlines.
- No row of three identical feature cards as the default section. Vary the layout from section to section: a split, a list, a single large statement, a table.
- No centred-everything hero with a pill badge, a gradient headline and two buttons. Lead with the most specific thing about the product.
- No filler copy: no "Unlock your potential", "Revolutionize", "Seamless", "Elevate", "Supercharge", "Next-gen" or lorem ipsum. Write plain, specific sentences a real company would publish.
- No made-up statistics, testimonials or customer logos. Leave a clearly marked [content: …] slot instead.
- No radius larger than the largest one listed.
- Do not use substack.com's name, logo, copy or images. This is its design system, not its content.

BEFORE YOU FINISH
- Search your code for every colour, font size, spacing value, radius, shadow and easing, and replace anything that is not a variable above.
- Check each section against the order and alignment in Layout, top to bottom.
- Check that no [content: …] slot has been filled with invented facts.
- Search your CSS for gradient(. There should be none.
- Every colour value in your CSS is one of the allowed colours. Nothing else, including in hover states.
- #2F6D5D covers about 0.9% of the page or less, as on the original.
- #FF6719 covers about 0.3% of the page or less, as on the original.
- No text 24px or larger has a font-weight above 500.
- There is no placeholder copy (lorem ipsum, "Feature one", "Your headline here"). Every word is real.
- Take a screenshot at 1440px and put it beside the original. List every difference in colour, weight, spacing, alignment and imagery, and fix each before finishing. Then check 390px wide: nothing scrolls sideways and no heading runs off the screen.
