Contra
MIT
Playful yet professional design system with teal and purple accents, bold typography, and vibrant accent surfaces—built for modern SaaS platforms and landing pages
Layout StudioImport this kit into a Studio project and start editing.
CLI installRun it in any project. No account needed.
npx @layoutdesign/context install contra# layout.md — Contra Design System
---
## 0. Quick Reference
> **Stack:** Contra.com · Reconstructed from computed styles · Low confidence baseline, high confidence on extracted tokens
> **How to apply:** Use as `var(--contra-*)` in CSS, `style={{ prop: 'var(--contra-*)' }}` in JSX, or `bg-[var(--contra-bg-surface)]` in Tailwind.
```css
:root {
/* Colours */
--contra-bg-surface: rgb(214, 233, 235); /* teal-tinted page background */
--contra-accent: rgb(106, 87, 227); /* primary purple accent */
--contra-accent-hover: rgb(34, 40, 52); /* CTA button background (dark navy) */
--contra-text-primary: rgb(20, 23, 31); /* h1 / high-contrast text */
--contra-text-secondary: rgb(103, 112, 132); /* body / muted text */
--contra-bg-app: rgb(240, 251, 150); /* lime-yellow decorative surface */
--contra-surface-white: rgb(255, 255, 255); /* card / modal background */
--contra-border-default: rgb(229, 231, 235); /* subtle border */
/* Typography */
--contra-font-display: "GT Standard L", Arial, sans-serif;
--contra-font-body: "GT Standard M", Arial, sans-serif;
--contra-font-size-3xl: 58px; /* hero heading */
--contra-font-size-2xl: 23px; /* section headings */
--contra-font-size-xl: 19px; /* lead body */
--contra-font-size-lg: 16px; /* standard body */
--contra-font-size-sm: 14px; /* captions / labels */
--contra-font-size-xs: 12px; /* eyebrow / meta */
/* Spacing */
--contra-space-xs: 12px; --contra-space-sm: 24px;
--contra-space-md: 26px; --contra-space-lg: 32px;
--contra-space-xl: 36px; --contra-space-2xl: 48px;
/* Radius */
--contra-radius-sm: 10px; --contra-radius-md: 24px;
--contra-radius-lg: 32px; --contra-radius-full: 50%;
/* Motion */
--contra-duration-fast: 0.17s; --contra-duration-base: 0.3s;
--contra-ease-default: ease;
/* Elevation */
--contra-shadow-sm: rgba(34,40,52,0.05) 0px 4px 10px 0px,
rgba(34,40,52,0.04) -2px 18px 18px 0px;
}
```
```tsx
// Primary CTA button — correct token usage
<button className="btn-primary">Sign up</button>
// CSS
// .btn-primary {
// font-family: var(--contra-font-body);
// font-size: var(--contra-font-size-sm); /* 14px */
// font-weight: 600;
// line-height: 20px;
// color: var(--contra-surface-white);
// background-color: var(--contra-accent-hover); /* rgb(34,40,52) */
// border-radius: var(--contra-radius-md); /* 24px pill */
// padding: 8px 14px;
// border: none;
// transition: background-color var(--contra-duration-fast) var(--contra-ease-default);
// }
// .btn-primary:hover { background-color: var(--contra-accent); }
```
**NEVER rules:**
- NEVER use `border-radius` values outside `10px / 24px / 32px / 50%`
- NEVER use Inter, Roboto, or system-ui — always `"GT Standard L"` or `"GT Standard M"`
- NEVER use `rgb(214, 233, 235)` as a text colour — it is a background surface only
- NEVER hardcode hex or rgb literals — always reference a `--contra-*` token
- NEVER construct Tailwind classes dynamically (e.g. `` `bg-${color}` ``) — they won't purge correctly
- NEVER omit hover/focus/disabled states on interactive elements
- NEVER use spacing values not on the curated scale (12 / 24 / 26 / 32 / 36 / 48px)
> 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
Contra is a **professional freelance marketplace** with a **bold, editorial aesthetic**. The visual language is confident and creative-industry-forward: oversized display type, vibrant decorative colour pops against a neutral teal-white canvas, and pill-shaped interactive elements that feel modern without being playful.
### Aesthetic Intent
- **Type-first hierarchy:** The 58px display heading at `font-weight: 500` (not bold) signals editorial restraint — weight comes from size, not thickness.
- **Controlled colour chaos:** The brand palette includes purple (`#6A57E3`), lime-yellow (`#F0FB96`), teal (`#D6E9EB`), orchid (`#CD74DD`), and warm red (`#F04438`) — but these are deployed as **surface tiles and decorative accents**, not as text or interactive states.
- **Dark navy CTA:** The primary call-to-action (`rgb(34, 40, 52)`) is intentionally dark and grounding, anchoring the page amid colourful decoration.
- **Pill geometry everywhere:** Buttons, badges, and avatar containers all use large radius values (24px–100px), reinforcing a friendly-but-professional feel.
### What This Design Explicitly Rejects
- **Warm beige / off-white neutrals** — the surface is cool teal-tinted, not warm
- **Heavy font weights (700–900)** — max weight in use is `600` (semibold)
- **Sharp corners on interactive elements** — no `border-radius: 0` on buttons or cards
- **Muted monochromatic palettes** — colour saturation is celebrated in tile/illustration contexts
- **Dense information layouts** — generous spacing (`48px` section gaps) and breathing room are core
- **Generic sans-serif fallbacks as primary** — GT Standard is the brand font; Arial is fallback only
---
## 2. Colour System
### Tier 1: Primitive Values
```css
:root {
/* Neutrals */
--primitive-navy-900: rgb(20, 23, 31); /* darkest — h1 text, links */
--primitive-navy-800: rgb(34, 40, 52); /* near-black — CTA button bg */
--primitive-gray-500: rgb(103, 112, 132); /* muted — body text, h2/h3 */
--primitive-gray-200: rgb(229, 231, 235); /* lightest gray — border default */
--primitive-white: rgb(255, 255, 255); /* pure white — card/modal bg */
/* Brand Saturated */
--primitive-purple: rgb(106, 87, 227); /* primary brand purple */
--primitive-teal: rgb(214, 233, 235); /* brand background teal */
--primitive-lime: rgb(240, 251, 150); /* decorative lime-yellow */
--primitive-orchid: rgb(205, 116, 221); /* decorative orchid */
--primitive-maroon: rgb(69, 25, 47); /* deep maroon surface */
--primitive-red: rgb(240, 68, 56); /* brand mark / error red */
--primitive-blue: rgb(20, 110, 245); /* brand mark blue */
}
```
### Tier 2: Semantic Aliases
```css
:root {
/* Surfaces */
--contra-bg-surface: var(--primitive-teal); /* page/section background tint */
--contra-bg-app: var(--primitive-lime); /* decorative feature tile bg */ /* extracted: low confidence — role inferred */
--contra-surface-white: var(--primitive-white); /* card, modal, button default bg */
--contra-surface-maroon: var(--primitive-maroon); /* decorative tile variant */ /* extracted: low confidence */
/* Text */
--contra-text-primary: var(--primitive-navy-900); /* headings, links, high contrast */
--contra-text-secondary: var(--primitive-gray-500); /* body copy, captions */
--contra-text-on-dark: var(--primitive-white); /* text on dark CTA buttons */
/* Actions */
--contra-accent: var(--primitive-purple); /* hover state, accent elements */
--contra-accent-hover: var(--primitive-navy-800); /* primary CTA background */
--contra-accent-warm: var(--primitive-red); /* destructive / brand mark */
--contra-accent-cool: var(--primitive-blue); /* secondary brand mark */
/* Borders */
--contra-border-default: var(--primitive-gray-200); /* input, card, button borders */
/* Additional Brand Accent (decorative only — NOT for interactive states) */
--contra-accent-orchid: var(--primitive-orchid); /* decorative tile / illustration accent */
}
```
### Tier 3: Component Tokens
```css
:root {
/* Button */
--btn-primary-bg: var(--contra-accent-hover); /* rgb(34, 40, 52) */
--btn-primary-bg-hover: var(--contra-accent); /* rgb(106, 87, 227) */
--btn-primary-text: var(--contra-text-on-dark);
--btn-secondary-bg: var(--contra-surface-white);
--btn-secondary-border: var(--contra-border-default);
--btn-secondary-text: var(--contra-text-secondary);
/* Nav */
--nav-text: var(--contra-text-primary);
--nav-bg: transparent;
/* Card */
--card-bg: var(--contra-surface-white);
--card-border: var(--contra-border-default);
--card-shadow: var(--contra-shadow-sm);
--card-radius: var(--contra-radius-sm); /* 10px */
/* Modal */
--modal-bg: var(--contra-surface-white);
--modal-radius: 0px; /* modals are NOT rounded — extracted from computed */
--modal-padding: var(--contra-space-lg) var(--contra-space-sm); /* 32px 24px */
--modal-gap: var(--contra-space-sm); /* 24px */
}
```
### Colour Usage Table
| Token | Value | Usage |
|---|---|---|
| `--contra-bg-surface` | `rgb(214, 233, 235)` | Page tint, section backgrounds |
| `--contra-accent` | `rgb(106, 87, 227)` | Hover states, accent highlights |
| `--contra-accent-hover` | `rgb(34, 40, 52)` | **Primary CTA background** |
| `--contra-text-primary` | `rgb(20, 23, 31)` | H1, links, high-contrast text |
| `--contra-text-secondary` | `rgb(103, 112, 132)` | Body, captions, H2/H3 eyebrows |
| `--contra-bg-app` | `rgb(240, 251, 150)` | Decorative feature tile bg |
| `--contra-accent-orchid` | `rgb(205, 116, 221)` | Decorative tile / illustration |
| `--contra-accent-warm` | `rgb(240, 68, 56)` | Brand mark, destructive actions |
| `--contra-accent-cool` | `rgb(20, 110, 245)` | Brand mark, secondary accent |
| `--contra-surface-white` | `rgb(255, 255, 255)` | Cards, modals, secondary buttons |
---
## 3. Typography System
> **Font stack:** GT Standard L (display/body) + GT Standard M (UI/medium weight) — both `font-weight: 400` at the @font-face level; weight variation is handled by the font-family switch, not by numeric weight alone.
### Composite Type Groups
```css
:root {
/* ── Display / Hero ── */
--type-display: {
font-family: "GT Standard L", Arial, sans-serif;
font-size: 58px; /* --contra-font-size-3xl */
font-weight: 500;
line-height: 60.9px; /* ≈ 1.05 ratio — very tight */
letter-spacing: -0.58px; /* -0.01em — condensed feel */
color: var(--contra-text-primary);
text-align: center;
text-transform: none;
};
/* ── Section Heading (h2 eyebrow style) ── */
--type-eyebrow: {
font-family: "GT Standard L", Arial, sans-serif;
font-size: 12px; /* --contra-font-size-xs */
font-weight: 500;
line-height: 16px;
letter-spacing: 0.6px; /* tracked out for label legibility */
color: var(--contra-text-secondary);
text-transform: uppercase;
text-align: start;
};
/* ── Sub-section Label (h3 eyebrow) ── */
--type-label: {
font-family: "GT Standard M", Arial, sans-serif;
font-size: 12px; /* --contra-font-size-xs */
font-weight: 500;
line-height: 16px;
letter-spacing: 1.2px; /* more tracked than h2 — distinguishes hierarchy */
color: var(--contra-text-secondary);
text-transform: uppercase;
text-align: start;
};
/* ── Card / Section Title ── */
--type-heading-md: {
font-family: "GT Standard L", Arial, sans-serif;
font-size: 23px; /* --contra-font-size-2xl */
font-weight: 500;
line-height: 28px;
letter-spacing: normal;
color: var(--contra-text-primary);
};
/* ── Lead Body ── */
--type-body-lead: {
font-family: "GT Standard L", Arial, sans-serif;
font-size: 19px; /* --contra-font-size-xl */
font-weight: 400;
line-height: 28px;
letter-spacing: 0.38px;
color: var(--contra-text-secondary);
text-align: center;
};
/* ── Standard Body ── */
--type-body: {
font-family: "GT Standard L", Arial, sans-serif;
font-size: 16px; /* --contra-font-size-lg */
font-weight: 400;
line-height: 18.4px;
letter-spacing: normal;
color: var(--contra-text-primary);
};
/* ── Small Body / Caption ── */
--type-body-sm: {
font-family: "GT Standard L", Arial, sans-serif;
font-size: 14px; /* --contra-font-size-sm */
font-weight: 400;
line-height: 20px; /* --contra-line-height-normal */
letter-spacing: normal;
color: var(--contra-text-secondary);
};
/* ── UI / Button Label ── */
--type-ui: {
font-family: "GT Standard M", Arial, sans-serif;
font-size: 14px; /* --contra-font-size-sm */
font-weight: 400;
line-height: 20px;
letter-spacing: -0.14px;
color: var(--contra-text-secondary);
};
/* ── Nav / Link ── */
--type-nav: {
font-family: "GT Standard M", Arial, sans-serif;
font-size: 16px; /* --contra-font-size-lg */
font-weight: 400;
line-height: 18.4px;
letter-spacing: normal;
color: var(--contra-text-primary);
};
/* ── Semibold UI Label (e.g. footer links, "Payments") ── */
--type-ui-semibold: {
font-family: "GT Standard M", Arial, sans-serif;
font-size: 16px;
font-weight: 600;
line-height: 18.4px;
letter-spacing: normal;
color: var(--contra-text-primary);
};
}
```
### Type Scale Summary
| Token group | Family | Size | Weight | Line-height | Letter-spacing |
|---|---|---|---|---|---|
| `--type-display` | GT Standard **L** | 58px | 500 | 60.9px | -0.58px |
| `--type-heading-md` | GT Standard **L** | 23px | 500 | 28px | normal |
| `--type-eyebrow` | GT Standard **L** | 12px | 500 | 16px | +0.6px |
| `--type-label` | GT Standard **M** | 12px | 500 | 16px | +1.2px |
| `--type-body-lead` | GT Standard **L** | 19px | 400 | 28px | +0.38px |
| `--type-body` | GT Standard **L** | 16px | 400 | 18.4px | normal |
| `--type-body-sm` | GT Standard **L** | 14px | 400 | 20px | normal |
| `--type-ui` | GT Standard **M** | 14px | 400 | 20px | -0.14px |
| `--type-nav` | GT Standard **M** | 16px | 400 | 18.4px | normal |
### Font Pairing Rules
- **GT Standard L** = display, editorial, large headlines, body paragraph text
- **GT Standard M** = UI elements (buttons, nav, modals, interactive controls)
- H2 and H3 both render at 12px uppercase — differentiate them **only by letter-spacing** (H2: `0.6px`, H3: `1.2px`) and font family (H2: L, H3: M)
- Weight scale is `400 / 500 / 600` only — **never 700 or 800**
---
## 4. Spacing & Layout
### Base Unit & Scale
> The spacing scale is **not a pure 4px or 8px grid** — `26px (--contra-space-md)` is one off-grid value. Treat `--contra-space-md` as a legacy exception. All other new spacing should use the 4px-multiple values.
```css
:root {
/* Spacing scale (use var() — never raw px literals) */
--contra-space-xs: 12px; /* 3× base — tight internal padding, icon gaps */
--contra-space-sm: 24px; /* 6× base — card inner padding, input padding */
--contra-space-md: 26px; /* ⚠ off-grid — legacy; prefer --contra-space-sm or --contra-space-lg */
--contra-space-lg: 32px; /* 8× base — section inner padding, modal padding */
--contra-space-xl: 36px; /* 9× base — component spacing */
--contra-space-2xl: 48px; /* 12× base — between page sections */
/* Layout */
--contra-container-max: 1280px; /* reconstructed: moderate confidence — standard breakpoint */
--contra-container-padding: var(--contra-space-sm); /* 24px gutter on mobile */
--contra-grid-gap: var(--contra-space-sm); /* 24px grid column gaps */
--contra-section-gap: var(--contra-space-2xl); /* 48px between sections */
}
```
### Spacing Scale Table
| Token | Value | On 4px grid? | Usage |
|---|---|---|---|
| `--contra-space-xs` | 12px | ✅ (3×4) | Icon gaps, chip padding, tight rows |
| `--contra-space-sm` | 24px | ✅ (6×4) | Card padding, modal gap, gutter |
| `--contra-space-md` | 26px | ⚠️ off-grid | Legacy — prefer sm or lg |
| `--contra-space-lg` | 32px | ✅ (8×4) | Section padding, modal block padding |
| `--contra-space-xl` | 36px | ✅ (9×4) | Component vertical rhythm |
| `--contra-space-2xl` | 48px | ✅ (12×4) | **Between page sections** |
### Grid & Breakpoints
```css
/* Breakpoints — reconstructed: moderate confidence */
@media (min-width: 768px) { /* tablet */ }
@media (min-width: 1024px) { /* desktop */ }
@media (min-width: 1280px) { /* wide */ }
/* Container */
.container {
max-width: var(--contra-container-max); /* 1280px */
margin-inline: auto;
padding-inline: var(--contra-container-padding);
}
/* Navigation layout — from computed styles */
[role="navigation"] {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: normal; /* nav items use individual padding, not a gap value */
}
/* Modal layout — from computed styles */
.modal {
display: flex;
flex-direction: column;
gap: var(--contra-space-sm); /* 24px */
padding: var(--contra-space-lg) var(--contra-space-sm); /* 32px 24px */
}
```
### Radius Scale
```css
:root {
--contra-radius-sm: 10px; /* cards, chips, tags */
--contra-radius-md: 24px; /* standard buttons (Sign up, Message) */
--contra-radius-lg: 32px; /* featured/badge buttons */
--contra-radius-full: 50%; /* avatars, circular icon buttons */
/* NOTE: button_primary computed at borderRadius:100px — maps to --contra-radius-md (pill) */
}
```
---
## 5. Page Structure & Layout Patterns
> **Source:** LAYOUT DIGEST + component inventory. No screenshots available. Rows marked **(inferred)** are not visually confirmed.
### 5.1 Section Map
| # | Section | Layout Type | Approx. Height | Key Elements | Source |
|---|---|---|---|---|---|
| 1 | **Navigation / Header** | `flex row`, `justify: center`, `align: center` | 60–80px | Logo, nav links (`--type-nav`), CTA button (`btn-primary`, radius-md) | extracted |
| 2 | **Hero** | `flex column`, centered | 480–600px | H1 display (58px), lead body (19px), 1–2 CTA buttons (dark navy bg, radius-md) | inferred |
| 3 | **Feature / Value Prop Grid** | `flex row` or CSS grid, multi-column | 400–500px | Coloured tile cards (`--contra-bg-surface`, `--contra-bg-app`, `--contra-accent-orchid`), H2 eyebrow, body-sm | inferred |
| 4 | **Project Showcase** | Horizontal scroll or grid | 350–450px | Project cards (white bg, `--contra-radius-sm: 10px`, `--contra-shadow-sm`), H1 at 23px, caption at 14px | inferred |
| 5 | **Trending / Category Tags** | `flex row wrap` | 80–120px | Pill tags (`--contra-radius-sm: 10px`), `--type-body-sm` | inferred |
| 6 | **For Independents / For Companies** | Two-column split, `flex row` | 300–400px | H3 label (uppercase 12px 1.2px tracking), body-lead, CTA | inferred |
| 7 | **Social Proof / Testimonials** | Grid or carousel | 300px | Cards, avatar circles (`--contra-radius-full`), body-sm text | inferred |
| 8 | **CTA Banner** | `flex column`, centered | 200–300px | Large heading, primary CTA button (dark navy), `--contra-bg-surface` bg | inferred |
| 9 | **Footer** | `flex row`, multi-column | 200–300px | Semibold nav links (`font-weight: 600`), body-sm, brand mark SVG | inferred |
### 5.2 Layout Patterns
- **Navigation:** `display: flex; flex-direction: column; justify-content: center; align-items: center` (from computed `role_navigation`). On desktop, likely reflows to `flex-direction: row`.
- **Modal / Drawer:** `display: flex; flex-direction: column; gap: 24px; padding: 32px 24px` — confirmed from computed styles.
- **Cards:** white background (`--contra-surface-white`), `border-radius: 10px` (`--contra-radius-sm`), `box-shadow: var(--contra-shadow-sm)`, border `1px solid var(--contra-border-default)`.
- **Feature tiles:** coloured surface backgrounds (teal / lime / orchid / maroon) — no borders, likely larger radius (`--contra-radius-md: 24px` or `--contra-radius-lg: 32px`). Gap between tiles: `--contra-space-sm` (24px).
- **Section gaps:** `--contra-space-2xl: 48px` vertical rhythm between major sections.
- **Column ratios:** Single-column on mobile; inferred 2-col (50/50) for "For Independents / For Companies"; inferred 3-col or 4-col grid for project cards.
### 5.3 Visual Hierarchy
- **H1 at 58px** (`--type-display`) is the single dominant element per hero — **one per page section maximum**.
- **Primary CTAs** use `background-color: rgb(34, 40, 52)` (dark navy) with white text — confirmed from button census (8 instances of this CTA pattern).
- **Hover state** shifts CTA to `--contra-accent: rgb(106, 87, 227)` (purple).
- **Coloured tiles** create visual rhythm — lime, teal, orchid, and maroon surfaces break the white/light monotony without using text colour.
- **Eyebrow labels** (H2/H3 at 12px uppercase) appear above larger headings, establishing section context before hierarchy.
- **Whitespace rhythm:** `--contra-space-2xl: 48px` between sections; `--contra-space-lg: 32px` internal section padding.
- **Avatar/profile images** use `--contra-radius-full: 50%` — circular crop is consistent across user-generated content.
### 5.4 Content Patterns
- **Card pattern:** Coloured-surface tile → eyebrow label (12px uppercase) → heading (23px or 16px) → body text (14–16px) → optional CTA link.
- **Project listing pattern:** Image thumbnail → title (14px `--type-body-sm`) → creator name + avatar (12px + circle image).
- **Two-up feature pattern:** Left: label + heading + body; Right: illustration or feature tile. Gap: `--contra-space-2xl`.
- **Pill tag pattern:** `border-radius: 10px`, white bg, `1px solid --contra-border-default`, text `--type-body-sm`.
- **Repeating CTA pattern:** Dark navy button (`--contra-accent-hover` bg) + ghost/secondary white button side-by-side, both `border-radius: 24px`.
---
## 6. Component Patterns
### 6.1 Primary Button
**Anatomy:** `<button>` → `[icon?]` + `<span>` label
**Token-to-property mappings:**
| State | Background | Text colour | Border | Transform |
|---|---|---|---|---|
| Default | `--contra-accent-hover` (`rgb(34,40,52)`) | `--contra-text-on-dark` | none | — |
| Hover | `--contra-accent` (`rgb(106,87,227)`) | `--contra-text-on-dark` | none | — |
| Focus | `--contra-accent` | `--contra-text-on-dark` | `2px solid --contra-accent` offset `2px` | — |
| Active | `rgb(85, 70, 185)` (darkened purple) | `--contra-text-on-dark` | none | `scale(0.98)` |
| Disabled | `--contra-border-default` | `--contra-text-secondary` | none | — |
| Loading | `--contra-accent-hover` | transparent | none | — |
```tsx
import { ButtonHTMLAttributes, ReactNode } from 'react';
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary';
isLoading?: boolean;
children: ReactNode;
}
export function Button({ variant = 'primary', isLoading, disabled, children, ...props }: ButtonProps) {
const isPrimary = variant === 'primary';
return (
<button
{...props}
disabled={disabled || isLoading}
aria-busy={isLoading}
style={{
fontFamily: 'var(--contra-font-body)', /* "GT Standard M" */
fontSize: 'var(--contra-font-size-sm)', /* 14px */
fontWeight: 600,
lineHeight: '20px',
letterSpacing: '-0.14px',
color: isPrimary
? 'var(--contra-text-on-dark)' /* white */
: 'var(--contra-text-secondary)', /* rgb(103,112,132) */
backgroundColor: isPrimary
? 'var(--contra-accent-hover)' /* rgb(34,40,52) */
: 'var(--contra-surface-white)', /* white */
border: isPrimary
? 'none'
: '1px solid var(--contra-border-default)',/* rgb(229,231,235) */
borderRadius: 'var(--contra-radius-md)', /* 24px */
padding: '8px 14px',
cursor: (disabled || isLoading) ? 'not-allowed' : 'pointer',
opacity: (disabled || isLoading) ? 0.5 : 1,
transition: `background-color var(--contra-duration-fast) var(--contra-ease-default)`,
display: 'inline-flex',
alignItems: 'center',
gap: 'var(--contra-space-xs)', /* 12px */
position: 'relative',
}}
>
{isLoading ? (
<span aria-hidden="true" style={{ visibility: 'hidden' }}>{children}</span>
) : children}
{isLoading && (
<span
aria-label="Loading"
style={{
position: 'absolute',
width: '14px',
height: '14px',
borderRadius: 'var(--contra-radius-full)', /* 50% */
border: '2px solid var(--contra-text-on-dark)',
borderTopColor: 'transparent',
animation: 'spin 0.6s linear infinite',
}}
/>
)}
</button>
);
}
```
---
### 6.2 Nav Item
**Anatomy:** `<a>` or `<button role="button">` → optional icon + label text
**Token mappings:**
| State | Colour | Font | Weight |
|---|---|---|---|
| Default | `--contra-text-primary` (`rgb(20,23,31)`) | `--contra-font-body` | 400 |
| Hover | `--contra-accent` (`rgb(106,87,227)`) | `--contra-font-body` | 400 |
| Focus | `--contra-accent` | `--contra-font-body` | 400 |
| Active | `--contra-accent-hover` | `--contra-font-body` | 600 |
| Disabled | `--contra-text-secondary` | `--contra-font-body` | 400 |
```tsx
interface NavItemProps {
href: string;
children: ReactNode;
isActive?: boolean;
disabled?: boolean;
}
export function NavItem({ href, children, isActive, disabled }: NavItemProps) {
return (
<a
href={disabled ? undefined : href}
aria-current={isActive ? 'page' : undefined}
aria-disabled={disabled}
style={{
fontFamily: 'var(--contra-font-body)', /* "GT Standard M" */
fontSize: 'var(--contra-font-size-lg)', /* 16px */
fontWeight: isActive ? 600 : 400,
lineHeight: '18.4px',
letterSpacing: 'normal',
color: disabled
? 'var(--contra-text-secondary)'
: isActive
? 'var(--contra-accent-hover)'
: 'var(--contra-text-primary)',
textDecoration: 'none',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: 'var(--contra-space-xs)', /* 12px */
transition: `color var(--contra-duration-fast) var(--contra-ease-default)`,
cursor: disabled ? 'not-allowed' : 'pointer',
pointerEvents: disabled ? 'none' : 'auto',
}}
onMouseEnter={e => {
if (!disabled && !isActive) {
(e.currentTarget as HTMLElement).style.color = 'var(--contra-accent)';
}
}}
onMouseLeave={e => {
if (!disabled && !isActive) {
(e.currentTarget as HTMLElement).style.color = 'var(--contra-text-primary)';
}
}}
>
{children}
</a>
);
}
```
---
### 6.3 Card (Project / Feature)
**Anatomy:** `<article>` → thumbnail image → content block → title + meta
**Token mappings:**
| Property | Token | Value |
|---|---|---|
| Background | `--contra-surface-white` | `rgb(255,255,255)` |
| Border radius | `--contra-radius-sm` | `10px` |
| Shadow | `--contra-shadow-sm` | see token |
| Border | `--contra-border-default` | `rgb(229,231,235)` |
| Title font | `--type-body-sm` | 14px / 20px / GT Standard L |
| Gap (internal) | `--contra-space-xs` | `12px` |
| Hover transition | `--contra-duration-fast` | `0.17s ease` |
```tsx
interface CardProps {
imageUrl: string;
title: string;
creatorName: string;
creatorAvatarUrl: string;
}
export function ProjectCard({ imageUrl, title, creatorName, creatorAvatarUrl }: CardProps) {
return (
<article
style={{
backgroundColor: 'var(--contra-surface-white)',
borderRadius: 'var(--contra-radius-sm)', /* 10px */
border: '1px solid var(--contra-border-default)',
boxShadow: 'var(--contra-shadow-sm)',
overflow: 'hidden',
display: 'flex',
flexDirection: 'column',
gap: 'var(--contra-space-xs)', /* 12px */
transition: `box-shadow var(--contra-duration-fast) var(--contra-ease-default),
transform var(--contra-duration-fast) var(--contra-ease-default)`,
cursor: 'pointer',
}}
onMouseEnter={e => {
const el = e.currentTarget as HTMLElement;
el.style.transform = 'translateY(-2px)';
el.style.boxShadow = 'rgba(34,40,52,0.10) 0px 8px 20px 0px';
}}
onMouseLeave={e => {
const el = e.currentTarget as HTMLElement;
el.style.transform = 'translateY(0)';
el.style.boxShadow = 'var(--contra-shadow-sm)';
}}
>
<img src={imageUrl} alt={title} style={{ width: '100%', display: 'block', aspectRatio: '16/9', objectFit: 'cover' }} />
<div style={{ padding: `0 var(--contra-space-xs) var(--contra-space-xs)` }}>
<p style={{
fontFamily: 'var(--contra-font-display)',
fontSize: 'var(--contra-font-size-sm)', /* 14px */
fontWeight: 400,
lineHeight: '20px',
color: 'var(--contra-text-secondary)',
margin: '0 0 var(--contra-space-xs)',
}}>
{title}
</p>
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--contra-space-xs)' }}>
<img
src={creatorAvatarUrl}
alt={creatorName}
style={{
width: '24px', height: '24px',
borderRadius: 'var(--contra-radius-full)', /* 50% */
objectFit: 'cover',
}}
/>
<span style={{
fontFamily: 'var(--contra-font-body)',
fontSize: 'var(--contra-font-size-xs)', /* 12px */
fontWeight: 400,
lineHeight: '16px',
color: 'var(--contra-text-secondary)',
}}>
{creatorName}
</span>
</div>
</div>
</article>
);
}
```
---
### 6.4 Input Field
**Token mappings:**
| State | Border | Background | Text |
|---|---|---|---|
| Default | `1px solid --contra-border-default` | `--contra-surface-white` | `--contra-text-primary` |
| Hover | `1px solid --contra-text-secondary` | `--contra-surface-white` | `--contra-text-primary` |
| Focus | `2px solid --contra-accent` | `--contra-surface-white` | `--contra-text-primary` |
| Disabled | `1px solid --contra-border-default` | `--contra-bg-surface` | `--contra-text-secondary` |
| Error | `2px solid --contra-accent-warm` | `--contra-surface-white` | `--contra-text-primary` |
```tsx
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
hasError?: boolean;
errorMessage?: string;
label?: string;
}
export function Input({ hasError, errorMessage, label, id, disabled, ...props }: InputProps) {
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--contra-space-xs)' }}>
{label && (
<label htmlFor={id} style={{
fontFamily: 'var(--contra-font-body)',
fontSize: 'var(--contra-font-size-xs)', /* 12px */
fontWeight: 500,
lineHeight: '16px',
letterSpacing: '0.6px',
textTransform: 'uppercase',
color: 'var(--contra-text-secondary)',
}}>
{label}
</label>
)}
<input
id={id}
disabled={disabled}
aria-invalid={hasError}
aria-describedby={hasError && errorMessage ? `${id}-error` : undefined}
style={{
fontFamily: 'var(--contra-font-body)',
fontSize: 'var(--contra-font-size-lg)', /* 16px */
fontWeight: 400,
lineHeight: '18.4px',
color: disabled ? 'var(--contra-text-secondary)' : 'var(--contra-text-primary)',
backgroundColor: disabled ? 'var(--contra-bg-surface)' : 'var(--contra-surface-white)',
border: `1px solid ${hasError ? 'var(--contra-accent-warm)' : 'var(--contra-border-default)'}`,
borderRadius: 'var(--contra-radius-sm)', /* 10px */
padding: '8px var(--contra-space-xs)',
outline: 'none',
width: '100%',
transition: `border-color var(--contra-duration-fast) var(--contra-ease-default)`,
cursor: disabled ? 'not-allowed' : 'text',
}}
{...props}
/>
{hasError && errorMessage && (
<span id={`${id}-error`} role="alert" style={{
fontFamily: 'var(--contra-font-body)',
fontSize: 'var(--contra-font-size-xs)',
fontWeight: 400,
lineHeight: '16px',
color: 'var(--contra-accent-warm)', /* rgb(240,68,56) */
}}>
{errorMessage}
</span>
)}
</div>
);
}
```
---
### 6.5 Pill Tag / Category Chip
**Anatomy:** `<span>` or `<button>` → text label, optionally leading icon
```tsx
interface TagProps {
label: string;
isInteractive?: boolean;
isSelected?: boolean;
}
export function Tag({ label, isInteractive, isSelected }: TagProps) {
const Tag = isInteractive ? 'button' : 'span';
return (
<Tag
style={{
fontFamily: 'var(--contra-font-display)',
fontSize: 'var(--contra-font-size-sm)', /* 14px */
fontWeight: 400,
lineHeight: '20px',
color: isSelected ? 'var(--contra-text-on-dark)' : 'var(--contra-text-secondary)',
backgroundColor: isSelected ? 'var(--contra-accent-hover)' : 'var(--contra-surface-white)',
border: `1px solid ${isSelected ? 'transparent' : 'var(--contra-border-default)'}`,
borderRadius: 'var(--contra-radius-sm)', /* 10px */
padding: '6px var(--contra-space-xs)', /* 6px 12px */
cursor: isInteractive ? 'pointer' : 'default',
display: 'inline-flex',
alignItems: 'center',
transition: `background-color var(--contra-duration-fast) var(--contra-ease-default),
color var(--contra-duration-fast) var(--contra-ease-default)`,
userSelect: 'none',
}}
>
{label}
</Tag>
);
}
```
---
## 7. Elevation & Depth
```css
:root {
/* Shadow tokens */
--contra-shadow-sm: rgba(34, 40, 52, 0.05) 0px 4px 10px 0px,
rgba(34, 40, 52, 0.04) -2px 18px 18px 0px;
/* extracted: high confidence — used on cards and floating elements */
/* Reconstructed shadows — moderate confidence, inferred from scale */
--contra-shadow-md: rgba(34, 40, 52, 0.08) 0px 8px 20px 0px,
rgba(34, 40, 52, 0.06) -2px 24px 24px 0px;
/* reconstructed: moderate confidence — for modals, dropdowns */
--contra-shadow-none: none; /* flat surfaces — page bg, nav */
/* Border tokens */
--contra-border-default: rgb(229, 231, 235); /* 1px solid — cards, inputs, secondary buttons */
--contra-border-width: 1px; /* standard border width */
/* Z-index scale — reconstructed: moderate confidence */
--contra-z-base: 0; /* normal document flow */
--contra-z-above: 10; /* cards on hover */
--contra-z-dropdown: 100; /* menus, popovers */
--contra-z-modal: 400; /* modals, drawers */
--contra-z-toast: 500; /* notifications */
--contra-z-tooltip: 600; /* tooltips */
}
```
### Layering Principles
- **Flat surfaces** (page bg, section bg tiles) use `--contra-shadow-none`
- **Interactive cards** gain `--contra-shadow-sm` at rest; intensity increases on hover
- **Modals** use a full-viewport overlay at `z-index: --contra-z-modal` — **modal `border-radius` is `0px`** (confirmed from computed styles, despite other pill shapes in the design)
- The shadow uses `rgb(34, 40, 52)` as its shadow colour — the same dark navy as the primary CTA. This ensures tonal consistency.
---
## 8. Motion
```css
:root {
/* Duration tokens — extracted: high confidence */
--contra-duration-fast: 0.17s; /* micro-interactions: button bg, colour changes */
--contra-duration-base: 0.3s; /* standard transitions: menu open, hover reveals */
--contra-duration-slow: 0.32s; /* large surface transitions: modal enter, page slides */
/* Easing tokens — extracted: high confidence */
--contra-ease-default: ease; /* CSS default ease — covers 139 elements */
/* Reconstructed easing — moderate confidence */
--contra-ease-enter: ease-out; /* elements entering the viewport */
--contra-ease-exit: ease-in; /* elements leaving the viewport */
}
```
### Motion Usage Rules
| Trigger | Duration | Easing | Token |
|---|---|---|---|
| Button background change | `0.17s` | `ease` | `--contra-duration-fast` |
| Nav link colour change | `0.17s` | `ease` | `--contra-duration-fast` |
| Card hover lift | `0.17s` | `ease` | `--contra-duration-fast` |
| Menu / dropdown open | `0.3s` | `ease-out` | `--contra-duration-base` |
| Modal enter | `0.32s` | `ease-out` | `--contra-duration-slow` |
| Modal exit | `0.32s` | `ease-in` | `--contra-duration-slow` |
### When NOT to Animate
- Do **not** animate `width`, `height`, or `layout` properties — use `transform` and `opacity` only
- Do **not** apply `transition: all` in production components (it is present in extracted computed styles as a browser default, not a design intention)
- Do **not** animate colour changes on body text — only interactive elements
- Respect `prefers-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. **Hardcoded colour literals → Why it fails → What to do instead.**
AI agents default to writing `color: rgb(34, 40, 52)` inline when they see a dark colour in the brief. This creates a maintenance failure: the same navy value is used for CTA backgrounds, shadow colours, and link text — but updating one usage breaks all others. **Always reference `var(--contra-accent-hover)` (CTA bg), `var(--contra-text-primary)` (text), or `var(--contra-shadow-sm)` (shadow) by semantic token.**
2. **Using Inter, Roboto, or system-ui as the primary font → Why it fails → What to do instead.**
AI agents default to `font-family: 'Inter', sans-serif` because it is the most common design-system font in training data. Contra uses **GT Standard L** (display/body) and **GT Standard M** (UI). Using a fallback font changes letter-spacing, x-height, and optical kerning — the -0.58px letter-spacing on the hero h1 is tuned specifically for GT Standard L. **Always use `var(--contra-font-display)` or `var(--contra-font-body)` and never override with a generic.**
3. **Applying `border-radius: 8px` to buttons → Why it fails → What to do instead.**
Agents trained on Material Design or generic component libraries default to 8px radius. Contra uses **24px (`--contra-radius-md`)** for standard buttons — a near-pill shape. Using 8px produces a boxy button that is visually alien to the brand. **Always use `var(--contra-radius-md)` for `<button>` elements.**
4. **Rounding `--contra-space-md: 26px` down to `24px` → Why it fails → What to do instead.**
AI agents notice `26px` is off the 4px grid and "helpfully" round it to `24px`. This collapses two distinct spacing tokens into one, losing intentional rhythm. **Use the token `var(--contra-space-md)` as-is. If introducing new spacing, use 4px-grid values — but never silently alter an existing token value.**
5. **Dynamic Tailwind class construction → Why it fails → What to do instead.**
Writing `` className={`bg-${colorVar}`} `` or `` `text-${sizeToken}` `` causes Tailwind's PurgeCSS to never include that class in the build — the style silently fails in production. **Use static class names or `style={{ backgroundColor: 'var(--contra-accent-hover)' }}` with CSS variables directly.**
6. **Missing interactive states → Why it fails → What to do instead.**
AI agents frequently implement only `default` and `hover` states. Contra's button census shows 161 button instances — without `focus`, `active`, `disabled`, and `loading` states, keyboard navigation breaks and forms become unusable. **Every interactive component MUST implement all 6 states as documented in Section 6.**
7. **Using `transition: all` in component code → Why it fails → What to do instead.**
The `transition: all` found in computed styles is a browser default, not a design token. Using it in authored CSS causes unexpected layout thrashing when any CSS property changes (including `width`, `height`, or `grid` properties). **Always specify the exact property: `transition: background-color var(--contra-duration-fast) var(--contra-ease-default)`.**
8. **Treating decorative surface colours as text backgrounds → Why it fails → What to do instead.**
`--contra-bg-app: rgb(240, 251, 150)` (lime-yellow) and `--contra-bg-surface: rgb(214, 233, 235)` (teal) have **no contrast guarantee** for dark text — they are decorative tile backgrounds, not text-safe surfaces. Placing high-density body text on them will fail WCAG AA. **Use these only for large decorative surfaces, never for small text containers.**
9. **Using `position: absolute` for component layout → Why it fails → What to do instead.**
Contra's component layout is entirely flexbox-based (confirmed from all computed styles: `display: flex`, `flex-direction: column/row`). Reaching for `position: absolute` to align elements within a card or nav item breaks the flex flow, causing misalignment at different viewport sizes. **Use `display: flex` with `gap: var(--contra-space-xs)` as the primary layout mechanism.**
10. **Giving modals `border-radius` matching the button/card system → Why it fails → What to do instead.**
AI agents see `--contra-radius-md: 24px` and apply it to modals because it's the "brand radius." The computed modal style explicitly shows `border-radius: 0px`. Modals are full-bleed or edge-anchored — pill radius on a modal produces an incoherent floating shape. **Use `border-radius: 0` on modal containers. Reserve rounded radius for small components (cards, buttons, chips).**
11. **Fabricating spacing values between defined tokens → Why it fails → What to do instead.**
Agents interpolate `20px` or `40px` gaps when the defined scale jumps from `24px` to `32px`. This introduces off-system values that break the rhythm, particularly as `24px` and `32px` serve distinct semantic roles (internal card gap vs section padding). **Use only the six defined `--contra-space-*` tokens. If a value doesn't exist in the scale, use the nearest token and document the decision.**
---
## 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 (42) */
--brand-primary-cta: rgb(34, 40, 52); /* Primary CTA background, dominant on 8 buttons — e.g. "Sign up" /* mined from computed styles */ */
--brand-surface-2: rgb(214, 233, 235); /* Brand surface, dominant on 2 elements — e.g. "div" /* mined from computed styles */ */
--brand-surface-3: rgb(106, 87, 227); /* Brand surface, dominant on 2 elements — e.g. "div" /* mined from computed styles */ */
--brand-surface-4: rgb(240, 251, 150); /* Brand surface, dominant on 1 element — e.g. "div" /* mined from computed styles */ */
--brand-surface-5: rgb(205, 116, 221); /* Brand surface, dominant on 1 element — e.g. "div" /* mined from computed styles */ */
--brand-surface-6: rgb(69, 25, 47); /* Brand surface, dominant on 1 element — e.g. "div" /* mined from computed styles */ */
--brand-mark-1: rgb(240, 68, 56); /* Brand mark fill on 35 svg shapes — e.g. "path" /* mined from svg fill */ */
--brand-mark-2: rgb(20, 110, 245); /* Brand mark fill on 1 svg shape — e.g. "path" /* mined from svg fill */ */
--contra-bg-surface: rgb(214, 233, 235);
--contra-accent: rgb(106, 87, 227);
--contra-accent-hover: rgb(34, 40, 52);
--contra-text-primary: rgb(20, 23, 31);
--contra-text-secondary: rgb(103, 112, 132);
--contra-bg-app: rgb(240, 251, 150);
--contra-surface-white: rgb(255, 255, 255);
--contra-border-default: rgb(229, 231, 235);
--primitive-navy-900: rgb(20, 23, 31);
--primitive-navy-800: rgb(34, 40, 52);
--primitive-gray-500: rgb(103, 112, 132);
--primitive-gray-200: rgb(229, 231, 235);
--primitive-white: rgb(255, 255, 255);
--primitive-purple: rgb(106, 87, 227);
--primitive-teal: rgb(214, 233, 235);
--primitive-lime: rgb(240, 251, 150);
--primitive-orchid: rgb(205, 116, 221);
--primitive-maroon: rgb(69, 25, 47);
--primitive-red: rgb(240, 68, 56);
--primitive-blue: rgb(20, 110, 245);
--contra-text-on-dark: rgb(255, 255, 255);
--btn-primary-bg: var(--contra-accent-hover);
--btn-primary-bg-hover: var(--contra-accent);
--btn-secondary-bg: var(--contra-surface-white);
--btn-secondary-border: var(--contra-border-default);
--nav-bg: transparent;
--card-bg: var(--contra-surface-white);
--card-border: var(--contra-border-default);
--modal-bg: var(--contra-surface-white);
--contra-border-width: 1px;
--contra-accent-orchid: rgb(205, 116, 221);
--contra-accent-warm: rgb(240, 68, 56);
--contra-accent-cool: rgb(20, 110, 245);
--contra-surface-maroon: rgb(69, 25, 47);
/* Typography (23) */
--font-size-xs: 12px; /* 24 elements — e.g. h2 "TRENDING TOPICS", h3 "For independents", h3 "For companies" /* mined from computed styles */ */
--font-size-sm: 14px; /* 54 elements — e.g. p "Beauty Focused Brand", p "Premium Launch Video", p "Brand and web design" /* mined from computed styles */ */
--font-size-md: 15px; /* 13 elements — e.g. p "$100k in prizes for ", p "Stay current, speak ", p "Launch apps, not pro" /* mined from computed styles */ */
--font-size-lg: 16px; /* 79 elements — e.g. p "Hand-picked independ", p "Framer is the leadin", span "Sign up" /* mined from computed styles */ */
--font-size-xl: 19px; /* 3 elements — e.g. p "The network for crea", a "Projects", a "People" /* mined from computed styles */ */
--font-size-2xl: 23px; /* 8 elements — e.g. h1 "Projects we love", h1 "Web design projects ", h1 "AI projects" /* mined from computed styles */ */
--font-size-3xl: 58px; /* 1 element — e.g. h1 "Get more creative" /* mined from computed styles */ */
--font-weight-regular: 400; /* 83 elements — e.g. p "The network for crea", p "$100k in prizes for ", p "Stay current, speak " /* mined from computed styles */ */
--font-weight-medium: 500; /* 77 elements — e.g. h1 "Get more creative", h1 "Projects we love", h1 "Web design projects " /* mined from computed styles */ */
--font-weight-semibold: 600; /* 22 elements — e.g. span "Sign up", a "Payments", a "Partnerships" /* mined from computed styles */ */
--line-height-normal: 20px; /* 54 elements — e.g. p "Beauty Focused Brand", p "Premium Launch Video", p "Brand and web design" /* mined from computed styles */ */
--line-height-loose: 24px; /* 40 elements — e.g. p "$100k in prizes for ", p "Stay current, speak ", p "Launch apps, not pro" /* mined from computed styles */ */
--contra-font-display: "GT Standard L", Arial, sans-serif;
--contra-font-body: "GT Standard M", Arial, sans-serif;
--contra-font-size-3xl: 58px;
--contra-font-size-2xl: 23px;
--contra-font-size-xl: 19px;
--contra-font-size-lg: 16px;
--contra-font-size-sm: 14px;
--contra-font-size-xs: 12px;
--btn-primary-text: var(--contra-text-on-dark);
--btn-secondary-text: var(--contra-text-secondary);
--nav-text: var(--contra-text-primary);
/* Spacing (18) */
--space-xs: 12px; /* 3 elements — e.g. header .bYPztT, header .bYPztT, header .bYPztT /* mined from computed styles */ */
--space-sm: 24px; /* 3 elements — e.g. section .fPSBzf, section .fPSBzf, section .fPSBzf /* mined from computed styles */ */
--space-md: 26px; /* 2 elements — e.g. section .llPQEV, section .llPQEV /* mined from computed styles */ */
--space-lg: 32px; /* 1 element — e.g. section .fPSBzf /* mined from computed styles */ */
--space-xl: 36px; /* 2 elements — e.g. section .llPQEV, section .llPQEV /* mined from computed styles */ */
--space-2xl: 48px; /* 3 elements — e.g. nav .fpzYy, nav .fpzYy, nav .fpzYy /* mined from computed styles */ */
--contra-space-xs: 12px;
--contra-space-sm: 24px;
--contra-space-md: 26px;
--contra-space-lg: 32px;
--contra-space-xl: 36px;
--contra-space-2xl: 48px;
--modal-padding: var(--contra-space-lg) var(--contra-space-sm);
--modal-gap: var(--contra-space-sm);
--contra-container-max: 1280px;
--contra-container-padding: var(--contra-space-sm);
--contra-grid-gap: var(--contra-space-sm);
--contra-section-gap: var(--contra-space-2xl);
/* Radius (10) */
--radius-sm: 10px; /* 5 elements — e.g. a .fyrDQq "ChallengesKickstart ", a .fyrDQq "Expert networksFuel ", a .fyrDQq "Creative Human DataF" /* mined from computed styles */ */
--radius-md: 24px; /* 5 elements — e.g. button .hLtpET "Sign up", button .hLtpET "Sign up", button .hLtpET "Message" /* mined from computed styles */ */
--radius-lg: 32px; /* 1 element — e.g. button .bJhMbU "Featured" /* mined from computed styles */ */
--radius-full: 50%; /* 7 elements — e.g. button .gGTyfw "47", button .gGTyfw "34", div .eTJcRZ /* mined from computed styles */ */
--contra-radius-sm: 10px;
--contra-radius-md: 24px;
--contra-radius-lg: 32px;
--contra-radius-full: 50%;
--card-radius: var(--contra-radius-sm);
--modal-radius: 0px;
/* Effects (5) */
--shadow-sm: rgba(34, 40, 52, 0.05) 0px 4px 10px 0px, rgba(34, 40, 52, 0.04) -2px 18px 18px 0px; /* 2 elements — e.g. button .gGTyfw, button .gGTyfw /* mined from computed styles */ */
--contra-shadow-sm: rgba(34,40,52,0.05) 0px 4px 10px 0px, rgba(34,40,52,0.04) -2px 18px 18px 0px;
--card-shadow: var(--contra-shadow-sm);
--contra-shadow-md: rgba(34,40,52,0.08) 0px 8px 20px 0px, rgba(34,40,52,0.06) -2px 24px 24px 0px;
--contra-shadow-none: none;
/* Motion (4) */
--duration-fast: 0.17s; /* 32 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-base: 0.3s; /* 12 elements — e.g. button, a, a /* mined from computed styles */ */
--duration-slow: 0.32s; /* 5 elements — e.g. button, button, button /* mined from computed styles */ */
--ease-default: ease; /* 139 elements — e.g. button, button, button /* mined from computed styles */ */
```
## Appendix B: Token Source Metadata
```yaml
tokenSource: reconstructed-from-computed
extractionTarget: contra.com
confidenceOverall: low-to-medium
nativeCSSCustomProperties: 0
syntheticTokensCreated: 34 (curated) + 8 (additional primitives)
clusteringMethod:
colours: grouped by hue family (neutral-navy, neutral-gray, brand-purple, brand-teal,
brand-lime, brand-orchid, brand-maroon, brand-warm-red, brand-cool-blue)
then mapped to semantic roles: surface / text / action / decorative
spacing: identified distinct values (12, 24, 26, 32, 36, 48px), compared to 4px grid.
Note: 26px is one off-grid outlier; retained as legacy token, not rounded.
radius: identified 4 distinct values: 10px (cards), 24px (buttons), 32px (featured),
50% (avatars). Confirmed pill-shaped buttons from button census (borderRadius:100px
on button_primary → mapped to --contra-radius-md at 24px — pill intent confirmed).
typography: clustered by role (display=h1, eyebrow=h2/h3, body-lead=body, ui=button/nav)
and mapped to composite groups (never isolated properties)
tokenNamingStrategy:
- Curated set uses --contra-* prefix (synthesised semantic names)
- Original mined names documented as aliases (brand-primary-cta, brand-surface-*, etc.)
- Semantic names take priority over appearance names in all documentation
warnings:
- Modal border-radius is 0px — CONTRADICTS the pill-button design system. Do not apply
rounded radius to modals. This is confirmed from computed styles, not inferred.
- GT Standard L / GT Standard M are custom web fonts. Verify font loading is present
in the host environment before generating components; Arial fallback will degrade
letter-spacing precision on display text.
- --contra-space-md (26px) is off the 4px grid. Do not use as a new spacing value.
Use only for compatibility with existing extracted layout measurements.
- Colour confidence is variable: purple/teal (high), lime/orchid/maroon (low).
Low-confidence surface colours appear as decorative tiles; validate before using
as interactive state colours.
- h2 and h3 both render at 12px 500 weight. Semantic hierarchy is differentiated ONLY
by letter-spacing and font-family (GT Standard L vs M). Preserve both distinctions.
figmaAuthority: false # tokens are reverse-engineered, not from Figma source of truth
tailwindVersion: n/a # not a utility-class-based system
```More from the gallery
Browse all kits →You may also like

DoorDash
MITClean, accessible food-delivery system with DoorDash's signature red accent, light neutral palette, and system-font typography—built for rapid product development
03
lightecommsaasmobile

Mercedes-Benz
MITLuxe, minimalist design system with sharp black-and-white contrast and serif typography, crafted for premium automotive and lifestyle brands
05
darkminimalboldecomm

Wise
MITClean, accessible financial design system with lime-green accents and Swiss typography, built for fintech products that prioritise clarity and trust
05
lightfintechminimalsaas