Dovetail
MIT
Bold, vibrant design system with electric blue surfaces and carefully restricted colour palette, built for React applications that demand modern, branded 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 dovetail-2UI themeReskin shadcn or Layout UI components to match Dovetail.
npx shadcn add https://layout.design/r/dovetail-2/theme.json# layout.md — Dovetail Design System
---
## 0. Quick Reference
**Stack:** React/CSS-in-JS (Emotion, hashed class names) · Token source: reconstructed-from-computed (1 CSS custom property found; all other tokens synthesised from computed styles) · Confidence: low–high per token · Theme switching via `html[data-scroll-theme="dark|light"]`
**How to apply:** Use as `var(--dovetail-*)` in CSS, `style={{ prop: 'var(--dovetail-*)' }}` in JSX, or `bg-[var(--dovetail-*)]` in Tailwind.
```css
:root {
/* ── Colour ── */
--dovetail-bg-app: #fafafa; /* page background, light surfaces */
--dovetail-bg-surface: rgb(0, 68, 255); /* brand hero/accent surface */
--dovetail-accent: rgb(255, 153, 0); /* brand mark orange */
--dovetail-accent-cool: rgb(13, 157, 218); /* brand mark cyan */
--dovetail-text-primary: rgb(234, 67, 53); /* brand mark red (logo use) */
--dovetail-text-on-dark: rgb(250, 250, 250); /* body/heading text on dark bg */
--dovetail-text-on-light: rgb(10, 10, 10); /* body text on light bg */
/* ── Typography scale ── */
--dovetail-font-size-xs: 12px;
--dovetail-font-size-sm: 14px;
--dovetail-font-size-md: 16px;
--dovetail-font-size-lg: 18px;
--dovetail-font-size-xl: 20px;
--dovetail-font-size-2xl: 64px;
--dovetail-font-size-3xl: 112px;
--dovetail-font-weight-regular: 400;
--dovetail-font-weight-medium: 500;
--dovetail-font-weight-semibold: 600;
/* ── Spacing ── */
--dovetail-space-xs: 24px;
--dovetail-space-sm: 32px;
--dovetail-space-md: 64px;
--dovetail-space-lg: 96px;
--dovetail-space-xl: 100px;
--dovetail-space-2xl: 124px;
/* ── Radius ── */
--dovetail-radius-sm: 4px;
--dovetail-radius-md: 8px;
/* ── Motion ── */
--dovetail-duration-fast: 0.2s;
--dovetail-duration-base: 0.3s;
--dovetail-ease-default: ease;
}
```
```tsx
// Primary nav button — correct token usage
<button
style={{
fontFamily: '"JetBrains Mono", "SF Mono", Consolas, monospace',
fontSize: 'var(--dovetail-font-size-xs)',
fontWeight: 'var(--dovetail-font-weight-regular)',
letterSpacing: '1px',
textTransform: 'uppercase',
borderRadius: 'var(--dovetail-radius-md)',
padding: '8px 16px',
transition: `all var(--dovetail-duration-base) var(--dovetail-ease-default)`,
}}
>
Get started
</button>
```
**NEVER rules:**
- **NEVER** use warm colours (`rgb(255,153,0)`, `rgb(234,67,53)`) for body text or backgrounds — brand marks only.
- **NEVER** use `border-radius > 8px` on buttons or cards — Dovetail uses sharp/small radii, not pill shapes.
- **NEVER** hardcode `#ffffff` for text — use `rgb(250,250,250)` (`--dovetail-text-on-dark`) or `rgb(10,10,10)` (`--dovetail-text-on-light`).
- **NEVER** use Inter for monospace UI elements (labels, buttons, code) — use JetBrains Mono at `12px / 1px letter-spacing / uppercase`.
- **NEVER** use arbitrary spacing values — all gaps must map to the 24/32/64/96/100/124px scale.
- **NEVER** omit `html[data-scroll-theme]` attribute selectors on hover/focus states — the site uses scroll-driven theme switching.
> Full design system → see layout.md
<!-- Quick Reference truncated to fit the 75-line cap. See later sections for the full design system. -->
## 1. Design Direction & Philosophy
### Character & Mood
Dovetail is a **research intelligence platform** projecting authority, precision, and calm confidence. The aesthetic is **high-contrast, editorial, and restrained** — dominated by near-black backgrounds with white type, punctuated by vivid brand-mark colours used sparingly as accents rather than fills.
The type system is **deliberately dual-register**: Inter (sans-serif, geometric) carries headings and UI labels with tight negative letter-spacing (`-2px` at large sizes); JetBrains Mono carries body copy, buttons, and small labels in `uppercase` with `1px` tracking — evoking a technical/data-driven tool without being retro.
Spacing is **generous and intentional** — the scale jumps from 24px straight to 64px, skipping conventional 40/48 steps. This creates breathing room between sections without wasteful padding.
### What This Design Explicitly Rejects
- **Soft, rounded UI** — No pill buttons, no cards with `border-radius > 8px`, no bubbly forms.
- **Warm or saturated backgrounds** — The brand orange (`rgb(255,153,0)`) and red (`rgb(234,67,53)`) are logo/mark colours only. Never use them as surface fills.
- **Dense information layouts** — No tables, no cramped grids. Sections use large whitespace margins (`96px–124px`).
- **Generic SaaS gradients** — No purple-to-pink, no blue-to-teal gradients on hero backgrounds.
- **Heavy drop shadows** — No `box-shadow` on cards or buttons at rest (elevation only via subtle overlays).
- **Mixed type hierarchies** — EB Garamond is loaded as a font asset but is used contextually (likely for pull quotes); it should NOT be used for headings or body.
---
## 2. Colour System
### Tier 1 — Primitive Values
```css
/* Primitive palette — raw values, do not use directly in components */
:root {
--primitive-blue-600: rgb(0, 68, 255); /* extracted: high confidence — brand hero surface */
--primitive-orange-500: rgb(255, 153, 0); /* extracted: high confidence — brand mark orange */
--primitive-cyan-500: rgb(13, 157, 218); /* extracted: moderate confidence — brand mark cyan */
--primitive-red-500: rgb(234, 67, 53); /* extracted: moderate confidence — brand mark red */
--primitive-white-soft: rgb(250, 250, 250); /* extracted: high confidence — primary light text/bg */
--primitive-black-soft: rgb(10, 10, 10); /* reconstructed: high confidence — light-theme text */
--primitive-white-pure: rgb(255, 255, 255); /* extracted: high confidence — full white text/icons */
--primitive-grey-200: rgb(216, 216, 216); /* reconstructed: high confidence — light-theme hover bg */
--primitive-grey-300: rgb(206, 206, 206); /* reconstructed: high confidence — dark-theme hover bg */
--primitive-grey-800: rgb(59, 59, 59); /* reconstructed: moderate confidence — light-theme btn hover */
--primitive-grey-600: rgb(88, 88, 88); /* reconstructed: moderate confidence — secondary icon hover */
--primitive-white-64: rgba(255,255,255,0.64); /* extracted: high confidence — nav button default text */
--primitive-white-16: rgba(255,255,255,0.16); /* extracted: high confidence — dark-theme hover overlay */
--primitive-white-08: rgba(255,255,255,0.08); /* extracted: high confidence — ghost hover overlay */
--primitive-black-04: rgba(10,10,10,0.04); /* extracted: high confidence — light-theme ghost hover */
}
```
### Tier 2 — Semantic Aliases
```css
/* Semantic aliases — use these in all component work */
:root {
/* Surfaces */
--dovetail-bg-app: var(--primitive-white-soft); /* #fafafa — page/app background */
--dovetail-bg-surface: var(--primitive-blue-600); /* brand hero section background */
--dovetail-bg-overlay-dark: var(--primitive-white-16); /* hover overlay on dark theme */
--dovetail-bg-overlay-ghost: var(--primitive-white-08); /* ghost button hover on dark */
--dovetail-bg-overlay-light: var(--primitive-black-04); /* ghost button hover on light */
--dovetail-bg-hover-dark: var(--primitive-grey-300); /* solid button hover, dark theme */
--dovetail-bg-hover-light: var(--primitive-grey-800); /* solid button hover, light theme */
/* Text */
--dovetail-text-on-dark: var(--primitive-white-soft); /* rgb(250,250,250) — headings/body on dark bg */
--dovetail-text-on-light: var(--primitive-black-soft); /* rgb(10,10,10) — text on light bg */
--dovetail-text-muted: var(--primitive-white-64); /* rgba(255,255,255,0.64) — secondary nav/button text */
--dovetail-text-full-white: var(--primitive-white-pure); /* rgb(255,255,255) — high-contrast labels */
/* Brand marks (logo/illustration only — not for backgrounds or body text) */
--dovetail-brand-surface: var(--primitive-blue-600); /* logo fill, hero bg */
--dovetail-brand-orange: var(--primitive-orange-500); /* logo mark, accent illustration */
--dovetail-brand-cyan: var(--primitive-cyan-500); /* logo mark, accent illustration */
--dovetail-brand-red: var(--primitive-red-500); /* logo mark only */
/* Interactive states */
--dovetail-focus-ring-dark: rgb(255, 255, 255); /* 1px solid outline on dark theme */
--dovetail-focus-ring-light: rgb(10, 10, 10); /* 1px solid outline on light theme */
--dovetail-disabled-opacity: 0.6; /* opacity for disabled elements */
}
```
### Tier 3 — Component Tokens
```css
/* Component-level tokens — map semantic → specific component property */
:root {
/* Navigation */
--dovetail-nav-bg: transparent;
--dovetail-nav-text: var(--dovetail-text-full-white);
--dovetail-nav-btn-text: var(--dovetail-text-muted);
--dovetail-nav-hover-bg: var(--dovetail-bg-overlay-dark);
/* Primary CTA button (white/light background, dark text — from link computed styles) */
--dovetail-btn-primary-bg: rgb(255, 255, 255);
--dovetail-btn-primary-text: rgb(0, 0, 0);
--dovetail-btn-primary-hover-bg: var(--dovetail-bg-hover-dark); /* dark theme */
--dovetail-btn-primary-radius: var(--dovetail-radius-sm); /* 4px */
--dovetail-btn-primary-padding: 8px 16px;
/* Ghost/secondary button */
--dovetail-btn-ghost-bg: transparent;
--dovetail-btn-ghost-text: var(--dovetail-text-on-dark);
--dovetail-btn-ghost-hover-bg: var(--dovetail-bg-overlay-ghost);
--dovetail-btn-ghost-radius: var(--dovetail-radius-md); /* 8px */
}
```
### Theme Switching
Dovetail uses `html[data-scroll-theme="dark|light"]` for scroll-driven theme transitions. **All hover/focus states must include both theme variants.**
| Token | Dark theme value | Light theme value |
|---|---|---|
| Button hover bg | `rgba(255,255,255,0.16)` | `rgb(216,216,216)` |
| Ghost hover bg | `rgba(255,255,255,0.08)` | `rgba(10,10,10,0.04)` |
| Solid btn hover bg | `rgb(206,206,206)` | `rgb(59,59,59)` |
| Focus ring colour | `rgb(255,255,255)` | `rgb(10,10,10)` |
| Text on hover | `rgb(255,255,255)` | `rgb(10,10,10)` |
---
## 3. Typography System
### Font Stacks
```css
:root {
--dovetail-font-sans: 'Inter', -apple-system, system-ui, BlinkMacSystemFont, Helvetica, Arial, 'Segoe UI', Roboto, sans-serif;
--dovetail-font-mono: 'JetBrains Mono', 'SF Mono', Consolas, Roboto, sans-serif;
--dovetail-font-serif: 'EB Garamond', Georgia, serif; /* pull-quotes / editorial only */
--dovetail-font-display: 'Space Grotesk', var(--dovetail-font-sans); /* reconstructed: low confidence — loaded but role unclear */
}
```
### Composite Type Scale
| Role | font-family | font-size | font-weight | line-height | letter-spacing | transform | Usage |
|---|---|---|---|---|---|---|---|
| **Display / H1** | `--font-sans` | `112px` | `600` | `80px` | `-2px` | none | Hero headline, single use per page |
| **Heading / H2** | `--font-sans` | `64px` | `600` | `60px` | `-2px` | none | Section headings |
| **Subheading / H2-sm** | `--font-sans` | `56px` | `600` | `60px` | `-2px` | none | Extracted from computed h2 (reconstructed: moderate confidence) |
| **Card Title / H3** | `--font-sans` | `20px` | `500` | `24px` | `-0.25px` | none | Feature card headings, nav mega-menu titles |
| **Body Large** | `--font-sans` | `18px` | `400` | `24px` | normal | none | ROI stats, pull-out figures |
| **Body** | `--font-sans` | `16px` | `400` | `24px` | normal | none | Long-form body copy |
| **Body Small** | `--font-sans` | `14px` | `400` | `20px` | normal | none | Captions, meta, testimonial attribution |
| **Label / UI** | `--font-mono` | `12px` | `400` | `12px` | `1px` | `UPPERCASE` | **All buttons, nav items, tags, status labels** |
```css
/* Composite token groups — use these, not individual properties */
.dovetail-type-display {
font-family: var(--dovetail-font-sans);
font-size: var(--dovetail-font-size-3xl); /* 112px */
font-weight: var(--dovetail-font-weight-semibold);
line-height: var(--dovetail-line-height-loose); /* 80px */
letter-spacing: -2px;
text-align: center;
}
.dovetail-type-heading {
font-family: var(--dovetail-font-sans);
font-size: var(--dovetail-font-size-2xl); /* 64px */
font-weight: var(--dovetail-font-weight-semibold);
line-height: 60px;
letter-spacing: -2px;
text-align: left;
}
.dovetail-type-card-title {
font-family: var(--dovetail-font-sans);
font-size: var(--dovetail-font-size-xl); /* 20px */
font-weight: var(--dovetail-font-weight-medium);
line-height: var(--dovetail-line-height-normal); /* 24px */
letter-spacing: -0.25px;
}
.dovetail-type-label {
font-family: var(--dovetail-font-mono);
font-size: var(--dovetail-font-size-xs); /* 12px */
font-weight: var(--dovetail-font-weight-regular);
line-height: 12px;
letter-spacing: 1px;
text-transform: uppercase;
}
```
### Pairing Rules
- **Headings** always use `--dovetail-font-sans` at weight `600` with negative letter-spacing (`-2px` at 64px+, `-0.25px` at 20px).
- **All interactive labels** (buttons, nav items, tags) use `--dovetail-font-mono` at `12px / uppercase / 1px tracking`. NEVER use Inter for these.
- **EB Garamond** is editorial-only (pull quotes, testimonials). NEVER use for headings or nav.
- **Space Grotesk** role is unconfirmed — do not use until context is clarified.
---
## 4. Spacing & Layout
### Spacing Scale
```css
:root {
/* Dovetail spacing scale — NOTE: jumps are intentional, no 8/16/40/48 intermediates */
--dovetail-space-xs: 24px; /* component inner padding (nav: 24px all-around) */
--dovetail-space-sm: 32px; /* card inner padding, tight section gaps */
--dovetail-space-md: 64px; /* section vertical padding, column gaps */
--dovetail-space-lg: 96px; /* large section vertical margins */
--dovetail-space-xl: 100px; /* hero vertical padding */
--dovetail-space-2xl: 124px; /* maximum section separation */
/* Button-specific (from computed link styles) */
--dovetail-btn-padding-y: 8px;
--dovetail-btn-padding-x: 16px;
}
```
### Grid & Breakpoints
```css
/* Breakpoints extracted from media queries */
:root {
--dovetail-bp-mobile: 799px; /* mobile → tablet transition */
--dovetail-bp-tablet: 850px; /* tablet layout threshold */
--dovetail-bp-desktop-sm: 1185px; /* compact desktop */
--dovetail-bp-desktop-md: 1280px; /* standard desktop */
--dovetail-bp-desktop-lg: 1281px; /* large desktop (1px above 1280 — boundary guard) */
--dovetail-bp-desktop-xl: 1512px; /* wide/ultrawide */
}
```
| Breakpoint | Max container width | Columns | Behaviour |
|---|---|---|---|
| `≤ 799px` | 100% | 1 | Mobile: stack all columns, full-width sections |
| `800–850px` | 100% | 1–2 | Tablet: limited two-column layouts |
| `851–1185px` | ~960px | 2–3 | Compact desktop |
| `1186–1280px` | ~1200px | 3–4 | Standard desktop |
| `1281–1512px` | ~1400px | 4+ | Large desktop |
| `> 1512px` | ~1512px | 4+ | Capped wide layout |
### Layout Principles
- **Navigation:** `display: flex; flex-direction: row; justify-content: space-between; align-items: center; padding: 24px` — full-width, no max-width container on the nav itself.
- **Sections:** Full-width background, content constrained to container. Vertical rhythm driven by `--dovetail-space-lg` (96px) to `--dovetail-space-2xl` (124px).
- **Flex over Grid** for nav, button groups, and feature rows. Grid for card matrices.
- **No 4px or 8px base-unit micro-spacing** inside sections — smallest gap is `24px`.
---
## 5. Page Structure & Layout Patterns
> **Source:** Layout Digest + Component Inventory. No screenshots available. Rows marked **(inferred)** are not visually confirmed — they are derived from the component inventory, computed styles, and typographic hierarchy.
### 5.1 Section Map
| # | Section | Layout type | Approx height | Key elements | Source |
|---|---|---|---|---|---|
| 1 | **Navigation / Header** | Flex row, space-between | ~72px | Logo (left), nav links (centre), CTA buttons (right), `padding: 24px` | Digest (computed) |
| 2 | **Hero** | Centred column, full-width dark bg | ~600–800px | H1 at `112px`, subheading, dual CTA buttons, animated text swap | Inferred + computed h1 |
| 3 | **Social proof / Logo strip** | Flex row, horizontal scroll animation | ~120px | Customer logos, marquee scroll (`animation-t9qc24` translateX(-50%)) | Inferred from motion tokens |
| 4 | **Product feature — primary** | 2-col grid (text left, UI right) | ~500px | H2 `64px`, body copy, product screenshot/UI | Inferred |
| 5 | **Feature grid** | 3-col card grid | ~400px | H3 `20px` cards, icon, title, description text | Inferred from h3 inventory |
| 6 | **ROI / Metrics** | 3-col stat row | ~300px | Large stat figures (`18px` bold), labels | Inferred from font-size-lg usage |
| 7 | **Testimonials / Quotes** | Centred single-col or 2-col | ~400px | Pull-quote in EB Garamond, attribution `14px`, photo | Inferred from font declarations |
| 8 | **Secondary feature / AI** | 2-col alternating | ~500px | H2 "AI native customer", product UI | Inferred from h2 inventory |
| 9 | **Pricing / CTA** | Centred column | ~400px | H2, CTA button (white bg, black text, `radius: 4px`) | Inferred from link computed styles |
| 10 | **Footer** | Multi-col flex | ~300px | Nav links `12px mono uppercase`, legal, logo | Inferred from xs font-size usage |
### 5.2 Layout Patterns
**Navigation:**
```
[Logo] [Product] [Solutions] [Pricing] [Docs] [Log in] [Get started →]
← flex row, justify-content: space-between, padding: 24px, full viewport width →
```
- Transparent background (`rgba(0,0,0,0)` at extracted state)
- Text: `rgb(255,255,255)`, muted variant `rgba(255,255,255,0.64)` for secondary buttons
- All nav labels: JetBrains Mono, 12px, uppercase, 1px tracking
- CTA buttons: `border-radius: 8px`, `padding: 8px 16px`
**Hero Section:**
- Full-width dark background (likely near-black or deep navy — exact value TBD, `rgb(0,68,255)` is brand surface, not necessarily hero bg)
- H1 centred, `112px / 600 / line-height: 80px / letter-spacing: -2px`
- Animated word-swap in headline (vertical translateY scroll animation `animation-1wo539a`)
- Hero fade-in on load (`hero-fade-in` keyframes: opacity 0→1)
- Dual CTAs: primary (white bg, black text) + ghost (transparent, white text)
**Feature Cards (inferred):**
- 3-column CSS grid, gap: `var(--dovetail-space-sm)` (32px) (inferred)
- Each card: `border-radius: 8px`, internal padding `var(--dovetail-space-sm)` (32px)
- H3: Inter 20px / 500 / line-height 24px
**Logo Marquee (inferred):**
- `animation-t9qc24`: continuous `translateX(0 → -50%)` — duplicated list for seamless loop
### 5.3 Visual Hierarchy
1. **H1 at 112px** dominates the hero — only one per page, centred.
2. **H2 at 64px** anchors each major section — left-aligned within content containers.
3. **Brand colours** (orange, cyan, red) appear only in the SVG logo mark and potentially illustration accents — NEVER as section backgrounds.
4. **CTA button** (white bg, black text, `radius: 4px`, `font: JetBrains Mono 12px uppercase`) is the primary conversion element — placed in nav (right side) and repeated at section ends.
5. **Whitespace rhythm:** Sections separated by `96px–124px` vertical margin — no dense stacking.
### 5.4 Content Patterns
- **Headline + subhead + dual CTA:** Repeats in hero and closing CTA sections.
- **Animated text substitution:** H1/H2 with rotating word inside (the step animation `animation-1wo539a` cycles at `−6.25%` translateY intervals across 8 items).
- **Icon + title + body copy:** Card pattern for feature sections — H3 then 1–2 sentences in `14px` or `16px` Inter.
- **Stat + label:** `18px` bold figure above `12px mono uppercase` label — used for ROI metrics.
- **Quote + attribution:** EB Garamond italic for quote body, `14px` Inter for name + role.
---
## 6. Component Patterns
### 6.1 Navigation Bar
**Anatomy:** `<nav>` → `[logo]` + `[nav-links]` + `[action-buttons]`
**Token mapping:**
- Container: `display: flex; justify-content: space-between; align-items: center; padding: var(--dovetail-space-xs)` (24px all sides)
- Background: `transparent` (dark theme default)
- Text: `var(--dovetail-text-full-white)`
- Transition: `var(--dovetail-duration-base) var(--dovetail-ease-default)`
```tsx
// Navigation bar — correct token usage with theme-aware states
const Nav = () => (
<nav
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: 'var(--dovetail-space-xs)', // 24px
backgroundColor: 'transparent',
color: 'var(--dovetail-text-full-white)',
transition: `all var(--dovetail-duration-base) var(--dovetail-ease-default)`,
}}
role="navigation"
>
<Logo />
<NavLinks />
<div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
<NavTextButton>Log in</NavTextButton>
<NavCTAButton>Get started</NavCTAButton>
</div>
</nav>
);
```
---
### 6.2 Primary CTA Button
**Anatomy:** `<button>` → `[label-text]` + optional `[arrow-icon]`
| State | Background | Text | Border | Opacity | Extra |
|---|---|---|---|---|---|
| Default | `rgb(255,255,255)` | `rgb(0,0,0)` | none | 1 | — |
| Hover (dark theme) | `rgb(206,206,206)` | `rgb(255,255,255)` | none | 1 | Icon translates `+4px X` |
| Hover (light theme) | `rgb(59,59,59)` | `rgb(255,255,255)` | none | 1 | Icon translates `+4px X` |
| Focus-visible | same as hover | same | `1px solid rgb(10,10,10)`, offset 1px | 1 | — |
| Disabled | same | same | none | 0.6 | `pointer-events: none` |
| Active | [TBD - extract manually] | [TBD] | — | — | — |
| Loading | [TBD - extract manually] | — | — | — | spinner? |
```tsx
import { useState } from 'react';
interface PrimaryButtonProps {
children: React.ReactNode;
disabled?: boolean;
loading?: boolean;
onClick?: () => void;
theme?: 'dark' | 'light';
}
const PrimaryButton = ({
children,
disabled = false,
loading = false,
onClick,
theme = 'dark',
}: PrimaryButtonProps) => {
const [isHovered, setIsHovered] = useState(false);
const hoverBg = theme === 'dark' ? 'rgb(206, 206, 206)' : 'rgb(59, 59, 59)';
const defaultBg = 'rgb(255, 255, 255)';
const defaultText = 'rgb(0, 0, 0)';
const hoverText = 'rgb(255, 255, 255)';
return (
<button
onClick={onClick}
disabled={disabled || loading}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
style={{
fontFamily: '"JetBrains Mono", "SF Mono", Consolas, monospace',
fontSize: 'var(--dovetail-font-size-xs)', // 12px
fontWeight: 'var(--dovetail-font-weight-regular)',
lineHeight: '12px',
letterSpacing: '1px',
textTransform: 'uppercase',
backgroundColor: isHovered ? hoverBg : defaultBg,
color: isHovered ? hoverText : defaultText,
borderRadius: 'var(--dovetail-radius-sm)', // 4px
padding: 'var(--dovetail-btn-padding-y) var(--dovetail-btn-padding-x)', // 8px 16px
border: 'none',
cursor: disabled ? 'not-allowed' : 'pointer',
opacity: disabled ? 'var(--dovetail-disabled-opacity)' : 1,
pointerEvents: disabled ? 'none' : 'auto',
transition: `all var(--dovetail-duration-fast) var(--dovetail-ease-default)`,
display: 'inline-flex',
alignItems: 'center',
gap: '8px',
}}
>
{loading ? <span>Loading…</span> : children}
{!loading && (
<span
style={{
display: 'inline-block',
transform: isHovered ? 'translate3d(4px, 0px, 0px)' : 'translate3d(0,0,0)',
transition: `transform var(--dovetail-duration-fast) var(--dovetail-ease-default)`,
}}
aria-hidden="true"
>
→
</span>
)}
</button>
);
};
```
---
### 6.3 Ghost / Secondary Button
**Anatomy:** `<button>` → `[label-text]`
| State | Background | Text | Outline | Opacity |
|---|---|---|---|---|
| Default | `transparent` | `rgba(255,255,255,0.64)` | none | 1 |
| Hover (dark) | `rgba(255,255,255,0.08)` | `rgb(255,255,255)` | none | 1 |
| Hover (light) | `rgba(10,10,10,0.04)` | `rgb(10,10,10)` | none | 1 |
| Focus-visible | `rgba(255,255,255,0.08)` | `rgb(255,255,255)` | `1px solid rgb(255,255,255)`, offset 1px | 1 |
| Disabled | transparent | `rgba(255,255,255,0.64)` | none | 0.6 |
```tsx
const GhostButton = ({
children,
disabled = false,
theme = 'dark',
}: {
children: React.ReactNode;
disabled?: boolean;
theme?: 'dark' | 'light';
}) => (
<button
disabled={disabled}
style={{
fontFamily: '"JetBrains Mono", "SF Mono", Consolas, monospace',
fontSize: 'var(--dovetail-font-size-xs)',
fontWeight: 'var(--dovetail-font-weight-regular)',
letterSpacing: '1px',
textTransform: 'uppercase',
backgroundColor: 'transparent',
color: 'rgba(255, 255, 255, 0.64)',
borderRadius: 'var(--dovetail-radius-md)', // 8px
padding: '8px 16px',
border: '1px solid rgba(0, 0, 0, 0)',
opacity: disabled ? 0.6 : 1,
pointerEvents: disabled ? 'none' : 'auto',
transition: `all var(--dovetail-duration-fast) var(--dovetail-ease-default)`,
cursor: disabled ? 'not-allowed' : 'pointer',
}}
// CSS handles hover/focus-visible via stylesheet — see Section 6 state table
>
{children}
</button>
);
```
---
### 6.4 Nav Item / Menu Link
**Anatomy:** `<a>` or `<button>` → `[label]` + optional `[dropdown-chevron]`
| State | Colour | Background | Behaviour |
|---|---|---|---|
| Default | `rgb(255,255,255)` | transparent | — |
| Hover | `rgb(255,255,255)` | `rgba(255,255,255,0.16)` | dropdown appears |
| Focus-visible | `rgb(255,255,255)` | `rgba(255,255,255,0.16)` | 1px outline |
| Active | [TBD - extract manually] | — | — |
```tsx
const NavItem = ({ label, href }: { label: string; href: string }) => (
<a
href={href}
style={{
fontFamily: '"JetBrains Mono", "SF Mono", Consolas, monospace',
fontSize: 'var(--dovetail-font-size-xs)', // 12px
fontWeight: 'var(--dovetail-font-weight-regular)',
letterSpacing: '1px',
textTransform: 'uppercase',
color: 'var(--dovetail-text-full-white)',
textDecoration: 'none',
padding: '8px 12px',
borderRadius: 'var(--dovetail-radius-md)',
transition: `background-color var(--dovetail-duration-base) var(--dovetail-ease-default)`,
}}
// hover handled via CSS class with rgba(255,255,255,0.16) bg
>
{label}
</a>
);
```
---
### 6.5 Feature Card
**Anatomy:** `<div>` → `[icon]` + `<h3>[title]</h3>` + `<p>[description]</p>`
| State | Background | Text colour | Border |
|---|---|---|---|
| Default | [TBD - extract manually] | `rgb(250,250,250)` | [TBD] |
| Hover | — | `rgb(250,250,250)` | — |
```tsx
const FeatureCard = ({
title,
description,
icon,
}: {
title: string;
description: string;
icon: React.ReactNode;
}) => (
<div
style={{
borderRadius: 'var(--dovetail-radius-md)', // 8px
padding: 'var(--dovetail-space-sm)', // 32px
display: 'flex',
flexDirection: 'column',
gap: '16px',
}}
>
<div aria-hidden="true">{icon}</div>
<h3
style={{
fontFamily: 'var(--dovetail-font-sans)',
fontSize: 'var(--dovetail-font-size-xl)', // 20px
fontWeight: 'var(--dovetail-font-weight-medium)',
lineHeight: 'var(--dovetail-line-height-normal)', // 24px
letterSpacing: '-0.25px',
color: 'var(--dovetail-text-on-dark)',
margin: 0,
}}
>
{title}
</h3>
<p
style={{
fontFamily: 'var(--dovetail-font-sans)',
fontSize: 'var(--dovetail-font-size-sm)', // 14px
fontWeight: 'var(--dovetail-font-weight-regular)',
color: 'var(--dovetail-text-on-dark)',
margin: 0,
}}
>
{description}
</p>
</div>
);
```
---
### 6.6 Skip to Content Link (Accessibility)
**Anatomy:** `<a>` — visually hidden, appears on focus
| State | Transform | Behaviour |
|---|---|---|
| Default | `translateY(-100%)` (off-screen) | Hidden |
| Focus | `translateY(0px)` | Visible |
```tsx
const SkipLink = () => (
<a
href="#main-content"
style={{
position: 'absolute',
top: '8px',
left: '8px',
backgroundColor: 'rgb(255,255,255)',
color: 'rgb(0,0,0)',
borderRadius: 'var(--dovetail-radius-sm)', // 4px
padding: '8px 16px',
fontSize: 'var(--dovetail-font-size-xs)',
transform: 'translateY(-100%)',
transition: `transform var(--dovetail-duration-fast) var(--dovetail-ease-default)`,
zIndex: 9999,
}}
onFocus={e => ((e.target as HTMLElement).style.transform = 'translateY(0px)')}
onBlur={e => ((e.target as HTMLElement).style.transform = 'translateY(-100%)')}
>
Skip to main content
</a>
);
```
---
## 7. Elevation & Depth
```css
:root {
/* Box shadows — extracted: all elements show "none" at rest */
--dovetail-shadow-none: none; /* default resting state — NO shadows on cards or buttons */
--dovetail-shadow-overlay: rgba(255,255,255,0.16); /* hover state overlay — colour, not shadow */
/* Z-index scale — reconstructed: moderate confidence */
--dovetail-z-base: 0; /* static content */
--dovetail-z-sticky: 100; /* sticky nav */
--dovetail-z-dropdown: 200; /* nav mega-menu dropdowns */
--dovetail-z-overlay: 400; /* modal overlays */
--dovetail-z-modal: 500; /* modal dialogs */
--dovetail-z-toast: 600; /* toast notifications */
--dovetail-z-skip: 9999; /* skip-to-content link */
/* Border tokens */
--dovetail-border-transparent: 1px solid rgba(0,0,0,0); /* ghost button border at rest */
--dovetail-border-focus-dark: 1px solid rgb(255,255,255); /* focus ring, dark theme */
--dovetail-border-focus-light: 1px solid rgb(10,10,10); /* focus ring, light theme */
--dovetail-border-focus-offset: 1px;
/* Backdrop filter — none used in extracted elements */
--dovetail-backdrop-none: none;
}
```
**Key principle:** Dovetail achieves depth through **colour overlays and opacity shifts**, not drop shadows. Cards and buttons have `box-shadow: none` at rest. Hover depth is signalled by `rgba(255,255,255,0.08–0.16)` background overlays.
---
## 8. Motion
```css
:root {
/* Duration tokens */
--dovetail-duration-fast: 0.2s; /* micro-interactions: icon slide, colour shift */
--dovetail-duration-base: 0.3s; /* standard transitions: nav hover, theme switch */
/* Easing */
--dovetail-ease-default: ease; /* all transitions — no custom cubic-bezier found */
}
/* ── Keyframe animations ── */
/* Hero entrance — opacity fade in on page load */
@keyframes dovetail-hero-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
/* Logo marquee — infinite horizontal scroll (duplicate list for seamless loop) */
@keyframes dovetail-marquee {
0% { transform: translateX(0px); }
100% { transform: translateX(-50%); }
}
/* Dropdown/tooltip entrance — fade + lift */
@keyframes dovetail-fade-up {
0% { opacity: 0; transform: translateY(4px); }
100% { opacity: 1; transform: translateY(0px); }
}
/* Animated word-swap in headlines — stepped vertical cycle, 8 items */
@keyframes dovetail-word-cycle {
0%, 11% { transform: translateY(0%); }
16%, 23% { transform: translateY(-6.25%); }
28%, 35% { transform: translateY(-12.5%); }
40%, 47% { transform: translateY(-18.75%); }
52%, 59% { transform: translateY(-25%); }
64%, 71% { transform: translateY(-31.25%); }
76%, 83% { transform: translateY(-37.5%); }
88%, 95% { transform: translateY(-43.75%); }
100% { transform: translateY(-50%); }
}
```
### Motion Rules
- **Icon nudge on hover:** Arrow/chevron icons translate `+4px` on the X axis at `0.2s ease` — used on CTA buttons.
- **Dropdown reveal:** `opacity: 0 → 1`, `pointer-events: none → auto`, `visibility: hidden → visible` on parent hover/focus-within.
- **Word-cycle animation:** Applied to a vertically stacked list of words inside the H1/H2 — list is duplicated at 100% height so the translateY(-50%) loops seamlessly.
- **Marquee:** Always duplicate content list; animation runs from `0` to `translateX(-50%)` — never reverse.
- **NEVER animate** layout properties (`width`, `height`, `padding`) — only `transform`, `opacity`, `background-color`, `color`.
- **Respect `prefers-reduced-motion`:** Disable `dovetail-word-cycle` and `dovetail-marquee` for users who prefer reduced motion.
```css
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
```
---
## 9. Anti-Patterns & Constraints
1. **Using brand mark colours as surface fills**
**Rule:** `rgb(255,153,0)`, `rgb(234,67,53)`, `rgb(13,157,218)` are logo/illustration fills only.
**Why it fails:** AI agents see these as the most distinctive colours in the palette and incorrectly apply them to hero backgrounds, button fills, or highlight text — producing a garish, brand-inconsistent result.
**Instead:** Use these only inside `<svg>` path fills for the Dovetail logo mark. All backgrounds use near-black, white (`rgb(250,250,250)`), or `rgb(0,68,255)` for the brand surface specifically.
2. **Using Inter for buttons and labels**
**Rule:** NEVER use `--dovetail-font-sans` (Inter) for button text, nav labels, tags, or status indicators.
**Why it fails:** AI agents default to Inter for all text elements because it's the heading font. But Dovetail's UI labels are `JetBrains Mono / 12px / uppercase / 1px tracking` — a deliberate technical aesthetic. Using Inter produces a generic SaaS look that erases the brand voice.
**Instead:** Any text ≤ 14px in a button, tag, nav item, or label must use `--dovetail-font-mono`.
3. **Adding border-radius > 8px**
**Rule:** NEVER use `border-radius > 8px` on any interactive or container element.
**Why it fails:** AI agents routinely default to `border-radius: 9999px` (pill) for CTA buttons or `border-radius: 16px` for cards, inferring a "modern" style. Dovetail uses `4px` (primary button) and `8px` (secondary/card) — sharp, editorial corners.
**Instead:** Use `--dovetail-radius-sm` (4px) for primary buttons and `--dovetail-radius-md` (8px) for cards, nav items, and ghost buttons.
4. **Hardcoding `#ffffff` instead of `rgb(250, 250, 250)`**
**Rule:** NEVER use pure white `#ffffff` / `rgb(255,255,255)` for page background or primary text on dark backgrounds.
**Why it fails:** Dovetail's light surface is `rgb(250,250,250)` (softened white). Using `#ffffff` creates a brightness mismatch against the brand's muted palette and breaks the visual softness of the dark-theme text.
**Instead:** Use `var(--dovetail-text-on-dark)` = `rgb(250,250,250)` for text/light surfaces, and reserve `rgb(255,255,255)` for hover-state text colour changes only.
5. **Inventing arbitrary spacing values**
**Rule:** NEVER use spacing values not in the scale (24 / 32 / 64 / 96 / 100 / 124px).
**Why it fails:** AI agents interpolate to "sensible" values like `40px`, `48px`, or `80px` when generating section margins and card padding. Dovetail deliberately skips these — the large jumps create the signature breathing room. Adding intermediate values compresses the layout and destroys the whitespace rhythm.
**Instead:** Map every spacing need to the nearest token. If no token fits, check `--dovetail-btn-padding-y` (8px) / `--dovetail-btn-padding-x` (16px) for micro-spacing inside components.
6. **Omitting scroll-theme variants from interactive states**
**Rule:** NEVER write hover/focus CSS without both `html[data-scroll-theme="dark"]` and `html[data-scroll-theme="light"]` variants.
**Why it fails:** Dovetail sections switch theme as the user scrolls — a button styled only for dark theme will render with a white hover background on a white section, making it invisible. The AI doesn't know about this runtime attribute and writes single-theme styles.
**Instead:** Always provide both theme variants. Pattern: default → dark theme values, `html[data-scroll-theme="light"] .component:hover` → light theme values.
7. **Using `box-shadow` for depth**
**Rule:** NEVER add `box-shadow` to cards or buttons.
**Why it fails:** AI agents apply material-style shadows (`0 4px 12px rgba(0,0,0,0.1)`) to create card depth. Dovetail uses zero shadows at rest — depth is signalled through background overlays (`rgba(255,255,255,0.08–0.16)`) and contrast, not drop shadows.
**Instead:** For hover depth, apply `background-color: rgba(255,255,255,0.16)` (dark theme) or `rgba(10,10,10,0.04)` (light theme) overlay.
8. **Animating layout-affecting properties**
**Rule:** NEVER animate `width`, `height`, `padding`, `margin`, or `top/left` in transitions.
**Why it fails:** AI agents apply `transition: all` and then change padding or width on hover, triggering layout reflow on every animation frame. Dovetail uses `transition: all` in computed styles but only actually transitions `transform`, `opacity`, `background-color`, and `color`.
**Instead:** Explicitly scope transitions: `transition: transform 0.2s ease, background-color 0.2s ease, color 0.2s ease, opacity 0.2s ease`.
9. **Using EB Garamond for headings or body text**
**Rule:** NEVER use `'EB Garamond'` for section headings, nav, or body copy.
**Why it fails:** EB Garamond is loaded as a font asset and AI agents may select it as a "distinctive brand font" for headings. It is an editorial serif used only for testimonial pull-quotes and is visually inconsistent at heading scale on a dark SaaS dashboard.
**Instead:** Use `--dovetail-font-sans` (Inter) for all headings and `--dovetail-font-mono` (JetBrains Mono) for UI labels. Reserve EB Garamond exclusively for `<blockquote>` / testimonial contexts.
10. **Constructing dynamic Tailwind class names with string interpolation**
**Rule:** NEVER write `className={\`text-${size}\`}` or `className={\`bg-${color}\`}` in Tailwind.
**Why it fails:** Tailwind's JIT compiler cannot purge or generate classes that aren't written as complete strings in source. AI agents frequently generate dynamic class names for variant props, which results in missing styles in production builds.
**Instead:** Use complete class names in conditional expressions: `className={isLarge ? 'text-xl' : 'text-xs'}`, or use CSS custom properties via `style={{ fontSize: 'var(--dovetail-font-size-xl)' }}`.
---
## 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 (51) */
--quote-color: #fafafa;
--brand-surface-1: rgb(0, 68, 255); /* Brand surface, dominant on 1 element — e.g. "The best never guessGet 60 day" /* mined from computed styles */ */
--brand-mark-2: rgb(255, 153, 0); /* Brand mark fill on 2 svg shapes — e.g. "path" /* mined from svg fill */ */
--brand-mark-3: rgb(13, 157, 218); /* Brand mark fill on 1 svg shape — e.g. "path" /* mined from svg fill */ */
--brand-mark-4: rgb(234, 67, 53); /* Brand mark fill on 1 svg shape — e.g. "path" /* mined from svg fill */ */
--dovetail-bg-app: #fafafa;
--dovetail-bg-surface: rgb(0, 68, 255);
--dovetail-accent: rgb(255, 153, 0);
--dovetail-accent-cool: rgb(13, 157, 218);
--dovetail-text-primary: rgb(234, 67, 53);
--dovetail-text-on-dark: rgb(250,250,250);
--dovetail-text-on-light: rgb(10,10,10);
--primitive-blue-600: rgb(0, 68, 255);
--primitive-orange-500: rgb(255, 153, 0);
--primitive-cyan-500: rgb(13, 157, 218);
--primitive-red-500: rgb(234, 67, 53);
--primitive-white-soft: rgb(250, 250, 250);
--primitive-black-soft: rgb(10, 10, 10);
--primitive-white-pure: rgb(255, 255, 255);
--primitive-grey-200: rgb(216, 216, 216);
--primitive-grey-300: rgb(206, 206, 206);
--primitive-grey-800: rgb(59, 59, 59);
--primitive-grey-600: rgb(88, 88, 88);
--primitive-white-64: rgba(255,255,255,0.64);
--primitive-white-16: rgba(255,255,255,0.16);
--primitive-white-08: rgba(255,255,255,0.08);
--primitive-black-04: rgba(10,10,10,0.04);
--dovetail-bg-overlay-dark: rgba(255,255,255,0.16);
--dovetail-bg-overlay-ghost: rgba(255,255,255,0.08);
--dovetail-bg-overlay-light: rgba(10,10,10,0.04);
--dovetail-bg-hover-dark: rgb(206,206,206);
--dovetail-bg-hover-light: rgb(59,59,59);
--dovetail-text-muted: rgba(255,255,255,0.64);
--dovetail-text-full-white: rgb(255,255,255);
--dovetail-focus-ring-dark: rgb(255,255,255);
--dovetail-focus-ring-light: rgb(10,10,10);
--dovetail-nav-bg: transparent;
--dovetail-nav-hover-bg: var(--dovetail-bg-overlay-dark);
--dovetail-btn-primary-bg: rgb(255,255,255);
--dovetail-btn-primary-text: rgb(0,0,0);
--dovetail-btn-primary-hover-bg: var(--dovetail-bg-hover-dark);
--dovetail-btn-ghost-bg: transparent;
--dovetail-btn-ghost-hover-bg: var(--dovetail-bg-overlay-ghost);
--dovetail-border-transparent: 1px solid rgba(0,0,0,0);
--dovetail-border-focus-dark: 1px solid rgb(255,255,255);
--dovetail-border-focus-light: 1px solid rgb(10,10,10);
--dovetail-border-focus-offset: 1px;
--dovetail-brand-surface: rgb(0,68,255);
--dovetail-brand-orange: rgb(255,153,0);
--dovetail-brand-cyan: rgb(13,157,218);
--dovetail-brand-red: rgb(234,67,53);
/* Typography (31) */
--font-size-xs: 12px; /* 14 elements — e.g. p "Platform overview", p "Roles", p "Use cases" /* mined from computed styles */ */
--font-size-sm: 14px; /* 70 elements — e.g. p "Constance Docos", p "Senior UX Designer, ", p "More ways to query y" /* mined from computed styles */ */
--font-size-md: 16px; /* 19 elements — e.g. p "More than double you", p "A full workweek back", p "Faster from discover" /* mined from computed styles */ */
--font-size-lg: 18px; /* 7 elements — e.g. p "Return on investment", p "Weekly time saved pe", p "Faster shipping" /* mined from computed styles */ */
--font-size-xl: 20px; /* 72 elements — e.g. h3 "Built for trust at s", h3 "Drive consistency", h3 "Privacy without fric" /* mined from computed styles */ */
--font-size-2xl: 64px; /* 8 elements — e.g. h2 "Your AI native cust", h2 "The best [] never gu", h2 "Turn feedback into " /* mined from computed styles */ */
--font-size-3xl: 112px; /* 20 elements — e.g. h1 "Build with facts,not", span "B", span "u" /* mined from computed styles */ */
--font-weight-regular: 400; /* 90 elements — e.g. p "Platform overview", p "Roles", p "Use cases" /* mined from computed styles */ */
--font-weight-medium: 500; /* 100 elements — e.g. h3 "Built for trust at s", h3 "Drive consistency", h3 "Privacy without fric" /* mined from computed styles */ */
--font-weight-semibold: 600; /* 31 elements — e.g. h1 "Build with facts,not", h2 "Dovetail for", h2 "Your AI native cust" /* mined from computed styles */ */
--line-height-normal: 24px; /* 45 elements — e.g. h3 "Built for trust at s", h3 "Drive consistency", h3 "Privacy without fric" /* mined from computed styles */ */
--line-height-loose: 80px; /* 25 elements — e.g. h1 "Build with facts,not", span "B", span "u" /* mined from computed styles */ */
--dovetail-font-size-xs: 12px;
--dovetail-font-size-sm: 14px;
--dovetail-font-size-md: 16px;
--dovetail-font-size-lg: 18px;
--dovetail-font-size-xl: 20px;
--dovetail-font-size-2xl: 64px;
--dovetail-font-size-3xl: 112px;
--dovetail-font-weight-regular: 400;
--dovetail-font-weight-medium: 500;
--dovetail-font-weight-semibold: 600;
--dovetail-nav-text: var(--dovetail-text-full-white);
--dovetail-nav-btn-text: var(--dovetail-text-muted);
--dovetail-btn-ghost-text: var(--dovetail-text-on-dark);
--dovetail-font-sans: Inter, -apple-system, ...;
--dovetail-font-mono: 'JetBrains Mono', 'SF Mono', ...;
--dovetail-font-serif: 'EB Garamond', Georgia, serif;
--dovetail-font-display: 'Space Grotesk', var(--dovetail-font-sans);
--dovetail-line-height-normal: 24px;
--dovetail-line-height-loose: 80px;
/* Spacing (21) */
--space-xs: 24px; /* 7 elements — e.g. section .css-1ptmokx, section .css-1ptmokx, section .css-1ptmokx /* mined from computed styles */ */
--space-sm: 32px; /* 8 elements — e.g. section .css-16ufn4r, section .css-16ufn4r, section .css-16ufn4r /* mined from computed styles */ */
--space-md: 64px; /* 1 element — e.g. section .css-1ea3w3z /* mined from computed styles */ */
--space-lg: 96px; /* 18 elements — e.g. section .css-t1wuou, section .css-t1wuou, section .css-t1wuou /* mined from computed styles */ */
--space-xl: 100px; /* 3 elements — e.g. section .css-29xcf, section .css-29xcf, section .css-29xcf /* mined from computed styles */ */
--space-2xl: 124px; /* 30 elements — e.g. section .css-16ufn4r, section .css-16ufn4r, section .css-t1wuou /* mined from computed styles */ */
--dovetail-space-xs: 24px;
--dovetail-space-sm: 32px;
--dovetail-space-md: 64px;
--dovetail-space-lg: 96px;
--dovetail-space-xl: 100px;
--dovetail-space-2xl: 124px;
--dovetail-btn-primary-padding: 8px 16px;
--dovetail-btn-padding-y: 8px;
--dovetail-btn-padding-x: 16px;
--dovetail-bp-mobile: 799px;
--dovetail-bp-tablet: 850px;
--dovetail-bp-desktop-sm: 1185px;
--dovetail-bp-desktop-md: 1280px;
--dovetail-bp-desktop-lg: 1281px;
--dovetail-bp-desktop-xl: 1512px;
/* Radius (6) */
--radius-sm: 4px; /* 1 element — e.g. a .css-6vl99j "Skip to main content" /* mined from computed styles */ */
--radius-md: 8px; /* 39 elements — e.g. button .e1un6o8d0, button .egaom870 "Product", button .egaom870 "Solutions" /* mined from computed styles */ */
--dovetail-radius-sm: 4px;
--dovetail-radius-md: 8px;
--dovetail-btn-primary-radius: var(--dovetail-radius-sm);
--dovetail-btn-ghost-radius: var(--dovetail-radius-md);
/* Effects (3) */
--dovetail-disabled-opacity: 0.6;
--dovetail-shadow-none: none;
--dovetail-shadow-overlay: rgba(255,255,255,0.16);
/* Motion (7) */
----motion-hero-fade-in: @keyframes hero-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}; /* @keyframes hero-fade-in */
----motion-animation-t9qc24: @keyframes animation-t9qc24 {
0% { transform: translateX(0px); }
100% { transform: translateX(-50%); }
}; /* @keyframes animation-t9qc24 */
----motion-animation-19t9kju: @keyframes animation-19t9kju {
0% { opacity: 0; transform: translateY(4px); }
100% { opacity: 1; transform: translateY(0px); }
}; /* @keyframes animation-19t9kju */
----motion-animation-1wo539a: @keyframes animation-1wo539a {
0%, 11% { transform: translateY(0px); }
16%,… <0.4KB elided>; /* @keyframes animation-1wo539a */
--duration-fast: 0.2s; /* 24 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-base: 0.3s; /* 42 elements — e.g. button, button, button /* mined from computed styles */ */
--ease-default: ease; /* 146 elements — e.g. button, button, button /* mined from computed styles */ */
```
## Appendix B: Token Source Metadata
| Property | Value |
|---|---|
| **Token source** | `reconstructed-from-computed` |
| **Extraction method** | Computed styles from DOM elements + CSS rule mining + SVG fill extraction |
| **CSS custom properties found** | 1 (`--quote-color: #fafafa`) |
| **Curated tokens synthesised** | 28 (mapped from computed styles to `--dovetail-*` namespace) |
| **Overall confidence** | Low (colour semantic roles partially ambiguous) |
| **Clustering method** | Hue family grouping for colours; multiplicative proximity for spacing; element-context labelling for radii |
### Per-token confidence breakdown
| Token group | Confidence | Notes |
|---|---|---|
| Font sizes (`xs` → `3xl`) | **High** | Directly extracted from computed styles across 200+ elements |
| Font weights (400/500/600) | **High** | Directly extracted, consistent across element types |
| Line heights (24px, 80px) | **High** | Extracted from h1/h3; intermediate values TBD |
| Spacing scale (24–124px) | **High** | Extracted from curated set with original token names preserved |
| Radius (4px, 8px) | **High** | 40 elements; no pill shapes found — confirmed sharp radius brand |
| Motion (0.2s, 0.3s, ease) | **High** | Extracted from 150+ computed transition values |
| `--dovetail-bg-surface` (`rgb(0,68,255)`) | **Medium** | Identified on 1 element; role inferred as brand hero surface |
| `--dovetail-bg-app` (`#fafafa`) | **Low** | Mapped from `--quote-color` — semantic role inferred, not confirmed |
| `--dovetail-text-primary` (`rgb(234,67,53)`) | **Low** | SVG fill on 1 shape; labelled "text-primary" in curation but may be logo-only |
| `--dovetail-accent-cool` (`rgb(13,157,218)`) | **Low** | SVG fill on 1 shape — logo mark context only |
| H2 line-height (60px) | **Medium** | Computed from h2 element; not in original token set |
| Dark-theme text (`rgb(10,10,10)`) | **High** | Reconstructed from interactive state styles |
| Hover overlay values | **High** | Extracted directly from CSS rule set for interactive states |
| `Space Grotesk` role | **Low** | Font loaded but no computed usage context found |
| `EB Garamond` role | **Medium** | Font loaded with italic variant; inferred for testimonials by convention |
### Reconstruction notes
- **Colour semantic names** were synthesised because only 1 CSS custom property existed. The original curated names (`brand-surface-1`, `brand-mark-2/3/4`) have been preserved as aliases in documentation.
- **Spacing scale** matches a non-standard grid (24/32/64/96/100/124px). No 4px or 8px base unit was found — do NOT assume an 8px grid underlies this system.
- **Radius scale** is exclusively 4px and 8px across 40 elements. The absence of larger radii is confirmed, not a gap in extraction.
- **Detected library:** Bootstrap was flagged, but all extracted styles appear to be custom CSS-in-JS (Emotion) with hashed class names — Bootstrap may be a legacy dependency or used only for specific utilities.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