Fey
MIT
A dark-first design system with custom typefaces and refined glassmorphic surfaces, built for sophisticated SaaS and fintech products
Layout StudioImport this kit into a Studio project and start editing.
CLI installRun it in any project. No account needed.
npx @layoutdesign/context install feyUI themeReskin shadcn or Layout UI components to match Fey.
npx shadcn add https://layout.design/r/fey/theme.json# layout.md — Fey Design System
---
## 0. Quick Reference
> Standalone — copy-paste into `CLAUDE.md` or `.cursorrules`.
> Use as `var(--token-name)` in CSS, `style={{ prop: 'var(--token-name)' }}` in JSX, or `bg-[var(--token-name)]` in Tailwind.
**Stack:** Next.js · CSS custom properties (47 extracted, high confidence) · Styled-components (hashed class names)
**Theme:** Dark-first · Custom typeface (Calibre + Financier) · No Tailwind · Token source: `extracted-css-vars`
```css
/* ── CORE TOKENS ── */
:root {
/* Colours */
--fey-info: hsl(208, 100%, 97%);
--fey-error: hsl(359, 100%, 97%);
--fey-accent: rgb(255, 161, 108);
--fey-bg-app: --normal-bg;
--fey-border: var(--gray4);
--fey-success: hsl(143, 85%, 96%);
--fey-warning: hsl(49, 100%, 97%);
--fey-bg-hover: hsl(0, 0%, 12%);
--fey-bg-surface: linear-gradient(87.11deg, rgb(255, 255, 255) 17.87%, rgba(255, 255, 255, 0.9) 45.18%, rgba(255, 255, 255, 0.86) 57.76%);
--fey-text-muted: rgba(255, 255, 255, 0.5);
--fey-accent-cool: rgb(71, 159, 250);
--fey-text-primary: rgb(134, 143, 151);
--fey-border-strong: --normal-border-hover;
/* Other */
--fey-space-lg: 87px;
--fey-space-md: 42px;
--fey-space-sm: 40px;
--fey-space-xl: 98px;
--fey-space-xs: 20px;
--fey-font-sans: 'calibre', 'calibre Fallback', Arial, sans-serif;
--fey-radius-lg: 50%;
--fey-radius-md: 6px;
--fey-radius-sm: 4px;
--fey-shadow-lg: rgba(255, 255, 255, 0.25) 0px 0px 14px 0px;
--fey-shadow-md: rgba(255, 255, 255, 0.5) 0px -2px 4px 0px inset,
rgb(255, 255, 255) 0px 0px 2px 0px inset,
rgba(255, 255, 255, 0.25) 0px 2px 2px 0px inset,
rgba(0, 0, 0, 0.5) 0px 0.5px 1px 0px inset,
rgba(0, 0, 0, 0.4) 0px 2px 8px 0px;
--fey-shadow-sm: rgba(0, 0, 0, 0.85) 0px 1px 0px 0px;
--fey-space-2xl: 128px;
--fey-space-3xl: 152px;
--fey-font-serif: 'financier', 'financier Fallback', Arial, serif;
--fey-radius-full: 99px;
--fey-ease-default: ease;
--fey-font-size-md: 14px;
--fey-font-size-sm: 12px;
--fey-font-size-xl: 18px;
--fey-font-size-xs: 11px;
--fey-duration-base: 0.15s;
--fey-duration-fast: 0.1s;
--fey-duration-slow: 0.25s;
--fey-font-size-2xl: 24px;
--fey-font-size-3xl: 54px;
--fey-font-weight-bold: 700;
--fey-line-height-loose: 33.6px;
--fey-line-height-tight: 21.12px;
--fey-font-weight-medium: 500;
--fey-line-height-normal: 22.4px;
--fey-font-weight-regular: 400;
--fey-font-weight-semibold: 600;
}
```
```tsx
// Primary CTA Button — correct token usage
<button
style={{
background: 'rgba(255,255,255,0.08)',
border: '0.5px solid rgba(255,255,255,0.14)',
borderRadius: 'var(--radius-full)',
color: 'rgb(255,255,255)',
font: '400 16px/1 var(--font-family-sans)',
transition: `background var(--duration-fast) var(--ease-default)`,
}}
onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,255,255,0.12)'}
>
Get started
</button>
```
**NEVER rules:**
- NEVER use `Inter`, `Roboto`, or `Arial` as the primary font — use `calibre`
- NEVER use `border-radius: 8px` on buttons — use `--radius-full` (99px) for pill CTAs, `--radius-md` (6px) for inline buttons
- NEVER hardcode hex/rgb colours — use CSS custom properties
- NEVER apply `outline: none` on focus — use `outline: 2px solid rgba(255,255,171,0.8)`
- NEVER invent spacing values — use `--space-*` tokens only
- NEVER render h1 as plain white text — it requires the gradient `backgroundImage` + `backgroundClip: text`
**Full design system → see `layout.md`**
---
## 1. Design Direction & Philosophy
### Character & Mood
Fey is a **premium financial intelligence product**. The aesthetic is cinematic dark-mode — deep blacks, luminous white typography with gradient fade, subtle translucent surfaces. The mood is confident, data-dense, and sophisticated without being sterile. Think Bloomberg terminal reimagined by a luxury product studio.
### What this design explicitly rejects
- **No warm backgrounds.** The app surface is near-black. `--normal-bg: #fff` is a system token for toast/alert components, not the page canvas.
- **No rounded corners on primary CTAs.** Pill radius (`--radius-full: 99px`) defines the CTA language. Never use `8px` on buttons.
- **No heavy drop shadows.** Shadows are either razor-thin dark lines (`--shadow-sm`) or soft white glows (`--shadow-lg`) — never coloured, never blurred more than 14px.
- **No generic system fonts.** Calibre (geometric sans, custom) is the only permitted body/heading typeface. Financier (serif) is for editorial accents only.
- **No opaque button fills.** Interactive surfaces use `rgba(255,255,255,0.08–0.20)` — translucency is a core brand trait.
- **No garish status colours.** Status backgrounds are near-white pastels; only status text carries saturation.
### Aesthetic principles
1. **Gradient text on hero headings** — h1 fades from pure white → 86% white across a shallow angle (87.11deg). This is structural, not decorative.
2. **Translucent layering** — buttons, cards, and overlays are glass-like: white at 6–25% opacity over the dark canvas.
3. **Sparse colour accents** — the only saturated accent visible in the computed styles is the link blue (`rgb(71, 159, 250)`) and the h3 orange (`rgb(255, 161, 108)`). These are used sparingly.
4. **Typography does the hierarchy work** — size jumps are dramatic (54px → 26px → 18px → 14px). Weight and colour opacity carry secondary hierarchy.
---
## 2. Colour System
### Tier 1 — Primitive Values
```css
/* Gray scale — Radix Gray (light mode reference, used in toast/alert components) */
--gray1: hsl(0, 0%, 99%); /* near-white */
--gray2: hsl(0, 0%, 97.3%);
--gray3: hsl(0, 0%, 95.1%);
--gray4: hsl(0, 0%, 93%); /* default border colour */
--gray5: hsl(0, 0%, 90.9%);
--gray6: hsl(0, 0%, 88.7%);
--gray7: hsl(0, 0%, 85.8%);
--gray8: hsl(0, 0%, 78%);
--gray9: hsl(0, 0%, 56.1%);
--gray10: hsl(0, 0%, 52.3%);
--gray11: hsl(0, 0%, 43.5%);
--gray12: hsl(0, 0%, 9%); /* near-black, --normal-text alias */
```
### Tier 2 — Semantic Aliases
```css
/* Surfaces */
--normal-bg: #fff; /* light component surfaces (toasts, alerts) */
--normal-bg-hover: hsl(0, 0%, 12%); /* hover state on dark interactive elements */
/* Borders */
--normal-border: var(--gray4); /* = hsl(0,0%,93%) — default component border */
--normal-border-hover: hsl(0, 0%, 25%); /* border on hover */
/* Text */
--normal-text: var(--gray12); /* = hsl(0,0%,9%) — used in light components */
/* Dark canvas (synthesised — no explicit CSS var found) */
/* The page background is near-black; computed styles confirm rgba(0,0,0,0) on body
but the visual canvas is dark. Use rgb(0,0,0) or rgb(10,10,10). */
/* Status — backgrounds */
--success-bg: hsl(143, 85%, 96%);
--warning-bg: hsl(49, 100%, 97%);
--error-bg: hsl(359, 100%, 97%);
--info-bg: hsl(208, 100%, 97%);
/* Status — borders */
--success-border: hsl(145, 92%, 91%);
--warning-border: hsl(49, 91%, 91%);
--error-border: hsl(359, 100%, 94%);
--info-border: hsl(221, 91%, 91%);
/* Status — text */
--success-text: hsl(140, 100%, 27%);
--warning-text: hsl(31, 92%, 45%);
--error-text: hsl(360, 100%, 45%);
--info-text: hsl(210, 92%, 45%);
```
### Tier 3 — Component Tokens
```css
/* Interactive surface glass fills (from computed styles) */
--button-bg-default: rgba(255, 255, 255, 0.08);
--button-bg-hover: rgba(255, 255, 255, 0.12);
--button-bg-hover-strong: rgba(255, 255, 255, 0.20); /* role=button variant */
--button-border-hover: rgba(255, 255, 255, 0.14);
--button-border-hover-alt: rgba(255, 255, 255, 0.24); /* pill CTA variant */
/* Text colours from computed styles */
--text-page-body: rgb(230, 230, 230); /* body text on dark canvas */
--text-muted: rgba(255, 255, 255, 0.5); /* h2 colour */
--text-link: rgb(71, 159, 250); /* anchor elements */
--text-alert-muted: rgb(134, 143, 151); /* alert/secondary text */
--text-accent-warm: rgb(255, 161, 108); /* h3 accent orange */
/* Gradient — hero h1 text */
--gradient-h1: linear-gradient(
87.11deg,
rgb(255, 255, 255) 17.87%,
rgba(255, 255, 255, 0.9) 45.18%,
rgba(255, 255, 255, 0.86) 57.76%
);
```
### Colour palette summary
| Token | Value | Usage |
|---|---|---|
| `--normal-bg` | `#fff` | Alert/toast surfaces |
| `--normal-bg-hover` | `hsl(0,0%,12%)` | Dark hover surface |
| `--gray4` | `hsl(0,0%,93%)` | Default border |
| `--normal-border-hover` | `hsl(0,0%,25%)` | Hover border |
| `--text-page-body` | `rgb(230,230,230)` | Body copy on dark |
| `--text-muted` | `rgba(255,255,255,0.5)` | H2, secondary headings |
| `--text-link` | `rgb(71,159,250)` | All anchor elements |
| `--text-accent-warm` | `rgb(255,161,108)` | H3 section labels |
| `--button-bg-default` | `rgba(255,255,255,0.08)` | Button resting fill |
---
## 3. Typography System
**Primary typeface: `calibre`** (custom woff2, weights 200/400/600, fallback: `Arial`)
**Editorial typeface: `financier`** (custom woff2, weight 500, fallback: `Arial`)
```css
/* Font stacks */
--font-family-sans: 'calibre', 'calibre Fallback', Arial, sans-serif;
--font-family-serif: 'financier', 'financier Fallback', Arial, serif;
```
### Composite Type Scale
```css
/* ── Hero Heading (h1) ── */
/* font-family: var(--font-family-sans) */
/* font-size: 54px (= --font-size-3xl) */
/* font-weight: 600 (= --font-weight-semibold) */
/* line-height: 54px (1:1 — tight) */
/* letter-spacing: normal */
/* color: transparent */
/* background: var(--gradient-h1) */
/* -webkit-background-clip: text; background-clip: text */
/* ── Section Heading (h2) ── */
/* font-family: var(--font-family-sans) */
/* font-size: 26px */
/* font-weight: 600 (= --font-weight-semibold) */
/* line-height: 26px (1:1 — tight) */
/* letter-spacing: normal */
/* color: rgba(255,255,255,0.5) (= --text-muted) */
/* ── Feature Label (h3) ── */
/* font-family: var(--font-family-sans) */
/* font-size: 18px (= --font-size-xl) */
/* font-weight: 700 (= --font-weight-bold) */
/* line-height: 23.4px */
/* letter-spacing: normal */
/* color: rgb(255,161,108) (= --text-accent-warm) */
/* margin-bottom: 6px */
/* ── Body / Default ── */
/* font-family: var(--font-family-sans) */
/* font-size: 14px (= --font-size-md) */
/* font-weight: 600 (= --font-weight-semibold) ← NOTE: body is semibold, not regular */
/* line-height: 19.04px */
/* letter-spacing: normal */
/* color: rgb(230,230,230) (= --text-page-body) */
/* ── Body Regular / UI labels ── */
/* font-family: var(--font-family-sans) */
/* font-size: 16px */
/* font-weight: 400 (= --font-weight-regular) */
/* line-height: 22.4px (= --line-height-normal) */
/* letter-spacing: normal */
/* color: rgb(255,255,255) */
/* ── Caption / Metadata ── */
/* font-family: var(--font-family-sans) */
/* font-size: 12px (= --font-size-sm) */
/* font-weight: 400 */
/* line-height: var(--line-height-tight) = 21.12px */
/* color: --text-alert-muted = rgb(134,143,151) */
/* ── Micro / Tag ── */
/* font-family: var(--font-family-sans) */
/* font-size: 11px (= --font-size-xs) */
/* font-weight: 400 */
/* line-height: var(--line-height-tight) = 21.12px */
```
### Font size tokens
| Token | Value | Usage |
|---|---|---|
| `--font-size-xs` | `11px` | Timestamps, micro labels |
| `--font-size-sm` | `12px` | Ticker symbols, captions |
| `--font-size-md` | `14px` | Body text, article previews |
| `--font-size-xl` | `18px` | Feature section body, h3 |
| `--font-size-2xl` | `24px` | Feature card headings |
| `--font-size-3xl` | `54px` | Hero h1, section h2 (alternate) |
### Weight scale
| Token | Value | Usage |
|---|---|---|
| `--font-weight-regular` | `400` | UI controls, nav links, buttons |
| `--font-weight-medium` | `500` | Financial figures (e.g. `31.54`) |
| `--font-weight-semibold` | `600` | h1, h2, body default |
| `--font-weight-bold` | `700` | h3 feature labels |
### Line-height tokens
| Token | Value | Usage |
|---|---|---|
| `--line-height-tight` | `21.12px` | Footer links, captions |
| `--line-height-normal` | `22.4px` | UI body text, nav labels |
| `--line-height-loose` | `33.6px` | Feature card paragraphs |
> **Critical rule:** Body (`<p>`, `<body>`) defaults to `font-weight: 600` in this system — NOT 400. NEVER assume regular weight for body copy.
---
## 4. Spacing & Layout
Fey's spacing scale is **not a uniform 4px or 8px grid**. It uses named semantic spacing tokens that define section-level rhythm. Token values at 42px, 87px, and 98px are intentionally off common grids — do not round them.
```css
/* ── Spacing scale ── */
--space-xs: 20px; /* tight internal padding, small gaps */
--space-sm: 40px; /* nav padding, section internal spacing */
--space-md: 42px; /* component group separation */
--space-lg: 87px; /* major section vertical padding */
--space-xl: 98px; /* hero/large section vertical spacing */
--space-2xl: 128px; /* between top-level page sections */
--space-3xl: 152px; /* maximum section gap */
/* ── Border radius ── */
--radius-sm: 4px; /* textarea, small form elements */
--radius-md: 6px; /* inline action buttons, tags */
--radius-lg: 50%; /* circular icon buttons */
--radius-full: 99px; /* pill CTA buttons ("Learn more", "Get started") */
/* NOTE: --border-radius: 8px exists in CSS but applies to toast/system components only.
NEVER apply 8px to brand buttons or cards. */
/* ── Navigation layout ── */
/* display: flex; flex-direction: row; justify-content: space-between */
/* padding: 0px 40px (= --space-sm) */
/* margin: 0px 110px (lateral offset for wide viewports) */
/* ── Grid system ── */
/* No CSS grid framework detected. Layout is flex-based at navigation level.
Feature sections use flex-column or grid for card layouts (inferred). */
```
### Breakpoints
Fey has a **dense custom breakpoint set** — 24 breakpoints across 340px–1440px. No Tailwind breakpoint names apply.
| Breakpoint | px | Likely purpose |
|---|---|---|
| xs-phone | 340px | Smallest supported phone |
| phone | 376px | iPhone SE/standard |
| sm-phone | 540px / 580px | Large phone / small fold |
| tablet-sm | 600px / 650px | Small tablet / landscape phone |
| tablet | 720px / 735px / 750px | Tablet portrait |
| tablet-lg | 768px / 769px | iPad standard |
| tablet-xl | 780px / 810px | iPad landscape |
| desktop-sm | 960px / 961px | Small desktop |
| desktop | 1000px / 1024px / 1025px | Standard laptop |
| desktop-lg | 1124px / 1130px / 1200px | Large laptop |
| desktop-xl | 1280px | 13" desktop |
| wide | 1440px | Max-width canvas |
**Container:** Nav uses `margin: 0 110px` at wide viewports. No explicit `max-width` extracted — inferred max ~1200px.
---
## 5. Page Structure & Layout Patterns
> Source: LAYOUT DIGEST. No screenshots available. Rows marked **(inferred)** are derived from component inventory and digest signals, not visually confirmed.
### 5.1 Section Map
| # | Section | Layout type | Approx. height | Key elements |
|---|---|---|---|---|
| 1 | **Navigation / Header** | `flex row, space-between` | ~60–80px | Logo (left), nav links (centre), CTA button (right). `padding: 0 40px`, `margin: 0 110px`. |
| 2 | **Hero** (inferred) | Block / centred column | ~600–700px | h1 gradient text (54px/600), subheadline (18px), pill CTA button (`--radius-full`), product screenshot/animation |
| 3 | **Feature intro — "From overwhelming to…"** (inferred) | Centred column | ~300px | h2 (26px/600), supporting paragraph, possibly secondary CTA |
| 4 | **Feature grid — Highlights, Earnings, Alerts** (inferred) | Flex or grid, 3 columns | ~500px | h3 orange labels (18px/700), 24px feature headings, body copy, product UI illustration |
| 5 | **Live data / Real-time section** (inferred) | Split: text left, UI right | ~400px | h2 "Earnings in real time" (54px), animated data elements, CTA |
| 6 | **Social proof / News feed** (inferred) | Card list or grid | ~400px | News preview cards, ticker symbols (12px), timestamps (11px) |
| 7 | **Pricing / Trial** (inferred) | Centred column | ~300px | "7-day free trial" h3, pill CTA, pricing copy |
| 8 | **Footer** (inferred) | Flex row, space-between | ~120px | Logo, nav links (11px, `--line-height-tight`), legal copy "© 2026, Fey Labs Inc" |
### 5.2 Layout Patterns
**Navigation:**
```
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 var(--space-sm); /* 0 40px */
margin: 0 110px; /* lateral inset at wide viewport */
```
**Feature grid (inferred from 3-column feature labels in computed styles):**
```
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-md); /* ~42px */
```
**Hero section (inferred):**
```
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: var(--space-xl) 0; /* 98px top/bottom */
```
**Section vertical rhythm:** Sections separated by `--space-2xl` (128px) to `--space-3xl` (152px).
### 5.3 Visual Hierarchy
- **H1 at 54px with gradient text** is the unambiguous primary focal point. Nothing competes with it.
- **CTA buttons are `--radius-full` (99px) pill shape**, white or translucent-white fill — the primary conversion element.
- **H3 orange labels (`rgb(255,161,108)`)** act as section eyebrow text, drawing the eye before the main heading.
- **Link colour (`rgb(71,159,250)`)** is the only blue on the page — immediately identifiable as interactive.
- **Whitespace is generous** — `--space-2xl` (128px) between sections creates a slow, editorial scroll pace.
- **Product UI illustrations/animations** sit right of or below their text, giving visual weight to the right side of split sections.
### 5.4 Content Patterns
**Feature card pattern (repeating):**
```
[Orange h3 label — 18px/700]
[Feature heading — 24px/400–700]
[Body paragraph — 18px/400, line-height: --line-height-loose]
[Optional: secondary CTA link]
```
**News/data card pattern:**
```
[Ticker symbol — 12px/400]
[Headline — 14px/400]
[Timestamp — 11px/400, muted colour]
```
**CTA pill pattern (consistent across sections):**
```
[Pill button, --radius-full, rgba(255,255,255,0.08) bg]
[Label: 16px/400, white]
[Hover: rgba(255,255,255,0.12) bg + 0.5px solid rgba(255,255,255,0.14) border]
```
---
## 6. Component Patterns
### 6.1 Button — Primary CTA (Pill)
**Anatomy:** `<button>` → `[label text]`
- `border-radius: var(--radius-full)` — **99px**
- `background: rgba(255,255,255,0.08)`
- `color: rgb(255,255,255)`
- `font: 400 16px/1 var(--font-family-sans)`
- `transition: background var(--duration-fast) var(--ease-default)`
**State table:**
| State | Background | Border | Color | Shadow |
|---|---|---|---|---|
| Default | `rgba(255,255,255,0.08)` | none | `#fff` | none |
| Hover | `rgba(255,255,255,0.12)` | `0.5px solid rgba(255,255,255,0.14)` | `rgb(230,230,230)` | `var(--shadow-sm)` |
| Focus-visible | `rgba(255,255,255,0.15)` | — | `#fff` | `outline: 2px solid rgba(255,255,171,0.8); outline-offset: 3px` |
| Active | `rgba(255,255,255,0.20)` | `0.5px solid rgba(255,255,255,0.24)` | `#fff` | — |
| Disabled | `rgba(255,255,255,0.04)` | none | `rgba(255,255,255,0.3)` | none |
```tsx
// Button — Primary CTA (Pill) — production example
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
children: React.ReactNode;
loading?: boolean;
}
export function ButtonCTA({ children, disabled, loading, ...props }: ButtonProps) {
return (
<button
{...props}
disabled={disabled || loading}
style={{
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
gap: '8px',
padding: '10px 20px',
borderRadius: 'var(--radius-full)', /* 99px — pill shape */
background: 'rgba(255,255,255,0.08)',
border: '0.5px solid transparent',
color: disabled || loading ? 'rgba(255,255,255,0.3)' : 'rgb(255,255,255)',
font: `400 16px/1 var(--font-family-sans)`,
cursor: disabled || loading ? 'not-allowed' : 'pointer',
transition: `background var(--duration-fast) var(--ease-default),
border-color var(--duration-fast) var(--ease-default),
color var(--duration-fast) var(--ease-default)`,
outline: 'none',
}}
onMouseEnter={e => {
if (disabled || loading) return;
const el = e.currentTarget;
el.style.background = 'rgba(255,255,255,0.12)';
el.style.borderColor = 'rgba(255,255,255,0.14)';
el.style.boxShadow = 'rgba(0,0,0,0.85) 0px 1px 0px 0px';
}}
onMouseLeave={e => {
const el = e.currentTarget;
el.style.background = 'rgba(255,255,255,0.08)';
el.style.borderColor = 'transparent';
el.style.boxShadow = 'none';
}}
onFocus={e => {
e.currentTarget.style.outline = '2px solid rgba(255,255,171,0.8)';
e.currentTarget.style.outlineOffset = '3px';
e.currentTarget.style.background = 'rgba(255,255,255,0.15)';
}}
onBlur={e => {
e.currentTarget.style.outline = 'none';
e.currentTarget.style.background = 'rgba(255,255,255,0.08)';
}}
>
{loading ? <span aria-label="Loading…">⟳</span> : children}
</button>
);
}
```
---
### 6.2 Button — Inline Action (Rounded)
**Anatomy:** `<button>` → `[label]`
- `border-radius: var(--radius-md)` — **6px**
- Applied to: "Press release", "Join live call" type labels
| State | Background | Border | Color |
|---|---|---|---|
| Default | `rgba(255,255,255,0.08)` | `0.5px solid rgba(255,255,255,0.14)` | `rgb(255,255,255)` |
| Hover | `rgba(255,255,255,0.12)` | `0.5px solid rgba(255,255,255,0.24)` | `rgb(230,230,230)` |
| Focus-visible | `rgba(255,255,255,0.15)` | — | `outline: 2px solid rgba(255,255,171,0.8)` |
| Disabled | `rgba(255,255,255,0.04)` | `0.5px solid rgba(255,255,255,0.06)` | `rgba(255,255,255,0.3)` |
---
### 6.3 Button — Circular Icon Button
**Anatomy:** `<button>` → `[SVG icon]`
- `border-radius: 50%` (`--radius-lg`)
- `display: grid; place-items: center`
- `background: rgba(255,255,255,0.08)` (primary variant) or `rgba(255,255,255,0.20)` (role=button variant)
- `transition: background var(--duration-fast)`
| State | Background |
|---|---|
| Default (primary) | `rgba(255,255,255,0.08)` |
| Default (strong) | `rgba(255,255,255,0.20)` |
| Hover | `rgba(255,255,255,0.20)` |
| Focus-visible | `background: rgba(255,255,255,0.15)` + `outline: 2px solid rgba(255,255,171,0.8); offset: 3px` |
---
### 6.4 Navigation
**Anatomy:** `<nav>` → `[Logo]` + `[Nav links]` + `[CTA button]`
```css
/* nav layout */
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding: 0 var(--space-sm); /* 0 40px */
margin: 0 110px;
```
**Nav link states:**
| State | Color | Notes |
|---|---|---|
| Default | `rgb(255,255,255)` | 16px/400, `--font-family-sans` |
| Hover | `rgb(255,255,255)` | opacity increase via component class |
| Active/current | `rgb(255,255,255)` | — |
| Focus-visible | `outline: 2px solid rgba(255,255,171,0.8)` | — |
```tsx
// Navigation — production example
export function Nav() {
return (
<nav
role="navigation"
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
padding: '0 var(--space-sm)', /* 0 40px */
margin: '0 110px',
}}
>
<a href="/" aria-label="Fey home" style={{ color: 'rgb(255,255,255)' }}>
{/* Logo SVG */}
</a>
<ul style={{ display: 'flex', gap: 'var(--space-xs)', listStyle: 'none', margin: 0, padding: 0 }}>
{['Features', 'Earnings', 'Pricing'].map(label => (
<li key={label}>
<a
href={`/${label.toLowerCase()}`}
style={{
color: 'rgb(255,255,255)',
font: `400 16px/var(--line-height-normal) var(--font-family-sans)`,
textDecoration: 'none',
transition: `opacity var(--duration-fast) var(--ease-default)`,
outline: 'none',
}}
onFocus={e => { e.currentTarget.style.outline = '2px solid rgba(255,255,171,0.8)'; }}
onBlur={e => { e.currentTarget.style.outline = 'none'; }}
>
{label}
</a>
</li>
))}
</ul>
<ButtonCTA>Get started</ButtonCTA>
</nav>
);
}
```
---
### 6.5 Text Input
**Anatomy:** `<input>` or `<textarea>` → `[label]` + `[input field]`
- `border-radius: var(--radius-sm)` — **4px**
- `font: 14px/1 var(--font-family-sans)`
| State | Outline | Border |
|---|---|---|
| Default | none | `0.5px solid rgba(255,255,255,0.14)` |
| Focus / Active | `outline: none` (reset) | [TBD — extract manually] |
| Error | background: `var(--error-bg)`, border: `var(--error-border)` | — |
> **Note:** `input:focus, textarea:focus` explicitly set `outline: none` — focus styling relies on border or custom CSS ring, not browser default outline.
---
### 6.6 Toast / Alert
**Anatomy:** `[icon]` + `[message]` + `[close button]`
| Variant | Background | Border | Text |
|---|---|---|---|
| Success | `var(--success-bg)` | `var(--success-border)` | `var(--success-text)` |
| Warning | `var(--warning-bg)` | `var(--warning-border)` | `var(--warning-text)` |
| Error | `var(--error-bg)` | `var(--error-border)` | `var(--error-text)` |
| Info | `var(--info-bg)` | `var(--info-border)` | `var(--info-text)` |
- Uses Sonner toast library internals (`data-sonner-toast`)
- Focus: `box-shadow: rgba(0,0,0,0.1) 0px 4px 12px, rgba(0,0,0,0.2) 0px 0px 0px 2px`
- Icon margin: `--toast-icon-margin-start: -3px`, `--toast-icon-margin-end: 4px`
---
### 6.7 H1 Gradient Text
**This is a structural pattern, not decoration.** All hero h1 elements MUST use gradient text rendering.
```tsx
<h1
style={{
fontFamily: 'var(--font-family-sans)',
fontSize: 'var(--font-size-3xl)', /* 54px */
fontWeight: 'var(--font-weight-semibold)', /* 600 */
lineHeight: '54px',
letterSpacing: 'normal',
backgroundImage: 'var(--gradient-h1)',
WebkitBackgroundClip: 'text',
backgroundClip: 'text',
color: 'transparent',
margin: 0,
}}
>
Make better investments
</h1>
```
---
## 7. Elevation & Depth
```css
/* ── Shadow tokens ── */
--shadow-sm: rgba(0, 0, 0, 0.85) 0px 1px 0px 0px;
/* Usage: bottom edge line on buttons at hover — creates a pressed-into-surface effect */
--shadow-md: rgba(255, 255, 255, 0.5) 0px -2px 4px 0px inset,
rgb(255, 255, 255) 0px 0px 2px 0px inset,
rgba(255, 255, 255, 0.25) 0px 2px 2px 0px inset,
rgba(0, 0, 0, 0.5) 0px 0.5px 1px 0px inset,
rgba(0, 0, 0, 0.4) 0px 2px 8px 0px;
/* Usage: glass panels, elevated card surfaces — inner white rim + outer dark drop */
--shadow-lg: rgba(255, 255, 255, 0.25) 0px 0px 14px 0px;
/* Usage: floating elements, glowing product UI overlays — soft white ambient glow */
```
### Z-index scale (inferred — no explicit z-index CSS vars extracted)
| Layer | z-index | Usage |
|---|---|---|
| Base | `0` | Page content, sections |
| Sticky nav | `10` | Navigation header |
| Overlays | `50` | Modals, drawers |
| Toast | `100` | Sonner toast notifications (top-most) |
### Layering principles
- **Glass layering:** Translucent surfaces (`rgba(255,255,255,0.06–0.25)`) stack against the dark canvas. Avoid fully opaque fills in interactive components.
- **Shadow direction:** `--shadow-sm` is always a 1px bottom line (not a spread). NEVER use a blurred drop shadow on buttons.
- **Glow = elevation:** `--shadow-lg` (white glow) signals a floating/elevated component. Used on product UI illustrations, not interactive controls.
---
## 8. Motion
```css
/* ── Duration tokens ── */
--duration-fast: 0.1s; /* button background transitions */
--duration-base: 0.15s; /* standard UI transitions (toggles, dropdowns) */
--duration-slow: 0.25s; /* page-level, link, and decorative transitions */
/* ── Easing tokens ── */
--ease-default: ease; /* 116 elements — most UI transitions */
--ease-in: cubic-bezier(0.55, 0.085, 0.68, 0.53); /* exit animations */
--ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94); /* entrance animations */
--ease-in-out: cubic-bezier(0.455, 0.03, 0.515, 0.955); /* bidirectional (modals, drawers) */
```
### Named animations
```css
/* Fade in/out — used for UI element entrances */
@keyframes fadein { from { opacity: 0; } to { opacity: 1; } }
@keyframes fadeout { from { opacity: 1; } to { opacity: 0; } }
@keyframes fadeinout { 0% { opacity:0; } 50% { opacity:1; } 100% { opacity:0; } }
/* Audio waveform bars (product UI decoration) */
/* Multiple scaleY keyframes: cOLSAX, iVkkDZ, cGfXEy, ehACCn, hfYfcK, jWUnqV, jQXhcc */
/* Each bar oscillates between scaleY(0.25) and scaleY(0.5–1.3) */
/* Spinner */
@keyframes round { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
/* Wipe reveal (product screenshots) */
@keyframes wipeAnimation {
from { --mask-position: -100%; }
to { --mask-position: 120%; }
}
/* Toast entrance/exit (Sonner) */
@keyframes sonner-fade-in { from { opacity:0; transform:scale(0.8); } to { opacity:1; transform:scale(1); } }
@keyframes sonner-fade-out { from { opacity:1; transform:scale(1); } to { opacity:0; transform:scale(0.8); } }
/* Swipe-to-dismiss (Sonner) */
/* swipe-out-left, swipe-out-right, swipe-out-up, swipe-out-down */
```
### Motion rules
- **Button hover:** `transition: background var(--duration-fast) var(--ease-default)` — background only, never transform
- **Link hover arrow nudge:** `transform: translateX(2px)` on SVG arrow icon
- **Nav hover:** opacity change, `var(--duration-fast)`
- **Blur effect on card hover:** `filter: blur(6px)` applied to card overlay on hover
- NEVER animate `width`, `height`, or `layout` properties — use `opacity` and `transform` only
---
## 9. Anti-Patterns & Constraints
1. **NEVER use `Inter`, `Roboto`, or system fonts as primary typeface → Why it fails:** AI agents default to Inter when no font context is given. Fey's entire typographic character depends on Calibre — its geometric warmth at large sizes and its narrow metrics at small sizes are non-negotiable. Substituting Inter produces a generic SaaS aesthetic that directly contradicts Fey's premium positioning. **What to do instead:** Always declare `font-family: 'calibre', 'calibre Fallback', Arial, sans-serif` and ensure the `@font-face` declarations are loaded from `/_next/static/media/`.
2. **NEVER apply `border-radius: 8px` to brand buttons → Why it fails:** AI agents see `--border-radius: 8px` in the CSS variable list and apply it to buttons. This token belongs to toast/system components only. Fey's CTAs are `--radius-full` (99px pill) and inline action buttons are `--radius-md` (6px). Using 8px produces a "default SaaS button" look and breaks the brand's distinctive pill language. **What to do instead:** Check element type — pill CTA → `--radius-full`, inline action → `--radius-md`, icon button → `50%`, form inputs → `--radius-sm`.
3. **NEVER render h1 as solid white text → Why it fails:** The computed colour of h1 is `rgba(255,255,255,0.047)` — near-transparent. The visual text comes entirely from the gradient background clip. An AI generating `color: white` on h1 produces invisible or incorrectly rendered text. **What to do instead:** Always apply `background-image: var(--gradient-h1); -webkit-background-clip: text; background-clip: text; color: transparent` to h1 elements.
4. **NEVER assume body text is `font-weight: 400` → Why it fails:** The body computed style is `font-weight: 600` (semibold). AI agents assume regular weight for body copy universally. Using 400 makes body text appear visually lighter than the design intent, breaking the dense, confident data-product aesthetic. **What to do instead:** Explicitly set `font-weight: var(--font-weight-semibold)` (600) for body paragraphs. Use 400 only for UI controls, buttons, and nav items.
5. **NEVER use opaque fills for interactive button backgrounds → Why it fails:** AI agents generating dark-mode buttons often use solid dark fills (e.g. `background: #333`). Fey's entire interactive layer is translucent — `rgba(255,255,255,0.08)`. Opaque fills block the dark canvas texture and eliminate the glass depth effect that defines the brand's visual language. **What to do instead:** Use `rgba(255,255,255,0.08)` default, `rgba(255,255,255,0.12)` hover, `rgba(255,255,255,0.20)` active — never solid fills.
6. **NEVER construct focus styles using `outline: none` alone → Why it fails:** The CSS explicitly resets `outline: none` on `input:focus` — but this is a component-level reset, not a global rule. AI agents reading this apply it globally, removing all keyboard navigation affordance. Fey's interactive elements use `outline: 2px solid rgba(255,255,171,0.8); outline-offset: 3px` — a warm yellow glow. **What to do instead:** Apply the yellow outline on all focusable elements except inputs (which use a custom border ring instead).
7. **NEVER invent spacing values outside the `--space-*` scale → Why it fails:** Fey's spacing tokens do not follow a 4px or 8px grid. Using arbitrary values like `padding: 24px 16px` breaks the visual rhythm that `--space-xs` (20px) through `--space-3xl` (152px) establish. AI agents picking "reasonable" values from conventional grids produce layouts that feel compressed or airy compared to the actual product. **What to do instead:** Map all spacing to `var(--space-xs)` through `var(--space-3xl)`. For micro-spacing within components (e.g. icon gaps), use `4px`, `6px`, `8px` explicitly — these are intra-component, not section-level.
8. **NEVER use dynamic Tailwind class construction for state variants → Why it fails:** Fey uses styled-components with hashed class names — not Tailwind. Generating `className={`bg-[rgba(255,255,255,${opacity})]`}` produces classes that are never in a stylesheet and silently fail. **What to do instead:** Use inline styles for dynamic values, CSS custom property transitions, or stable Tailwind utility classes if Tailwind is introduced. Prefer `style={{ background: 'rgba(255,255,255,0.08)' }}` for one-off values.
9. **NEVER apply `position: absolute` to solve layout structure problems → Why it fails:** The nav and section layouts are flex-based (`justify-content: space-between`). AI agents unfamiliar with this pattern sometimes use `position: absolute; right: 0` for the CTA button. This breaks responsive behaviour at non-`1440px` widths and ignores the `margin: 0 110px` lateral constraint on the nav. **What to do instead:** Use `display: flex; justify-content: space-between` on the nav container. Let flex handle distribution.
10. **NEVER mix light-mode colour tokens with the dark canvas → Why it fails:** Tokens like `--normal-bg: #fff`, `--gray1` through `--gray12`, and status backgrounds (`--success-bg`, etc.) are light-mode tokens for toast/alert overlays. AI agents see these and apply them as page background or card colours, producing a jarring white card on a dark page. **What to do instead:** The dark canvas is `rgb(0,0,0)` or `rgb(10,10,10)` — there is no extracted light-mode page token because the page is dark. Light tokens are alert-only. Never use `--normal-bg` or `--gray*` as a card or section background.
---
## 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 (43) */
--gray1: hsl(0, 0%, 99%);
--gray2: hsl(0, 0%, 97.3%);
--gray3: hsl(0, 0%, 95.1%);
--gray4: hsl(0, 0%, 93%);
--gray5: hsl(0, 0%, 90.9%);
--gray6: hsl(0, 0%, 88.7%);
--gray7: hsl(0, 0%, 85.8%);
--gray8: hsl(0, 0%, 78%);
--gray9: hsl(0, 0%, 56.1%);
--gray10: hsl(0, 0%, 52.3%);
--gray11: hsl(0, 0%, 43.5%);
--gray12: hsl(0, 0%, 9%);
--normal-bg: #fff;
--success-bg: hsl(143, 85%, 96%);
--success-border: hsl(145, 92%, 91%);
--success-text: hsl(140, 100%, 27%);
--info-bg: hsl(208, 100%, 97%);
--info-border: hsl(221, 91%, 91%);
--info-text: hsl(210, 92%, 45%);
--warning-bg: hsl(49, 100%, 97%);
--warning-border: hsl(49, 91%, 91%);
--warning-text: hsl(31, 92%, 45%);
--error-bg: hsl(359, 100%, 97%);
--error-border: hsl(359, 100%, 94%);
--error-text: hsl(360, 100%, 45%);
--normal-bg-hover: hsl(0, 0%, 12%);
--normal-border-hover: hsl(0, 0%, 25%);
----gradient-h1-bg: linear-gradient(87.11deg, rgb(255, 255, 255) 17.87%, rgba(255, 255, 255, 0.9) 45.18%, rgba(255, 255, 255, 0.86) 57.76%); /* Gradient from h1 background /* reconstructed */ */
--normal-border: var(--gray4);
--button-bg-default: rgba(255, 255, 255, 0.08);
--button-bg-hover: rgba(255, 255, 255, 0.12);
--button-bg-hover-strong: rgba(255, 255, 255, 0.20);
--button-border-hover: rgba(255, 255, 255, 0.14);
--button-border-hover-alt: rgba(255, 255, 255, 0.24);
--text-page-body: rgb(230, 230, 230);
--text-muted: rgba(255, 255, 255, 0.5);
--text-link: rgb(71, 159, 250);
--text-alert-muted: rgb(134, 143, 151);
--text-accent-warm: rgb(255, 161, 108);
--fey-bg-hover: --normal-bg-hover;
--fey-border: --success-border;
--fey-border-strong: --normal-border-hover;
--fey-bg-app: --normal-bg;
/* Typography (47) */
--ease-in: cubic-bezier(0.55,0.085,0.68,0.53);
--ease-out: cubic-bezier(0.25,0.46,0.45,0.94);
--ease-in-out: cubic-bezier(0.455,0.03,0.515,0.955);
--toast-close-button-transform: translate(-35%, -35%);
--normal-text: var(--gray12);
--size: 16px;
--font-size-xs: 11px; /* 3 elements — e.g. span "Summarized at 1:13PM", span "Today · Just now", span "Today · 2 minutes ag" /* mined from computed styles */ */
--font-size-sm: 12px; /* 3 elements — e.g. span "Morning recap", span "NVDA", span "MSFT" /* mined from computed styles */ */
--font-size-md: 14px; /* 11 elements — e.g. h4 "Fey has joined Wealt", p "Trump's renewed tari", p "Saudi Arabia partner" /* mined from computed styles */ */
--font-size-xl: 18px; /* 16 elements — e.g. h3 "Highlights", h3 "7-day free trial. Ca", p "Fey alerts you the s" /* mined from computed styles */ */
--font-size-2xl: 24px; /* 11 elements — e.g. h3 "Beautiful Stock and ", h3 "Insider transactions", h3 "Graph comparison" /* mined from computed styles */ */
--font-size-3xl: 54px; /* 12 elements — e.g. h1 "Make better investme", h2 "From overwhelming to", h2 "Earnings in real tim" /* mined from computed styles */ */
--font-weight-regular: 400; /* 51 elements — e.g. h3 "News and market over", h3 "Analysis", h3 "Earnings" /* mined from computed styles */ */
--font-weight-medium: 500; /* 1 element — e.g. span "31.54" /* mined from computed styles */ */
--font-weight-semibold: 600; /* 27 elements — e.g. h1 "Make better investme", h2 "Fey", h2 "From overwhelming to" /* mined from computed styles */ */
--font-weight-bold: 700; /* 12 elements — e.g. h3 "Highlights", h3 "Beautiful Stock and ", h3 "Insider transactions" /* mined from computed styles */ */
--line-height-tight: 21.12px; /* 10 elements — e.g. span "© 2026, Fey Labs Inc", a "Features", a "Earnings" /* mined from computed styles */ */
--line-height-normal: 22.4px; /* 13 elements — e.g. h3 "News and market over", h3 "Analysis", h3 "Earnings" /* mined from computed styles */ */
--line-height-loose: 33.6px; /* 10 elements — e.g. h3 "Beautiful Stock and ", h3 "Insider transactions", h3 "Graph comparison" /* mined from computed styles */ */
--font-family-sans: 'calibre', 'calibre Fallback', Arial, sans-serif;
--font-family-serif: 'financier', 'financier Fallback', Arial, serif;
--font-size-xs: 11px;
--font-size-sm: 12px;
--font-size-md: 14px;
--font-size-xl: 18px;
--font-size-2xl: 24px;
--font-size-3xl: 54px;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--line-height-tight: 21.12px;
--line-height-normal: 22.4px;
--line-height-loose: 33.6px;
--fey-font-size-xs: font-size-xs;
--fey-font-size-sm: font-size-sm;
--fey-font-size-md: font-size-md;
--fey-font-size-xl: font-size-xl;
--fey-font-size-2xl: font-size-2xl;
--fey-font-size-3xl: font-size-3xl;
--fey-font-weight-regular: font-weight-regular;
--fey-font-weight-medium: font-weight-medium;
--fey-font-weight-semibold: font-weight-semibold;
--fey-font-weight-bold: font-weight-bold;
--fey-line-height-tight: line-height-tight;
--fey-line-height-normal: line-height-normal;
--fey-line-height-loose: line-height-loose;
/* Spacing (29) */
--toast-icon-margin-start: -3px;
--toast-icon-margin-end: 4px;
--toast-svg-margin-start: -1px;
--toast-svg-margin-end: 0px;
--toast-button-margin-start: auto;
--toast-button-margin-end: 0;
--normal-border: var(--gray4);
--space-xs: 20px; /* 2 elements — e.g. footer .sc-23255b36-3, footer .sc-23255b36-3 /* mined from computed styles */ */
--space-sm: 40px; /* 2 elements — e.g. nav .sc-493a7449-18, nav .sc-493a7449-18 /* mined from computed styles */ */
--space-md: 42px; /* 5 elements — e.g. footer .sc-23255b36-2, footer .sc-23255b36-2, footer .sc-23255b36-2 /* mined from computed styles */ */
--space-lg: 87px; /* 1 element — e.g. section .sc-72d9a4e-0 /* mined from computed styles */ */
--space-xl: 98px; /* 2 elements — e.g. section .sc-6fa28a49-7, section .sc-6fa28a49-7 /* mined from computed styles */ */
--space-2xl: 128px; /* 2 elements — e.g. section .sc-87d73b3b-17, section .sc-87d73b3b-17 /* mined from computed styles */ */
--space-3xl: 152px; /* 2 elements — e.g. section .sc-9194b3ec-8, section .sc-9194b3ec-8 /* mined from computed styles */ */
--space-xs: 20px;
--space-sm: 40px;
--space-md: 42px;
--space-lg: 87px;
--space-xl: 98px;
--space-2xl: 128px;
--space-3xl: 152px;
--fey-space-xs: --space-xs;
--fey-space-sm: --space-sm;
--fey-space-md: --space-md;
--fey-space-lg: --space-lg;
--fey-space-xl: --space-xl;
--fey-space-2xl: --space-2xl;
--fey-space-3xl: --space-3xl;
--focus-ring-offset: 3px;
/* Radius (13) */
--border-radius: 8px;
--radius-sm: 4px; /* 2 elements — e.g. textarea .sc-50e228fb-25, textarea .sc-50e228fb-25 "Undervalued tech sto" /* mined from computed styles */ */
--radius-md: 6px; /* 4 elements — e.g. button .sc-b8674878-0 "Press release", button .sc-b8674878-0 "Join live call", button .sc-b8674878-0 "Press release" /* mined from computed styles */ */
--radius-lg: 50%; /* 10 elements — e.g. button, button, button .sc-23255b36-17 /* mined from computed styles */ */
--radius-full: 99px; /* 2 elements — e.g. button .sc-493a7449-8 "Learn more", a .sc-493a7449-10 "Fey has joined Wealt" /* mined from computed styles */ */
--radius-sm: 4px;
--radius-md: 6px;
--radius-full: 99px;
--radius-lg: 50%;
--fey-radius-sm: --radius-sm;
--fey-radius-md: --radius-md;
--fey-radius-lg: --radius-lg;
--fey-radius-full: --radius-full;
/* Effects (15) */
--toast-close-button-start: 0;
--toast-close-button-end: unset;
--y: translateY(100%);
--lift-amount: calc(var(--lift) * var(--gap));
--lift: 1;
--scale: var(--toasts-before) * .05 + 1;
--shadow-sm: rgba(0, 0, 0, 0.85) 0px 1px 0px 0px; /* 4 elements — e.g. button .sc-b8674878-0, button .sc-b8674878-0, button .sc-b8674878-0 /* mined from computed styles */ */
--shadow-md: rgba(255, 255, 255, 0.5) 0px -2px 4px 0px inset, rgb(255, 255, 255) 0px 0px 2px… <0.2KB elided>; /* 2 elements — e.g. button .sc-50e228fb-27, button .sc-50e228fb-27 /* mined from computed styles */ */
--shadow-lg: rgba(255, 255, 255, 0.25) 0px 0px 14px 0px; /* 1 element — e.g. button .sc-493a7449-8 /* mined from computed styles */ */
--shadow-sm: rgba(0, 0, 0, 0.85) 0px 1px 0px 0px;
--shadow-lg: rgba(255, 255, 255, 0.25) 0px 0px 14px 0px;
--shadow-md: rgba(255, 255, 255, 0.5) 0px -2px 4px 0px inset,
rgb(255, 255, 255)… <0.3KB elided>;
--fey-shadow-sm: --shadow-sm;
--fey-shadow-md: --shadow-md;
--fey-shadow-lg: --shadow-lg;
/* Motion (27) */
----motion-fadein: @keyframes fadein {
0% { opacity: 0; }
100% { opacity: 1; }
}; /* @keyframes fadein */
----motion-fadeout: @keyframes fadeout {
0% { opacity: 1; }
100% { opacity: 0; }
}; /* @keyframes fadeout */
----motion-fadeinout: @keyframes fadeinout {
0% { opacity: 0; }
50% { opacity: 1; }
100% { opacity: 0; }
}; /* @keyframes fadeinout */
----motion-cOLSAX: @keyframes cOLSAX {
0%, 100% { transform: scaleY(0.25); }
50% { transform: scaleY(0.6); }
}; /* @keyframes cOLSAX */
----motion-iVkkDZ: @keyframes iVkkDZ {
0%, 100% { transform: scaleY(0.25); }
50% { transform: scaleY(0.8); }
}; /* @keyframes iVkkDZ */
----motion-cGfXEy: @keyframes cGfXEy {
0%, 100% { transform: scaleY(0.25); }
50% { transform: scaleY(1); }
}; /* @keyframes cGfXEy */
----motion-ehACCn: @keyframes ehACCn {
0%, 100% { transform: scaleY(0.25); }
50% { transform: scaleY(1.3); }
}; /* @keyframes ehACCn */
----motion-hfYfcK: @keyframes hfYfcK {
0%, 100% { transform: scaleY(0.25); }
50% { transform: scaleY(1.2); }
}; /* @keyframes hfYfcK */
----motion-jWUnqV: @keyframes jWUnqV {
0%, 100% { transform: scaleY(0.25); }
50% { transform: scaleY(1.1); }
}; /* @keyframes jWUnqV */
----motion-jQXhcc: @keyframes jQXhcc {
0%, 100% { transform: scaleY(0.25); }
50% { transform: scaleY(0.5); }
}; /* @keyframes jQXhcc */
----motion-hkPLLX: @keyframes hkPLLX {
50% { transform: rotate(360deg); }
}; /* @keyframes hkPLLX */
----motion-bBFzzY: @keyframes bBFzzY {
100% { background-position: -168px center; }
}; /* @keyframes bBFzzY */
----motion-fadeInSafari: @-webkit-keyframes fadeInSafari {
0% { opacity: 0; }
100% { opacity: 1; }
}; /* @keyframes fadeInSafari */
----motion-wipeAnimation: @keyframes wipeAnimation {
0% { --mask-position: -100%; }
100% { --mask-position: 120%; }
}; /* @keyframes wipeAnimation */
----motion-jBcSpD: @keyframes jBcSpD {
0% { opacity: 0; }
100% { opacity: 1; }
}; /* @keyframes jBcSpD */
----motion-round: @keyframes round {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}; /* @keyframes round */
----motion-swipe-out-left: @keyframes swipe-out-left {
0% { transform: var(--y) translate(var(--swipe-amount-x)); opacity: 1; }
100% { transform: var(--y) translate(calc(var(--swipe-amount-x) - 100%)); opacity: 0; }
}; /* @keyframes swipe-out-left */
----motion-swipe-out-right: @keyframes swipe-out-right {
0% { transform: var(--y) translate(var(--swipe-amount-x)); opacity: 1; }
100% { transform: var(--y) translate(calc(var(--swipe-amount-x) + 100%)); opacity: 0; }
}; /* @keyframes swipe-out-right */
----motion-swipe-out-up: @keyframes swipe-out-up {
0% { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
100% { transform: var(--y) translateY(calc(var(--swipe-amount-y) - 100%)); opacity: 0; }
}; /* @keyframes swipe-out-up */
----motion-swipe-out-down: @keyframes swipe-out-down {
0% { transform: var(--y) translateY(var(--swipe-amount-y)); opacity: 1; }
100% { transform: var(--y) translateY(calc(var(--swipe-amount-y) + 100%)); opacity: 0; }
}; /* @keyframes swipe-out-down */
----motion-sonner-fade-in: @keyframes sonner-fade-in {
0% { opacity: 0; transform: scale(0.8); }
100% { opacity: 1; transform: scale(1); }
}; /* @keyframes sonner-fade-in */
----motion-sonner-fade-out: @keyframes sonner-fade-out {
0% { opacity: 1; transform: scale(1); }
100% { opacity: 0; transform: scale(0.8); }
}; /* @keyframes sonner-fade-out */
----motion-sonner-spin: @keyframes sonner-spin {
0% { opacity: 1; }
100% { opacity: 0.15; }
}; /* @keyframes sonner-spin */
--duration-fast: 0.1s; /* 33 elements — e.g. button, button, a /* mined from computed styles */ */
--duration-base: 0.15s; /* 12 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-slow: 0.25s; /* 17 elements — e.g. button, a, a /* mined from computed styles */ */
--ease-default: ease; /* 116 elements — e.g. button, button, button /* mined from computed styles */ */
```
## Appendix B: Token Source Metadata
```
tokenSource: extracted-css-vars
site: fey.com
extractionDate: [TBD — record at extraction time]
confidence: high
cssVarsFound: 47
curatedTokens: 41
framework: Next.js (detected)
stylingMethod: styled-components (hashed class names: .sc-*, .kmDCBM, etc.)
tailwind: none detected
figma: not provided
Clustering method:
- CSS custom properties extracted directly — no clustering required for named vars
- Computed styles used to synthesise component-level tokens (button fills, text colours,
font stacks) where no CSS var existed
- Border-radius values mined from 18 elements and matched to semantic scale:
4px (2 el) → --radius-sm
6px (4 el) → --radius-md
50% (10 el) → --radius-lg (circular icon buttons — dominant)
99px (2 el) → --radius-full (pill CTAs)
8px (--border-radius) → toast/system only, excluded from brand button scale
- Spacing tokens extracted directly from --space-* CSS vars (not grid-derived)
Notes:
- --fey-font-sans maps incorrectly to --ease-in in the curated token set (cubic-bezier value).
This appears to be a curation error. The actual font-family token is synthesised as
--font-family-sans from computed fontFamily values. Do not use --fey-font-sans for type.
- --fey-bg-app has low confidence (mapped to --normal-bg: #fff, which is a light-mode
component surface, not the app canvas — the actual dark canvas has no explicit CSS var).
- --fey-text-primary maps to a gradient value which is structurally correct for h1 gradient
text but labelled with low confidence in the curated set.
- The gray scale (--gray1 through --gray12) appears to be Radix UI Gray (light mode).
It is used in toast/alert components, not as the page colour palette.
- Body font-weight is 600 (semibold) — unusual; confirmed by computed styles on <body>.
```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