Intercom
MIT
A modern SaaS design system with clean typography, precise spacing tokens, and a sophisticated blue–black palette built for product interfaces and customer communication platforms
Layout StudioImport this kit into a Studio project and start editing.
CLI installRun it in any project. No account needed.
npx @layoutdesign/context install intercomUI themeReskin shadcn or Layout UI components to match Intercom.
npx shadcn add https://layout.design/r/intercom/theme.json# layout.md — Intercom Design System
---
## 0. Quick Reference
> Copy-paste into `CLAUDE.md` or `.cursorrules` for instant AI context.
**Stack:** Next.js / React · Tailwind CSS + Bootstrap · Custom CSS vars · Token source: extracted CSS vars (high confidence, 23 properties) + computed styles synthesis.
**How to apply:** Use as `var(--token-name)` in CSS, `style={{ prop: 'var(--token-name)' }}` in JSX, or `bg-[var(--token-name)]` in Tailwind.
```css
/* ── Core Tokens ── */
--button-primary-background: var(--color-off-black); /* #111111 — primary CTA fill */
--button-primary-hover-background: var(--color-helpdesk); /* blue accent on hover */
--button-primary-foreground: var(--color-white); /* white label on dark bg */
--button-secondary-border: var(--color-off-black); /* outlined button border */
--button-secondary-foreground: var(--color-off-black); /* outlined button label */
--button-radius: .25rem; /* 4px — all button corners */
--button-height: 2.5rem; /* 40px — standard button height */
--button-padding-x: .875rem; /* 14px — horizontal padding */
--intercom-radius-sm: 4px;
--intercom-radius-md: 6px;
--intercom-space-xs: 8px;
--intercom-space-sm: 12px;
--intercom-space-md: 16px;
--intercom-space-lg: 24px;
--intercom-space-xl: 32px;
--intercom-space-2xl: 80px;
--intercom-space-3xl: 112px;
--intercom-font-size-xs: 14px;
--intercom-font-size-sm: 16px;
--intercom-font-size-md: 20px;
--intercom-font-size-lg: 24px;
--intercom-font-size-xl: 28px;
--intercom-font-size-2xl: 40px;
--intercom-font-size-3xl: 54px;
--intercom-duration-fast: 0.2s;
--intercom-duration-base: 0.4s;
--intercom-ease-default: ease;
```
```tsx
// Primary Button — correct token usage
<button
className="inline-flex items-center justify-center"
style={{
height: 'var(--button-height)',
padding: '0 var(--button-padding-x)',
borderRadius: 'var(--button-radius)',
background: 'var(--button-primary-background)',
color: 'var(--button-primary-foreground)',
fontSize: '16px',
fontFamily: 'Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif',
transition: 'background-color var(--intercom-duration-base) var(--intercom-ease-default)',
}}
>
Start free trial
</button>
```
**NEVER rules:**
- NEVER use `Inter`, `Roboto`, or `Arial` as the primary font — Intercom uses `Saans` (custom)
- NEVER use `border-radius > 6px` on standard UI elements — max is `--intercom-radius-md: 6px`
- NEVER hardcode `#111111` — use `var(--button-primary-background)` / `var(--color-off-black)`
- NEVER construct Tailwind classes dynamically (e.g. `` `bg-${color}` ``) — use CSS vars
- NEVER omit hover/active/disabled states — all interactive elements require full state coverage
- NEVER use `font-weight: bold` — Intercom's "regular" is weight `400`, "light" is `300`
- NEVER use warm accent colours for primary actions — the brand accent is near-black or blue (`var(--color-helpdesk)`)
**Full design system → see `layout.md`**
---
## 1. Design Direction & Philosophy
### Character & Mood
Intercom's visual identity is **precision-modern B2B SaaS** — clean, confident, and typographically ambitious. The design leans on large-scale type (80px h1, tight negative letter-spacing), generous whitespace, and a restrained colour palette anchored to near-black (`#111111`) with a blue brand accent (`--color-helpdesk`). The overall feeling is authoritative and product-forward, not playful.
### Typographic Ambition
The brand deploys an unusually large custom font library (MediumLL, IvoryLL, Saans, SaansMono, Serrif, DmMono, Maxi, Ozik, Planar, Raster, Honey, Beretta) — indicating intentional typographic craft. `Saans` is the workhorse UI font for headings and body. Specialty display faces (Maxi, Ozik, Planar, Honey, Beretta) signal that campaign sections or hero areas may use expressive one-off treatments.
### Aesthetic Principles
- **Tight letter-spacing on large type:** h1 at `-2.4px`, h2 at `-1.6px`, h3 at `-0.48px` — negative tracking scales with size.
- **Weight discipline:** "Regular" font weight is mapped to `300` (light), "Medium" to `400` — visually lighter than typical.
- **Colour restraint:** Near-black primary, transparent/outlined secondary, blue only on hover. No warm accents in the core system.
- **Radius discipline:** Maximum `6px` (`--intercom-radius-md`) on interactive elements. No rounded pill buttons in the core system.
### What This Design Explicitly Rejects
- Rounded/pill-shaped buttons (none observed ≥ 50px radius in the design system)
- Warm colour palettes (no reds, oranges, or yellows in the token system)
- Heavy font weights (700 exists in the font files but is not in the weight token system)
- Decorative shadows or heavy elevation (boxShadow: none across all computed elements)
- Generic system fonts as primary — `Saans` always leads the font stack
---
## 2. Colour System
### Tier 1 — Primitives (referenced via CSS aliases)
```css
/* These primitives are referenced by the extracted vars but not directly defined in extracted CSS */
/* extracted: high confidence — inferred from var() references */
--color-off-black: #111111; /* Near-black — Intercom's primary dark colour */
--color-black: #000000; /* Pure black — active states */
--color-white: #ffffff; /* Pure white — foreground on dark surfaces */
--color-helpdesk: rgb(0, 7, 203); /* Brand blue — hover accent (0007CB) */
--color-chalk: rgb(241, 238, 233);/* Warm off-white — tooltip bg, focus states */
```
### Tier 2 — Semantic Aliases
```css
/* extracted: high confidence */
--intercom-bg-app: var(--color-off-black); /* App-level background / primary CTA fill */
--intercom-bg-surface: rgb(0, 7, 203); /* Brand surface — blue accent areas */
--intercom-bg-selected: #0007cbd9; /* Selected/active state — blue at 85% opacity */
--intercom-border: #000c; /* Default border — black at ~80% opacity */
--intercom-accent: var(--color-helpdesk); /* Primary accent — hover background */
--intercom-accent-foreground: #0000004d; /* Disabled foreground — black at 30% opacity */
--intercom-text-primary: #111111bf; /* Body text variant — off-black at 75% opacity */
```
### Tier 3 — Component Tokens
```css
/* BUTTON — PRIMARY */
/* extracted: high confidence */
--button-primary-background: var(--color-off-black); /* default bg: #111111 */
--button-primary-foreground: var(--color-white); /* default label: #ffffff */
--button-primary-hover-background: var(--color-helpdesk); /* hover bg: rgb(0,7,203) */
--button-primary-hover-foreground: var(--color-white); /* hover label: #ffffff */
--button-primary-hover-border: #000c; /* hover border: black ~80% */
--button-primary-active-background: #0007cbd9; /* active bg: blue 85% */
--button-primary-active-foreground: var(--color-white); /* active label: #ffffff */
--button-primary-disabled-background: #0000004d; /* disabled bg: black 30% */
--button-primary-disabled-foreground: var(--color-white); /* disabled label: #ffffff */
/* BUTTON — SECONDARY */
/* extracted: high confidence */
--button-secondary-background: transparent; /* default bg: transparent */
--button-secondary-border: var(--color-off-black); /* default border: #111111 */
--button-secondary-foreground: var(--color-off-black); /* default label: #111111 */
--button-secondary-hover-background: transparent; /* hover bg: still transparent */
--button-secondary-hover-border: #111111bf; /* hover border: off-black 75% */
--button-secondary-hover-foreground: #111111bf; /* hover label: off-black 75% */
--button-secondary-active-background: #11111105; /* active bg: off-black 2% */
--button-secondary-active-foreground: var(--color-black); /* active label: pure black */
--button-secondary-disabled-background: transparent; /* disabled bg: transparent */
--button-secondary-disabled-border: #0000004d; /* disabled border: black 30% */
--button-secondary-disabled-foreground: #0000004d; /* disabled label: black 30% */
```
### Colour Palette Reference
| Token | Value | Usage |
|---|---|---|
| `--color-off-black` | `#111111` | Primary CTA bg, body text, borders |
| `--color-black` | `#000000` | Active states, role_button bg |
| `--color-white` | `#ffffff` | Foreground on dark surfaces |
| `--color-helpdesk` | `rgb(0, 7, 203)` | Hover accent, brand surface |
| `--color-chalk` | `rgb(241, 238, 233)` | Tooltip bg, focus surface |
| `--button-primary-disabled-background` | `#0000004d` | 30% opacity black — disabled |
| `--button-secondary-hover-border` | `#111111bf` | 75% opacity off-black — hover |
---
## 3. Typography System
**Primary font:** `Saans` — custom sans-serif. ALWAYS use the full stack.
**Font stack:** `Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif`
**Mono stack:** `SaansMono, "SaansMono Fallback", ui-monospace, monospace`
**Serif stack:** `Serrif, "Serrif Fallback", Georgia, serif`
**Display stacks:** `Maxi`, `Ozik`, `Planar`, `Honey`, `Beretta` — campaign/hero sections only
### Composite Type Scale
```css
/* ── Heading — Display (H1) ── */
/* extracted: high confidence from computed styles */
.type-display {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: 80px; /* --intercom-font-size-display (synthesised) */
font-weight: 400; /* --intercom-font-weight-medium */
line-height: 80px; /* 1:1 ratio — tight display setting */
letter-spacing: -2.4px; /* strong negative tracking at large size */
}
/* ── Heading — H2 / Section ── */
/* extracted: high confidence from computed styles */
.type-h2 {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-3xl); /* 54px */
font-weight: 400; /* --intercom-font-weight-medium */
line-height: 54px; /* 1:1 ratio */
letter-spacing: -1.6px;
margin-bottom: var(--intercom-space-lg); /* 24px */
}
/* ── Heading — H3 / Subsection ── */
/* extracted: high confidence from computed styles */
.type-h3 {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-lg); /* 24px */
font-weight: 400; /* --intercom-font-weight-medium */
line-height: 24px; /* 1:1 ratio */
letter-spacing: -0.48px;
margin-bottom: var(--intercom-space-md); /* 16px */
}
/* ── Heading — H4 / Card Title (md) ── */
/* reconstructed: moderate confidence, inferred from computed elements */
.type-h4-md {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-md); /* 20px */
font-weight: 400;
line-height: 24px;
letter-spacing: -0.4px;
}
/* ── Heading — H4 / Card Title (xl) ── */
/* reconstructed: moderate confidence, inferred from computed elements */
.type-h4-xl {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-xl); /* 28px */
font-weight: 400;
line-height: 32px;
letter-spacing: -0.56px;
}
/* ── Section Heading — 2XL ── */
/* reconstructed: moderate confidence */
.type-section-lg {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-2xl); /* 40px */
font-weight: 400;
line-height: 44px;
letter-spacing: -1.2px;
}
/* ── Body — Default ── */
/* extracted: high confidence from computed styles */
.type-body {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-sm); /* 16px */
font-weight: 300; /* --intercom-font-weight-regular */
line-height: 24px; /* var(--intercom-line-height-normal): 22.4px → computed: 24px */
letter-spacing: normal;
}
/* ── Body — Small / Caption ── */
/* extracted: high confidence from computed + link element */
.type-body-sm {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-xs); /* 14px */
font-weight: 400;
line-height: 20px; /* var(--intercom-line-height-tight): 19.6px → computed: 20px */
letter-spacing: normal;
}
/* ── UI / Button label ── */
/* extracted: high confidence from computed role_button */
.type-ui {
font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif;
font-size: var(--intercom-font-size-sm); /* 16px */
font-weight: 400;
line-height: 16px; /* 1:1 — tight for button labels */
letter-spacing: -0.4px;
}
/* ── Mono ── */
/* reconstructed: moderate confidence */
.type-mono {
font-family: SaansMono, "SaansMono Fallback", DmMono, "DmMono Fallback", ui-monospace, monospace;
font-size: var(--intercom-font-size-xs); /* 14px */
font-weight: 400;
line-height: 20px;
letter-spacing: normal;
}
```
### Font Weight Scale
| Token | Value | Saans variant | Usage |
|---|---|---|---|
| `--intercom-font-weight-regular` | `300` | Light | Body copy, supporting text |
| `--intercom-font-weight-medium` | `400` | Regular | Headings, UI labels, buttons |
| *(available in files, no token)* | `500` | Medium (Saans) | [TBD - extract manually] |
| *(available in files, no token)* | `600` | SemiBold (MediumLL) | [TBD - extract manually] |
| *(available in files, no token)* | `700` | Bold (MediumLL) | [TBD - extract manually] |
> **Critical:** `font-weight: 300` is Intercom's "regular" body weight. `font-weight: 400` is their "medium"/heading weight. This is inverted from most design systems.
---
## 4. Spacing & Layout
**Base unit: 4px** (inferred from --radius-sm: 4px and the spacing scale multiples).
```css
/* ── Spacing Scale ── */
/* extracted: high confidence */
--intercom-space-xs: 8px; /* 2× base — icon gaps, tight internal padding */
--intercom-space-sm: 12px; /* 3× base — compact component padding */
--intercom-space-md: 16px; /* 4× base — standard component padding, h3 margin-bottom */
--intercom-space-lg: 24px; /* 6× base — card gaps, h2 margin-bottom */
--intercom-space-xl: 32px; /* 8× base — section internal spacing */
--intercom-space-2xl: 80px; /* 20× base — section vertical padding */
--intercom-space-3xl: 112px; /* 28× base — major section separation */
/* ── Border Radius Scale ── */
/* extracted: high confidence */
--intercom-radius-sm: 4px; /* Buttons, tooltips, badges — dominant (24 elements) */
--intercom-radius-md: 6px; /* Nav items, role buttons — secondary (20 elements) */
--button-radius: .25rem; /* = 4px — canonical button radius */
/* NOTE: No pill-shaped buttons in the design system. Max radius is 6px. */
/* ── Button Geometry ── */
/* extracted: high confidence */
--button-height: 2.5rem; /* 40px — all standard buttons */
--button-padding-x: .875rem; /* 14px — horizontal padding */
/* ── Typography Spacing (computed) ── */
--intercom-line-height-tight: 19.6px; /* 14px text → ~1.4 ratio */
--intercom-line-height-normal: 22.4px; /* 16px text → ~1.4 ratio */
```
### Spacing Scale Table
| Token | Value | Ratio to Base | Usage |
|---|---|---|---|
| `--intercom-space-xs` | 8px | 2× | Icon gaps, chip padding |
| `--intercom-space-sm` | 12px | 3× | Compact padding (tooltip: 12px 14px) |
| `--intercom-space-md` | 16px | 4× | Component padding, h3 bottom margin |
| `--intercom-space-lg` | 24px | 6× | Card gaps, h2 bottom margin |
| `--intercom-space-xl` | 32px | 8× | Section internal spacing |
| `--intercom-space-2xl` | 80px | 20× | Section vertical rhythm |
| `--intercom-space-3xl` | 112px | 28× | Major section separation |
### Grid & Breakpoints
```css
/* reconstructed: moderate confidence — Tailwind detected, breakpoints inferred from standard config */
/* [TBD - extract manually for confirmed breakpoint values] */
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
/* Container — reconstructed: moderate confidence */
/* max-width inferred from typical SaaS marketing patterns + Tailwind defaults */
--container-max: 1280px;
--container-padding: var(--intercom-space-lg); /* 24px sides */
```
### Layout Principles
- **Flex for component-level layout:** buttons, nav items, and role buttons use `display: flex; flex-direction: row; align-items: center; justify-content: center`
- **Block for section-level layout:** headings, body, navigation containers use `display: block`
- **Tab component uses column flex:** `display: flex; flex-direction: column` — vertical stacking pattern
- Gap on flex containers: use `--intercom-space-xs` (8px) or `--intercom-space-sm` (12px) for tight rows; `--intercom-space-lg` (24px) for card grids
---
## 5. Page Structure & Layout Patterns
> No screenshots available. Layout inferred from LAYOUT DIGEST, component inventory, computed styles, and button census. Rows marked **(inferred)** are not visually confirmed.
### 5.1 Section Map
| # | Section | Layout Type | Approx. Height | Key Elements |
|---|---|---|---|---|
| 1 | **Nav / Header** | `display:block` (role_navigation) | 64–80px | Logo, nav links (15×), CTA button (role_button black bg), dropdown menus |
| 2 | **Hero** (inferred) | Full-width, centered/split | 560–720px | H1 at 80px, subhead body text, primary + secondary CTA buttons, product visual |
| 3 | **Social Proof / Logos** (inferred) | Marquee / flex row | 80–120px | Marquee animation (carouselAutoplay), customer logos |
| 4 | **Feature Grid — Primary** (inferred) | 2-col or full-width alternating | 480–640px | H2 at 54px, body copy at 16px, product screenshot, "Learn more" links |
| 5 | **Feature Cards** (inferred) | 3-col grid | 400–560px | H4 (20px or 28px), body text, icon or illustration per card |
| 6 | **Section CTA / Banner** (inferred) | Full-width centered | 240–320px | H2 or H3, primary button (off-black bg), secondary button (outlined) |
| 7 | **Testimonials / Social Proof** (inferred) | Carousel or grid | 360–480px | Quote text, customer name, logo, badge |
| 8 | **Pricing / Plans** (inferred) | 3-col grid | 480–640px | Badge (4 instances), CTA per plan, feature list |
| 9 | **Footer** (inferred) | Multi-col flex | 240–320px | Nav links, legal text (14px), social icons |
### 5.2 Layout Patterns
**Navigation:**
- `display: block` container with inner flex row for logo + links + CTA
- Nav links: `display: flex; flex-direction: row; border-radius: 6px` on hover highlight
- CTA button (nav): `display: inline-flex; align-items: center; justify-content: center; background: #000000; border-radius: 6px; padding: 12px 16px`
- Dropdown trigger: `display: flex; flex-direction: column`
**Hero Section (inferred):**
- Full-width section, content constrained to `max-width: ~1280px`, centred
- H1 at 80px/400 weight, negative tracking -2.4px
- Two-button row: primary (off-black fill) + secondary (transparent + border) side-by-side
- Gap between buttons: `--intercom-space-xs` (8px) — from `role_button` `margin-left: 8px`
**Feature Grid (inferred):**
- H2 (54px) leads each feature section, followed by body at 16px/300 weight
- Product screenshots or illustrations fill remaining column
- "Learn more" links at 14px with `padding: 8px 0` (top/bottom only)
**Card Grid (inferred):**
- 3 columns at desktop, each card contains H4 (20px or 28px) + body + optional CTA
- Gap: `--intercom-space-lg` (24px) between cards
- Card radius: `--intercom-radius-sm` (4px)
### 5.3 Visual Hierarchy
- **Most prominent element:** H1 at 80px with -2.4px tracking — zero competition at hero level
- **Primary CTA colour:** Off-black (`#111111`) — high contrast against white background. On hover: blue (`var(--color-helpdesk)`)
- **Secondary CTA:** Transparent with off-black border — visually subordinate, same size
- **"Learn more" links:** 14px, no visible underline by default, `padding: 8px 0` for click area
- **Whitespace rhythm:** `--intercom-space-2xl` (80px) between major sections; `--intercom-space-3xl` (112px) for hero/footer breathing room
- **Tooltips/badges:** `background: rgb(241, 238, 233)` (chalk) at 4px radius — subtle warm surface
### 5.4 Content Patterns
**Repeating Feature Block (inferred):**
```
[H2 — 54px, left-aligned]
[Body — 16px/300, max ~60ch wide]
[Product visual — right or below]
[Optional "Learn more" — 14px link]
```
**Card Pattern (inferred):**
```
[Icon or illustration — top]
[H4 — 20px or 28px]
[Body — 16px/300]
```
**CTA Pairing (confirmed from button census):**
```
[Primary button — off-black fill, white text, 4px radius, 40px height]
[Secondary button — transparent, off-black border, off-black text, same size]
```
---
## 6. Component Patterns
### 6.1 Button — Primary
**Anatomy:** `<button>` → `[label text]`
- Container: `display: inline-flex; align-items: center; justify-content: center`
- Height: `var(--button-height)` (40px)
- Padding: `0 var(--button-padding-x)` (0 14px)
- Radius: `var(--button-radius)` (4px)
- Font: `Saans` 16px / weight 400 / line-height 16px / tracking -0.4px
**State Table:**
| State | Background | Foreground | Border |
|---|---|---|---|
| Default | `var(--button-primary-background)` = `#111111` | `var(--button-primary-foreground)` = `#fff` | none |
| Hover | `var(--button-primary-hover-background)` = `rgb(0,7,203)` | `var(--button-primary-hover-foreground)` = `#fff` | `var(--button-primary-hover-border)` = `#000c` |
| Active | `var(--button-primary-active-background)` = `#0007cbd9` | `var(--button-primary-active-foreground)` = `#fff` | inherited |
| Disabled | `var(--button-primary-disabled-background)` = `#0000004d` | `var(--button-primary-disabled-foreground)` = `#fff` | none |
| Focus | off-black bg + 2px outline offset | white | system focus ring |
```tsx
// Button — Primary: production-ready with all states
interface PrimaryButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
loading?: boolean;
children: React.ReactNode;
}
export function PrimaryButton({ loading, disabled, children, ...props }: PrimaryButtonProps) {
return (
<button
{...props}
disabled={disabled || loading}
style={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
height: 'var(--button-height)',
padding: '0 var(--button-padding-x)',
borderRadius: 'var(--button-radius)',
background: disabled || loading
? 'var(--button-primary-disabled-background)'
: 'var(--button-primary-background)',
color: 'var(--button-primary-foreground)',
border: 'none',
fontFamily: 'Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif',
fontSize: '16px',
fontWeight: 400,
lineHeight: '16px',
letterSpacing: '-0.4px',
cursor: disabled || loading ? 'not-allowed' : 'pointer',
transition: [
'background-color var(--intercom-duration-base) cubic-bezier(0.165, 0.84, 0.44, 1)',
'border-color var(--intercom-duration-base) cubic-bezier(0.165, 0.84, 0.44, 1)',
'color var(--intercom-duration-base) cubic-bezier(0.165, 0.84, 0.44, 1)',
].join(', '),
}}
// Hover/active handled via CSS — add a className for CSS pseudo-states
className="intercom-btn-primary"
>
{loading ? <span aria-hidden>…</span> : children}
</button>
);
}
/*
CSS to pair with this component:
.intercom-btn-primary:hover:not(:disabled) {
background: var(--button-primary-hover-background);
border-color: var(--button-primary-hover-border);
color: var(--button-primary-hover-foreground);
}
.intercom-btn-primary:active:not(:disabled) {
background: var(--button-primary-active-background);
color: var(--button-primary-active-foreground);
}
.intercom-btn-primary:focus-visible {
outline: 2px solid var(--color-off-black);
outline-offset: 2px;
}
*/
```
---
### 6.2 Button — Secondary
**Anatomy:** `<button>` → `[label text]`
- Same geometry as primary (`height`, `padding`, `radius`)
- Background: transparent; border: `1px solid var(--button-secondary-border)`
**State Table:**
| State | Background | Foreground | Border |
|---|---|---|---|
| Default | transparent | `var(--button-secondary-foreground)` = `#111111` | `var(--button-secondary-border)` = `#111111` |
| Hover | transparent | `var(--button-secondary-hover-foreground)` = `#111111bf` | `var(--button-secondary-hover-border)` = `#111111bf` |
| Active | `var(--button-secondary-active-background)` = `#11111105` | `var(--button-secondary-active-foreground)` = `#000` | inherited |
| Disabled | transparent | `var(--button-secondary-disabled-foreground)` = `#0000004d` | `var(--button-secondary-disabled-border)` = `#0000004d` |
| Focus | transparent + 2px outline | `#111111` | `#111111` |
```tsx
export function SecondaryButton({ disabled, children, ...props }: React.ButtonHTMLAttributes<HTMLButtonElement> & { children: React.ReactNode }) {
return (
<button
{...props}
disabled={disabled}
className="intercom-btn-secondary"
style={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
height: 'var(--button-height)',
padding: '0 var(--button-padding-x)',
borderRadius: 'var(--button-radius)',
background: 'var(--button-secondary-background)',
color: disabled ? 'var(--button-secondary-disabled-foreground)' : 'var(--button-secondary-foreground)',
border: `1px solid ${disabled ? 'var(--button-secondary-disabled-border)' : 'var(--button-secondary-border)'}`,
fontFamily: 'Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif',
fontSize: '16px',
fontWeight: 400,
lineHeight: '16px',
letterSpacing: '-0.4px',
cursor: disabled ? 'not-allowed' : 'pointer',
transition: 'color var(--intercom-duration-base) var(--intercom-ease-default), border-color var(--intercom-duration-base) var(--intercom-ease-default)',
}}
>
{children}
</button>
);
}
/*
CSS:
.intercom-btn-secondary:hover:not(:disabled) {
color: var(--button-secondary-hover-foreground);
border-color: var(--button-secondary-hover-border);
}
.intercom-btn-secondary:active:not(:disabled) {
background: var(--button-secondary-active-background);
color: var(--button-secondary-active-foreground);
}
.intercom-btn-secondary:focus-visible {
outline: 2px solid var(--color-off-black);
outline-offset: 2px;
}
*/
```
---
### 6.3 Nav Item
**Anatomy:** `<nav>` container (block) → `<button>` or `<a>` nav items (flex row, 6px radius on hover bg)
**State Table:**
| State | Background | Foreground | Radius |
|---|---|---|---|
| Default | transparent | `rgb(0,0,0)` | 0 |
| Hover | `var(--color-chalk)` or nav highlight bg | `rgb(0,0,0)` | `--intercom-radius-md` (6px) |
| Active | nav highlight bg | `rgb(0,0,0)` | `--intercom-radius-md` (6px) |
| Focus-visible | transparent + 2px outline | `rgb(0,0,0)` | 6px |
```tsx
export function NavItem({ children, href }: { children: React.ReactNode; href: string }) {
return (
<a
href={href}
className="intercom-nav-item"
style={{
display: 'inline-flex',
alignItems: 'center',
padding: '0 var(--intercom-space-sm)', /* 12px */
height: '36px',
borderRadius: 'var(--intercom-radius-md)', /* 6px */
fontFamily: 'Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif',
fontSize: 'var(--intercom-font-size-sm)', /* 16px */
fontWeight: 400,
lineHeight: '24px',
color: 'rgb(0, 0, 0)',
textDecoration: 'none',
background: 'transparent',
transition: 'background-color var(--intercom-duration-fast) var(--intercom-ease-default)',
}}
>
{children}
</a>
);
}
/*
CSS:
.intercom-nav-item:hover { background-color: var(--color-chalk); }
.intercom-nav-item:focus-visible { outline: 2px solid var(--color-off-black); outline-offset: 2px; }
*/
```
---
### 6.4 Badge
**Anatomy:** `<span>` → label text
- Background: `var(--color-chalk)` (rgb(241, 238, 233))
- Radius: `--intercom-radius-sm` (4px)
- Font: 14px / weight 400 / Saans
```tsx
export function Badge({ children }: { children: React.ReactNode }) {
return (
<span
style={{
display: 'inline-flex',
alignItems: 'center',
padding: '4px var(--intercom-space-xs)', /* 4px 8px */
borderRadius: 'var(--intercom-radius-sm)', /* 4px */
background: 'rgb(241, 238, 233)', /* --color-chalk */
fontFamily: 'Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif',
fontSize: 'var(--intercom-font-size-xs)', /* 14px */
fontWeight: 400,
lineHeight: '20px',
color: 'var(--color-off-black)',
}}
>
{children}
</span>
);
}
```
---
### 6.5 Tooltip
**Anatomy:** `<div role="tooltip">` → text content
```tsx
export function Tooltip({ children }: { children: React.ReactNode }) {
return (
<div
role="tooltip"
style={{
display: 'block',
padding: 'var(--intercom-space-sm) .875rem', /* 12px 14px */
borderRadius: 'var(--intercom-radius-sm)', /* 4px */
background: 'rgb(241, 238, 233)', /* --color-chalk */
fontFamily: 'Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif',
fontSize: 'var(--intercom-font-size-xs)', /* 14px */
fontWeight: 400,
lineHeight: '20px',
color: 'rgb(0, 0, 0)',
transition: 'opacity var(--intercom-duration-fast) var(--intercom-ease-default)',
}}
>
{children}
</div>
);
}
```
---
### 6.6 Input
**State Table:**
| State | Background | Border / Outline |
|---|---|---|
| Default | transparent | `1px solid var(--color-off-black)` |
| Focus-within | transparent | outline: none (custom focus) |
| Focus-within (brand-i2y4) | `var(--color-chalk)` | outline: none |
| Disabled | transparent | 35% opacity on whole element |
```tsx
export function Input(props: React.InputHTMLAttributes<HTMLInputElement>) {
return (
<input
{...props}
className="input intercom-input"
style={{
display: 'block',
width: '100%',
height: 'var(--button-height)', /* reuses 40px height */
padding: '0 var(--intercom-space-md)', /* 0 16px */
borderRadius: 'var(--intercom-radius-sm)', /* 4px */
border: '1px solid var(--color-off-black)',
background: 'transparent',
fontFamily: 'Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif',
fontSize: 'var(--intercom-font-size-sm)', /* 16px */
fontWeight: 400,
color: 'var(--color-off-black)',
transition: 'background-color var(--intercom-duration-fast) var(--intercom-ease-default)',
...(props.disabled ? { opacity: 0.35, cursor: 'not-allowed' } : {}),
}}
/>
);
}
```
---
## 7. Elevation & Depth
> All computed elements returned `boxShadow: none` — Intercom uses a **flat design system** with no shadow-based elevation. Depth is communicated through colour contrast and border definition, not shadows.
```css
/* ── Shadow Scale ── */
/* extracted: high confidence — all elements return boxShadow: none */
--shadow-none: none; /* Default for ALL components */
--shadow-sm: none; /* No shadow tier defined in extracted tokens */
--shadow-md: none; /* [TBD - extract manually if shadows exist on modals] */
/* ── Border Tokens ── */
/* extracted: high confidence */
--border-default: 1px solid var(--color-off-black); /* buttons, inputs */
--border-subtle: 1px solid #111111bf; /* hover borders, 75% opacity */
--border-disabled: 1px solid #0000004d; /* disabled state, 30% opacity */
--border-strong: 1px solid #000c; /* primary hover border, ~80% opacity */
/* ── Z-Index Scale ── */
/* reconstructed: moderate confidence */
--z-base: 0;
--z-above: 10; /* floating elements, tooltips */
--z-dropdown: 100; /* dropdown menus */
--z-nav: 200; /* sticky navigation */
--z-modal: 300; /* modal overlays */
--z-toast: 400; /* toast notifications / launchers */
```
**Layering Principles:**
- No decorative shadows — use borders for separation
- Tooltips: `background: var(--color-chalk)` to contrast against white body
- Nav: sticky position at `--z-nav`; transparent bg that may gain colour on scroll (inferred)
- Launcher widget (`launcherEnter` keyframe) appears at highest z-index above page content
---
## 8. Motion
```css
/* ── Duration Tokens ── */
/* extracted: high confidence */
--intercom-duration-fast: 0.2s; /* Micro-interactions: link hover, tab switch, input focus */
--intercom-duration-base: 0.4s; /* Component transitions: button colour, nav highlight */
/* ── Easing Tokens ── */
/* extracted: high confidence */
--intercom-ease-default: ease; /* Standard easing for most transitions */
/* ── Named Easing (from computed role_button transition) ── */
/* extracted: high confidence from computed transition property */
--intercom-ease-out-quart: cubic-bezier(0.165, 0.84, 0.44, 1); /* All button colour/bg/border transitions */
/* ── Keyframe Animations ── */
/* extracted: high confidence */
@keyframes marquee-left {
0% { transform: translate(0px); }
100% { transform: translateX(var(--marquee-translate)); }
}
@keyframes marquee-right {
0% { transform: translateX(var(--marquee-translate)); }
100% { transform: translate(0px); }
}
@keyframes carouselAutoplay {
0% { transform: scaleX(0); }
100% { transform: scaleX(1); }
}
@keyframes launcherEnter {
0% { transform: scale(0.8); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}
```
### When to Animate
- **Button bg/color/border:** `var(--intercom-duration-base)` + `var(--intercom-ease-out-quart)` — always
- **Link hover colour:** `var(--intercom-duration-fast)` + `var(--intercom-ease-default)`
- **Nav hover highlight:** `var(--intercom-duration-fast)` + `var(--intercom-ease-default)`
- **Tooltip appear/disappear:** `var(--intercom-duration-fast)` — opacity only, no translate
- **Logo marquee:** continuous loop, `marquee-left` / `marquee-right` depending on direction
- **Carousel progress:** `carouselAutoplay` — scaleX from 0→1 for progress indicator
### When NOT to Animate
- **Layout shifts** (height, width, position) — never animate geometry
- **Page-level transitions** — no evidence in extracted tokens
- **On `prefers-reduced-motion`** — respect via media query, set duration to 0.01ms
---
## 9. Anti-Patterns & Constraints
1. **Using `Inter`, `Roboto`, or `Arial` as a primary font → Why it fails:** AI agents default to `Inter` when no font context is provided, producing a visually generic result. Intercom uses the custom `Saans` typeface; any fallback-first render will look wrong and break the brand identity. **What to do instead:** Always use `font-family: Saans, "Saans Fallback", ui-sans-serif, system-ui, sans-serif` — the fallback chain is load-ordered, not the primary.
2. **Hardcoding colour hex values like `#111111` → Why it fails:** When the site's CSS variables resolve (e.g. `--color-off-black` changes in a dark mode or rebrand), hardcoded hex values won't update. AI agents that scan computed styles often extract hex and write it inline, breaking the token system. **What to do instead:** Use `var(--button-primary-background)` for primary CTA fill, `var(--color-off-black)` for text/border, never bare `#111111`.
3. **Applying `border-radius > 6px` to standard components → Why it fails:** AI agents with no radius context default to `8px` or `12px` rounded corners, producing a rounded/friendly aesthetic that directly contradicts Intercom's sharp, precise B2B identity. The largest token is `--intercom-radius-md: 6px`. **What to do instead:** Use `var(--button-radius)` (4px) for buttons/inputs, `var(--intercom-radius-md)` (6px) for nav highlights and card elements. Never exceed 6px.
4. **Setting `font-weight: 700` or `font-weight: bold` on headings → Why it fails:** Intercom's heading weight is `400` (their "Regular") and their body weight is `300` (their "Light"). An AI defaulting to bold headings produces heavy, un-branded typography that does not match the site's refined, airy aesthetic. **What to do instead:** Use `font-weight: 400` for headings and UI labels, `font-weight: 300` for body copy, per `--intercom-font-weight-medium` and `--intercom-font-weight-regular`.
5. **Constructing Tailwind class names dynamically → Why it fails:** Tailwind's JIT compiler tree-shakes classes that don't appear as complete strings in source. An agent writing `` `bg-[var(--${tokenName})]` `` will produce classes that never get generated. **What to do instead:** Use complete, static class strings like `bg-[var(--button-primary-background)]`, or apply via inline `style={{ background: 'var(--button-primary-background)' }}`.
6. **Omitting hover/active/disabled states on buttons → Why it fails:** Intercom's button system defines 5 explicit states per variant (default, hover, active, disabled, hover-foreground). An agent that only implements default state produces buttons that feel broken and fail accessibility audits. **What to do instead:** Always implement all states from Section 6.1/6.2 state tables, using the exact CSS custom property values.
7. **Using `box-shadow` for elevation/card separation → Why it fails:** Every extracted element on intercom.com returns `boxShadow: none`. Adding shadows creates visual noise inconsistent with the flat design language, making components look like they belong to a different design system. **What to do instead:** Use `border: 1px solid var(--border-default)` or background-colour contrast for separation. See Section 7.
8. **Using warm accent colours (red, orange, yellow) → Why it fails:** The Intercom colour system has zero warm-toned tokens. The only accent is a deep blue (`--color-helpdesk` = `rgb(0, 7, 203)`). An agent interpolating a "warning" orange or "success" green from generic defaults will produce off-brand UI. **What to do instead:** For all accents, use `var(--intercom-accent)` (`--color-helpdesk`). For error/warning/success states, mark as `[TBD - extract manually]` rather than inventing values.
9. **Applying `!important` to override token-based styles → Why it fails:** Intercom's Tailwind + CSS variable architecture is layered by specificity. `!important` breaks the cascade, making theming (e.g. the `.brand-i2y4` variant) impossible to apply cleanly. **What to do instead:** Use proper CSS specificity — component classes, then modifier classes — without `!important`.
10. **Using arbitrary spacing values not on the scale → Why it fails:** The spacing scale has deliberate gaps (8, 12, 16, 24, 32, 80, 112px). An agent interpolating `20px`, `40px`, or `64px` introduces off-scale values that break the vertical rhythm. **What to do instead:** Always use a token from `--intercom-space-xs` through `--intercom-space-3xl`. If no token fits, report it rather than inventing a value.
---
## 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 (28) */
--button-primary-background: var(--color-off-black);
--button-primary-hover-background: var(--color-helpdesk);
--button-primary-hover-border: #000c;
--button-primary-active-background: #0007cbd9;
--button-primary-disabled-background: #0000004d;
--button-secondary-hover-background: transparent;
--button-secondary-hover-border: #111111bf;
--button-secondary-active-background: #11111105;
--brand-surface-1: rgb(0, 7, 203); /* Brand surface, dominant on 9 elements — e.g. "div" /* mined from computed styles */ */
--button-secondary-border: var(--color-off-black);
--color-off-black: #111111;
--color-black: #000000;
--color-white: #ffffff;
--color-helpdesk: rgb(0, 7, 203);
--color-chalk: rgb(241, 238, 233);
--intercom-bg-app: var(--color-off-black);
--intercom-bg-surface: rgb(0, 7, 203);
--intercom-bg-selected: #0007cbd9;
--intercom-border: #000c;
--intercom-accent-foreground: #0000004d;
--intercom-text-primary: #111111bf;
--button-secondary-hover-foreground: #111111bf;
--button-secondary-disabled-border: #0000004d;
--button-secondary-disabled-foreground: #0000004d;
--border-default: 1px solid var(--color-off-black);
--border-subtle: 1px solid #111111bf;
--border-disabled: 1px solid #0000004d;
--border-strong: 1px solid #000c;
/* Typography (22) */
--font-size-xs: 14px; /* 31 elements — e.g. a "Learn more", a "Learn more", a "Learn more" /* mined from computed styles */ */
--font-size-sm: 16px; /* 143 elements — e.g. p "With an omnichannel ", p "A single solution me", p "Our AI engine automa" /* mined from computed styles */ */
--font-size-md: 20px; /* 9 elements — e.g. h4 "Copilot", h4 "AI-enhanced ticketin", h4 "No code automations" /* mined from computed styles */ */
--font-size-lg: 24px; /* 8 elements — e.g. h3 "AI-powered agent wor", h3 "A seamless experienc", h3 "AI-powered Insights " /* mined from computed styles */ */
--font-size-xl: 28px; /* 6 elements — e.g. h4 "Omnichannel", h4 "Live customer intell", h4 "Apps and integration" /* mined from computed styles */ */
--font-size-2xl: 40px; /* 5 elements — e.g. h3 "A world-class AI Age", h3 "Resolve queries fast", h3 "Deliver tailored exp" /* mined from computed styles */ */
--font-size-3xl: 54px; /* 5 elements — e.g. h2 "A complete solution ", h2 "A fully-featured AI-", h2 "A true partner with " /* mined from computed styles */ */
--font-weight-regular: 300; /* 29 elements — e.g. p "With an omnichannel ", p "A single solution me", p "Our AI engine automa" /* mined from computed styles */ */
--font-weight-medium: 400; /* 183 elements — e.g. h2 "A complete solution ", h2 "A fully-featured AI-", h2 "A true partner with " /* mined from computed styles */ */
--line-height-tight: 19.6px; /* 24 elements — e.g. a "Learn more", a "Learn more", a "Learn more" /* mined from computed styles */ */
--line-height-normal: 22.4px; /* 29 elements — e.g. p "With an omnichannel ", p "A single solution me", p "Our AI engine automa" /* mined from computed styles */ */
--intercom-font-size-xs: 14px;
--intercom-font-size-sm: 16px;
--intercom-font-size-md: 20px;
--intercom-font-size-lg: 24px;
--intercom-font-size-xl: 28px;
--intercom-font-size-2xl: 40px;
--intercom-font-size-3xl: 54px;
--intercom-line-height-tight: 19.6px;
--intercom-line-height-normal: 22.4px;
--intercom-font-weight-regular: 300;
--intercom-font-weight-medium: 400;
/* Spacing (24) */
--button-height: 2.5rem;
--button-padding-x: .875rem;
--button-secondary-border: var(--color-off-black);
--space-xs: 8px; /* 38 elements — e.g. div .grid, div .grid, div .grid /* mined from computed styles */ */
--space-sm: 12px; /* 30 elements — e.g. div .flex, div .flex, div .flex /* mined from computed styles */ */
--space-md: 16px; /* 107 elements — e.g. section .container, section .container, section .bg-off-white /* mined from computed styles */ */
--space-lg: 24px; /* 15 elements — e.g. div .mx-auto, div .mx-auto, div .@3xl:hidden /* mined from computed styles */ */
--space-xl: 32px; /* 20 elements — e.g. div .mx-auto, div .mx-auto, figure .2xl:aspect-1250/500 /* mined from computed styles */ */
--space-2xl: 80px; /* 18 elements — e.g. section .bg-chalk, section .bg-chalk, section .bg-off-white /* mined from computed styles */ */
--space-3xl: 112px; /* 16 elements — e.g. section .bg-off-white, section .bg-off-white, section .container /* mined from computed styles */ */
--intercom-space-xs: 8px;
--intercom-space-sm: 12px;
--intercom-space-md: 16px;
--intercom-space-lg: 24px;
--intercom-space-xl: 32px;
--intercom-space-2xl: 80px;
--intercom-space-3xl: 112px;
--breakpoint-sm: 640px;
--breakpoint-md: 768px;
--breakpoint-lg: 1024px;
--breakpoint-xl: 1280px;
--breakpoint-2xl: 1536px;
--container-max: 1280px;
--container-padding: var(--intercom-space-lg);
/* Radius (5) */
--button-radius: .25rem;
--radius-sm: 4px; /* 24 elements — e.g. a .font-inherit "Start free trial", a .font-inherit "View demo", a .font-inherit "Start free trial" /* mined from computed styles */ */
--radius-md: 6px; /* 20 elements — e.g. button .hover:bg-nav-background-highlight, button .flex "Product", button .flex "Resources" /* mined from computed styles */ */
--intercom-radius-sm: 4px;
--intercom-radius-md: 6px;
/* Effects (6) */
--button-primary-hover-foreground: var(--color-white);
--button-secondary-foreground: var(--color-off-black);
--button-secondary-active-foreground: var(--color-black);
--shadow-none: none;
--shadow-sm: none;
--shadow-md: none;
/* Motion (7) */
----motion-marquee-left: @keyframes marquee-left {
0% { transform: translate(0px); }
100% { transform: translateX(var(--marquee-translate)); }
}; /* @keyframes marquee-left */
----motion-marquee-right: @keyframes marquee-right {
0% { transform: translateX(var(--marquee-translate)); }
100% { transform: translate(0px); }
}; /* @keyframes marquee-right */
----motion-carouselAutoplay: @keyframes carouselAutoplay {
0% { transform: scaleX(0); }
100% { transform: scaleX(1); }
}; /* @keyframes carouselAutoplay */
----motion-launcherEnter: @keyframes launcherEnter {
0% { transform: scale(0.8); opacity: 0; }
100% { transform: scale(1); opacity: 1; }
}; /* @keyframes launcherEnter */
--duration-fast: 0.2s; /* 63 elements — e.g. a, a, a /* mined from computed styles */ */
--duration-base: 0.4s; /* 12 elements — e.g. a, a, a /* mined from computed styles */ */
--ease-default: ease; /* 205 elements — e.g. button, button, button /* mined from computed styles */ */
```
## Appendix B: Token Source Metadata
```
tokenSource: extracted-css-vars + computed-styles synthesis
confidence: high (23 CSS custom properties extracted directly)
moderate (computed styles used to synthesise typography composites,
spacing intent, and layout patterns)
extractionMethod: CSS custom property extraction (primary)
Computed style sampling across 12 element types (supplementary)
Border-radius census across 44+ elements (supplementary)
Interactive state rule extraction from stylesheet (supplementary)
curatedTokenCount: 31 (intercom-* prefix)
preservedOriginal: 25 (--button-* and --button-radius vars kept exactly as extracted)
synthesised: 6 (--intercom-ease-out-quart, --color-off-black resolved value,
--color-chalk from tooltip bg, display composite type tokens,
z-index scale, border composite tokens)
fontSource: @font-face declarations extracted from stylesheet
Primary UI font: Saans (custom, not a system font)
Fallback strategy: "Saans Fallback" → ui-sans-serif → system-ui → sans-serif
Specialty display fonts: MediumLL, IvoryLL, Serrif, DmMono, Maxi,
Ozik, Planar, Raster, Honey, Beretta
Inter: present in font files but NOT in any computed element font-family
detectedLibraries: Tailwind CSS (v3, utility-class-based, no CSS var @theme)
Bootstrap (detected, likely legacy or partial use)
caveats:
- --color-off-black and --color-helpdesk are referenced in CSS vars but their
primitive definitions were not directly extracted. Values inferred from computed
styles (off-black = #111111, helpdesk = rgb(0, 7, 203)).
- No dark mode variants detected in extracted tokens.
- No shadow tokens extracted — flat design confirmed across all sampled elements.
- Breakpoints not extracted — standard Tailwind v3 breakpoints documented as
moderate-confidence reconstructions. Extract manually to confirm.
- Section 5 layout patterns are inferred from component inventory and computed
structural styles, not from visual screenshots. Confirm with page screenshots.
```More from the gallery
Browse all kits →You may also like

atlassian.com
MITEnterprise design system for Atlassian's marketing site, featuring bold brand colours, custom typography, and structured spacing tokens built for large-scale SaaS platforms
06
boldsaaslanding-pagelight

github.com
MITGitHub's production design system with 568 CSS custom properties, featuring clean typography, accessible colour tokens, and motion primitives built for developer platforms and collaborative workflows
04
darkdeveloper-toolsaas

Customer.io
MITDark SaaS design system with mint-green accents and clean typography, built for product dashboards and developer-focused platforms
011
darksaasdeveloper-tooldashboard