Attio
MIT
Modern SaaS design system with a dark foundation, vibrant blue and cyan accents, and a soft brand palette suited for B2B product interfaces
Layout StudioImport this kit into a Studio project and start editing.
CLI installRun it in any project. No account needed.
npx @layoutdesign/context install attioUI themeReskin shadcn or Layout UI components to match Attio.
npx shadcn add https://layout.design/r/attio/theme.json# layout.md — Attio Design System
---
## 0. Quick Reference
**Stack:** Next.js marketing site · Tailwind CSS + Bootstrap · CSS custom properties (56 vars extracted, high confidence)
**Token source:** `extracted-css-vars` — use original variable names exactly as written.
**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 ── */
:root {
/* Colour */
--color-page-background: var(--color-primary-background); /* App bg — near-black dark surface */
--color-banner-background: var(--color-black-0); /* Banner strip bg */
--color-overscroll-bottom: var(--color-black-50); /* Overscroll fill at page bottom */
--attio-bg-surface: rgb(229, 238, 255); /* Brand surface — soft blue tint */
--attio-accent: rgb(38, 109, 240); /* Primary brand blue */
--attio-accent-cool: rgb(23, 189, 233); /* Brand cyan accent */
--attio-accent-warm: rgb(245, 185, 0); /* Brand amber accent */
--attio-text-primary: rgb(15, 194, 123); /* Brand green accent */
/* Button outline states */
--button-outline-bg: var(--color-black-50);
--button-outline-bg-hover: var(--color-black-100);
--button-outline-bg-focus: var(--color-black-200);
--button-outline-bg-active: var(--color-black-350);
--button-outline-border: var(--color-black-400);
--button-outline-text: var(--color-white-400);
/* Button ghost states */
--button-ghost-bg: transparent;
--button-ghost-bg-hover: var(--color-black-400);
--button-ghost-bg-active: var(--color-black-500);
--button-ghost-text: var(--color-white-500);
/* Typography */
--font-size-xs: 11.3px; --font-size-sm: 12px;
--font-size-md: 14px; --font-size-lg: 15px;
--font-size-xl: 16px; --font-size-2xl: 32px;
--font-size-3xl: 56px;
--font-weight-regular: 400; --font-weight-medium: 500;
--font-weight-semibold: 600; --font-weight-bold: 700;
--line-height-normal: 20px; --line-height-loose: 22px;
/* Spacing */
--space-xs: 1px; --space-sm: 6px; --space-md: 8px;
--space-lg: 10px; --space-xl: 12px; --space-2xl: 16px; --space-3xl: 24px;
/* Radius */
--radius-sm: 2px; --radius-md: 10px; --radius-lg: 12px; --radius-full: 50%;
/* Shadow */
--shadow-sm: lab(74.4644 0.482172 -39.075 / 0.25) 0px 0px 0px 4px; /* Focus ring glow */
--shadow-md: rgba(0,0,0,0.06) 0px 1px 6px 0px, rgba(0,0,0,0.16) 0px 2px 32px 0px;
/* Motion */
--duration-fast: 0.15s; --duration-base: 0.3s; --duration-slow: 0.4s;
--ease-default: cubic-bezier(0.2, 0, 0, 1);
/* Layout */
--site-header-nav-height: 68px;
}
```
```tsx
// Primary button — correct token usage, all states
<button className="button-primary" style={{
fontFamily: 'inter, "inter Fallback"', fontSize: 'var(--font-size-md)',
fontWeight: 'var(--font-weight-medium)', lineHeight: 'var(--line-height-normal)',
borderRadius: 'var(--radius-md)', padding: '0px var(--space-xl)',
display: 'flex', alignItems: 'center', gap: 'var(--space-sm)',
transition: `color var(--duration-base) var(--ease-default), border-color var(--duration-base) var(--ease-default)`,
}}>Try Attio free</button>
```
**NEVER rules:**
- **NEVER** use `Inter` (system) — use the custom `inter` or `interDisplay` font families (lowercase, self-hosted).
- **NEVER** use `border-radius` values other than 2px, 10px, 12px, or 50%. The brand is NOT pill-shaped.
<!-- Quick Reference truncated to fit the 75-line cap. See later sections for the full design system. -->
## 1. Design Direction & Philosophy
### Character
Attio is a **technical, precision-engineered CRM** for modern revenue teams. The visual language is **dark, dense, and data-forward** — it communicates competence, not friendliness. Think developer tool aesthetics applied to a B2B SaaS product.
### Aesthetic Intent
- **Dark-first:** The primary background is near-black. Content panels float as dark surfaces with subtle elevation.
- **Typographically exacting:** `interDisplay` is used for display headings with tight negative letter-spacing (`-1.28px` at 64px). Body copy uses `inter`. Editorial long-form uses `tiemposText` (serif). Three distinct families, each with a defined role.
- **Brand colour as accent only:** The four brand marks (blue `rgb(38,109,240)`, amber `rgb(245,185,0)`, cyan `rgb(23,189,233)`, green `rgb(15,194,123)`) appear in logos, illustrations, and icon fills — NOT as background fills or button colours in the UI shell.
- **Restrained radius:** Corners are sharp-to-moderate: 2px (micro elements), 10px (buttons/dropdowns), 12px (cards/modals). No pill buttons. No 24px+ radius on UI components.
- **Motion is purposeful:** Gradient spins, blur-in navigation transitions, and gradient angle animations are used in hero illustrations and marketing sequences — NOT on every hover interaction.
### What This Design Explicitly Rejects
- Warm colour palettes or light-mode-first thinking.
- Rounded "friendly" corners (no border-radius > 12px on UI components, 50% only for avatar/launcher circles).
- Heavy drop shadows as decoration (shadows exist only for elevation and focus rings).
- System fonts for display text — `interDisplay` and `tiemposText` are intentional brand fonts.
- Generic SaaS blue/green CTA buttons — primary buttons are near-black with gradient fills, not blue.
- Busy layouts — whitespace is generous at section level despite dense component interiors.
---
## 2. Colour System
### Tier 1 — Primitive Scale (referenced via CSS variables, exact resolved values noted where confirmed)
```css
:root {
/* Black scale — dark UI surfaces, borders, button states */
--color-black-0: /* Lightest — banner backgrounds */ /* [TBD - extract manually: likely ~rgb(255,255,255) or near-white] */;
--color-black-50: /* Button outline resting bg */ /* [TBD - extract manually: likely ~rgb(245,246,248)] */;
--color-black-100: /* Button outline hover bg */ /* [TBD - extract manually: likely ~rgb(230,232,236)] */;
--color-black-200: /* Button outline focus bg / disabled */ /* [TBD - extract manually] */;
--color-black-350: /* Button outline active bg */ /* [TBD - extract manually] */;
--color-black-400: /* Ghost hover bg, outline border */ /* [TBD - extract manually] */;
--color-black-500: /* Ghost active bg, disabled text */ /* [TBD - extract manually] */;
--color-black-600: /* Ghost disabled text */ /* [TBD - extract manually] */;
/* White scale — text on dark surfaces */
--color-white-100: /* Active/brightest text */ /* [TBD - extract manually: likely rgb(255,255,255)] */;
--color-white-400: /* Default button text */ /* [TBD - extract manually: likely ~rgb(220,224,230)] */;
--color-white-500: /* Ghost button text */ /* [TBD - extract manually] */;
/* Brand primitives — resolved RGB values confirmed */
--brand-mark-1: rgb(38, 109, 240); /* Primary brand blue — SVG fills, illustrations */
--brand-mark-2: rgb(245, 185, 0); /* Amber — accent SVG fills */
--brand-mark-3: rgb(23, 189, 233); /* Cyan — accent SVG fills */
--brand-mark-4: rgb(15, 194, 123); /* Green — accent SVG fills */
--brand-surface-1: rgb(229, 238, 255); /* Soft blue tint — feature surface backgrounds */
/* Structural */
--color-primary-background: /* Near-black app bg */ /* [TBD - extract manually: likely ~rgb(10,10,12) or similar] */;
}
```
### Tier 2 — Semantic Aliases
```css
:root {
/* Surfaces */
--color-page-background: var(--color-primary-background); /* Primary app background — extracted: high confidence */
--color-banner-background: var(--color-black-0); /* Top announcement banner bg — extracted: high confidence */
--color-overscroll-top: var(--color-page-background); /* Overscroll region at top of page */
--color-overscroll-bottom: var(--color-black-50); /* Overscroll region at page bottom */
/* Brand accent semantic roles */
--attio-accent: rgb(38, 109, 240); /* Primary brand identity — logos, hero illustrations */
--attio-accent-cool: rgb(23, 189, 233); /* Secondary cool accent — feature illustrations */
--attio-accent-warm: rgb(245, 185, 0); /* Secondary warm accent — feature illustrations */
--attio-accent-green: rgb(15, 194, 123); /* Success / growth accent — feature illustrations */
--attio-bg-surface: rgb(229, 238, 255); /* Elevated feature panel surface — extracted: medium confidence */
}
```
### Tier 3 — Component Tokens (Button)
```css
:root {
/* ── Outline Button ── */
--button-outline-bg: var(--color-black-50); /* Resting background */
--button-outline-border: var(--color-black-400); /* Resting border */
--button-outline-text: var(--color-white-400); /* Resting text */
--button-outline-bg-hover: var(--color-black-100); /* Hover background */
--button-outline-border-hover: var(--color-black-500); /* Hover border */
--button-outline-text-hover: var(--color-white-400); /* Hover text */
--button-outline-bg-focus: var(--color-black-200); /* Focus background */
--button-outline-border-focus: var(--color-black-600); /* Focus border */
--button-outline-text-focus: var(--color-white-400); /* Focus text */
--button-outline-bg-active: var(--color-black-350); /* Active/pressed background */
--button-outline-border-active: var(--color-white-100); /* Active border */
--button-outline-text-active: var(--color-white-100); /* Active text — brightened */
--button-outline-bg-disabled: var(--color-black-200); /* Disabled background */
--button-outline-border-disabled: var(--color-black-400); /* Disabled border */
--button-outline-text-disabled-foreground: var(--color-black-500); /* Disabled text — muted */
/* ── Ghost Button ── */
--button-ghost-bg: transparent; /* Resting — no background */
--button-ghost-text: var(--color-white-500); /* Resting text */
--button-ghost-bg-hover: var(--color-black-400); /* Hover fill appears */
--button-ghost-text-hover: var(--color-white-100); /* Hover text — brightened */
--button-ghost-bg-focus: var(--color-black-400); /* Focus fill */
--button-ghost-text-focus: var(--color-white-100); /* Focus text */
--button-ghost-bg-active: var(--color-black-500); /* Active — deepened fill */
--button-ghost-text-active: var(--color-white-100); /* Active text */
--button-ghost-bg-disabled: transparent; /* Disabled — no fill */
--button-ghost-text-disabled-foreground: var(--color-black-600); /* Disabled text — most muted */
}
```
### Colour Modes
The site defines both light and dark mode with identical button state variable names. Mode switching uses `[data-theme="dark"]` selectors. The primary UI shell is dark-first; the `--color-primary-background` resolves to a near-black surface.
```css
/* Dark mode overrides — same variable names, potentially different resolved primitives */
[data-theme="dark"] {
--button-outline-bg: var(--color-black-50);
--button-outline-bg-hover: var(--color-black-100);
--button-outline-bg-focus: var(--color-black-200);
--button-outline-bg-active: var(--color-black-350);
--button-ghost-bg-disabled: transparent;
--button-ghost-bg-hover: var(--color-black-400);
--button-ghost-bg-active: var(--color-black-500);
}
```
---
## 3. Typography System
**Font families (self-hosted, NOT system fonts):**
```css
:root {
/* Display headings — Inter Display variant, used for H1/H3 and large marketing text */
--font-display: interDisplay, "interDisplay Fallback", Arial, sans-serif;
/* Body / UI text — Inter, used for buttons, inputs, nav, body copy */
--font-body: inter, "inter Fallback", Arial, sans-serif;
/* Editorial / testimonial text — Tiempos Text serif */
--font-editorial: tiemposText, "tiemposText Fallback", Georgia, serif;
/* Code / monospace — JetBrains Mono */
--font-mono: "JetBrains Mono", "JetBrains Mono Fallback", "Courier New", monospace;
}
```
### Composite Type Tokens
| Role | font-family | font-size | font-weight | line-height | letter-spacing | text-transform |
|---|---|---|---|---|---|---|
| **Display H1** | `interDisplay` | `64px` | `600` | `64px` | `-1.28px` | none |
| **Section Label (H2)** | `interDisplay` | `12px` | `600` | `14px` | `+0.72px` | `uppercase` |
| **Section Heading (H3)** | `interDisplay` | `32px` | `600` | `38px` | `-0.32px` | none |
| **Body / UI Text** | `inter` | `16px` | `500` | `22px` | `-0.16px` | none |
| **Button Text** | `inter` | `14px` | `500` | `20px` | `-0.07px` | none |
| **Input Text** | `inter` | `16px` | `500` | `24px` | `-0.16px` | none |
| **Nav Item** | `inter` | `15px` | `500` | `22px` | `-0.16px` | none |
| **Tab Label** | `inter` | `11px` | `500` | `11px` | `-2%` | none |
| **Testimonial Quote** | `tiemposText` | `56px` | `400` | — | — | none |
| **Mono / Code** | `JetBrains Mono` | — | `100–800` | — | — | none |
| **Author/Bold callout** | `inter` | `16px` | `700` | `22px` | `-0.16px` | none |
```css
/* Composite CSS block for AI agent reference */
:root {
/* Display H1 — hero headline */
--type-display-h1-family: interDisplay, "interDisplay Fallback", Arial, sans-serif;
--type-display-h1-size: 64px;
--type-display-h1-weight: 600;
--type-display-h1-line: 64px;
--type-display-h1-tracking: -1.28px;
--type-display-h1-align: center;
/* Section label (H2 used as eyebrow/label) */
--type-section-label-family: interDisplay, "interDisplay Fallback", Arial, sans-serif;
--type-section-label-size: 12px;
--type-section-label-weight: 600;
--type-section-label-line: 14px;
--type-section-label-tracking: 0.72px;
--type-section-label-transform: uppercase;
/* Section heading (H3) */
--type-section-heading-family: interDisplay, "interDisplay Fallback", Arial, sans-serif;
--type-section-heading-size: 32px;
--type-section-heading-weight: 600;
--type-section-heading-line: 38px;
--type-section-heading-tracking: -0.32px;
/* Button */
--type-button-family: inter, "inter Fallback", Arial, sans-serif;
--type-button-size: var(--font-size-md); /* 14px */
--type-button-weight: var(--font-weight-medium); /* 500 */
--type-button-line: var(--line-height-normal); /* 20px */
--type-button-tracking: -0.07px;
/* Body/UI */
--type-body-family: inter, "inter Fallback", Arial, sans-serif;
--type-body-size: var(--font-size-xl); /* 16px */
--type-body-weight: var(--font-weight-medium); /* 500 */
--type-body-line: var(--line-height-loose); /* 22px */
--type-body-tracking: -0.16px;
}
```
**Pairing rules:**
- `interDisplay` is **only** for headings (H1, H3, large display numbers) — never for body copy.
- `inter` handles all UI chrome: buttons, inputs, nav, labels, body paragraphs.
- `tiemposText` is **only** for large testimonial quotes or editorial pull-quotes — never for UI elements.
- `JetBrains Mono` is for code snippets and technical monospace callouts only.
---
## 4. Spacing & Layout
```css
:root {
/* ── Spacing Scale (7 tokens) ── */
--space-xs: 1px; /* Micro gap — hairline separators, borders */
--space-sm: 6px; /* Tight gap — icon-to-label, inline flex gaps */
--space-md: 8px; /* Base unit — compact padding, small insets */
--space-lg: 10px; /* Comfortable gap — vertical rhythm in lists */
--space-xl: 12px; /* Button horizontal padding, card inner gaps */
--space-2xl: 16px; /* Section inner padding, grid gutters */
--space-3xl: 24px; /* Section top margin, large component spacing */
/* ── Layout — Header ── */
--site-header-height: calc(
var(--site-header-polaris-toolbar-height) +
var(--site-header-nav-height) +
var(--site-header-subheader-height) +
var(--site-header-banner-height)
);
--site-header-nav-height: 68px; /* Primary nav bar height */
--site-header-banner-hidden-height: 0px;
--site-header-banner-visible-height: 48px; /* Announcement banner height when shown */
--site-header-banner-height: var(--site-header-banner-hidden-height); /* Default: hidden */
--site-header-polaris-toolbar-hidden-height: 0px;
--site-header-polaris-toolbar-visible-height: 36px;
--site-header-polaris-toolbar-height: var(--site-header-polaris-toolbar-hidden-height);
--site-header-subheader-desktop-height: 0px;
--site-header-subheader-mobile-hidden-height: 0px;
--site-header-subheader-mobile-visible-height: 63px; /* Mobile sub-nav when open */
--site-header-subheader-mobile-height: var(--site-header-subheader-mobile-hidden-height);
--site-header-subheader-height: var(--site-header-subheader-mobile-height);
}
```
### Grid System
| Breakpoint | Name | Min Width | Layout |
|---|---|---|---|
| Mobile | xs | 0px | Single column, full-width |
| Mobile L | sm | 600px | 2-column grid available |
| Tablet | md | 992px | Multi-column, nav collapses to hamburger below |
| Desktop | lg | 1199px | Full desktop layout |
| Desktop XL | xl | 1200px | Max-width container, centred |
**Container:** Max-width centred with horizontal padding of `var(--space-2xl)` (16px) at mobile, increasing at breakpoints. Exact max-width: `[TBD - extract manually]`.
**Flex vs Grid rules:**
- Use `display: flex; flex-direction: row; align-items: center; gap: var(--space-sm)` for inline button/icon combos, nav items, and horizontal chip groups.
- Use CSS Grid for feature card sections (2–3 column layouts at 992px+).
- Cards within grid sections use `display: block` internally.
- Modals use `display: flex; flex-direction: row; justify-content: flex-start`.
---
## 5. Page Structure & Layout Patterns
### 5.1 Section Map
| # | Section | Layout Type | Approx Height | Key Elements | Confidence |
|---|---|---|---|---|---|
| 1 | **Announcement Banner** | Full-width strip, flex row | 48px (when visible) | Text + CTA link | extracted |
| 2 | **Primary Navigation** | Full-width, block, `padding: 16px 0 15px` | 68px | Logo, nav items (15px inter 500), CTA buttons | extracted |
| 3 | **Hero** | Centred column, text-centre | Tall — ~80vh | H1 (64px), subheader body (16px), primary + outline CTAs, hero illustration | inferred |
| 4 | **Social Proof / Logos** | Flex row, centred, gap-based | ~100px | Customer logo strip | inferred |
| 5 | **Feature Sections (×3)** | Alternating text + visual, grid | ~500px each | Section label (H2 eyebrow, uppercase, 12px), H3 heading (32px), body copy (16px), feature card visuals | inferred from H2 labels `[01]`, `[02]`, `[03]` |
| 6 | **Testimonial / Quote** | Full-width, centred | ~400px | Large serif quote (56px `tiemposText`), author bold (700 inter), company name | inferred from `font-size-3xl` elements |
| 7 | **Feature Card Grid** | CSS grid, 2–3 columns | ~600px | Cards (`border-radius: var(--radius-lg)` = 12px), section label, headings | inferred from card inventory (59 instances) |
| 8 | **CTA / Sign-up Section** | Centred column | ~300px | Headline, email input (`border-radius: var(--radius-md)`), primary button | inferred from input inventory (5 instances) |
| 9 | **Footer** | Multi-column grid | ~300px | Nav link groups (14px inter 400), logo, social links | inferred |
### 5.2 Layout Patterns
**Navigation (extracted):**
```
display: block → role="navigation" wrapper
└── Inner: flex row, justify-between, align-center
├── Logo (left)
├── Nav items: flex row, gap: var(--space-sm) — 15px inter 500, padding: 16px 0 15px
└── CTA cluster: flex row, gap: var(--space-sm)
├── Ghost button ("Sign in")
└── Outline button ("Get started") — border-radius: var(--radius-md) = 10px
```
**Feature section layout (inferred from digest + H2 eyebrow labels):**
```
Section container: max-width centred, padding: 0 var(--space-2xl)
├── Eyebrow label: H2, 12px interDisplay 600, uppercase, letter-spacing +0.72px, flex row gap: 6px
├── Section heading: H3, 32px interDisplay 600, line-height 38px
├── Body copy: 16px inter 500, line-height 22px
└── Visual panel: bg var(--attio-bg-surface), border-radius var(--radius-lg)
```
**Card grid (inferred):**
```
CSS Grid: repeat(auto-fit, minmax(300px, 1fr)), gap: var(--space-3xl)
└── Card: display block, border-radius var(--radius-lg) = 12px, padding var(--space-3xl)
box-shadow var(--shadow-md)
```
**Primary button layout (extracted):**
```
display: flex, flex-direction: row, justify-content: center,
align-items: center, gap: var(--space-sm) [6px], padding: 0 var(--space-xl) [12px]
border-radius: var(--radius-md) [10px]
```
### 5.3 Visual Hierarchy
- **H1 at 64px semibold** with `text-align: center` dominates the hero — the single largest text element on the page.
- **H2 used as eyebrow labels** (not section headings) — 12px uppercase with `+0.72px` tracking. This is counterintuitive; H2 is visually subordinate.
- **CTAs:** Primary button is near-black with gradient fill, placed above the fold in the hero. Outline button (`--button-outline-bg`) is secondary. Ghost buttons appear in navigation.
- **Brand accent colours** (blue, amber, cyan, green) appear only in SVG illustrations and brand marks — never as button fills or background colours.
- **Whitespace rhythm:** `margin-top: 24px` on H1 establishes the section top rhythm. Cards have no padding in computed styles — padding is applied by inner content.
- **Testimonial quotes** at 56px `tiemposText` italic create a dramatic typographic contrast moment in the page flow.
### 5.4 Content Patterns
**Repeating feature block pattern:**
1. Eyebrow label (H2, uppercase, muted, numbered `[01]`, `[02]`, `[03]`)
2. Section heading (H3, 32px, tight tracking)
3. Body paragraph (16px inter, line-height 22px)
4. Visual/illustration panel (brand surface bg, 12px radius)
**Card pattern:**
- No border at rest, `box-shadow: var(--shadow-md)` for elevation
- 12px radius (`--radius-lg`)
- Inner content uses `inter` 16px 500
**Nav item pattern:**
- 15px inter 500, letter-spacing -0.16px
- Flex row with 6px gap (for chevron/icon)
- `padding: 16px 0 15px` creates the 68px nav bar height
---
## 6. Component Patterns
### 6.1 Button — Primary
**Anatomy:** `<button>` → [optional icon] + label text
**Token-to-property mapping:**
| State | Background | Border | Text | Transition |
|---|---|---|---|---|
| Default | `radial-gradient(at 50% -10%, lab(12.72...) 0%, lab(12.72...) 100%)` | `1px solid lab(37.43...)` | `lab(96.16...)` ≈ near-white | — |
| Hover | `var(--button-primary-bg-hover-from/to)` | — | `var(--button-primary-text-hover)` | `color var(--duration-base) var(--ease-default)` |
| Focus | `var(--button-primary-bg-focus-from/to)` | — | `var(--button-primary-text-focus)` | same |
| Active | `var(--button-primary-bg-active-from/to)` | — | `var(--button-primary-text-active)` | same |
| Disabled | `var(--button-primary-bg-disabled-from/to)` | — | `var(--button-primary-text-disabled-foreground)` | none |
```tsx
// Primary Button — production-ready, all states
import { ButtonHTMLAttributes } from 'react';
interface PrimaryButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
isLoading?: boolean;
}
export function PrimaryButton({ children, disabled, isLoading, ...props }: PrimaryButtonProps) {
return (
<button
{...props}
disabled={disabled || isLoading}
data-active={props['aria-pressed'] === 'true' ? 'true' : undefined}
className="button-primary"
style={{
fontFamily: 'inter, "inter Fallback", Arial, sans-serif',
fontSize: '14px', /* --font-size-md */
fontWeight: 500, /* --font-weight-medium */
lineHeight: '20px', /* --line-height-normal */
letterSpacing: '-0.07px',
borderRadius: '10px', /* --radius-md */
padding: '0px 12px', /* --space-xl horizontal */
height: '36px',
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: '6px', /* --space-sm */
border: '1px solid lab(37.426 -1.09151 -9.33263)',
cursor: disabled || isLoading ? 'not-allowed' : 'pointer',
opacity: disabled ? 0.5 : 1,
transition: [
'color 0.3s cubic-bezier(0.2, 0, 0, 1)',
'border-color 0.3s cubic-bezier(0.2, 0, 0, 1)',
'--button-primary-bg-from 0.3s cubic-bezier(0.2, 0, 0, 1)',
'--button-primary-bg-to 0.3s cubic-bezier(0.2, 0, 0, 1)',
].join(', '),
}}
>
{isLoading ? (
<svg
aria-hidden="true"
width="14" height="14"
style={{ animation: 'spin 1s linear infinite' }}
viewBox="0 0 14 14"
fill="none"
>
<circle cx="7" cy="7" r="6" stroke="currentColor" strokeWidth="2" strokeOpacity="0.25" />
<path d="M7 1a6 6 0 0 1 6 6" stroke="currentColor" strokeWidth="2" strokeLinecap="round" />
</svg>
) : null}
{children}
</button>
);
}
```
---
### 6.2 Button — Outline
**Anatomy:** `<button class="button-outline">` → label text
```tsx
export function OutlineButton({ children, disabled, ...props }: PrimaryButtonProps) {
return (
<button
{...props}
disabled={disabled}
className="button-outline"
style={{
fontFamily: 'inter, "inter Fallback", Arial, sans-serif',
fontSize: '14px',
fontWeight: 500,
lineHeight: '20px',
letterSpacing: '-0.07px',
borderRadius: '10px', /* --radius-md */
padding: '0px 12px',
height: '36px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '6px',
backgroundColor: 'var(--button-outline-bg)',
border: '1px solid var(--button-outline-border)',
color: 'var(--button-outline-text)',
cursor: disabled ? 'not-allowed' : 'pointer',
transition: 'background-color 0.3s cubic-bezier(0.2, 0, 0, 1), border-color 0.3s cubic-bezier(0.2, 0, 0, 1), color 0.3s cubic-bezier(0.2, 0, 0, 1)',
}}
onMouseEnter={e => {
const el = e.currentTarget;
el.style.backgroundColor = 'var(--button-outline-bg-hover)';
el.style.borderColor = 'var(--button-outline-border-hover)';
}}
onMouseLeave={e => {
const el = e.currentTarget;
el.style.backgroundColor = 'var(--button-outline-bg)';
el.style.borderColor = 'var(--button-outline-border)';
}}
>
{children}
</button>
);
}
```
> **Prefer CSS class `.button-outline` with the full CSS rule set below over inline JS event handlers in production:**
```css
.button-outline {
background-color: var(--button-outline-bg);
border: 1px solid var(--button-outline-border);
color: var(--button-outline-text);
border-radius: var(--radius-md);
padding: 0 var(--space-xl);
font-family: inter, "inter Fallback", Arial, sans-serif;
font-size: var(--font-size-md);
font-weight: var(--font-weight-medium);
line-height: var(--line-height-normal);
transition: background-color var(--duration-base) cubic-bezier(0.2, 0, 0, 1),
border-color var(--duration-base) cubic-bezier(0.2, 0, 0, 1),
color var(--duration-base) cubic-bezier(0.2, 0, 0, 1);
}
.button-outline:hover, .button-outline[data-state="open"] {
background-color: var(--button-outline-bg-hover);
border-color: var(--button-outline-border-hover);
color: var(--button-outline-text-hover);
}
.button-outline:focus-visible {
background-color: var(--button-outline-bg-focus);
border-color: var(--button-outline-border-focus);
color: var(--button-outline-text-focus);
box-shadow: var(--shadow-sm); /* Focus ring */
outline: none;
}
.button-outline:active, .button-outline[data-active="true"] {
background-color: var(--button-outline-bg-active);
border-color: var(--button-outline-border-active);
color: var(--button-outline-text-active);
}
.button-outline:disabled {
background-color: var(--button-outline-bg-disabled);
border-color: var(--button-outline-border-disabled);
color: var(--button-outline-text-disabled-foreground);
cursor: not-allowed;
}
```
---
### 6.3 Button — Ghost
```css
.button-ghost {
background-color: var(--button-ghost-bg); /* transparent */
color: var(--button-ghost-text);
border: none;
border-radius: var(--radius-md);
padding: 0 var(--space-xl);
font-family: inter, "inter Fallback", Arial, sans-serif;
font-size: var(--font-size-md);
font-weight: var(--font-weight-medium);
line-height: var(--line-height-normal);
transition: background-color var(--duration-base) cubic-bezier(0.2, 0, 0, 1),
color var(--duration-base) cubic-bezier(0.2, 0, 0, 1);
}
.button-ghost:hover, .button-ghost[data-state="open"] {
background-color: var(--button-ghost-bg-hover);
color: var(--button-ghost-text-hover);
}
.button-ghost:focus-visible {
background-color: var(--button-ghost-bg-focus);
color: var(--button-ghost-text-focus);
box-shadow: var(--shadow-sm);
outline: none;
}
.button-ghost:active, .button-ghost[data-active="true"] {
background-color: var(--button-ghost-bg-active);
color: var(--button-ghost-text-active);
}
.button-ghost:disabled {
background-color: var(--button-ghost-bg-disabled); /* transparent */
color: var(--button-ghost-text-disabled-foreground);
cursor: not-allowed;
}
```
---
### 6.4 Input
**Anatomy:** `<input>` with border, white bg, dark text — anomalous light-surface element in a dark UI.
| State | Background | Border | Shadow |
|---|---|---|---|
| Default | `lab(99.9987...)` ≈ white | `1px solid lab(86.10...)` ≈ light grey | none |
| Focus | white | Focus ring via `--shadow-sm` | `var(--shadow-sm)` |
| Disabled | `[TBD]` | `[TBD]` | — |
| Error | `[TBD]` | red border | — |
```tsx
export function TextInput({
id, label, error, disabled, ...props
}: React.InputHTMLAttributes<HTMLInputElement> & { label: string; error?: string }) {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
<label htmlFor={id} style={{
fontFamily: 'inter, "inter Fallback", Arial, sans-serif',
fontSize: '14px', fontWeight: 500, color: 'var(--button-outline-text)',
}}>{label}</label>
<input
id={id}
disabled={disabled}
aria-invalid={!!error}
aria-describedby={error ? `${id}-error` : undefined}
style={{
fontFamily: 'inter, "inter Fallback", Arial, sans-serif',
fontSize: '16px', /* --font-size-xl */
fontWeight: 500,
lineHeight: '24px',
letterSpacing: '-0.16px',
borderRadius: '10px', /* --radius-md */
padding: '10px 13px',
border: error
? '1px solid rgb(239, 68, 68)' /* error state — use a red primitive */
: '1px solid lab(86.0989 -0.77799 -4.0961)',
backgroundColor: 'lab(99.9987 0.0337958 0.000309944)', /* ≈ white */
color: 'lab(14.599 -0.159428 -2.99254)', /* ≈ near-black text */
outline: 'none',
transition: '0.3s cubic-bezier(0, 0, 0, 1)',
opacity: disabled ? 0.5 : 1,
cursor: disabled ? 'not-allowed' : 'text',
width: '100%',
boxSizing: 'border-box',
}}
{...props}
/>
{error && (
<span id={`${id}-error`} role="alert" style={{
fontSize: '12px', color: 'rgb(239, 68, 68)', fontFamily: 'inter, "inter Fallback"',
}}>{error}</span>
)}
</div>
);
}
```
---
### 6.5 Card
**Anatomy:** `<div>` → [optional header] + content + [optional footer]
| State | Appearance |
|---|---|
| Default | `display: block`, no border, `box-shadow: var(--shadow-md)`, `border-radius: var(--radius-lg)` |
| Hover | Slight shadow elevation increase (inferred — extract manually) |
| Focus | Focus ring via `--shadow-sm` if interactive |
```tsx
export function Card({ children, interactive = false, ...props }:
React.HTMLAttributes<HTMLDivElement> & { interactive?: boolean }) {
return (
<div
tabIndex={interactive ? 0 : undefined}
role={interactive ? 'button' : undefined}
style={{
fontFamily: 'inter, "inter Fallback", Arial, sans-serif',
fontSize: '16px',
fontWeight: 500,
lineHeight: '22px',
letterSpacing: '-0.16px',
borderRadius: '12px', /* --radius-lg */
padding: '24px', /* --space-3xl */
boxShadow: 'rgba(0,0,0,0.06) 0px 1px 6px 0px, rgba(0,0,0,0.16) 0px 2px 32px 0px', /* --shadow-md */
transition: 'box-shadow 0.3s cubic-bezier(0.2, 0, 0, 1)',
display: 'block',
cursor: interactive ? 'pointer' : 'default',
}}
{...props}
>
{children}
</div>
);
}
```
---
### 6.6 Nav Item
**Anatomy:** `<a>` or `<button>` inside `role="navigation"` → label + optional chevron icon
```tsx
export function NavItem({ children, active, href, ...props }:
React.AnchorHTMLAttributes<HTMLAnchorElement> & { active?: boolean }) {
return (
<a
href={href}
aria-current={active ? 'page' : undefined}
style={{
fontFamily: 'inter, "inter Fallback", Arial, sans-serif',
fontSize: '15px', /* --font-size-lg */
fontWeight: 500,
lineHeight: '22px',
letterSpacing: '-0.16px',
color: active
? 'lab(99.9987 0.0337958 0.000309944)' /* white — active */
: 'lab(10.7201 -0.0959039 -1.54182)', /* near-black — resting (context-dependent) */
textDecoration: 'none',
display: 'inline-flex',
alignItems: 'center',
gap: '6px', /* --space-sm */
padding: '16px 0 15px', /* extracted nav padding */
transition: 'color 0.15s cubic-bezier(0.2, 0, 0, 1)',
cursor: 'pointer',
}}
{...props}
>
{children}
</a>
);
}
```
---
### 6.7 Dropdown / Nav Trigger
**Anatomy:** `<button>` → label + chevron, `border-radius: var(--radius-md)`, transparent border at rest.
```css
.dropdown-trigger {
font-family: inter, "inter Fallback", Arial, sans-serif;
font-size: var(--font-size-lg); /* 15px */
font-weight: var(--font-weight-medium);
line-height: var(--line-height-loose); /* 22px */
letter-spacing: -0.16px;
border-radius: var(--radius-md); /* 10px */
padding: 0 var(--space-md) 0 var(--space-xl); /* 0 8px 0 12px */
display: inline-flex;
align-items: center;
gap: var(--space-sm); /* 6px */
border: 1px solid transparent;
background: transparent;
transition: color var(--duration-base) cubic-bezier(0.2, 0, 0, 1),
background-color var(--duration-base) cubic-bezier(0.2, 0, 0, 1),
border-color var(--duration-base) cubic-bezier(0.2, 0, 0, 1);
}
.dropdown-trigger[data-state="open"] {
background-color: var(--button-ghost-bg-hover);
}
```
---
## 7. Elevation & Depth
```css
:root {
/* ── Shadows ── */
--shadow-sm: /* Focus ring — blue-tinted glow */
rgba(0,0,0,0) 0px 0px 0px 0px,
rgba(0,0,0,0) 0px 0px 0px 0px,
rgba(0,0,0,0) 0px 0px 0px 0px,
lab(74.4644 0.482172 -39.075 / 0.25) 0px 0px 0px 4px,
rgba(0,0,0,0) 0px 0px 0px 0px;
/* Use for: focus rings on interactive elements */
--shadow-md: /* Card / floating panel elevation */
rgba(0,0,0,0.06) 0px 1px 6px 0px,
rgba(0,0,0,0.16) 0px 2px 32px 0px;
/* Use for: cards, modals, dropdowns, floating buttons */
--shadow-badge: /* Legacy badge shadow */
rgb(128,128,128) 0px 0px 5px 0px;
/* Use for: Intercom-style chat launcher, third-party widgets only */
/* ── Z-Index Scale ── */
--mobile-nav-drawer-overlay-z-index: 90; /* Mobile nav overlay backdrop */
--mobile-nav-drawer-content-z-index: 91; /* Mobile nav drawer panel */
--site-header-z-index: 92; /* Sticky site header */
--navigation-menu-z-index: 93; /* Nav dropdown menus */
--dialog-overlay-z-index: 100; /* Modal/dialog backdrop */
--dialog-content-z-index: 101; /* Modal/dialog panel */
--context-menu-portal-z-index: 200; /* Context menu portal root */
--context-menu-positioner-z-index: 201; /* Context menu positioner */
--context-menu-popup-z-index: 202; /* Context menu popup */
--style-overlay-z-index: 999; /* Style/inspector overlay — development only */
}
```
**Border tokens (from computed styles):**
```css
:root {
--border-default: 1px solid lab(86.0989 -0.77799 -4.0961); /* Light grey — inputs, dividers */
--border-button: 1px solid lab(37.426 -1.09151 -9.33263); /* Dark — primary button border */
--border-focus: var(--button-outline-border-focus); /* Focus border escalation */
}
```
**Layering principles:**
- Site header (z: 92) is always above content, always below modals.
- Context menus (z: 202) always above dialogs (z: 101).
- Never use `z-index: 9999` — use `--style-overlay-z-index: 999` as the absolute maximum.
- Elevation is communicated through `--shadow-md` (soft dual-layer shadow), not through background colour change.
---
## 8. Motion
```css
:root {
/* ── Duration Tokens ── */
--duration-fast: 0.15s; /* Micro-interactions: navigation enter/exit, icon swaps */
--duration-base: 0.3s; /* Standard UI transitions: button states, colour changes */
--duration-slow: 0.4s; /* Larger element transitions: SVG animations, panel slides */
/* ── Easing ── */
--ease-default: cubic-bezier(0.2, 0, 0, 1); /* Standard — slightly ease-out, snappy */
--ease-out-cubic: cubic-bezier(0.33, 0, 0, 1); /* Navigation animations — more pronounced */
--ease-input: cubic-bezier(0, 0, 0, 1); /* Input fields — immediate response feel */
--ease-launcher: cubic-bezier(0.45, 0, 0.2, 1); /* Float button active press */
}
```
### Named Animations
```css
/* Navigation transitions — blur + translate */
--animate-navigation-enter-from-right: navigation-fade-in 0.1s var(--ease-out-cubic);
--animate-navigation-enter-from-left: navigation-fade-in 0.1s var(--ease-out-cubic);
--animate-navigation-exit-to-right: navigation-fade-out 0.1s var(--ease-out-cubic);
--animate-navigation-exit-to-left: navigation-fade-out 0.1s var(--ease-out-cubic);
/* Navigation panel — blur + translate (full keyframes) */
@keyframes navigation-enter-from-right {
0% { opacity: 0; filter: blur(8px); transform: translate(200px); }
100% { opacity: 1; filter: blur(0); transform: translate(0px); }
}
@keyframes navigation-enter-from-left {
0% { opacity: 0; filter: blur(8px); transform: translate(-200px); }
100% { opacity: 1; filter: blur(0); transform: translate(0px); }
}
/* Dialog */
@keyframes dialog-scale-in { 0% { opacity: 0; transform: scale(0.96); } }
@keyframes dialog-scale-out { 100% { opacity: 0; transform: scale(0.96); } }
/* Accordion / collapsible */
@keyframes slideDown { 0% { height: 0; } 100% { height: var(--radix-accordion-content-height); } }
@keyframes slideUp { 0% { height: var(--radix-accordion-content-height); } 100% { height: 0; } }
/* Utility */
@keyframes spin { 100% { transform: rotate(360deg); } }
@keyframes pulse { 50% { opacity: 0.5; } }
@keyframes fadeIn { 0% { opacity: 0; } 100% { opacity: 1; } }
@keyframes fadeOut { 100% { opacity: 0; } }
```
**When to animate:**
- Button state transitions: always use `--duration-base` + `--ease-default`.
- Navigation open/close: use `--duration-fast` (0.1s) + `--ease-out-cubic` with blur filter.
- Dialog open: `dialog-scale-in` 200ms; close: `dialog-scale-out` 150ms.
- Hero gradient spins and feature card animations: use dedicated `--motion-*` keyframes; these are marketing-layer animations, NOT UI interaction patterns.
- Accordion/collapsible height transitions: `slideDown`/`slideUp` with `var(--duration-base)`.
**When NOT to animate:**
- Do not animate `width` or `height` for layout shifts outside of explicitly designed accordion patterns.
- Do not use `transition: all` in new components — enumerate specific properties.
- Do not apply marketing keyframe animations (gradient spin, rotate-reporting) to UI components.
- Respect `prefers-reduced-motion` — wrap decorative animations in `@media (prefers-reduced-motion: no-preference)`.
---
## 9. Anti-Patterns & Constraints
1. **Using system `Inter` instead of the custom `inter` font family → Why it fails → What to do instead.**
AI agents default to `font-family: 'Inter', sans-serif` which resolves to the Google Fonts/system version. Attio self-hosts a custom-subset `inter` (lowercase) with specific weights (400, 500, 600, 700) and a `"inter Fallback"` Arial fallback. Using the system font produces different metrics, subpixel rendering, and missing weights — breaking typographic fidelity. **Always write `inter, "inter Fallback", Arial, sans-serif`** — never `'Inter'` with a capital I or from a CDN.
2. **Hardcoding hex colours instead of using the CSS variable chain → Why it fails → What to do instead.**
Attio's colour system uses a multi-tier variable chain: component token → scale token → primitive. If you write `background: #1a1b1e` directly, it breaks when the dark/light mode variable resolves differently, and it's invisible to the design system. The extraction shows resolved `lab()` colour values from computed styles — these are browser-resolved values, not the source tokens. **Always resolve through the variable chain:** `var(--button-outline-bg)` → `var(--color-black-50)` → primitive.
3. **Using border-radius values not in the defined scale → Why it fails → What to do instead.**
AI agents frequently apply `border-radius: 4px`, `border-radius: 8px`, or `border-radius: 999px` (pill) by default. Attio's scale is specifically **2px, 10px, 12px, 50%**. Pill-shaped buttons do not exist in the Attio UI (50% only applies to avatar/launcher circles). Using `border-radius: 8px` on a button produces a visibly wrong corner that clashes with every other rounded element on the page. **Use `var(--radius-sm)`, `var(--radius-md)`, `var(--radius-lg)`, or `var(--radius-full)` only.**
4. **Using `transition: all` instead of specific properties → Why it fails → What to do instead.**
`transition: all` creates unexpected transitions on properties like `border-radius`, `display`, `position`, and layout properties — causing jank, layout thrash, and battery drain. Attio's own extracted styles explicitly enumerate `color`, `border-color`, `background-color`, `--button-primary-bg-from`, `--button-primary-bg-to`. **List specific properties:** `transition: color 0.3s, background-color 0.3s, border-color 0.3s` using `var(--duration-base)` and `var(--ease-default)`.
5. **Constructing dynamic Tailwind class names via string concatenation → Why it fails → What to do instead.**
Tailwind's JIT compiler tree-shakes classes at build time. Writing `className={\`bg-${colour}\`}` or `className={isActive ? 'bg-black-350' : 'bg-black-50'}` means the classes don't exist in the purged CSS bundle at runtime — the element is unstyled. **Use CSS custom properties with inline styles** (`style={{ backgroundColor: 'var(--button-outline-bg)' }}`) or enumerate complete class strings in conditional logic.
6. **Omitting focus-visible styles → Why it fails → What to do instead.**
AI agents frequently generate components with hover states but no focus styles, assuming hover is sufficient. Attio uses a custom focus ring via `box-shadow: var(--shadow-sm)` (a blue-tinted 4px ring) combined with `outline: none`. Missing this leaves keyboard users with invisible focus indicators and fails WCAG 2.4.7. **Every interactive element must implement `:focus-visible { box-shadow: var(--shadow-sm); outline: none; }`.**
7. **Using warm colours or light backgrounds in UI chrome → Why it fails → What to do instead.**
Attio's brand accent colours (amber `rgb(245,185,0)`, green `rgb(15,194,123)`, cyan `rgb(23,189,233)`) are extracted from SVG brand mark paths — they are illustration colours, not UI colours. An AI agent might use them as button backgrounds, badge colours, or hover fills. This contradicts the dark, restrained Attio aesthetic entirely. **Brand mark colours are illustration-only.** UI surfaces use the black/white scale. Accent colours appear only in SVGs and `attio-bg-surface` feature panels.
8. **Placing elements with `position: absolute` for general layout → Why it fails → What to do instead.**
AI agents sometimes use absolute positioning to align items that should be in flex or grid flow — particularly for overlay badges, icons inside buttons, or centred hero text. This breaks responsive reflow, causes overlap at unexpected breakpoints, and fights with Attio's flex-centred button anatomy. **Use `display: flex; align-items: center; justify-content: center; gap: var(--space-sm)` for button interiors.** Reserve `position: absolute` for genuine overlay elements (tooltips, dropdowns, focus rings that escape the box model).
9. **Using spacing values not in the 7-token scale → Why it fails → What to do instead.**
With 12+ unique spacing values in the raw extraction, AI agents may use any observed value (e.g. `padding: 13px`, `gap: 15px`). These off-scale values break visual rhythm because adjacent components use the token scale and they won't align. The extraction warning explicitly flags `1px, 6px, 10px, 63px` as off-grid (the `63px` value comes from a mobile sub-nav internal variable, not a design intent). **Map all spacing to the nearest token:** `--space-xs` (1px) through `--space-3xl` (24px). For anything larger, use multiples: `calc(2 * var(--space-3xl))` = 48px.
10. **Using `!important` to override component styles → Why it fails → What to do instead.**
Attio uses a layered CSS variable system where component tokens reference semantic tokens which reference primitive tokens. Injecting `!important` breaks the override chain at the wrong layer — the dark mode `[data-theme="dark"]` override can no longer update the value, and component state variables stop functioning. **Increase specificity through selectors** (add a wrapping class) or **move the value up the token chain** to the correct tier.
---
## 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 (53) */
--button-outline-bg: var(--color-black-50);
--button-outline-bg-hover: var(--color-black-100);
--button-outline-bg-focus: var(--color-black-200);
--button-outline-bg-active: var(--color-black-350);
--button-ghost-bg-disabled: transparent;
--button-ghost-bg-hover: var(--color-black-400);
--button-ghost-bg-active: var(--color-black-500);
--color-page-background: var(--color-primary-background);
--color-banner-background: var(--color-black-0);
--color-overscroll-top: var(--color-page-background);
--button-outline-bg: var(--color-black-50); /* mode: dark */
--button-outline-bg-hover: var(--color-black-100); /* mode: dark */
--button-outline-bg-focus: var(--color-black-200); /* mode: dark */
--button-outline-bg-active: var(--color-black-350); /* mode: dark */
--button-ghost-bg-disabled: transparent; /* mode: dark */
--button-ghost-bg-hover: var(--color-black-400); /* mode: dark */
--button-ghost-bg-active: var(--color-black-500); /* mode: dark */
----gradient-button_primary-bg: radial-gradient(at 50% -10%, lab(12.7212 0.103362 -2.22102) 0%, lab(12.7212 0.103362 -2.22102) 100%); /* Gradient from button_primary background /* reconstructed */ */
--color-overscroll-bottom: var(--color-black-50);
--attio-bg-surface: rgb(229, 238, 255);
--attio-accent: rgb(38, 109, 240);
--attio-accent-cool: rgb(23, 189, 233);
--attio-accent-warm: rgb(245, 185, 0);
--attio-text-primary: rgb(15, 194, 123);
--button-outline-border: var(--color-black-400);
--button-ghost-bg: transparent;
--color-black-0: /* Lightest — banner backgrounds */ /* [TBD - extract manually: likely ~rgb(255,255,255) or near-white] */;
--color-black-50: /* Button outline resting bg */ /* [TBD - extract manually: likely ~rgb(245,246,248)] */;
--color-black-100: /* Button outline hover bg */ /* [TBD - extract manually: likely ~rgb(230,232,236)] */;
--color-black-200: /* Button outline focus bg / disabled */ /* [TBD - extract manually] */;
--color-black-350: /* Button outline active bg */ /* [TBD - extract manually] */;
--color-black-400: /* Ghost hover bg, outline border */ /* [TBD - extract manually] */;
--color-black-500: /* Ghost active bg, disabled text */ /* [TBD - extract manually] */;
--color-black-600: /* Ghost disabled text */ /* [TBD - extract manually] */;
--color-white-100: /* Active/brightest text */ /* [TBD - extract manually: likely rgb(255,255,255)] */;
--color-white-400: /* Default button text */ /* [TBD - extract manually: likely ~rgb(220,224,230)] */;
--color-white-500: /* Ghost button text */ /* [TBD - extract manually] */;
--brand-mark-1: rgb(38, 109, 240);
--brand-mark-2: rgb(245, 185, 0);
--brand-mark-3: rgb(23, 189, 233);
--brand-mark-4: rgb(15, 194, 123);
--brand-surface-1: rgb(229, 238, 255);
--color-primary-background: /* Near-black app bg */ /* [TBD - extract manually: likely ~rgb(10,10,12) or similar] */;
--attio-accent-green: rgb(15, 194, 123);
--button-outline-border-hover: var(--color-black-500);
--button-outline-border-focus: var(--color-black-600);
--button-outline-border-active: var(--color-white-100);
--button-outline-bg-disabled: var(--color-black-200);
--button-outline-border-disabled: var(--color-black-400);
--button-ghost-bg-focus: var(--color-black-400);
--border-default: 1px solid lab(86.0989 -0.77799 -4.0961);
--border-button: 1px solid lab(37.426 -1.09151 -9.33263);
--border-focus: var(--button-outline-border-focus);
/* Typography (48) */
--button-outline-text-hover: var(--color-white-400);
--button-outline-text-active: var(--color-white-100);
--button-outline-text-disabled-foreground: var(--color-black-500);
--button-ghost-text: var(--color-white-500);
--button-ghost-text-disabled-foreground: var(--color-black-600);
--context-menu-portal-z-index: 200;
--context-menu-positioner-z-index: 201;
--context-menu-popup-z-index: 202;
--button-outline-text-hover: var(--color-white-400); /* mode: dark */
--button-outline-text-active: var(--color-white-100); /* mode: dark */
--button-outline-text-disabled-foreground: var(--color-black-500); /* mode: dark */
--button-ghost-text: var(--color-white-500); /* mode: dark */
--button-ghost-text-disabled-foreground: var(--color-black-600); /* mode: dark */
--font-size-xs: 11.3px; /* 9 elements — e.g. p "Domains", p "Name", p "Basepoint" /* mined from computed styles */ */
--font-size-sm: 12px; /* 25 elements — e.g. h2 "[01]Powerful platfor", h2 "[02]Adaptive model", h2 "[03]data enrichment" /* mined from computed styles */ */
--font-size-md: 14px; /* 59 elements — e.g. h2 "Platform", h2 "Company", h2 "Import from" /* mined from computed styles */ */
--font-size-lg: 15px; /* 10 elements — e.g. span "Platform", span "Resources", a "Customers" /* mined from computed styles */ */
--font-size-xl: 16px; /* 45 elements — e.g. p "Margaret Shen Head ", p "You’re in control. A", p "Search and create wi" /* mined from computed styles */ */
--font-size-2xl: 32px; /* 13 elements — e.g. h2 "Scale with security.", h3 "GTM at full throttle", h3 "A seismic shift in C" /* mined from computed styles */ */
--font-size-3xl: 56px; /* 18 elements — e.g. p "“When I first opened", span "“When", span "I" /* mined from computed styles */ */
--font-weight-regular: 400; /* 20 elements — e.g. a "Refer a teamNew", a "Changelog", a "Gmail extension" /* mined from computed styles */ */
--font-weight-medium: 500; /* 131 elements — e.g. h2 "Platform", h2 "Company", h2 "Import from" /* mined from computed styles */ */
--font-weight-semibold: 600; /* 44 elements — e.g. h1 "Customer relationshi", h2 "[01]Powerful platfor", h2 "Start with a 14-dayf" /* mined from computed styles */ */
--font-weight-bold: 700; /* 2 elements — e.g. span "Margaret Shen", span "Margaret Shen" /* mined from computed styles */ */
--line-height-normal: 20px; /* 63 elements — e.g. h2 "Platform", h2 "Company", h2 "Import from" /* mined from computed styles */ */
--line-height-loose: 22px; /* 53 elements — e.g. p "Margaret Shen Head ", p "You’re in control. A", p "Search and create wi" /* mined from computed styles */ */
--button-outline-text: var(--color-white-400);
--font-size-xs: 11.3px;
--font-size-sm: 12px;
--font-size-md: 14px;
--font-size-lg: 15px;
--font-size-xl: 16px;
--font-size-2xl: 32px;
--font-size-3xl: 56px;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--line-height-normal: 20px;
--line-height-loose: 22px;
--button-outline-text-focus: var(--color-white-400);
--button-ghost-text-hover: var(--color-white-100);
--button-ghost-text-focus: var(--color-white-100);
--button-ghost-text-active: var(--color-white-100);
--font-display: interDisplay, "interDisplay Fallback", Arial, sans-serif;
--font-body: inter, "inter Fallback", Arial, sans-serif;
--font-editorial: tiemposText, "tiemposText Fallback", Georgia, serif;
--font-mono: "JetBrains Mono", "JetBrains Mono Fallback", "Courier New", monospace;
/* Spacing (37) */
--button-outline-border-disabled: var(--color-black-400);
--button-outline-border-hover: var(--color-black-500);
--button-outline-border-focus: var(--color-black-600);
--button-outline-border-active: var(--color-white-100);
--site-header-nav-height: 68px;
--site-header-banner-hidden-height: 0px;
--site-header-banner-visible-height: 48px;
--site-header-subheader-mobile-visible-height: 63px;
--button-outline-border-disabled: var(--color-black-400); /* mode: dark */
--button-outline-border-hover: var(--color-black-500); /* mode: dark */
--button-outline-border-focus: var(--color-black-600); /* mode: dark */
--button-outline-border-active: var(--color-white-100); /* mode: dark */
--space-xs: 1px; /* 53 elements — e.g. div .grid, div .grid, div .home-bento-grid-system-module__2bkuOG__h /* mined from computed styles */ */
--space-sm: 6px; /* 71 elements — e.g. ul .hidden, ul .hidden, button .relative /* mined from computed styles */ */
--space-md: 8px; /* 106 elements — e.g. button .relative, button .relative, a .relative /* mined from computed styles */ */
--space-lg: 10px; /* 28 elements — e.g. a .relative, a .relative, a .relative /* mined from computed styles */ */
--space-xl: 12px; /* 69 elements — e.g. ul .relative, ul .relative, ul .relative /* mined from computed styles */ */
--space-2xl: 16px; /* 27 elements — e.g. nav .pt-2, ul .relative, ul .relative /* mined from computed styles */ */
--space-3xl: 24px; /* 78 elements — e.g. div .container, div .container, div .container /* mined from computed styles */ */
--space-xs: 1px;
--space-sm: 6px;
--space-md: 8px;
--space-lg: 10px;
--space-xl: 12px;
--space-2xl: 16px;
--space-3xl: 24px;
--type-display-h1-size: 64px;
--type-display-h1-line: 64px;
--type-section-label-size: 12px;
--type-section-label-line: 14px;
--type-section-label-tracking: 0.72px;
--type-section-heading-size: 32px;
--type-section-heading-line: 38px;
--site-header-polaris-toolbar-hidden-height: 0px;
--site-header-polaris-toolbar-visible-height: 36px;
--site-header-subheader-desktop-height: 0px;
--site-header-subheader-mobile-hidden-height: 0px;
/* Radius (8) */
--radius-sm: 2px; /* 1 element — e.g. div .grecaptcha-badge /* mined from computed styles */ */
--radius-md: 10px; /* 29 elements — e.g. button .inline-flex, button .relative "Platform", button .relative "Resources" /* mined from computed styles */ */
--radius-lg: 12px; /* 30 elements — e.g. button .inline-flex "Send me a demo", button .inline-flex "Send me a demo", button .group "Add objectWorkspaceS" /* mined from computed styles */ */
--radius-full: 50%; /* 1 element — e.g. div .intercom-lightweight-app-launcher /* mined from computed styles */ */
--radius-sm: 2px;
--radius-md: 10px;
--radius-lg: 12px;
--radius-full: 50%;
/* Effects (18) */
--animate-navigation-enter-from-right: navigation-fade-in .1s var(--ease-out-cubic);
--animate-navigation-exit-to-right: navigation-fade-out .1s var(--ease-out-cubic);
--mobile-nav-drawer-overlay-z-index: 90;
--mobile-nav-drawer-content-z-index: 91;
--site-header-z-index: 92;
--navigation-menu-z-index: 93;
--dialog-overlay-z-index: 100;
--dialog-content-z-index: 101;
--style-overlay-z-index: 999;
--site-header-height: calc(var(--site-header-nav-height) + var(--site-header-subheader-height) + var(--site-header-banner-height));
--site-header-banner-height: var(--site-header-banner-hidden-height);
--site-header-subheader-mobile-height: var(--site-header-subheader-mobile-hidden-height);
--site-header-subheader-height: var(--site-header-subheader-mobile-height);
--shadow-sm: rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, rgba(0, 0, 0, 0) 0px 0px 0px 0px, lab(74.4644 0.482172 -39.075 / 0.25) 0px 0px 0px 4px, rgba(0, 0, 0, 0) 0px 0px 0px 0px; /* 4 elements — e.g. button .group, button .group, button .group /* mined from computed styles */ */
--shadow-md: rgba(0, 0, 0, 0.06) 0px 1px 6px 0px, rgba(0, 0, 0, 0.16) 0px 2px 32px 0px; /* 1 element — e.g. div .intercom-lightweight-app-launcher /* mined from computed styles */ */
--shadow-sm: lab(74.4644 0.482172 -39.075 / 0.25) 0px 0px 0px 4px;
--shadow-md: rgba(0,0,0,0.06) 0px 1px 6px 0px, rgba(0,0,0,0.16) 0px 2px 32px 0px;
--shadow-badge: /* Legacy badge shadow */
rgb(128,128,128) 0px 0px 5px 0px;
/* Motion (50) */
----motion-ai-hero-box-gradient-spin: @keyframes ai-hero-box-gradient-spin {
0% { --ai-hero-box-gradient-angle: 0deg; }
100% { --ai-hero-box-gradient-angle: 360deg; }
}; /* @keyframes ai-hero-box-gradient-spin */
----motion-data-model-cards-mobile-connection-container: @keyframes data-model-cards-mobile-connection-container {
0% { opacity: 1; }
50% { opacity: 0.99; }
}; /* @keyframes data-model-cards-mobile-connection-container */
----motion-data-model-cards-mobile-connection-forwards: @keyframes data-model-cards-mobile-connection-forwards {
0% { stroke-dashoffset: 1px; }
100% { stroke-dashoffset: 0; }
}; /* @keyframes data-model-cards-mobile-connection-forwards */
----motion-data-model-cards-mobile-connection-backwards: @keyframes data-model-cards-mobile-connection-backwards {
0% { stroke-dashoffset: -1px; }
100% { stroke-dashoffset: 0; }
}; /* @keyframes data-model-cards-mobile-connection-backwards */
----motion-data-model-progress-process: @keyframes data-model-progress-process {
0%, 100% { --data-model-progress-col… <0.3KB elided>; /* @keyframes data-model-progress-process */
----motion-rotate-reporting: @keyframes rotate-reporting {
0% { --reporting-hero-card-reports-gradient-angle: 0deg; }
100% { --reporting-hero-card-reports-gradient-angle: 360deg; }
}; /* @keyframes rotate-reporting */
----motion-rotate: @keyframes rotate {
0% { --workflows-card-gradient-angle: 0deg; }
18% { --w… <0.3KB elided>; /* @keyframes rotate */
----motion-connection: @keyframes connection {
100% { opacity: 1; }
}; /* @keyframes connection */
----motion-running: @keyframes running {
0% { opacity: 0; top: 0px; }
15%, 85% { opacity: 1; top: -28px; }
100% { opacity: 0; top: 0px; }
}; /* @keyframes running */
----motion-completed: @keyframes completed {
0%, 85% { opacity: 0; top: 0px; }
100% { opacity: 1; top: -28px; }
}; /* @keyframes completed */
----motion-pricing-cards-grid-line-appear: @keyframes pricing-cards-grid-line-appear {
100% { --pricing-cards-grid-line-position-start: 100%; --pricing-cards-grid-line-position-end: 130%; }
}; /* @keyframes pricing-cards-grid-line-appear */
----motion-spin: @keyframes spin {
100% { transform: rotate(360deg); }
}; /* @keyframes spin */
----motion-pulse: @keyframes pulse {
50% { opacity: 0.5; }
}; /* @keyframes pulse */
----motion-enter: @keyframes enter {
0% { opacity: var(--tw-enter-opacity,1); transform: transl… <0.4KB elided>; /* @keyframes enter */
----motion-exit: @keyframes exit {
100% { opacity: var(--tw-exit-opacity,1); transform: transl… <0.3KB elided>; /* @keyframes exit */
----motion-navigation-enter-from-right: @keyframes navigation-enter-from-right {
0% { opacity: 0; filter: blur(8px); transform: translate(200px); }
100% { opacity: 1; filter: blur(); transform: translate(0px); }
}; /* @keyframes navigation-enter-from-right */
----motion-navigation-enter-from-left: @keyframes navigation-enter-from-left {
0% { opacity: 0; filter: blur(8px); transform: translate(-200px); }
100% { opacity: 1; filter: blur(); transform: translate(0px); }
}; /* @keyframes navigation-enter-from-left */
----motion-navigation-exit-to-right: @keyframes navigation-exit-to-right {
0% { opacity: 1; filter: blur(); transform: translate(0px); }
100% { opacity: 0; filter: blur(8px); transform: translate(200px); }
}; /* @keyframes navigation-exit-to-right */
----motion-navigation-exit-to-left: @keyframes navigation-exit-to-left {
0% { opacity: 1; filter: blur(); transform: translate(0px); }
100% { opacity: 0; filter: blur(8px); transform: translate(-200px); }
}; /* @keyframes navigation-exit-to-left */
----motion-dialog-scale-in: @keyframes dialog-scale-in {
0% { opacity: 0; transform: scale(0.96); }
}; /* @keyframes dialog-scale-in */
----motion-dialog-scale-out: @keyframes dialog-scale-out {
100% { opacity: 0; transform: scale(0.96); }
}; /* @keyframes dialog-scale-out */
----motion-productivity-intro-width: @keyframes productivity-intro-width {
0% { opacity: 0.8; width: 0px; }
100% { opacity: 0.3; width: 100vw; }
}; /* @keyframes productivity-intro-width */
----motion-productivity-intro-height: @keyframes productivity-intro-height {
0% { opacity: 0.8; height: 0px; }
100% { height: var(--productivity-intro-height-to); opacity: 0.3; }
}; /* @keyframes productivity-intro-height */
----motion-slideDown: @keyframes slideDown {
0% { height: 0px; }
100% { height: var(--radix-accordion-content-height); }
}; /* @keyframes slideDown */
----motion-slideUp: @keyframes slideUp {
0% { height: var(--radix-accordion-content-height); }
100% { height: 0px; }
}; /* @keyframes slideUp */
----motion-collapsibleSlideDown: @keyframes collapsibleSlideDown {
0% { height: 0px; }
100% { height: var(--radix-collapsible-content-height); }
}; /* @keyframes collapsibleSlideDown */
----motion-collapsibleSlideUp: @keyframes collapsibleSlideUp {
0% { height: var(--radix-collapsible-content-height); }
100% { height: 0px; }
}; /* @keyframes collapsibleSlideUp */
----motion-reveal-to-right: @keyframes reveal-to-right {
0% { mask-position: 100% 0px; }
100% { mask-position: 0px 0px; }
}; /* @keyframes reveal-to-right */
----motion-reveal-to-bottom: @keyframes reveal-to-bottom {
0% { mask-position: 0px 100%; }
100% { mask-position: 0px 0px; }
}; /* @keyframes reveal-to-bottom */
----motion-swipe-out-left: @keyframes swipe-out-left {
0% { transform: var(--y) translateX(var(--swipe-amount-x)); opacity: 1; }
100% { transform: var(--y) translateX(calc(var(--swipe-amount-x) - 100%)); opacity: 0; }
}; /* @keyframes swipe-out-left */
----motion-swipe-out-right: @keyframes swipe-out-right {
0% { transform: var(--y) translateX(var(--swipe-amount-x)); opacity: 1; }
100% { transform: var(--y) translateX(calc(var(--swipe-amount-x) + 100%)); opacity: 0; }
}; /* @keyframes swipe-out-right */
----motion-swipe-out-up: @keyframes swipe-out-up {
0% { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
100% { transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%)); opacity: 0; }
}; /* @keyframes swipe-out-up */
----motion-swipe-out-down: @keyframes swipe-out-down {
0% { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
100% { transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%)); opacity: 0; }
}; /* @keyframes swipe-out-down */
----motion-sonner-fade-in: @keyframes sonner-fade-in {
0% { opacity: 0; transform: scale(0.8); }
100% { opacity: 1; transform: scale(1); }
}; /* @keyframes sonner-fade-in */
----motion-sonner-fade-out: @keyframes sonner-fade-out {
0% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.8); }
}; /* @keyframes sonner-fade-out */
----motion-sonner-spin: @keyframes sonner-spin {
0% { opacity: 1; }
100% { opacity: 0.15; }
}; /* @keyframes sonner-spin */
----motion-fadeIn: @keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}; /* @keyframes fadeIn */
----motion-fadeOut: @keyframes fadeOut {
100% { opacity: 0; }
}; /* @keyframes fadeOut */
----motion-slideFromBottom: @keyframes slideFromBottom {
0% { transform: translate3d(0,var(--initial-transform,100%),0); }
100% { transform: translate3d(0px, 0px, 0px); }
}; /* @keyframes slideFromBottom */
----motion-slideToBottom: @keyframes slideToBottom {
100% { transform: translate3d(0,var(--initial-transform,100%),0); }
}; /* @keyframes slideToBottom */
----motion-slideFromTop: @keyframes slideFromTop {
0% { transform: translate3d(0,calc(var(--initial-transform,100%) * -1),0); }
100% { transform: translate3d(0px, 0px, 0px); }
}; /* @keyframes slideFromTop */
----motion-slideToTop: @keyframes slideToTop {
100% { transform: translate3d(0,calc(var(--initial-transform,100%) * -1),0); }
}; /* @keyframes slideToTop */
----motion-slideFromLeft: @keyframes slideFromLeft {
0% { transform: translate3d(calc(var(--initial-transform,100%) * -1),0,0); }
100% { transform: translate3d(0px, 0px, 0px); }
}; /* @keyframes slideFromLeft */
----motion-slideToLeft: @keyframes slideToLeft {
100% { transform: translate3d(calc(var(--initial-transform,100%) * -1),0,0); }
}; /* @keyframes slideToLeft */
----motion-slideFromRight: @keyframes slideFromRight {
0% { transform: translate3d(var(--initial-transform,100%),0,0); }
100% { transform: translate3d(0px, 0px, 0px); }
}; /* @keyframes slideFromRight */
----motion-slideToRight: @keyframes slideToRight {
100% { transform: translate3d(var(--initial-transform,100%),0,0); }
}; /* @keyframes slideToRight */
--duration-fast: 0.15s; /* 20 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-base: 0.3s; /* 66 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-slow: 0.4s; /* 6 elements — e.g. svg, svg, svg /* mined from computed styles */ */
--ease-default: 0; /* 183 elements — e.g. button, button, button /* mined from computed styles */ */
```
## Appendix B: Token Source Metadata
```
tokenSource: extracted-css-vars
confidence: high
cssVarsFound: 56
curatedTokensMapped: 40
Extraction method:
- CSS custom properties extracted directly from attio.com stylesheet
- Computed styles sampled from 14 key element types (h1, h2, h3, body, button_primary,
input, link, card, modal, tab, badge, dropdown, role_button, role_navigation)
- Border-radius values mined from computed styles across full DOM (census of 61+ elements)
- Interactive states extracted from CSS rule selectors (hover, focus-visible, active, disabled)
- Font declarations extracted from @font-face rules (4 families, 20 declarations)
- Motion tokens extracted from @keyframes rules (35 named animations)
- Breakpoints extracted from media queries (600px, 992px, 1199px, 1200px)
Colour note:
- Primitive colour scale (--color-black-0 through --color-black-600, --color-white-100
through --color-white-500) are referenced by component tokens but not directly extracted
with resolved values. Mark as [TBD - extract manually] where noted.
- Computed colours expressed in lab() colour space (browser-resolved) — these are
authoritative rendered values, not necessarily source token values.
- Brand mark colours (--brand-mark-1 through --brand-mark-4) extracted from SVG fill
attributes — high confidence on RGB values.
Token naming:
- Original CSS variable names PRESERVED exactly (--button-outline-bg, --button-ghost-text, etc.)
- Curated --attio-* aliases synthesised from computed styles where no direct CSS var existed
(--attio-bg-surface, --attio-accent, --attio-accent-cool, --attio-accent-warm)
- Semantic naming applied only to synthesised tokens; extracted vars keep original names
Libraries detected:
- Tailwind CSS (utility classes present; v3 assumed — no @theme CSS vars observed)
- Bootstrap (detected alongside Tailwind — likely for responsive grid/breakpoints)
- Radix UI (--radix-accordion-content-height, --radix-collapsible-content-height in keyframes)
- Sonner (toast notification animations)
- Vaul (drawer component — [data-vaul-handle] selectors)
- Intercom (launcher widget — external, not part of Attio design system)
Confidence per section:
- Colour (component tokens): HIGH — direct CSS var extraction
- Colour (primitives): LOW — referenced but not resolved in extraction
- Typography (font families): HIGH — @font-face declarations confirmed
- Typography (sizes/weights): HIGH — both CSS vars and computed styles confirm values
- Spacing: HIGH — 7 tokens confirmed via CSS vars and usage census
- Radius: HIGH — census of 61 elements confirms 4-value scale
- Shadows: HIGH — direct CSS var extraction with resolved values
- Motion (durations/easing): HIGH — CSS vars + computed transition values confirmed
- Motion (keyframes): HIGH — full @keyframes rules extracted
- Layout/breakpoints: HIGH — media queries extracted directly
- Page structure (Section 5): INFERRED — no screenshots provided; derived from component
inventory, layout digest, computed styles, and H2 eyebrow label text patterns
```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