PostHog
MIT
Developer-focused analytics design system with warm amber CTAs, IBM Plex Sans typography, and precise 4px spacing grid for product interfaces and marketing sites
Colour (72)
color.bordercta1px solid rgb(177, 120, 22)
color.borderfocus2px solid rgb(37, 99, 235)
color.colorbgdarkrgb(35, 37, 29)
color.colorbgpagergb(253, 253, 248)
color.borderstrong1px solid rgb(191, 193, 183)
color.bordersubtle1px solid rgb(210, 211, 204)
color.colorbgbrandrgb(245, 78, 0)
color.colortextnavrgb(0, 0, 0)
color.posthogbgapp#f54e00
color.borderdefault1px solid rgb(182, 183, 175)
color.colortextbodyrgb(55, 65, 81)
color.posthogaccentrgb(235, 157, 42)
color.colorbgsurfacergb(238, 239, 233)
color.colorborderctargb(177, 120, 22)
color.colortextmutedrgb(101, 103, 94)
color.colortextonctargb(35, 37, 29)
color.colortextontabrgb(255, 255, 255)
color.gradientcodebglinear-gradient(90deg, rgb(1, 67, 203), rgb(43, 111, 244) 24.04%, rgb(210, 52, 1) 46.63%, rgb(255, 101, 31) 65.87%, rgb(251, 160, 0) 83.17%, rgb(1, 67, 203))
color.brandprimaryctargb(235, 157, 42)
color.posthogctabrandrgb(245, 78, 0)
color.posthogtextbodyrgb(55, 65, 81)
color.primitivewarm10rgb(253, 253, 248)
color.primitivewarm20rgb(238, 239, 233)
color.primitivewarm30rgb(229, 231, 224)
color.primitivewarm50rgb(210, 211, 204)
color.colortextheadingrgb(17, 24, 39)
color.posthogborderctargb(177, 120, 22)
color.primitiveblue200rgb(150, 219, 250)
color.primitiveblue300rgb(87, 197, 247)
color.primitiveblue400rgb(47, 128, 250)
color.primitiveblue500rgb(43, 111, 244)
color.primitiveblue700rgb(1, 67, 203)
color.primitivebluesys#007aff
color.primitivewarm100rgb(200, 202, 193)
color.primitivewarm150rgb(191, 193, 183)
color.primitivewarm200rgb(182, 183, 175)
color.primitivewarm300rgb(158, 160, 150)
color.primitivewarm400rgb(101, 103, 94)
color.primitivewarm500rgb(77, 79, 70)
color.primitivewarm600rgb(58, 59, 55)
color.primitivewarm700rgb(44, 45, 42)
color.primitivewarm800rgb(35, 37, 29)
color.primitivewarm900rgb(27, 28, 26)
color.primitivewarm950rgb(17, 17, 17)
color.swiperthemecolor#007aff
color.brandsecondaryctargb(47, 128, 250)
color.colorbgctaprimaryrgb(235, 157, 42)
color.colorborderstrongrgb(191, 193, 183)
Spacing (46)
spacing.spacexs2px
spacing.posthogspacexs2px
spacing.space14px
spacing.spacesm4px
spacing.posthogspace14px
spacing.posthogspacesm4px
spacing.spacemd5px
spacing.posthogspacemd5px
spacing.spacelg6px
spacing.posthogspacelg6px
spacing.space28px
spacing.spacexl8px
spacing.posthogspace28px
spacing.posthogspacexl8px
spacing.space2xl10px
spacing.posthogspace2xl10px
spacing.space312px
spacing.space416px
spacing.space3xl16px
spacing.posthogspace416px
spacing.posthogspace3xl16px
spacing.space520px
spacing.space624px
spacing.space832px
spacing.space1040px
spacing.space1248px
spacing.space1664px
spacing.space2080px
spacing.space2496px
spacing.bpxs425px
spacing.bpnarrow482px
spacing.bpsm640px
spacing.containersm640px
spacing.bpmd768px
spacing.containermd768px
spacing.bpwidenav800px
spacing.bpmid900px
spacing.bplg1024px
spacing.containerlg1024px
spacing.bpcontent1076px
spacing.bpwide1160px
spacing.bpxl1280px
spacing.containerxl1280px
spacing.containermax1280px
spacing.bp2xl1536px
spacing.container2xl1536px
Radius (8)
radiussm0px 0px 6px 6px
posthogradiussm0px 0px 6px 6px
radiusmd4px
posthogradiusmd4px
radiuslg5px
posthogradiuslg5px
radiusfull6px
posthogradiusfull6px
Shadow (3)
effect.shadownonenone
effect.shadowsliderrgb(87, 197, 247) 0px 0px 5px
effect.shadowfocusinput0 0 2px 2px var(--color-border-default)
# PostHog Design System — layout.md
---
## 0. Quick Reference
**How to apply:** Use as `var(--token-name)` in CSS, `style={{ prop: 'var(--token-name)' }}` in JSX, or `bg-[var(--token-name)]` in Tailwind.
**Stack:** React/Gatsby site · Tailwind CSS (v3, utility-class-based) · CSS custom properties for theming · Token source: reconstructed-from-computed + 6 extracted CSS vars
```css
/* ── Core Colours ── */
--posthog-cta-primary: rgb(235, 157, 42); /* Primary CTA button bg (amber/gold) */
--posthog-cta-secondary: rgb(47, 128, 250); /* Secondary CTA / active tab bg (blue) */
--posthog-cta-brand: rgb(245, 78, 0); /* Brand orange — squeak buttons, swiper */
--posthog-text-heading: rgb(17, 24, 39); /* h1–h3 colour */
--posthog-text-body: rgb(55, 65, 81); /* Body paragraph colour */
--posthog-text-on-dark: rgb(35, 37, 29); /* Dark-surface primary text */
--posthog-border-cta: rgb(177, 120, 22); /* CTA button border (amber darken) */
/* ── Typography ── */
--posthog-font-sans: "IBM Plex Sans Variable", "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "avenir next", avenir, "segoe ui", "helvetica neue", helvetica, Ubuntu, roboto, noto, arial, sans-serif;
--posthog-font-mono: "Source Code Pro", Menlo, Consolas, monaco, monospace;
/* ── Spacing (4px base — note: 5px/6px/10px tokens exist but are off-grid) ── */
--posthog-space-1: 4px; /* xs gap, button inner gap */
--posthog-space-2: 8px; /* button padding-x, h1 margin-bottom */
--posthog-space-4: 16px; /* 3xl — component internal spacing */
/* ── Radius ── */
--posthog-radius-md: 4px; /* Most buttons (17 elements) */
--posthog-radius-lg: 6px; /* CTA buttons, tabs top corners (13 elements) */
/* ── Motion ── */
--posthog-duration-fast: 0.15s;
--posthog-ease-ui: cubic-bezier(0.4, 0, 0.2, 1); /* Tailwind default ease */
```
```tsx
// Primary CTA Button — correct token usage
<a
href="/signup"
className="relative inline-flex items-center gap-1 px-4 py-2 text-sm font-semibold rounded-[6px]
bg-[rgb(235,157,42)] border border-[rgb(177,120,22)] text-[rgb(35,37,29)]
transition-all duration-[0.15s] ease-[cubic-bezier(0.4,0,0.2,1)]
hover:-translate-y-px active:scale-[0.99] active:translate-y-[3px]"
>
Get started – free
</a>
```
**NEVER rules:**
1. **NEVER** use Inter, Roboto, or Arial as the primary font — PostHog uses `IBM Plex Sans Variable`
2. **NEVER** use rounded corners > 6px on buttons — the brand maximum is `6px`
3. **NEVER** hardcode `#f54e00` inline — use `var(--posthog-cta-brand)` or the Tailwind class
4. **NEVER** use spacing values not on the 4px grid (5px, 6px, 10px exist in legacy tokens — do not propagate)
5. **NEVER** omit the `hover:-translate-y-px` / `active:translate-y-[3px]` press effect on `.click` interactive elements
6. **NEVER** use pill-shaped buttons (border-radius ≥ 50px) — PostHog uses sharp-to-subtle radius only
7. **NEVER** set `font-weight: 800` on anything other than the h1 hero — heading weight scale is 800/700/600
**Full design system → see layout.md**
---
## 1. Design Direction & Philosophy
### Character & Aesthetic Intent
PostHog is an opinionated developer tool with a **product-led, no-nonsense personality**. The design is dense and information-rich without feeling cluttered. It communicates through specificity — exact numbers, real code, real product screenshots — rather than marketing abstraction.
The colour palette pairs **warm ambers and oranges** (the primary CTA brand colour) against a near-neutral olive-tinged dark and clean off-whites. This creates warmth without losing technical credibility. Blue appears only as a functional accent (active tab state, secondary CTA).
The site uses a **custom typeface stack** built around IBM Plex Sans Variable — a variable-weight grotesque that reads as both corporate-precise and engineering-forward. Monospace content uses Source Code Pro.
PostHog has strong micro-animation investment: the `.click` pattern lifts elements on hover (`-2px`) and presses them on active (`+3px, scale(0.99)`), creating a tactile affordance. Carousels, floating elements, wiggle states, and bounce are all present.
### What This Design Explicitly Rejects
- **Rounded pill buttons** — radius never exceeds 6px
- **Bright white backgrounds as default** — surfaces use warm off-whites with olive undertones
- **Generic system fonts** — IBM Plex Sans Variable is mandatory
- **Heavy drop shadows** — elevation is communicated through borders and colour contrast, not shadows
- **Generic blue-primary CTAs** — the primary action colour is amber/gold (`rgb(235, 157, 42)`), not blue
- **Flat monotone typography** — aggressive weight contrast (800/700/600/500/400) is integral to hierarchy
### Mood
Technical warmth. Precise but playful. Hedgehog mascot is canonical. The design invites exploration through animation cues while remaining scannable for engineers evaluating a product.
---
## 2. Colour System
### Tier 1: Primitives
```css
/* ── Warm Neutral Scale (olive-tinted greys) ── */
--primitive-warm-950: rgb(17, 17, 17); /* near-black */
--primitive-warm-900: rgb(27, 28, 26); /* deep dark surface */
--primitive-warm-800: rgb(35, 37, 29); /* dark text / dark surface */
--primitive-warm-700: rgb(44, 45, 42); /* dark border */
--primitive-warm-600: rgb(58, 59, 55); /* muted dark border */
--primitive-warm-500: rgb(77, 79, 70); /* secondary text dark */
--primitive-warm-400: rgb(101, 103, 94); /* muted text */
--primitive-warm-300: rgb(158, 160, 150); /* light accent / disabled */
--primitive-warm-200: rgb(182, 183, 175); /* light border */
--primitive-warm-150: rgb(191, 193, 183); /* lighter border */
--primitive-warm-100: rgb(200, 202, 193); /* very light */
--primitive-warm-50: rgb(210, 211, 204); /* near-white accent */
--primitive-warm-30: rgb(229, 231, 224); /* off-white surface */
--primitive-warm-20: rgb(238, 239, 233); /* near-white input bg */
--primitive-warm-10: rgb(253, 253, 248); /* lightest surface */
/* ── Brand Amber/Orange ── */
--primitive-amber-600: rgb(177, 120, 22); /* CTA border, dark amber */
--primitive-amber-500: rgb(205, 132, 7); /* link bg hover state */
--primitive-amber-400: rgb(235, 157, 42); /* primary CTA background */
--primitive-amber-300: rgb(251, 160, 0); /* gradient stop, warm highlight */
/* ── Brand Orange ── */
--primitive-orange-600: rgb(210, 52, 1); /* gradient stop */
--primitive-orange-500: rgb(245, 78, 0); /* brand orange — squeak/swiper */
--primitive-orange-400: rgb(255, 101, 31); /* gradient stop */
/* ── Brand Blue ── */
--primitive-blue-700: rgb(1, 67, 203); /* gradient stop, dark blue */
--primitive-blue-500: rgb(43, 111, 244); /* gradient stop */
--primitive-blue-400: rgb(47, 128, 250); /* secondary CTA / active tab */
--primitive-blue-300: rgb(87, 197, 247); /* slider handle hover */
--primitive-blue-200: rgb(150, 219, 250); /* slider handle focus */
--primitive-blue-sys: #007aff; /* iOS-style blue — swiper theme */
/* ── Semantic Text ── */
--primitive-text-dark: rgb(17, 24, 39); /* headings */
--primitive-text-body: rgb(55, 65, 81); /* body */
--primitive-text-white: rgb(255, 255, 255); /* on-dark text */
```
### Tier 2: Semantic Aliases
```css
/* ── Text ── */
--color-text-heading: rgb(17, 24, 39); /* h1–h3 — extracted: high confidence */
--color-text-body: rgb(55, 65, 81); /* body paragraphs — extracted: high confidence */
--color-text-muted: rgb(101, 103, 94); /* secondary/muted — reconstructed: high confidence */
--color-text-on-cta: rgb(35, 37, 29); /* text on amber CTA buttons */
--color-text-on-tab: rgb(255, 255, 255); /* text on active tab (blue bg) */
--color-text-nav: rgb(0, 0, 0); /* navigation links — extracted: high confidence */
/* ── Backgrounds ── */
--color-bg-page: rgb(253, 253, 248); /* page/app background — reconstructed: moderate confidence */
--color-bg-surface: rgb(238, 239, 233); /* card/panel surface — reconstructed: moderate confidence */
--color-bg-dark: rgb(35, 37, 29); /* dark section background */
--color-bg-cta-primary: rgb(235, 157, 42); /* primary CTA bg — extracted: high confidence */
--color-bg-cta-secondary: rgb(47, 128, 250); /* secondary CTA / tab active bg */
--color-bg-brand: rgb(245, 78, 0); /* brand orange (squeak, swiper) */
/* ── Borders ── */
--color-border-cta: rgb(177, 120, 22); /* CTA button border — extracted: high confidence */
--color-border-default: rgb(182, 183, 175); /* default UI borders */
--color-border-strong: rgb(191, 193, 183); /* stronger border variant */
/* ── Special / Gradients ── */
--color-gradient-code: linear-gradient(90deg, rgb(1, 67, 203), rgb(43, 111, 244) 24.04%, rgb(210, 52, 1) 46.63%, rgb(255, 101, 31) 65.87%, rgb(251, 160, 0) 83.17%, rgb(1, 67, 203)); /* code element gradient text */
```
### Tier 3: Original CSS Variables (preserve exactly)
```css
/* ── Extracted CSS custom properties — use these names verbatim ── */
--squeak-primary-color: 0, 0, 0; /* Squeak comment component primary */
--squeak-default-avatar-fill: 255, 255, 255; /* Squeak avatar fill */
--squeak-button-color: 245, 78, 0; /* Squeak CTA button — brand orange */
--swiper-pagination-color: #f54e00; /* Swiper active pagination dot */
--swiper-pagination-bullet-inactive-color: #000; /* Swiper inactive dot */
--swiper-theme-color: #007aff; /* Swiper accent (iOS blue) */
```
### Colour Usage Table
| Token | Value | Usage |
|---|---|---|
| `--color-bg-cta-primary` | `rgb(235, 157, 42)` | **Primary CTA buttons** — 5 dominant instances |
| `--color-bg-cta-secondary` | `rgb(47, 128, 250)` | Active tab, secondary CTA |
| `--color-bg-brand` | `rgb(245, 78, 0)` | Brand identity, Squeak buttons |
| `--color-text-heading` | `rgb(17, 24, 39)` | All headings h1–h3 |
| `--color-text-body` | `rgb(55, 65, 81)` | Body text |
| `--color-border-cta` | `rgb(177, 120, 22)` | Button borders on amber CTA |
---
## 3. Typography System
### Font Stacks
```css
--posthog-font-sans: "IBM Plex Sans Variable", "IBM Plex Sans", -apple-system,
BlinkMacSystemFont, "avenir next", avenir, "segoe ui", "helvetica neue",
helvetica, Ubuntu, roboto, noto, arial, sans-serif;
--posthog-font-mono: "Source Code Pro", Menlo, Consolas, monaco, monospace;
/* Specialty fonts (editorial/decorative — use sparingly) */
--posthog-font-display: "Open Runde", sans-serif; /* variable weight 400–700 */
--posthog-font-serif: "Charter", Georgia, serif; /* blog / long-form content */
--posthog-font-decorative: "Fairytale", serif; /* extreme decorative use only */
```
### Composite Type Scale
```css
/* ── Heading 1 — Hero ── */
--type-h1: {
font-family: var(--posthog-font-sans);
font-size: 24px;
font-weight: 800; /* extracted: high confidence */
line-height: 32px;
letter-spacing: -0.6px;
color: var(--color-text-heading);
}
/* ── Heading 2 — Section titles ── */
--type-h2: {
font-family: var(--posthog-font-sans);
font-size: 21.4286px; /* ≈ 1.5rem at 14.2857px base */
font-weight: 700;
line-height: 30px;
letter-spacing: -0.5357px;
color: var(--color-text-heading);
}
/* ── Heading 3 — Sub-section ── */
--type-h3: {
font-family: var(--posthog-font-sans);
font-size: 19.2857px;
font-weight: 600;
line-height: 30px;
letter-spacing: -0.4821px;
color: var(--color-text-heading);
}
/* ── Body — Default prose ── */
--type-body: {
font-family: var(--posthog-font-sans);
font-size: 15px;
font-weight: 400;
line-height: 22.5px; /* 1.5× — extracted: high confidence */
letter-spacing: normal;
color: var(--color-text-body);
}
/* ── Body Large — Navigation, prominent inline text ── */
--type-body-lg: {
font-family: var(--posthog-font-sans);
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: normal;
color: var(--color-text-nav);
}
/* ── Body Small — Secondary labels, metadata ── */
--type-body-sm: {
font-family: var(--posthog-font-sans);
font-size: 14px;
font-weight: 400;
line-height: 20px;
letter-spacing: normal;
color: var(--color-text-body);
}
/* ── Label — Tabs, badges, UI controls ── */
--type-label: {
font-family: var(--posthog-font-sans);
font-size: 14px;
font-weight: 600;
line-height: 20px;
letter-spacing: normal;
color: var(--color-text-on-tab);
}
/* ── Button — CTA, action labels ── */
--type-button: {
font-family: var(--posthog-font-sans);
font-size: 13px;
font-weight: 500;
line-height: 13px; /* tight — button height controlled by padding */
letter-spacing: normal;
color: var(--color-text-on-cta);
}
/* ── Caption / Fine Print ── */
--type-caption: {
font-family: var(--posthog-font-sans);
font-size: 12px;
font-weight: 400;
line-height: 17.875px;
letter-spacing: normal;
color: var(--color-text-muted);
}
/* ── Code — Inline and block ── */
--type-code: {
font-family: var(--posthog-font-mono);
font-size: 14px;
font-weight: 500;
line-height: 20px;
letter-spacing: normal;
color: transparent; /* gradient text applied via background-clip */
}
```
### Typography Pairing Rules
- **Hero sections:** h1 (800) + body (400) — never combine 800 + 700 adjacent headings
- **Feature cards:** h3 (600) + body-sm (400)
- **Navigation:** body-lg (16px/400) for top-level, body-sm (14px/600) for active/tab items
- **Code blocks:** Always `Source Code Pro`, never IBM Plex Sans for code
- **The gradient text effect** on code uses `background: var(--color-gradient-code); -webkit-background-clip: text; color: transparent;`
---
## 4. Spacing & Layout
```css
/* ── Base unit: 4px ── */
/* WARNING: extracted tokens include off-grid values (5px, 6px, 10px).
These are legacy/component-internal. New work MUST use the 4px grid. */
--space-1: 4px; /* gap-1 — tightest: button icon gap, inline elements */
--space-2: 8px; /* gap-2 — button padding-x, h1 margin-bottom, h1 gap */
--space-3: 12px; /* gap-3 — tab padding-x */
--space-4: 16px; /* gap-4 — component internal rhythm */
--space-5: 20px; /* gap-5 — tab padding-y approx */
--space-6: 24px; /* gap-6 — section sub-spacing */
--space-8: 32px; /* gap-8 — section padding */
--space-10: 40px; /* gap-10 — medium section gaps */
--space-12: 48px; /* gap-12 — large section gaps */
--space-16: 64px; /* gap-16 — hero vertical padding */
--space-20: 80px; /* gap-20 — between major sections */
--space-24: 96px; /* gap-24 — page top/bottom padding */
/* ── Legacy off-grid tokens (extracted — do not propagate) ── */
--posthog-space-xs: 2px; /* original: --space-xs — off-grid, component internal */
--posthog-space-sm: 4px; /* original: --space-sm */
--posthog-space-md: 5px; /* original: --space-md — off-grid */
--posthog-space-lg: 6px; /* original: --space-lg — off-grid */
--posthog-space-xl: 8px; /* original: --space-xl */
--posthog-space-2xl: 10px; /* original: --space-2xl — off-grid */
--posthog-space-3xl: 16px; /* original: --space-3xl */
```
### Grid System
```css
/* ── Container widths ── */
--container-sm: 640px;
--container-md: 768px;
--container-lg: 1024px;
--container-xl: 1280px;
--container-2xl: 1536px;
--container-max: 1280px; /* primary content max-width — reconstructed: moderate confidence */
/* ── Responsive breakpoints (extracted from media queries) ── */
--bp-xs: 425px; /* small mobile */
--bp-sm: 640px; /* Tailwind sm */
--bp-md: 768px; /* Tailwind md */
--bp-lg: 1024px; /* Tailwind lg */
--bp-xl: 1280px; /* Tailwind xl */
--bp-2xl: 1536px; /* Tailwind 2xl */
/* Non-standard breakpoints (extracted — handle explicitly if needed) */
--bp-narrow: 482px;
--bp-wide-nav: 800px;
--bp-mid: 900px;
--bp-content: 1076px;
--bp-wide: 1160px;
```
### Layout Decision Rules
- **Navigation:** `display: block` at top level; flex row for nav items — extracted: high confidence
- **Buttons:** `display: flex; flex-direction: row; align-items: center; justify-content: space-between; gap: 4px` — extracted: high confidence
- **Hero section:** Full-width container, content max-width `1280px`, centred
- **Feature grids:** CSS Grid preferred for 2–3 column feature layouts (inferred from component inventory)
- **Code blocks:** `display: inline` for inline code; block-level for code panels with gradient background
---
## 5. Page Structure & Layout Patterns
### 5.1 Section Map
| # | Section | Layout Type | Key Elements | Notes |
|---|---|---|---|---|
| 1 | Navigation / Header | `display: block`, full-width | Logo, nav links (14 instances), CTA button | Extracted from `role_navigation` |
| 2 | Hero | Full-width container, flex col | h1 (24px/800), body copy, primary CTA (`rgb(235,157,42)`), code snippet | (inferred) |
| 3 | Social proof / Logos ticker | Full-width, overflow hidden | Company logo strip, `features-ticker` animation at -2700px | `hogfather-roll` / ticker animation extracted |
| 4 | Feature Tabs | Contained, tabbed UI | Tab bar (`rgb(47,128,250)` active bg), code panels, product screenshots | Tab computed style extracted directly |
| 5 | Feature Grid / Product OS | Contained grid, 2–3 col | h2 section title, feature cards (h3+body), CTAs | (inferred from component inventory) |
| 6 | Data Stack / How it works | Contained, flex row | h2, numbered steps or cards, body text | (inferred) |
| 7 | Social proof / Testimonials | Full-width swiper | Swiper pagination (`#f54e00`), testimonial cards | Swiper tokens extracted |
| 8 | Pricing CTA / Install | Contained, centred | h2, install command (code block with gradient), CTA button | Code gradient extracted |
| 9 | Footer | Full-width, multi-col grid | Nav links, legal, social | (inferred) |
### 5.2 Layout Patterns
**Navigation:**
- Container: full-width `display: block` wrapper
- Inner: flex row, `justify-content: space-between`, `align-items: center`
- Nav links: `font-size: 16px; font-weight: 400; color: rgb(0,0,0)` — tight spacing, no visible border
- CTA in nav: amber button (`rgb(235, 157, 42)`) with `border: 1px solid rgb(177, 120, 22)`, `border-radius: 6px`
**Hero:**
- Full viewport width, content max-width `1280px`, centred
- h1 has `display: flex; flex-direction: row; gap: 4px` — heading spans/words are likely flex children (emoji or icon inline)
- Primary CTA uses `.click` interaction pattern: `hover: top -2px`, `active: top 3px, scale(0.99)`
- Code snippet inline with gradient text (`--color-gradient-code`)
**Feature Tabs:**
- Tab items: `border-radius: 6px 6px 0px 0px` (top-rounded only)
- Active tab: `background: rgb(47, 128, 250); color: #fff; font-weight: 600`
- Inactive tabs: no background, text colour `var(--color-text-body)` (inferred)
- Tab padding: `10px 12px`
**CTA Buttons (primary):**
- `background: rgb(235, 157, 42)`
- `border: 1px solid rgb(177, 120, 22)`
- `border-radius: 6px`
- `font-size: 13px–16px; font-weight: 500–600`
- Layout: `display: flex; align-items: center; gap: 4px`
- Interaction: lift on hover (`-2px`), press on active (`+3px, scale 0.99`)
### 5.3 Visual Hierarchy
1. **Hero h1** is the dominant typographic element — 24px/800 with tight negative tracking (`-0.6px`)
2. **Primary CTA** in amber stands visually apart from the neutral palette — draws immediate attention
3. **Active tab state** uses `rgb(47, 128, 250)` — the only significant blue in the UI outside the gradient
4. **Code blocks** use the rainbow gradient (blue → orange → amber → blue) — a strong brand signal, appears in the hero and feature sections
5. **Section headings (h2)** at `21.4px/700` create clear scan stops with tight negative tracking
6. **Body copy** in `rgb(55, 65, 81)` recedes behind headings — sufficient contrast on warm whites
**CTA Placement:**
- Primary CTA is in the navigation (amber) and hero (amber + secondary blue variant) — extracted: high confidence
- Feature sections use text CTAs or labelled button groups
- Pricing/install sections repeat the amber CTA
### 5.4 Content Patterns
**Pattern A — Hero with code:**
h1 headline → body description → amber CTA button + secondary link → inline code block with gradient text
**Pattern B — Feature tab panel:**
Tab bar → active tab shows product screenshot or code panel → supporting copy alongside
**Pattern C — Feature card grid:**
Section h2 (centred or left) → 2–3 column grid of cards → each card: icon + h3 + body text → optional "Learn more" link (amber, `border-radius: 0 0 6px 6px` bottom)
**Pattern D — Testimonial/social proof:**
Swiper carousel with orange pagination dots (`#f54e00`) → logo grid or quote cards
---
## 6. Component Patterns
### 6.1 Primary CTA Button
**Anatomy:** `<a>` or `<button>` → optional leading icon → label text → optional trailing icon/arrow
**Token-to-property mappings:**
| State | Background | Border | Text colour | Transform |
|---|---|---|---|---|
| Default | `rgb(235, 157, 42)` | `1px solid rgb(177, 120, 22)` | `rgb(35, 37, 29)` | none |
| Hover | `rgb(235, 157, 42)` | same | same | `translateY(-2px)` |
| Active | `rgb(235, 157, 42)` | same | same | `translateY(3px) scale(0.99)` |
| Focus | same | `2px solid rgb(37, 99, 235)` outline | same | none |
| Disabled | `rgb(210, 211, 204)` | `1px solid rgb(182, 183, 175)` | `rgb(158, 160, 150)` | none |
```tsx
import { forwardRef } from 'react'
interface CTAButtonProps extends React.AnchorHTMLAttributes<HTMLAnchorElement> {
variant?: 'primary' | 'secondary'
size?: 'sm' | 'md' | 'lg'
disabled?: boolean
children: React.ReactNode
}
const CTAButton = forwardRef<HTMLAnchorElement, CTAButtonProps>(
({ variant = 'primary', size = 'md', disabled, children, className = '', ...props }, ref) => {
const base = [
'relative inline-flex items-center justify-center gap-1',
'font-["IBM_Plex_Sans_Variable",sans-serif] font-medium no-underline',
'border transition-all duration-[0.15s] ease-[cubic-bezier(0.4,0,0.2,1)]',
'select-none',
// .click pattern
'hover:-translate-y-px',
'active:translate-y-[3px] active:scale-[0.99]',
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[rgb(37,99,235)]',
]
const variants = {
primary: [
'bg-[rgb(235,157,42)] border-[rgb(177,120,22)] text-[rgb(35,37,29)]',
'rounded-[6px]',
],
secondary: [
'bg-[rgb(47,128,250)] border-transparent text-white',
'rounded-[6px]',
],
}
const sizes = {
sm: 'text-[13px] leading-[13px] px-3 py-1.5',
md: 'text-[13px] leading-[13px] px-4 py-2',
lg: 'text-[15px] leading-[22.5px] px-5 py-2.5',
}
const disabledStyles = disabled
? 'bg-[rgb(210,211,204)] border-[rgb(182,183,175)] text-[rgb(158,160,150)] pointer-events-none cursor-default'
: ''
return (
<a
ref={ref}
aria-disabled={disabled}
className={[...base, ...variants[variant], sizes[size], disabledStyles, className].join(' ')}
{...props}
>
{children}
</a>
)
}
)
CTAButton.displayName = 'CTAButton'
export default CTAButton
// Usage:
// <CTAButton href="/signup" variant="primary" size="md">Get started – free</CTAButton>
// <CTAButton href="/demo" variant="secondary" size="md">Watch a demo</CTAButton>
// <CTAButton disabled>Unavailable</CTAButton>
```
---
### 6.2 Tab Component
**Anatomy:** Tab bar wrapper → individual tab items (top-rounded) → active indicator via background colour change
| State | Background | Text | Radius |
|---|---|---|---|
| Active | `rgb(47, 128, 250)` | `#fff` | `6px 6px 0 0` |
| Inactive | transparent | `rgb(55, 65, 81)` | `6px 6px 0 0` |
| Hover (inactive) | `rgb(238, 239, 233)` | `rgb(17, 24, 39)` | same |
| Focus | outline `2px rgb(37, 99, 235)` | — | — |
```tsx
interface TabProps {
label: string
active?: boolean
onClick?: () => void
}
const Tab = ({ label, active = false, onClick }: TabProps) => (
<button
role="tab"
aria-selected={active}
onClick={onClick}
className={[
'px-3 py-[10px] text-[14px] font-semibold leading-[20px]',
'rounded-t-[6px] rounded-b-none',
'border-0 cursor-pointer',
'transition-[color,background-color] duration-[0.15s] ease-[cubic-bezier(0.4,0,0.2,1)]',
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-[rgb(37,99,235)]',
active
? 'bg-[rgb(47,128,250)] text-white'
: 'bg-transparent text-[rgb(55,65,81)] hover:bg-[rgb(238,239,233)] hover:text-[rgb(17,24,39)]',
].join(' ')}
>
{label}
</button>
)
```
---
### 6.3 Code Block (Gradient Text)
**Anatomy:** `<code>` or `<pre><code>` → gradient applied via `background-clip: text`
```tsx
const InlineCode = ({ children }: { children: React.ReactNode }) => (
<code
className={[
'font-["Source_Code_Pro",Menlo,Consolas,monaco,monospace]',
'text-[14px] font-medium leading-[20px]',
'rounded-[4px]',
// Gradient text
'bg-gradient-to-r from-[rgb(1,67,203)] via-[rgb(255,101,31)] to-[rgb(251,160,0)]',
'bg-clip-text text-transparent',
].join(' ')}
>
{children}
</code>
)
```
---
### 6.4 Navigation Item
**Anatomy:** `<nav>` wrapper → `<a>` or `<button>` items → optional dropdown indicator
| State | Colour | Weight | Transform |
|---|---|---|---|
| Default | `rgb(0, 0, 0)` | 400 | none |
| Hover | `rgb(245, 78, 0)` (brand orange) | 400 | none (inferred) |
| Active / current | `rgb(245, 78, 0)` | 500–600 | none (inferred) |
| Focus | outline `2px rgb(37, 99, 235)` | — | — |
```tsx
const NavItem = ({ href, label, active }: { href: string; label: string; active?: boolean }) => (
<a
href={href}
className={[
'text-[16px] font-normal leading-[24px]',
'font-["IBM_Plex_Sans_Variable",sans-serif]',
'no-underline transition-colors duration-[0.15s] ease-[cubic-bezier(0.4,0,0.2,1)]',
'focus-visible:outline focus-visible:outline-2 focus-visible:outline-[rgb(37,99,235)]',
active ? 'text-[rgb(245,78,0)] font-medium' : 'text-black hover:text-[rgb(245,78,0)]',
].join(' ')}
>
{label}
</a>
)
```
---
### 6.5 Feature Card
**Anatomy:** Card container → optional icon → h3 heading → body text → optional CTA link with bottom-only rounded corners
| State | Border | Shadow | Transform |
|---|---|---|---|
| Default | `1px solid rgb(182, 183, 175)` | none | none |
| Hover | `1px solid rgb(158, 160, 150)` | none | `translateY(-2px)` (inferred) |
| Focus-within | outline | — | — |
```tsx
const FeatureCard = ({
icon,
title,
body,
href,
}: {
icon?: React.ReactNode
title: string
body: string
href?: string
}) => (
<div
className={[
'flex flex-col gap-2 p-4',
'border border-[rgb(182,183,175)] rounded-[4px]',
'bg-[rgb(253,253,248)]',
'transition-transform duration-[0.15s] ease-[cubic-bezier(0.4,0,0.2,1)]',
'hover:-translate-y-px',
].join(' ')}
>
{icon && <div className="w-8 h-8">{icon}</div>}
<h3 className="text-[19.2857px] font-semibold leading-[30px] tracking-[-0.482143px] text-[rgb(17,24,39)] m-0">
{title}
</h3>
<p className="text-[15px] font-normal leading-[22.5px] text-[rgb(55,65,81)] m-0">
{body}
</p>
{href && (
<a
href={href}
className={[
'mt-auto text-[13px] font-medium text-[rgb(35,37,29)]',
'rounded-[0_0_6px_6px]',
'transition-colors duration-[0.15s]',
'hover:text-[rgb(245,78,0)]',
].join(' ')}
>
Learn more →
</a>
)}
</div>
)
```
---
### 6.6 Input / Form Field
**Anatomy:** `<input>` → label → optional error message
| State | Border | Background | Shadow |
|---|---|---|---|
| Default | `1px solid rgb(182, 183, 175)` | `rgb(253, 253, 248)` | none |
| Focus | `1px solid rgb(37, 99, 235)` | `rgb(255, 255, 255)` | `0 0 0 1px rgb(37,99,235)` |
| Error | `1px solid rgb(245, 78, 0)` | `rgb(253, 253, 248)` | none |
| Disabled | `1px solid rgb(210, 211, 204)` | `rgb(238, 239, 233)` | none |
```tsx
const TextInput = ({
label,
error,
disabled,
...props
}: React.InputHTMLAttributes<HTMLInputElement> & { label: string; error?: string }) => (
<div className="flex flex-col gap-1">
<label className="text-[14px] font-medium leading-[20px] text-[rgb(17,24,39)]">
{label}
</label>
<input
disabled={disabled}
className={[
'px-3 py-2 text-[15px] font-normal leading-[22.5px]',
'rounded-[4px] border outline-none',
'transition-[border-color,box-shadow] duration-[0.15s] ease-[cubic-bezier(0.4,0,0.2,1)]',
'font-["IBM_Plex_Sans_Variable",sans-serif]',
error
? 'border-[rgb(245,78,0)] bg-[rgb(253,253,248)]'
: 'border-[rgb(182,183,175)] bg-[rgb(253,253,248)]',
'focus:border-[rgb(37,99,235)] focus:shadow-[0_0_0_1px_rgb(37,99,235)] focus:bg-white',
disabled ? 'border-[rgb(210,211,204)] bg-[rgb(238,239,233)] cursor-default' : '',
].join(' ')}
{...props}
/>
{error && (
<span className="text-[12px] font-normal leading-[17.875px] text-[rgb(245,78,0)]">
{error}
</span>
)}
</div>
)
```
---
## 7. Elevation & Depth
PostHog uses **border-based depth** over shadow-based depth. Heavy drop shadows are absent from the extracted styles.
```css
/* ── Border scale ── */
--border-subtle: 1px solid rgb(210, 211, 204); /* disabled/muted state borders */
--border-default: 1px solid rgb(182, 183, 175); /* standard UI borders */
--border-strong: 1px solid rgb(191, 193, 183); /* emphasis borders */
--border-cta: 1px solid rgb(177, 120, 22); /* amber CTA button border */
--border-focus: 2px solid rgb(37, 99, 235); /* focus ring (Tailwind default) */
/* ── Shadow scale — minimal by design ── */
--shadow-none: none; /* default for all surface types */
--shadow-slider: rgb(87, 197, 247) 0px 0px 5px; /* rc-slider handle active state */
--shadow-focus-input: 0 0 2px 2px var(--color-border-default); /* focus-within form group */
/* ── Z-index scale ── */
--z-base: 0;
--z-raised: 10; /* focus-within relative stacking, popovers */
--z-nav: 20; /* navigation bar */
--z-modal: 30; /* modals, drawers */
--z-toast: 40; /* toast notifications (Radix slideIn/slideRight) */
--z-overlay: 50; /* full-screen overlays */
```
**Layering principle:** Interactive UI layers (nav, modals, toasts) stack numerically. Components at the same level use `border` to differentiate rather than shadow. Toasts use `slideIn`/`swipeOut` animations from Radix UI.
---
## 8. Motion
```css
/* ── Duration tokens ── */
--duration-instant: 0s;
--duration-fast: 0.15s; /* UI transitions: colour, bg, border — 42 elements */
--duration-moderate: 0.25s; /* slide and scale animations */
--duration-slow: 4s; /* background/carousel progress animations */
--duration-slowest: 4.9s; /* extended ambient animations */
/* ── Easing tokens ── */
--ease-default: ease; /* 185 elements — ambient animations */
--ease-ui: cubic-bezier(0.4, 0, 0.2, 1); /* UI state transitions (Tailwind ease-in-out) */
--ease-in-out: ease-in-out; /* slide transform animations */
/* ── Standard transition (use on all interactive UI components) ── */
--transition-ui: color var(--duration-fast) var(--ease-ui),
background-color var(--duration-fast) var(--ease-ui),
border-color var(--duration-fast) var(--ease-ui),
text-decoration-color var(--duration-fast) var(--ease-ui),
fill var(--duration-fast) var(--ease-ui),
stroke var(--duration-fast) var(--ease-ui);
```
### Named Animations (use sparingly — brand-defined)
| Name | Duration | Usage |
|---|---|---|
| `float` | `ease` cycling | Mascot/illustration floating (0→-6px→0) |
| `bounce` | `1s ease` | Chevron/arrow bounce on `.group:hover .bounce` |
| `wobble` | ambient | Decorative element side-to-side |
| `breathe` | ambient | Subtle scale pulse (1→1.03→1) |
| `shimmer` | looping | Skeleton loading states |
| `slideUpFadeIn` | `0.25s` | Content entrance (opacity 0→1, translateY 10→0) |
| `fadeIn` | `0.25s` | Modal/panel fade + scale (0.9→1) |
| `ping` | looping | Notification badge pulse |
| `features-ticker` | `4s–4.9s` | Logo strip scroll (-2700px translate) |
| `hero-carousel-progress` | `4s` | Progress bar fill (0→100% width) |
| `speechBubble` | `0.25s ease-in-out` | Tooltip/speech bubble entrance |
### Motion Rules
- **All colour/background/border changes:** use `--transition-ui` (0.15s, ease-in-out)
- **Position changes (hover lift):** `transition: all` or explicit transform, no delay
- **Ambient loops** (ticker, carousel): use `--duration-slow` / `--duration-slowest` with `ease`
- **Entrance animations:** prefer `slideUpFadeIn` or `fadeIn` at `0.25s`
- **Respect `prefers-reduced-motion`:** all ambient animations (float, wobble, breathe, ticker) must be paused under `@media (prefers-reduced-motion: reduce)`
---
## 9. Anti-Patterns & Constraints
**1. Using Inter, Arial, or Roboto as the font**
→ **Why it fails:** AI agents default to `font-family: 'Inter', sans-serif` because it's ubiquitous in design systems. PostHog uses `IBM Plex Sans Variable` — substituting Inter changes the typographic personality (weight axis, metrics, letter proportions all differ). The fallback stack matters too.
→ **Do instead:** Always use `var(--posthog-font-sans)` or the full stack: `"IBM Plex Sans Variable", "IBM Plex Sans", -apple-system, ...`
**2. Hardcoding hex colours instead of tokens**
→ **Why it fails:** Agents generate `bg-amber-400` or `#F59E0B` as an approximate amber, but PostHog's CTA amber is `rgb(235, 157, 42)` — a non-Tailwind value. The brand colour mismatch is immediately visible.
→ **Do instead:** Use `bg-[rgb(235,157,42)]` in Tailwind or `var(--color-bg-cta-primary)` in CSS.
**3. Using rounded pill buttons (border-radius ≥ 50px)**
→ **Why it fails:** Agents trained on modern design systems default to `rounded-full` (9999px) for CTAs. PostHog's extracted radius census shows a maximum of 6px on CTA buttons — pill shapes violate the brand.
→ **Do instead:** Use `rounded-[6px]` (primary CTA) or `rounded-[4px]` (standard buttons). Never exceed 6px on buttons.
**4. Propagating off-grid spacing tokens (5px, 6px, 10px)**
→ **Why it fails:** The extracted spacing scale includes `--posthog-space-md: 5px` and `--posthog-space-lg: 6px`. Agents reading these tokens will reuse them throughout new components, spreading off-grid values that break visual rhythm.
→ **Do instead:** Use only `--space-1` through `--space-24` (4px grid). The off-grid values are legacy/component-internal.
**5. Omitting the `.click` press interaction on CTAs**
→ **Why it fails:** Agents implement `hover:opacity-80` or `hover:brightness-110` as a generic hover state. PostHog uses a physical press metaphor: lift on hover (`-2px`) and press down on active (`+3px, scale 0.99`). Without this, CTAs feel flat and inconsistent with the site.
→ **Do instead:** Always add `hover:-translate-y-px active:translate-y-[3px] active:scale-[0.99]` to interactive CTAs.
**6. Using inline `style={{}}` for colours and spacing**
→ **Why it fails:** Inline styles bypass Tailwind's JIT and prevent theme overrides. They also make the component impossible to override with Tailwind utility classes, and scatter values that should be centralised.
→ **Do instead:** Use Tailwind arbitrary value syntax `bg-[rgb(235,157,42)]` or CSS custom properties via `className`. Reserve inline styles for truly dynamic runtime values only.
**7. Using `font-weight: 800` beyond the h1**
→ **Why it fails:** The weight 800 is extracted exclusively from the h1 hero element. Agents sometimes apply it to h2 or promotional text to "make it bold enough." This collapses the intentional weight hierarchy (800/700/600/500/400).
→ **Do instead:** h1=800, h2=700, h3=600. Never use 800 on any element other than the page-level hero h1.
**8. Missing dark-mode theme variable overrides**
→ **Why it fails:** PostHog uses `data-scheme` attributes with `--bg`, `--text-primary`, `--accent`, `--border` CSS variables that flip under `.dark` and `.light` selectors. Agents hardcoding `text-gray-900` bypass these entirely, causing components to fail in dark mode.
→ **Do instead:** Use the `--bg`, `--text-primary`, `--text-secondary`, `--border` semantic variables where dark-mode support is needed, or use Tailwind's `dark:` prefix consciously.
**9. Constructing Tailwind classes dynamically**
→ **Why it fails:** `className={'bg-' + color}` defeats Tailwind's static analysis. The JIT compiler won't include the class, so the style is silently missing in production.
→ **Do instead:** Use a lookup object mapping state → full class string, or `clsx`/`cn` with complete class literals. Never concatenate partial class names.
**10. Applying `!important` to override theme variables**
→ **Why it fails:** PostHog's theme system uses `data-scheme` CSS variable cascades. `!important` breaks the cascade intentionally built into `.light`/`.dark` selectors, causing components to permanently display in the wrong scheme.
→ **Do instead:** Increase specificity via proper selector nesting or pass the correct `data-scheme` attribute to the component.
**11. Using absolute positioning for multi-column layout**
→ **Why it fails:** Agents sometimes position feature grid items absolutely based on extracted `top`/`left` values from the `.click` hover pattern. PostHog uses CSS Grid and Flexbox for layout — absolute positioning is only used for mascot animations and decorative overlays.
→ **Do instead:** Use `grid` or `flex` for all content layouts. Absolute positioning is reserved for decorative animations (`hogfather-roll`, `scattered-float`) only.
---
## Appendix A: Complete Token Reference
Every token extracted from the source. §0 CORE TOKENS is the primary AI signal; this appendix is reference material an AI can cross-check against when a curated role is missing.
```css
/* Colours (72) */
--squeak-primary-color: 0,0,0;
--squeak-default-avatar-fill: 255,255,255;
--squeak-button-color: 245,78,0;
--swiper-pagination-color: #f54e00;
--swiper-pagination-bullet-inactive-color: #000;
--swiper-theme-color: #007aff;
----gradient-code-bg: linear-gradient(90deg, rgb(1, 67, 203), rgb(43, 111, 244) 24.04%, rgb(210, 52, 1) 46.63%, rgb(255, 101, 31) 65.87%, rgb(251, 160, 0) 83.17%, rgb(1, 67, 203)); /* Gradient from code background /* reconstructed */ */
--brand-primary-cta: rgb(235, 157, 42); /* Primary CTA background, dominant on 5 buttons — e.g. "Install with AI" /* mined from computed styles */ */
--brand-secondary-cta: rgb(47, 128, 250); /* Secondary CTA background, dominant on 1 button — e.g. "Understand product usage" /* mined from computed styles */ */
--posthog-cta-primary: rgb(235, 157, 42);
--posthog-cta-secondary: rgb(47, 128, 250);
--posthog-cta-brand: rgb(245, 78, 0);
--posthog-text-heading: rgb(17, 24, 39);
--posthog-text-body: rgb(55, 65, 81);
--posthog-text-on-dark: rgb(35, 37, 29);
--posthog-border-cta: rgb(177, 120, 22);
--primitive-warm-950: rgb(17, 17, 17);
--primitive-warm-900: rgb(27, 28, 26);
--primitive-warm-800: rgb(35, 37, 29);
--primitive-warm-700: rgb(44, 45, 42);
--primitive-warm-600: rgb(58, 59, 55);
--primitive-warm-500: rgb(77, 79, 70);
--primitive-warm-400: rgb(101, 103, 94);
--primitive-warm-300: rgb(158, 160, 150);
--primitive-warm-200: rgb(182, 183, 175);
--primitive-warm-150: rgb(191, 193, 183);
--primitive-warm-100: rgb(200, 202, 193);
--primitive-warm-50: rgb(210, 211, 204);
--primitive-warm-30: rgb(229, 231, 224);
--primitive-warm-20: rgb(238, 239, 233);
--primitive-warm-10: rgb(253, 253, 248);
--primitive-amber-600: rgb(177, 120, 22);
--primitive-amber-500: rgb(205, 132, 7);
--primitive-amber-400: rgb(235, 157, 42);
--primitive-amber-300: rgb(251, 160, 0);
--primitive-orange-600: rgb(210, 52, 1);
--primitive-orange-500: rgb(245, 78, 0);
--primitive-orange-400: rgb(255, 101, 31);
--primitive-blue-700: rgb(1, 67, 203);
--primitive-blue-500: rgb(43, 111, 244);
--primitive-blue-400: rgb(47, 128, 250);
--primitive-blue-300: rgb(87, 197, 247);
--primitive-blue-200: rgb(150, 219, 250);
--primitive-blue-sys: #007aff;
--primitive-text-dark: rgb(17, 24, 39);
--primitive-text-body: rgb(55, 65, 81);
--primitive-text-white: rgb(255, 255, 255);
--color-text-heading: rgb(17, 24, 39);
--color-text-body: rgb(55, 65, 81);
--color-text-muted: rgb(101, 103, 94);
--color-text-on-cta: rgb(35, 37, 29);
--color-text-on-tab: rgb(255, 255, 255);
--color-text-nav: rgb(0, 0, 0);
--color-bg-page: rgb(253, 253, 248);
--color-bg-surface: rgb(238, 239, 233);
--color-bg-dark: rgb(35, 37, 29);
--color-bg-cta-primary: rgb(235, 157, 42);
--color-bg-cta-secondary: rgb(47, 128, 250);
--color-bg-brand: rgb(245, 78, 0);
--color-border-cta: rgb(177, 120, 22);
--color-border-default: rgb(182, 183, 175);
--color-border-strong: rgb(191, 193, 183);
--color-gradient-code: linear-gradient(90deg, rgb(1,67,203), rgb(43,111,244) 24.04%, rgb(210,52,1) 46.63%, rgb(255,101,31) 65.87%, rgb(251,160,0) 83.17%, rgb(1,67,203));
--border-subtle: 1px solid rgb(210, 211, 204);
--border-default: 1px solid rgb(182, 183, 175);
--border-strong: 1px solid rgb(191, 193, 183);
--border-cta: 1px solid rgb(177, 120, 22);
--border-focus: 2px solid rgb(37, 99, 235);
--posthog-accent: rgb(235, 157, 42);
--posthog-bg-app: #f54e00;
--posthog-text-primary: #000;
--posthog-text-secondary: #007aff;
/* Typography (31) */
--font-size-xs: 12px; /* 2 elements — e.g. p "*PostHog is a web pr", a "Learn more" /* mined from computed styles */ */
--font-size-sm: 13px; /* 37 elements — e.g. span "More", span "Get started – free", span "1" /* mined from computed styles */ */
--font-size-md: 14px; /* 34 elements — e.g. p "MCP•Watch a demo•Tal", p "Measure engagement, ", p "How we analyze data " /* mined from computed styles */ */
--font-size-lg: 15px; /* 47 elements — e.g. p "Product development ", p "PostHog is the only ", p "Paste into your term" /* mined from computed styles */ */
--font-size-xl: 16px; /* 49 elements — e.g. span "Get started – free", span "home.mdx", span "Product OS" /* mined from computed styles */ */
--font-size-2xl: 21.4286px; /* 8 elements — e.g. h2 "Understand what user", h2 "Who's using PostHog?", h2 "PostHog data stack, " /* mined from computed styles */ */
--font-size-3xl: 24px; /* 1 element — e.g. h1 "The new way to build" /* mined from computed styles */ */
--font-weight-regular: 400; /* 111 elements — e.g. p "Product development ", p "PostHog is the only ", p "Paste into your term" /* mined from computed styles */ */
--font-weight-medium: 500; /* 34 elements — e.g. span "More", span "home.mdx", span "home.mdx" /* mined from computed styles */ */
--font-weight-semibold: 600; /* 16 elements — e.g. h3 "Install with AI in a", span "1", a "Get started - free" /* mined from computed styles */ */
--font-weight-bold: 700; /* 19 elements — e.g. h2 "Understand what user", h2 "Who's using PostHog?", h2 "PostHog data stack, " /* mined from computed styles */ */
--line-height-tight: 17.875px; /* 26 elements — e.g. span "home.mdx", span "home.mdx", span "Product OS" /* mined from computed styles */ */
--line-height-normal: 22.5px; /* 34 elements — e.g. p "Product development ", p "PostHog is the only ", p "Paste into your term" /* mined from computed styles */ */
--posthog-font-sans: "IBM Plex Sans Variable", "IBM Plex Sans", -apple-system,
BlinkMacSystemFont, "avenir next", avenir, "segoe ui", "helvetica neue",
helvetica, Ubuntu, roboto, noto, arial, sans-serif;
--posthog-font-mono: "Source Code Pro", Menlo, Consolas, monaco, monospace;
--posthog-font-display: "Open Runde", sans-serif;
--posthog-font-serif: "Charter", Georgia, serif;
--posthog-font-decorative: "Fairytale", serif;
--posthog-font-size-xs: 12px;
--posthog-font-size-sm: 13px;
--posthog-font-size-md: 14px;
--posthog-font-size-lg: 15px;
--posthog-font-size-xl: 16px;
--posthog-font-size-2xl: 21.4286px;
--posthog-font-size-3xl: 24px;
--posthog-font-weight-regular: 400;
--posthog-font-weight-medium: 500;
--posthog-font-weight-semibold: 600;
--posthog-font-weight-bold: 700;
--posthog-line-height-tight: 17.875px;
--posthog-line-height-normal: 22.5px;
/* Spacing (46) */
--space-xs: 2px; /* 76 elements — e.g. aside .flex, aside .flex, aside .flex /* mined from computed styles */ */
--space-sm: 4px; /* 111 elements — e.g. aside .flex, aside .flex, div .flex-shrink-0 /* mined from computed styles */ */
--space-md: 5px; /* 20 elements — e.g. button .relative, button .relative, button .relative /* mined from computed styles */ */
--space-lg: 6px; /* 10 elements — e.g. div .flex-shrink-0, button .min-w-6, button .min-w-6 /* mined from computed styles */ */
--space-xl: 8px; /* 36 elements — e.g. aside .bg-primary, aside .bg-primary, aside .bg-primary /* mined from computed styles */ */
--space-2xl: 10px; /* 10 elements — e.g. span .flex, span .flex, button .relative /* mined from computed styles */ */
--space-3xl: 16px; /* 10 elements — e.g. article .prose, article .prose, div .md:@2xs/not-full-width:px-0 /* mined from computed styles */ */
--posthog-space-1: 4px;
--posthog-space-2: 8px;
--posthog-space-4: 16px;
--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;
--space-16: 64px;
--space-20: 80px;
--space-24: 96px;
--posthog-space-xs: 2px;
--posthog-space-sm: 4px;
--posthog-space-md: 5px;
--posthog-space-lg: 6px;
--posthog-space-xl: 8px;
--posthog-space-2xl: 10px;
--posthog-space-3xl: 16px;
--container-sm: 640px;
--container-md: 768px;
--container-lg: 1024px;
--container-xl: 1280px;
--container-2xl: 1536px;
--container-max: 1280px;
--bp-xs: 425px;
--bp-sm: 640px;
--bp-md: 768px;
--bp-lg: 1024px;
--bp-xl: 1280px;
--bp-2xl: 1536px;
--bp-narrow: 482px;
--bp-wide-nav: 800px;
--bp-mid: 900px;
--bp-content: 1076px;
--bp-wide: 1160px;
/* Radius (8) */
--radius-sm: 0px 0px 6px 6px; /* 1 element — e.g. a .group "Learn more" /* mined from computed styles */ */
--radius-md: 4px; /* 17 elements — e.g. button .group, button .group "Product OS", button .group "Pricing" /* mined from computed styles */ */
--radius-lg: 5px; /* 2 elements — e.g. button .relative "Shuffle companies", span .flex "Shuffle companies" /* mined from computed styles */ */
--radius-full: 6px; /* 13 elements — e.g. a .relative "Get started – free", a .relative "Get started - free", a .bg-button-shadow "Get started - free" /* mined from computed styles */ */
--posthog-radius-md: 4px;
--posthog-radius-lg: 5px;
--posthog-radius-sm: 0px 0px 6px 6px;
--posthog-radius-full: 6px;
/* Effects (3) */
--shadow-none: none;
--shadow-slider: rgb(87, 197, 247) 0px 0px 5px;
--shadow-focus-input: 0 0 2px 2px var(--color-border-default);
/* Motion (47) */
----motion-slideRight: @keyframes slideRight {
0% { transform: translateX(var(--radix-toast-swipe-end-x)); }
100% { transform: translateX(100%); }
}; /* @keyframes slideRight */
----motion-rcSliderTooltipZoomDownIn: @keyframes rcSliderTooltipZoomDownIn {
0% { opacity: 0; transform: scale(0); transform-origin: 50% 100%; }
100% { transform: scale(1); transform-origin: 50% 100%; }
}; /* @keyframes rcSliderTooltipZoomDownIn */
----motion-rcSliderTooltipZoomDownOut: @keyframes rcSliderTooltipZoomDownOut {
0% { transform: scale(1); transform-origin: 50% 100%; }
100% { opacity: 0; transform: scale(0); transform-origin: 50% 100%; }
}; /* @keyframes rcSliderTooltipZoomDownOut */
----motion-wobble: @keyframes wobble {
0%, 100% { transform: rotate(-2deg) translateX(-5px); }
50% { transform: rotate(2deg) translateX(5px); }
}; /* @keyframes wobble */
----motion-breathe: @keyframes breathe {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.03); }
}; /* @keyframes breathe */
----motion-develop: @keyframes develop {
0% { filter: grayscale(100%) brightness(200%); opacity:… <0.2KB elided>; /* @keyframes develop */
----motion-flash: @keyframes flash {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.02); }
}; /* @keyframes flash */
----motion-float: @keyframes float {
0%, 100% { transform: translateY(0px); }
50% { transform: translateY(-6px); }
}; /* @keyframes float */
----motion-gradient-rotate: @keyframes gradient-rotate {
0%, 100% { background-position: 0px 50%; }
50% { background-position: 100% 50%; }
}; /* @keyframes gradient-rotate */
----motion-grow: @keyframes grow {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.2); }
}; /* @keyframes grow */
----motion-grow-sm: @keyframes grow-sm {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.05); }
}; /* @keyframes grow-sm */
----motion-hogfather-jump: @keyframes hogfather-jump {
0% { margin-top: 0px; }
30% { margin-top: -24px; }
60% { margin-top: 0px; }
80% { margin-top: -8px; }
100% { margin-top: 0px; }
}; /* @keyframes hogfather-jump */
----motion-hogfather-roll: @keyframes hogfather-roll {
0% { left: 0px; transform: scaleX(-1) translateY(… <0.4KB elided>; /* @keyframes hogfather-roll */
----motion-jump-out: @keyframes jump-out {
0% { transform: scale(1); }
20% { transform: scale(1.2); }
100% { transform: scale(0); }
}; /* @keyframes jump-out */
----motion-ping: @keyframes ping {
75%, 100% { opacity: 0; transform: scale(2); }
}; /* @keyframes ping */
----motion-pulse: @keyframes pulse {
50% { opacity: 0.5; }
}; /* @keyframes pulse */
----motion-shimmer: @keyframes shimmer {
0% { background-position: 200% 0px; }
100% { background-position: -200% 0px; }
}; /* @keyframes shimmer */
----motion-slideUpFadeIn: @keyframes slideUpFadeIn {
0% { opacity: 0; transform: translateY(10px); }
100% { opacity: 1; transform: translateY(0px); }
}; /* @keyframes slideUpFadeIn */
----motion-spin: @keyframes spin {
100% { transform: rotate(1turn); }
}; /* @keyframes spin */
----motion-wiggle: @keyframes wiggle {
0%, 100% { transform: rotate(6deg); }
50% { transform: rotate(-6deg); }
}; /* @keyframes wiggle */
----motion-bounce: @keyframes bounce {
20% { transform: translateX(20%); }
33% { transform: translateX(0px); }
34% { transform: translateX(10%); }
}; /* @keyframes bounce */
----motion-features-ticker: @keyframes features-ticker {
0% { transform: translateZ(0px); }
100% { transform: translate3d(-2700px, 0px, 0px); }
}; /* @keyframes features-ticker */
----motion-fade: @keyframes fade {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
}; /* @keyframes fade */
----motion-heart-pulse: @keyframes heart-pulse {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}; /* @keyframes heart-pulse */
----motion-text-gradient-wizard-scroll: @keyframes text-gradient-wizard-scroll {
0% { background-position-x: 0px; }
100% { background-position-x: 200%; }
}; /* @keyframes text-gradient-wizard-scroll */
----motion-wizard-copied-flash: @keyframes wizard-copied-flash {
0%, 50% { opacity: 1; }
100% { opacity: 0; }
}; /* @keyframes wizard-copied-flash */
----motion-fadeIn: @keyframes fadeIn {
0% { opacity: 0; transform: scale(0.9); }
100% { opacity: 1; transform: scale(1); }
}; /* @keyframes fadeIn */
----motion-questlog-sprite-animate: @keyframes questlog-sprite-animate {
0% { background-position: 0px 0px; }
100% { background-position: -528px 0px; }
}; /* @keyframes questlog-sprite-animate */
----motion-speechBubble: @keyframes speechBubble {
0% { opacity: 0; transform: translateY(-50%) scale(… <0.3KB elided>; /* @keyframes speechBubble */
----motion-speechBubbleLeft: @keyframes speechBubbleLeft {
0% { opacity: 0; transform: translateY(-50%) sc… <0.3KB elided>; /* @keyframes speechBubbleLeft */
----motion-questGlow: @keyframes questGlow {
0%, 100% { box-shadow: rgba(255, 165, 0, 0) 0px 0px 0px 0px; }
50% { box-shadow: rgba(255, 165, 0, 0.4) 0px 0px 12px 2px; }
}; /* @keyframes questGlow */
----motion-hero-carousel-progress: @keyframes hero-carousel-progress {
0% { width: 0px; }
100% { width: 100%; }
}; /* @keyframes hero-carousel-progress */
----motion-hero-carousel-fade-in: @keyframes hero-carousel-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}; /* @keyframes hero-carousel-fade-in */
----motion-scattered-float: @keyframes scattered-float {
0%, 100% { transform: translate(-50%, -50%); }… <0.3KB elided>; /* @keyframes scattered-float */
----motion-jump-in: @keyframes jump-in {
0% { transform: scale(0); }
80% { transform: scale(1.2); }
100% { transform: scale(1); }
}; /* @keyframes jump-in */
----motion-slideUp: @keyframes slideUp {
0% { height: var(--radix-accordion-content-height); }
100% { height: 0px; }
}; /* @keyframes slideUp */
----motion-slideDown: @keyframes slideDown {
0% { height: 0px; }
100% { height: var(--radix-accordion-content-height); }
}; /* @keyframes slideDown */
----motion-slideIn: @keyframes slideIn {
0% { transform: translateX(calc(100% + var(--viewport-padding))); }
100% { transform: translateX(0px); }
}; /* @keyframes slideIn */
----motion-swipeOut: @keyframes swipeOut {
0% { transform: translateX(var(--radix-toast-swipe-end-x)); }
100% { transform: translateX(calc(100% + var(--viewport-padding))); }
}; /* @keyframes swipeOut */
----motion-mapboxgl-spin: @keyframes mapboxgl-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(1turn); }
}; /* @keyframes mapboxgl-spin */
----motion-mapboxgl-user-location-dot-pulse: @keyframes mapboxgl-user-location-dot-pulse {
0% { opacity: 1; transform: scale(1); }
70% { opacity: 0; transform: scale(3); }
100% { opacity: 0; transform: scale(1); }
}; /* @keyframes mapboxgl-user-location-dot-pulse */
----motion-swiper-preloader-spin: @keyframes swiper-preloader-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(1turn); }
}; /* @keyframes swiper-preloader-spin */
----motion-dashdraw: @keyframes dashdraw {
0% { stroke-dashoffset: 10; }
}; /* @keyframes dashdraw */
--duration-fast: 0.15s; /* 42 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-base: 4s; /* 7 elements — e.g. div, div, div /* mined from computed styles */ */
--duration-slow: 4.9s; /* 7 elements — e.g. div, div, div /* mined from computed styles */ */
--ease-default: ease; /* 185 elements — e.g. button, button, button /* mined from computed styles */ */
```
## Appendix B: Token Source Metadata
```
tokenSource: reconstructed-from-computed
(with 6 extracted CSS custom properties preserved verbatim)
Confidence: medium overall
- CSS variable names (--squeak-*, --swiper-*): extracted: high confidence
- Font size/weight tokens (--posthog-font-size-*, --posthog-font-weight-*): extracted: high confidence
- Spacing tokens (--posthog-space-*): extracted: high confidence — note off-grid values
- Radius tokens: extracted from computed styles census — high confidence
- Colour semantics: reconstructed from computed styles — moderate confidence
- CTA amber (rgb(235,157,42)): high confidence — 5 button instances
- CTA secondary blue (rgb(47,128,250)): high confidence — active tab extracted
- Brand orange (#f54e00): high confidence — directly in CSS vars
- Page/surface backgrounds: moderate confidence — inferred from warm-neutral cluster
- Dark mode surface colours: moderate confidence — inferred from hover-invert CSS rules
- Typography composite tokens: extracted from computed styles — high confidence
- Motion tokens: keyframes extracted directly — high confidence
- duration-base (4s) and duration-slow (4.9s) are animation durations, not UI transitions
- Layout patterns: reconstructed from computed flexbox/display values — high confidence for nav/button
- Section ordering (Section 5): inferred from component inventory + layout digest, not visually confirmed
Rows marked "(inferred)" in the section map are probable but not screenshot-verified
Clustering method for colour reconstruction:
- Warm neutrals: clustered by rgb(R,G,B) values with R≈G≈B±5 and slight green-channel deficit
indicating olive undertone. 14 distinct values mapped to a named scale.
- Amber/orange: clustered hues 25°–45° into amber (235,157,42) and orange (245,78,0) sub-families
- Blue: functional blue (47,128,250) separated from brand-system iOS blue (#007aff) by context
- Gradient: code background gradient preserved as a single composite token
Library detection: Tailwind CSS v3 (utility-class-based, no CSS vars via @theme)
→ Tokens are NOT available as Tailwind theme extensions by default
→ Use Tailwind arbitrary value syntax: bg-[rgb(235,157,42)] or extend tailwind.config.js
Figma: Not applicable — no Figma source available
```More from the gallery
Browse all kits →You may also like

Vercel
MITClean, minimal developer-focused design system built on Geist typography with light surfaces, precise spacing, and fast interactions for modern web applications
00
lightminimaldeveloper-toolsaas

Sentry
MITDark, minimal design system for developer tools with a sophisticated purple palette and refined typography, tailored for error monitoring and performance platforms
00
darkminimaldeveloper-toolsaas

OpenAI
MITClean, minimal design system for AI-native products built on Next.js and Tailwind, featuring a light monochromatic palette with precise token-based spacing and typography
02
lightminimalsaasdeveloper-tool