Revolut
MIT
Modern fintech design system built on Next.js with carefully orchestrated typography, spacing, and motion tokens optimised for responsive financial products
Colour (12)
color.ruicolorgrey50[TBD]
color.brandprimaryctargb(141, 150, 158)
color.colortextinversergb(255, 255, 255)
color.colortextprimaryrgb(25, 28, 31)
color.ruicolordisabled[TBD]
color.colormodaloverlayrgba(0, 0, 0, 0.4)
color.ruicolorgreytone2rgb(141, 150, 158)
color.colortextsecondaryrgb(80, 90, 99)
color.ruicolorbackgroundrgb(255, 255, 255)
color.ruicolorforegroundrgb(25, 28, 31)
color.ruicolorstateoverlayrgb(255 255 255 / 0.08)
color.ruicoloractionbackgroundneutral[TBD]
Spacing (36)
spacing.ruispacenone0
spacing.ruispaces20.125rem
spacing.ruifocusoutlineoffset0.125rem
spacing.ruispaces40.25rem
spacing.ruispaces60.375rem
spacing.ruispaces80.5rem
spacing.ruispaces120.75rem
spacing.ruispaces161rem
spacing.ruispaces201.25rem
spacing.ruispaces241.5rem
spacing.ruisizebuttonxs1.875rem
spacing.ruispaces322rem
spacing.ruispaces402.5rem
spacing.ruisizebuttonsm2.5rem
spacing.ruispaces483rem
spacing.ruisizebuttonmd3.25rem
spacing.ruispaces563.5rem
spacing.ruispaces644rem
spacing.ruispaces724.5rem
spacing.ruisizelayoutmenumd5.5rem
spacing.ruisizelayoutmenulg6.5rem
spacing.ruisizelayoutmenuxl15.5rem
spacing.spacexs24px
spacing.revolutspacexs24px
spacing.ruisizelayoutmenuxxl25rem
spacing.ruisizelayoutsidemedium25rem
spacing.ruisizelayoutsidewide36.5rem
spacing.spacesm40px
spacing.revolutspacesm40px
spacing.spacemd48px
spacing.revolutspacemd48px
spacing.ruisizelayout94.375rem
spacing.spacelg120px
spacing.revolutspacelg120px
spacing.spacexl220px
spacing.revolutspacexl220px
Radius (14)
ruiradiusnoneunset
ruiradiusr20.125rem
ruiradiusr40.25rem
ruiradiusr60.375rem
ruiradiusr80.5rem
ruiradiusr120.75rem
ruiradiusr161rem
ruiradiusr241.5rem
ruiradiusr322rem
ruiradiusstatuspopup2.75rem
radiussm12px
ruiradiuswidgetvar(--rui-radius-r16)
ruiradiuspopupvar(--rui-radius-r24)
ruiradiusround9999px
Shadow (20)
effect.ruitiminglgvar(--rui-duration-lg) var(--rui-easing-default)
effect.ruitimingmdvar(--rui-duration-md) var(--rui-easing-default)
effect.ruitimingsmvar(--rui-duration-sm) var(--rui-easing-default)
effect.ruitimingxlvar(--rui-duration-xl) var(--rui-easing-default)
effect.ruidurationlg450ms
effect.ruidurationmd300ms
effect.ruidurationsm200ms
effect.ruidurationxl900ms
effect.ruidurationxs100ms
effect.ruishadownonenone
effect.ruishadowside0 0.125rem 0.25rem rgb(var(--rui-color-channel-black) / 0.05),
0 0.1875rem 1rem rgb(var(--rui-color-channel-black) / 0.1)
effect.ruieasinglinear"linear"
effect.ruieasingshadowcubic-bezier(0.4, 0.3, 0.8, 0.6)
effect.ruifocusoutline0.125rem solid rgb(var(--rui-color-channel-accent)/0.5)
effect.ruishadowlevel10 0.125rem 0.1875rem
rgb(var(--rui-color-channel-black) / 0.05)
effect.ruishadowlevel20 0.1875rem 0.5rem
rgb(var(--rui-color-channel-black) / 0.1)
effect.ruishadowlevel30 0.1875rem 1.875rem
rgb(var(--rui-color-channel-black) / 0.08)
effect.ruishadowlevel40 1rem 4rem
rgb(var(--rui-color-channel-black) / 0.12)
effect.ruitimingshadowvar(--rui-duration-sm) var(--rui-easing-shadow)
effect.ruidurationskeleton1500ms
# layout.md — Revolut Design System
---
## 0. Quick Reference
**Stack:** Next.js · CSS custom properties (135 vars extracted, high confidence) · Custom fonts: Aeonik Pro (marketing) + Inter (UI)
**Token source:** `extracted-css-vars` — use original `--rui-*` names exactly as written.
**How to apply:** Use as `var(--rui-font-brand)` in CSS, `style={{ fontFamily: 'var(--rui-font-brand)' }}` in JSX, or `font-[family-name:var(--rui-font-brand)]` in Tailwind.
```css
/* ── Core Tokens ── */
--rui-font-marketing: "Aeonik Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--rui-font-brand: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
/* Typography scale */
--rui-font-size-marketing-display1: 3.5rem; /* Hero headlines */
--rui-font-size-heading1: 2rem; /* Section titles */
--rui-font-size-body1: 1rem; /* Body copy */
--rui-font-size-body2: 0.875rem; /* Secondary text */
/* Spacing scale */
--rui-space-s8: 0.5rem; --rui-space-s16: 1rem;
--rui-space-s24: 1.5rem; --rui-space-s32: 2rem;
--rui-space-s48: 3rem; --rui-space-s64: 4rem;
/* Radius */
--rui-radius-r8: 0.5rem; /* Cards, inputs */
--rui-radius-r16: 1rem; /* Widgets */
--rui-radius-r24: 1.5rem; /* Popups, modals */
--rui-radius-round: 9999px; /* Pills, tabs, avatars */
/* Motion */
--rui-duration-sm: 200ms;
--rui-duration-md: 300ms;
--rui-easing-default: cubic-bezier(0.15, 0.5, 0.5, 1);
--rui-timing-sm: var(--rui-duration-sm) var(--rui-easing-default);
/* Elevation */
--rui-shadow-level1: 0 0.125rem 0.1875rem rgb(var(--rui-color-channel-black)/0.05);
--rui-shadow-level2: 0 0.1875rem 0.5rem rgb(var(--rui-color-channel-black)/0.1);
/* Focus */
--rui-focus-outline: 0.125rem solid rgb(var(--rui-color-channel-accent)/0.5);
--rui-focus-outline-offset: 0.125rem;
/* Layout */
--rui-size-layout: 94.375rem; /* Max container width ~1510px */
```
```tsx
// Primary button — correct token usage with all states
const PrimaryButton = ({ children, disabled, loading }: ButtonProps) => (
<button
disabled={disabled || loading}
aria-disabled={disabled || loading}
style={{
fontFamily: 'var(--rui-font-brand)',
fontSize: 'var(--rui-font-size-emphasis1)',
fontWeight: 500,
height: 'var(--rui-size-button-md)', // 3.25rem
borderRadius: 'var(--rui-radius-round)', // pill shape
padding: '0 var(--rui-space-s24)',
transition: 'var(--rui-timing-sm)',
}}
>
{children}
</button>
);
```
**NEVER rules:**
- **NEVER** use `border-radius: 8px` on buttons — Revolut primary buttons use `--rui-radius-round` (9999px pill shape)
- **NEVER** use Inter for marketing headlines — use `--rui-font-marketing` ("Aeonik Pro") at weight 900
- **NEVER** hardcode `#8d969e` or any colour literal — all colours must reference `--rui-color-*` tokens
- **NEVER** use spacing values not on the `--rui-space-s*` scale (multiples: 2,4,6,8,12,16,20,24,32,40,48,56,64)
- **NEVER** omit `pointer-events: none` on disabled buttons — Revolut uses this pattern explicitly
- **NEVER** use `transition: all` — scope transitions to specific properties using `--rui-timing-sm/md/lg`
- **NEVER** use font-weight 600 (semibold) for display text — Aeonik Pro display uses weight 900; Inter UI labels use 400/500/700
<!-- Quick Reference truncated to fit the 75-line cap. See later sections for the full design system. -->
## 1. Design Direction & Philosophy
### Character & Aesthetic Intent
Revolut is a **premium fintech brand** projecting confidence, modernity, and global scale. The visual language is clean, high-contrast, and bold — built around oversized Aeonik Pro headlines, precise spacing, and a near-monochromatic palette punctuated by specific accent colours.
The design is **unapologetically dark-on-hero**: the homepage deploys white text on dark/rich backgrounds for top-of-funnel moments, then pivots to a light-surface UI for feature detail sections. This contrast creates rhythm and signals transition from "brand statement" to "product utility."
### Personality
- **Bold but not aggressive** — massive type at 89px for the hero, but tightly tracked and precisely spaced
- **Premium and controlled** — no decorative flourishes, no gradients for their own sake
- **Motion-aware but restrained** — every transition has an assigned duration and easing; `transition: all` is a bug, not a feature
### What This Design Explicitly Rejects
- Rounded cards with >24px radius (the design system caps at `--rui-radius-r32`; most UI uses r16/r24)
- Warm colour palettes — the brand is cool grey (#8d969e), near-black (#191c1f), and white
- Decorative serifs or expressive typefaces — only "Aeonik Pro" (marketing) and "Inter" (UI)
- Generic system spacing (e.g. Tailwind default 4/8/12…) — use the `--rui-space-s*` scale exclusively
- Flat, shadowless UI — elevation is intentional and tiered across four shadow levels
- Heavy borders — focus rings use 0.125rem; dividers are implied by spacing and shadow, not thick lines
---
## 2. Colour System
### Tier 1 — Primitive Channels
Revolut uses an `rgb()` channel system for compositing. Primitive channels are defined as raw RGB triplets referenced with `/opacity` syntax. The channel names below are referenced throughout the codebase but the raw values are not fully exposed in the extracted CSS vars; they are documented here from computed styles.
```css
/* ── Primitive Colour Channels (reconstructed from computed styles: moderate confidence) ── */
:root {
/* These are referenced as rgb(var(--rui-color-channel-*)/opacity) */
/* --rui-color-channel-black → 0 0 0 (confirmed via shadow tokens) */
/* --rui-color-channel-white → 255 255 255 (confirmed via bWhzwu hover) */
/* --rui-color-channel-blue → inferred ~0 122 255 (used in focus ring) */
/* --rui-color-channel-accent → inferred ~0 122 255 (used in --rui-focus-outline) */
/* --rui-color-channel-action-background-neutral → inferred, used in disabled states */
}
```
### Tier 2 — Semantic Aliases
```css
/* ── Semantic Colour Tokens (extracted + reconstructed) ── */
:root {
/* Surfaces */
--rui-color-background: rgb(255, 255, 255); /* Page/component background (light mode) */
--rui-color-foreground: rgb(25, 28, 31); /* Primary foreground / near-black text */
/* Text */
/* extracted: high confidence — from h2 and modal computed styles */
--color-text-primary: rgb(25, 28, 31); /* Primary text on light surfaces */
--color-text-secondary: rgb(80, 90, 99); /* Secondary body text, captions */
--color-text-inverse: rgb(255, 255, 255); /* Text on dark/hero backgrounds */
/* Brand / CTA */
/* reconstructed: moderate confidence — brand-primary-cta mined from tab bg */
--rui-color-grey-tone-2: rgb(141, 150, 158); /* Tab backgrounds, neutral fills */
/* Semantic state colours (referenced in interactive states) */
--rui-color-grey-50: [TBD - extract manually]; /* Disabled text colour */
--rui-color-disabled: [TBD - extract manually]; /* Disabled overlay / state layer */
/* Focus */
--rui-focus-outline: 0.125rem solid rgb(var(--rui-color-channel-accent)/0.5);
--rui-focus-outline-offset: 0.125rem;
/* Action backgrounds */
--rui-color-action-background-neutral: [TBD - extract manually]; /* DropZone focus bg */
/* Modal overlay */
--color-modal-overlay: rgba(0, 0, 0, 0.4); /* Confirmed from modal computed style */
}
```
### Tier 3 — Component Tokens
```css
/* ── Component Colour Applications ── */
:root {
/* State overlay system (Revolut uses CSS var injection on :hover/:active) */
--rui-color-state-overlay: transparent; /* Default; overridden on interaction */
/* hover → rgb(var(--rui-color-channel-black) / 0.08) on filled buttons */
/* active → rgb(var(--rui-color-channel-black) / 0.15) on filled buttons */
/* hover → rgb(var(--rui-color-channel-white) / 0.05) on dark/inverted buttons */
/* active → rgb(var(--rui-color-channel-white) / 0.08) on dark/inverted buttons */
/* Button disabled opacity */
/* disabled children → opacity: 0.7 (via > * selector) */
/* state layer on disabled → opacity: 0.6 */
}
```
### Colour Census Summary
| Role | Value | Source |
|------|-------|--------|
| Near-black (text/fg) | `rgb(25, 28, 31)` | Computed h2, modal — **high confidence** |
| Secondary text | `rgb(80, 90, 99)` | Computed body, button — **high confidence** |
| White (inverse text) | `rgb(255, 255, 255)` | Computed h1, link — **high confidence** |
| Neutral grey fill | `rgb(141, 150, 158)` | Tab bg, brand-primary-cta — **medium confidence** |
| Modal overlay | `rgba(0, 0, 0, 0.4)` | Computed modal — **high confidence** |
| Focus ring | `rgb(channel-blue / 0.5)` | `--rui-focus-outline` — **high confidence** |
---
## 3. Typography System
### Font Families
```css
:root {
--rui-font-default: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
"Helvetica Neue", Helvetica, Arial, Arimo, sans-serif;
--rui-font-system: var(--rui-font-default);
--rui-font-brand: "Inter", var(--rui-font-system); /* UI, app, body copy */
--rui-font-marketing:"Aeonik Pro", var(--rui-font-system); /* Hero, display, marketing sections */
--rui-font-emoji: "Apple Color Emoji", "Segoe UI Emoji",
"Segoe UI Symbol", "Noto Color Emoji";
}
```
**Rule:** Use `--rui-font-marketing` for all h1/h2 display text on marketing pages. Use `--rui-font-brand` for all UI components, forms, nav, and body text.
### Composite Type Scale
| Token Group | font-family | font-size | font-weight | line-height | letter-spacing | Usage |
|---|---|---|---|---|---|---|
| `marketing-display1` | `--rui-font-marketing` | `3.5rem` | `900` | `1` (tight) | `calc(0.005em / 56)` | Hero section primary headline |
| `marketing-display2` | `--rui-font-marketing` | `2.5rem` | `900` | `1` | `calc(0.005em / 40)` | Sub-hero marketing headline |
| `marketing-display3` | `--rui-font-marketing` | `2rem` | `900` | `1` | `calc(0.005em / 32)` | Section intro statements |
| `display1` | `--rui-font-marketing` | `3.5rem` | `700` | `1.182` | `calc(-0.56em / 56)` | Large product headlines |
| `display2` | `--rui-font-marketing` | `2.75rem` | `700` | `1` | `calc(-0.56em / 44)` | Product feature titles |
| `heading1` | `--rui-font-marketing` | `2rem` | `700` | `1.1875` | `calc(-0.35em / 32)` | Page section headings |
| `heading2` | `--rui-font-marketing` | `1.5rem` | `700` | `1.25` | `calc(-0.31em / 24)` | Card/panel headings |
| `heading3` | `--rui-font-marketing` | `1.25rem` | `700` | `1.4` | `calc(-0.27em / 20)` | Sub-section headings |
| `emphasis1` | `--rui-font-brand` | `1rem` | `500` | `1.375` | `calc(-0.18em / 16)` | Labels, emphasized UI text |
| `emphasis2` | `--rui-font-brand` | `0.875rem` | `500` | `1.429` | `calc(-0.1em / 14)` | Secondary labels |
| `emphasis3` | `--rui-font-brand` | `0.75rem` | `500` | `1.5` | `0` | Tertiary labels, badges |
| `emphasis4` | `--rui-font-brand` | `0.6875rem` | `500` | `1.273` | `calc(0.16em / 11)` | Micro labels, legal |
| `body1` | `--rui-font-brand` | `1rem` | `400` | `1.375` | `calc(-0.18em / 16)` | Standard body copy |
| `body2` | `--rui-font-brand` | `0.875rem` | `400` | `1.429` | `calc(-0.1em / 14)` | Secondary body, descriptions |
| `body3` | `--rui-font-brand` | `0.75rem` | `400` | `1.5` | `0` | Fine print, captions |
### Real-world Computed Values (verified)
```css
/* ── Verified from page elements ── */
/* h1 "Banking & Beyond" — extracted: high confidence */
h1 {
font-family: "Aeonik Pro", sans-serif;
font-size: 89.216px; /* --font-size-3xl computed value */
font-weight: 500; /* Note: computed as 500, token says 900 — may be Aeonik Pro Medium variant */
line-height: 89.216px; /* tight 1:1 ratio */
letter-spacing: -2.08px;
color: rgb(255, 255, 255);
}
/* h2 section titles — extracted: high confidence */
h2 {
font-family: "Aeonik Pro", sans-serif;
font-size: 52.608px; /* --font-size-2xl computed value */
font-weight: 500;
line-height: 52.608px;
letter-spacing: -0.6px;
color: rgb(25, 28, 31);
text-align: center;
}
/* h3 subheadings — extracted: high confidence */
h3 {
font-family: "Aeonik Pro", sans-serif;
font-size: 18px;
font-weight: 400;
line-height: 24px;
letter-spacing: normal;
color: rgb(25, 28, 31);
}
/* body / default — extracted: high confidence */
body {
font-family: Inter, sans-serif;
font-size: 12px; /* --font-size-xs */
font-weight: 400;
line-height: 18px;
letter-spacing: 0.18px;
color: rgb(80, 90, 99);
}
```
### Font Loading
Fonts are served from `https://assets.revolut.com/assets/fonts/` with `font-display: swap`. Aeonik Pro Black Capitalised is a special variant for uppercase display usage.
---
## 4. Spacing & Layout
### Base Unit & Scale
Revolut's spacing uses a **rem-based scale with 4px granularity** at small sizes, stepping up to 8px increments. All spacing must come from `--rui-space-s*` tokens.
```css
:root {
/* ── Primitive Spacing Scale (extracted: high confidence) ── */
--rui-space-none: 0;
--rui-space-s2: 0.125rem; /* 2px — hairline separators */
--rui-space-s4: 0.25rem; /* 4px — icon internal padding */
--rui-space-s6: 0.375rem; /* 6px — tight inline gaps */
--rui-space-s8: 0.5rem; /* 8px — compact component padding */
--rui-space-s12: 0.75rem; /* 12px — small component padding */
--rui-space-s16: 1rem; /* 16px — standard component padding */
--rui-space-s20: 1.25rem; /* 20px — medium internal gaps */
--rui-space-s24: 1.5rem; /* 24px — card padding, button h-padding */
--rui-space-s32: 2rem; /* 32px — section sub-spacing */
--rui-space-s40: 2.5rem; /* 40px — component group gaps */
--rui-space-s48: 3rem; /* 48px — section spacing */
--rui-space-s56: 3.5rem; /* 56px — large component gaps */
--rui-space-s64: 4rem; /* 64px — section vertical rhythm */
--rui-space-s72: 4.5rem; /* 72px — generous section padding */
}
```
### Semantic Spacing (from curated tokens)
```css
:root {
/* ── Semantic Spacing Aliases ── */
--revolut-space-xs: 24px; /* Component internal padding minimum */
--revolut-space-sm: 40px; /* Component group separation */
--revolut-space-md: 48px; /* Standard section padding */
--revolut-space-lg: 120px; /* Large section vertical gap */
--revolut-space-xl: 220px; /* Hero section vertical breathing room */
}
```
### Layout Dimensions
```css
:root {
/* ── Container & Layout Sizes (extracted: high confidence) ── */
--rui-size-layout: 94.375rem; /* 1510px — max container width */
--rui-size-layout-menu-md: 5.5rem; /* 88px — compact side nav */
--rui-size-layout-menu-lg: 6.5rem; /* 104px — standard side nav */
--rui-size-layout-menu-xl: 15.5rem; /* 248px — expanded side nav */
--rui-size-layout-menu-xxl: 25rem; /* 400px — wide side nav/drawer */
--rui-size-layout-side-medium:25rem; /* 400px — medium side panel */
--rui-size-layout-side-wide: 36.5rem; /* 584px — wide side panel */
}
```
### Border Radius Scale
```css
:root {
/* ── Radius Scale (extracted: high confidence) ── */
--rui-radius-none: unset;
--rui-radius-r2: 0.125rem; /* 2px — barely rounded */
--rui-radius-r4: 0.25rem; /* 4px — subtle rounding */
--rui-radius-r6: 0.375rem; /* 6px — inputs, small elements */
--rui-radius-r8: 0.5rem; /* 8px — tags, chips */
--rui-radius-r12: 0.75rem; /* 12px — buttons (from computed styles) */
--rui-radius-r16: 1rem; /* 16px — widget containers */
--rui-radius-r24: 1.5rem; /* 24px — popups, modals */
--rui-radius-r32: 2rem; /* 32px — large cards */
--rui-radius-round: 9999px; /* pill — tabs, avatar badges, CTAs */
/* ── Semantic Radius Aliases ── */
--rui-radius-widget: var(--rui-radius-r16); /* App widget containers */
--rui-radius-popup: var(--rui-radius-r24); /* Popups, tooltips */
--rui-radius-status-popup: 2.75rem; /* Status/toast popups */
}
```
### Breakpoints
```css
/* ── Responsive Breakpoints (extracted from media queries) ── */
/* xs: 320px — small mobile */
/* sm: 400px — standard mobile */
/* md-: 719px — mobile max / below tablet */
/* md: 720px — tablet start */
/* lg-: 1023px — tablet max / below desktop */
/* lg: 1024px — desktop start */
/* xl: 1280px — wide desktop */
/* 2xl: 1920px — ultra-wide */
```
### Grid & Flex Patterns
```css
/* ── Layout Patterns ── */
.layout-container {
max-width: var(--rui-size-layout); /* 94.375rem */
margin-inline: auto;
padding-inline: var(--rui-space-s24);
}
/* Section vertical rhythm */
.section-padding {
padding-block: var(--revolut-space-md); /* 48px default */
}
/* Hero section */
.section-hero {
padding-block: var(--revolut-space-xl); /* 220px vertical breathing room */
}
```
### Button Size Scale
```css
:root {
--rui-size-button-xs: 1.875rem; /* 30px — compact/icon buttons */
--rui-size-button-sm: 2.5rem; /* 40px — small buttons */
--rui-size-button-md: 3.25rem; /* 52px — standard CTA buttons */
}
```
---
## 5. Page Structure & Layout Patterns
> **Note:** No page screenshots were provided. All section structures are inferred from the layout digest, component inventory, computed styles, and typographic hierarchy. Rows marked **(inferred)** are probabilistic reconstructions.
### 5.1 Section Map
| # | Section | Layout Type | Approx. Height | Key Elements | Confidence |
|---|---------|-------------|----------------|--------------|------------|
| 1 | **Navigation / Header** | Flex row, sticky | `--rui-size-layout-menu-lg` (6.5rem / 104px) | Logo, nav items (6 found), CTAs (buttons), language selector (tab pill) | Confirmed — component inventory |
| 2 | **Hero** | Full-width, dark bg, centered | ~100vh | h1 at 89.216px (white, Aeonik Pro), subtitle, primary CTA button | Inferred — from h1 computed style + `--revolut-space-xl` |
| 3 | **Product Feature Highlight** | 2-col flex or grid | ~600–800px | h2 at 52.608px, body text, product image/visual | Inferred — from h2 computed style + section spacing |
| 4 | **Pricing / Plans** | 3-col grid | ~500–700px | h2 "Standard/Plus/Premium" (24px), h3 pricing labels (18px), feature lists, CTA buttons | Confirmed — typography census mentions these exact strings |
| 5 | **Feature Sections (repeating)** | Alternating 2-col | ~400–600px each | h2 (52.608px, centred), h3 subheadings (18px), body copy, inline CTAs | Inferred — pattern from h2 center-align computed style |
| 6 | **App Download / CTA Band** | Centred single-col or split | ~300–400px | Marketing headline, app store badge buttons, QR code (inferred) | Inferred — standard fintech pattern |
| 7 | **Footer** | Multi-col grid | ~300–500px | Nav groups, legal text (12px body), social icons, language/region selector | Inferred — footer pattern consistent with typography scale |
### 5.2 Layout Patterns
**Navigation:**
- Flex row, `justify-content: space-between`, `align-items: center`
- Height: `var(--rui-size-layout-menu-lg)` (6.5rem)
- Nav items rendered as pill tabs (`border-radius: 9999px`, background `rgb(141, 150, 158)`)
- Container constrained to `--rui-size-layout` (94.375rem)
**Hero Section:**
- Full-width dark background
- Content centred or left-aligned within `--rui-size-layout` container
- Vertical padding: `--revolut-space-xl` (220px) top and bottom
- h1 white, Aeonik Pro, 89.216px, weight 500, tight line-height (1:1)
- CTA button: `--rui-radius-round` pill, `--rui-size-button-md` height (3.25rem)
**Pricing Grid (3-col):**
- CSS grid: `grid-template-columns: repeat(3, 1fr)`
- Gap: `--rui-space-s24` or `--rui-space-s32` (inferred)
- Card radius: `--rui-radius-r24` (1.5rem popup radius semantic alias)
- Elevation: `--rui-shadow-level2` on active/highlighted tier
**Repeating Feature Sections (2-col alternating):**
- `display: grid`, `grid-template-columns: 1fr 1fr`
- Gap: `--rui-space-s48` (inferred)
- h2 centred on light backgrounds (`text-align: center` confirmed from computed style)
- Section vertical separation: `--revolut-space-lg` (120px)
**Mobile (<720px):**
- All multi-col grids collapse to single column
- Horizontal padding: `--rui-space-s16` (1rem)
- Navigation collapses to hamburger/drawer (`--rui-size-layout-menu-xxl: 25rem` drawer width)
### 5.3 Visual Hierarchy
**Hero:** The h1 at 89.216px white on dark background is the single most dominant visual element. CTA buttons appear immediately below — pill-shaped (`--rui-radius-round`), contrasting fill colour.
**Section headers:** h2 at 52.608px, centred, near-black `rgb(25, 28, 31)`, tight line-height. These anchor each content section visually.
**Pricing:** h2 plan names at 24px (`--rui-font-size-xl`), price labels as h3 at 18px. The highlighted plan (Premium/Metal) likely uses `--rui-shadow-level2` or `--rui-shadow-level3` for elevation differentiation.
**CTAs:** Primary CTAs use pill radius (`--rui-radius-round`), height `--rui-size-button-md`. The brand-primary-cta background is `rgb(141, 150, 158)` as extracted; however, **this value originates from a tab/pill element and may represent a neutral selector rather than a primary CTA fill** — primary action buttons likely use foreground (`rgb(25, 28, 31)`) or a blue accent. **Treat primary CTA fill as `[TBD - extract manually]`.**
**Whitespace rhythm:** Section-to-section separation uses `--revolut-space-lg` (120px). Within sections, elements are separated by `--rui-space-s32`–`--rui-space-s48`.
### 5.4 Content Patterns
**Headline + Subtext + CTA (hero pattern):**
```
[h1 — Aeonik Pro, 89px, white]
[subtitle — Inter, 16–18px, white/muted]
[pill CTA button]
```
**Section header pattern:**
```
[h2 — Aeonik Pro, 52.608px, near-black, centred]
[body1 descriptor — Inter, 16px, secondary text, centred]
```
**Pricing card pattern:**
```
[h2 — plan name, 24px]
[h3 — price, 18px]
[feature list — body2, 14px]
[CTA button — pill, full-width on card]
```
**Feature block (2-col):**
```
[visual/image — 50% col]
[h2 + body + CTA — 50% col, vertically centred]
```
---
## 6. Component Patterns
### 6.1 Button
**Anatomy:**
- Wrapper (`<button>`) → State Layer (overlay div) → Content (icon? + label)
- State layer uses `--rui-color-state-overlay` injected via CSS var
**Token Mappings:**
| State | Background | Overlay | Text | Opacity | Border |
|-------|-----------|---------|------|---------|--------|
| Default | [CTA fill — TBD] | `transparent` | depends on variant | 1 | none |
| Hover | same | `rgb(black/0.08)` | — | 1 | none |
| Active | same | `rgb(black/0.15)` | — | 1 | none |
| Focus-visible | same | — | — | 1 | `--rui-focus-outline` inset |
| Disabled | `--rui-color-disabled` | `--rui-color-disabled` | `--rui-color-grey-50` | children: 0.7 | none |
| Inverted hover | same | `rgb(white/0.05)` | — | 1 | — |
| Inverted active | same | `rgb(white/0.08)` | — | 1 | — |
| Inverted disabled | — | — | — | 0.5 | — |
```tsx
// Button — production-ready with all states
// Token source: extracted-css-vars (high confidence for structure, states confirmed from CSS rules)
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'ghost' | 'inverted';
size?: 'xs' | 'sm' | 'md';
disabled?: boolean;
loading?: boolean;
children: React.ReactNode;
onClick?: () => void;
}
const sizeMap = {
xs: 'var(--rui-size-button-xs)', // 1.875rem
sm: 'var(--rui-size-button-sm)', // 2.5rem
md: 'var(--rui-size-button-md)', // 3.25rem
};
export const Button = ({
variant = 'primary',
size = 'md',
disabled = false,
loading = false,
children,
onClick,
}: ButtonProps) => {
const isDisabled = disabled || loading;
return (
<button
onClick={onClick}
disabled={isDisabled}
aria-disabled={isDisabled}
data-variant={variant}
style={{
// Layout
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
gap: 'var(--rui-space-s8)',
height: sizeMap[size],
padding: '0 var(--rui-space-s24)',
position: 'relative',
overflow: 'hidden',
// Typography
fontFamily: 'var(--rui-font-brand)',
fontSize: 'var(--rui-font-size-emphasis1)', // 1rem
fontWeight: Number(500),
lineHeight: String(1),
letterSpacing: 'calc(-0.18em / 16)',
// Shape
borderRadius: 'var(--rui-radius-round)', // 9999px pill
border: 'none',
cursor: isDisabled ? 'not-allowed' : 'pointer',
// Motion
transition: 'var(--rui-timing-sm)', // 200ms easing-default
// Disabled
pointerEvents: isDisabled ? 'none' : 'auto',
}}
>
{/* State overlay layer — colour injected via CSS */}
<span
data-rui="state-layer"
aria-hidden="true"
style={{
position: 'absolute',
inset: 0,
backgroundColor: 'var(--rui-color-state-overlay, transparent)',
borderRadius: 'inherit',
pointerEvents: 'none',
transition: 'var(--rui-timing-sm)',
}}
/>
<span style={{ position: 'relative', opacity: isDisabled ? 0.7 : 1 }}>
{loading ? <span aria-label="Loading…">…</span> : children}
</span>
</button>
);
};
```
```css
/* Button state CSS — add to global stylesheet */
button[data-variant="primary"]:hover { --rui-color-state-overlay: rgb(0 0 0 / 0.08); }
button[data-variant="primary"]:active { --rui-color-state-overlay: rgb(0 0 0 / 0.15); }
button[data-variant="primary"]:focus-visible {
outline: none;
box-shadow: 0 0 0 0.125rem rgb(var(--rui-color-channel-blue, 0 122 255) / 0.5) inset,
0 0 0 0.225rem var(--rui-color-background) inset;
}
button[data-variant="primary"]:is(:disabled, [aria-disabled="true"]) {
--rui-color-state-overlay: var(--rui-color-disabled);
}
button[data-variant="inverted"]:hover { --rui-color-state-overlay: rgb(255 255 255 / 0.05); }
button[data-variant="inverted"]:active { --rui-color-state-overlay: rgb(255 255 255 / 0.08); }
button[data-variant="inverted"]:is(:disabled, [aria-disabled="true"]) { opacity: 0.5; }
```
---
### 6.2 Tab / Pill Selector
**Anatomy:** Container (pill-shaped) → individual tab items (pill-shaped, `--rui-radius-round`)
```css
/* Tab component */
.tab-container {
display: inline-flex;
border-radius: var(--rui-radius-round);
background-color: rgb(141, 150, 158); /* --rui-color-grey-tone-2 */
overflow: hidden;
}
.tab-item {
font-family: Arial, sans-serif; /* Note: computed style shows Arial — system fallback */
font-size: 13.333px; /* Browser default for form elements */
border-radius: var(--rui-radius-round);
text-align: center;
padding: var(--rui-space-s8) var(--rui-space-s16);
transition: var(--rui-timing-sm);
cursor: pointer;
}
.tab-item[aria-selected="true"] {
background-color: var(--rui-color-background); /* White selected state */
}
```
---
### 6.3 Modal / Overlay
**Anatomy:** Backdrop → Dialog container → Content
| State | Property |
|-------|----------|
| Backdrop | `rgba(0,0,0,0.4)` confirmed from computed |
| Container radius | `--rui-radius-popup` (`--rui-radius-r24`, 1.5rem) |
| Container shadow | `--rui-shadow-level4` (deepest elevation) |
| Font | `--rui-font-brand` (Inter), 16px, `rgb(25,28,31)` |
| Transition in | `--rui-timing-lg` (450ms) |
```tsx
export const Modal = ({ isOpen, onClose, children }: ModalProps) => (
isOpen ? (
<div
role="dialog"
aria-modal="true"
style={{
position: 'fixed', inset: 0, zIndex: 1000,
backgroundColor: 'rgba(0, 0, 0, 0.4)', /* extracted from computed style */
display: 'flex', alignItems: 'center', justifyContent: 'center',
}}
onClick={onClose}
>
<div
onClick={e => e.stopPropagation()}
style={{
fontFamily: 'var(--rui-font-brand)',
fontSize: 'var(--rui-font-size-body1)',
color: 'rgb(25, 28, 31)',
backgroundColor: 'var(--rui-color-background)',
borderRadius: 'var(--rui-radius-popup)', /* 1.5rem */
boxShadow: 'var(--rui-shadow-level4)',
padding: 'var(--rui-space-s32)',
maxWidth: 'var(--rui-size-layout-side-medium)', /* 25rem */
width: '100%',
}}
>
{children}
</div>
</div>
) : null
);
```
---
### 6.4 Navigation Item
**Anatomy:** `<a>` or `<button>` → optional icon → label text
```tsx
export const NavItem = ({ label, href, active }: NavItemProps) => (
<a
href={href}
aria-current={active ? 'page' : undefined}
style={{
fontFamily: 'var(--rui-font-brand)',
fontSize: 'var(--rui-font-size-emphasis1)', /* 1rem */
fontWeight: active ? 500 : 400,
color: active ? 'rgb(25, 28, 31)' : 'rgb(80, 90, 99)',
textDecoration: 'none',
padding: 'var(--rui-space-s8) var(--rui-space-s12)',
borderRadius: 'var(--rui-radius-r8)',
transition: 'var(--rui-timing-sm)',
outline: 'none',
}}
>
{label}
</a>
);
```
```css
/* Nav item states */
a[href]:hover { color: rgb(25, 28, 31); background-color: var(--rui-color-grey-tone-2, rgb(141 150 158 / 0.2)); }
a[href]:focus-visible { outline: var(--rui-focus-outline); outline-offset: var(--rui-focus-outline-offset); }
```
---
### 6.5 DropZone / Interactive Surface
```css
/* Extracted directly from CSS rules */
.drop-zone {
transition: background-color var(--rui-timing-sm);
}
.drop-zone:hover { background-color: var(--rui-color-grey-tone-2); }
.drop-zone:focus-within { background-color: var(--rui-color-action-background-neutral); }
.drop-zone:active,
.drop-zone[data-active] {
background-color: rgb(var(--rui-color-channel-action-background-neutral) / 0.7);
}
```
---
## 7. Elevation & Depth
```css
:root {
/* ── Shadow Scale (extracted: high confidence) ── */
--rui-shadow-none: none;
--rui-shadow-level1: 0 0.125rem 0.1875rem
rgb(var(--rui-color-channel-black) / 0.05);
/* 2px 3px — barely lifted, cards at rest */
--rui-shadow-level2: 0 0.1875rem 0.5rem
rgb(var(--rui-color-channel-black) / 0.1);
/* 3px 8px — active cards, focused widgets */
--rui-shadow-level3: 0 0.1875rem 1.875rem
rgb(var(--rui-color-channel-black) / 0.08);
/* 3px 30px — floating panels, popovers */
--rui-shadow-level4: 0 1rem 4rem
rgb(var(--rui-color-channel-black) / 0.12);
/* 16px 64px — modals, deep drawers */
--rui-shadow-side: 0 0.125rem 0.25rem rgb(var(--rui-color-channel-black) / 0.05),
0 0.1875rem 1rem rgb(var(--rui-color-channel-black) / 0.1);
/* Lateral panels, side drawers */
/* ── Shadow Transition ── */
--rui-easing-shadow: cubic-bezier(0.4, 0.3, 0.8, 0.6);
--rui-timing-shadow: var(--rui-duration-sm) var(--rui-easing-shadow);
/* Use for box-shadow transitions specifically — distinct from default easing */
}
```
### Z-index Layering (inferred — TBD extract from CSS)
| Layer | Usage | z-index |
|-------|-------|---------|
| Base content | Page sections | 0 |
| Sticky nav | Navigation bar | [TBD] |
| Dropdowns | Nav flyouts | [TBD] |
| Modals | Overlays | ~1000 |
| Toasts | Notifications | ~1100 |
### Elevation Usage Rules
- **Level 1:** Cards at rest in grids
- **Level 2:** Cards on hover, pricing highlighted tier
- **Level 3:** Floating popovers, tooltips
- **Level 4:** Modals, sheet drawers
- **Side:** Lateral navigation panels
---
## 8. Motion
```css
:root {
/* ── Duration Scale (extracted: high confidence) ── */
--rui-duration-xs: 100ms; /* Instant feedback — toggles, checkboxes */
--rui-duration-sm: 200ms; /* Quick interactions — buttons, hover states */
--rui-duration-md: 300ms; /* Standard — panels, dropdowns opening */
--rui-duration-lg: 450ms; /* Deliberate — modals, page transitions */
--rui-duration-xl: 900ms; /* Slow — hero animations, onboarding sequences */
--rui-duration-skeleton: 1500ms; /* Loading skeleton pulse cycle */
/* ── Easing Functions (extracted: high confidence) ── */
--rui-easing-default: cubic-bezier(0.15, 0.5, 0.5, 1);
/* Snappy with soft landing — standard for interactive elements */
--rui-easing-shadow: cubic-bezier(0.4, 0.3, 0.8, 0.6);
/* Shadow-specific — slower out, for elevation transitions */
--rui-easing-toast: cubic-bezier(0.175, 0.885, 0.21, 1.65);
/* Overshoot spring — toast/snackbar entrance */
--rui-easing-linear: linear; /* Progress bars, skeleton shimmer */
/* ── Composite Timing Shorthands (extracted: high confidence) ── */
--rui-timing-sm: var(--rui-duration-sm) var(--rui-easing-default); /* 200ms — button hover */
--rui-timing-md: var(--rui-duration-md) var(--rui-easing-default); /* 300ms — panel open */
--rui-timing-lg: var(--rui-duration-lg) var(--rui-easing-default); /* 450ms — modal in */
--rui-timing-xl: var(--rui-duration-xl) var(--rui-easing-default); /* 900ms — hero anim */
--rui-timing-shadow: var(--rui-duration-sm) var(--rui-easing-shadow); /* shadow-specific */
}
```
### Motion Rules
**When to animate:**
- All interactive state changes (hover, active, focus) → `--rui-timing-sm`
- Panel/dropdown open/close → `--rui-timing-md`
- Modal entrance/exit → `--rui-timing-lg`
- Shadow elevation changes → `--rui-timing-shadow` (different easing curve)
- Loading skeleton shimmer → `--rui-duration-skeleton` with `--rui-easing-linear`
**When NOT to animate:**
- `prefers-reduced-motion: reduce` media query — always respect it
- Colour value changes on disabled states — apply immediately
- Layout reflow (avoid animating width/height — use transform/opacity)
**NEVER use `transition: all`** — always specify the property being transitioned. Button components use `transition: box-shadow var(--rui-timing-shadow)`.
---
## 9. Anti-Patterns & Constraints
1. **Hardcoded button radius → Why it fails → Use `--rui-radius-round`**
An AI agent defaulting to `border-radius: 8px` or `border-radius: 0.5rem` will produce squared-off buttons that contradict Revolut's pill-shaped CTA language. The brand's button elements have `border-radius: 9999px` in computed styles. **Always use `var(--rui-radius-round)` for primary and secondary action buttons.**
2. **Using Inter for marketing headlines → Why it fails → Use `--rui-font-marketing`**
AI agents default to Inter because it's the UI font and commonly used. But Revolut's h1/h2 marketing headings use "Aeonik Pro" (`--rui-font-marketing`) — a custom typeface not present in standard stacks. Rendering these headings in Inter produces generic-looking output that completely breaks the brand character. **All `h1` and large `h2` elements on marketing pages must use `var(--rui-font-marketing)`.**
3. **Hardcoding colour hex values → Why it fails → Always use `--rui-color-*` tokens**
AI agents may pluck `#191c1f` or `rgb(25, 28, 31)` from computed styles and inline them. This breaks dark mode compatibility and makes colour changes impossible to propagate. The entire colour system uses channel tokens for alpha compositing (`rgb(var(--rui-color-channel-black)/0.08)`). **Reference only named colour tokens; never write literal hex or rgb() values in component code.**
4. **Arbitrary spacing values → Why it fails → Use only `--rui-space-s*` tokens**
AI agents generate spacing like `padding: 20px` or `margin: 1.2rem` that sits outside the design system scale. Revolut's scale has specific increments (2, 4, 6, 8, 12, 16, 20, 24, 32, 40, 48, 56, 64, 72px). Off-scale values create visual inconsistency that accumulates across a page. **Every padding, margin, and gap value must map to a `--rui-space-s*` token.**
5. **Using `transition: all` → Why it fails → Scope to specific properties**
`transition: all` triggers a repaint on every animatable property change, including layout-affecting properties (width, height, padding), causing janky animations. Revolut uses scoped transitions: `box-shadow var(--rui-timing-shadow)` for elevation, `background-color var(--rui-timing-sm)` for state changes. **Always specify the exact CSS property in the transition shorthand.**
6. **Missing `pointer-events: none` on disabled buttons → Why it fails → Required by system**
Revolut's design system explicitly sets `pointer-events: none` on disabled button states (confirmed in extracted CSS). Omitting this means disabled buttons still respond to cursor events, showing hover effects that confuse users and break the intended disabled UX. **Disabled buttons must have `pointer-events: none`.**
7. **Wrong font weight for display text → Why it fails → Weight depends on font/context**
The Aeonik Pro marketing display uses weight 900 (Black) per CSS tokens, but computed styles show weight 500 on the live h1. This is because Aeonik Pro has a "Black Capitalised" variant that maps 900 → a specific optical variant. Defaulting to `font-weight: 700` (Bold, common AI default) produces the wrong character. **Use `font-weight: 900` for `--rui-font-weight-marketing-display*` tokens, 700 for headings, 500/400 for body.**
8. **Building responsive layout with fixed pixel widths → Why it fails → Use layout tokens**
AI agents often write `max-width: 1200px` (a generic default). Revolut's layout max-width is `--rui-size-layout: 94.375rem` (1510px). Using 1200px creates a narrower, non-authentic layout that wastes space on wide screens and misaligns with the actual grid. **Always use `var(--rui-size-layout)` for page container max-width.**
9. **Omitting focus-visible styles → Why it fails → Critical for accessibility and brand**
Revolut has a specifically designed focus system: an inset double-ring — `0 0 0 0.125rem rgb(blue/0.5) inset, 0 0 0 0.225rem var(--rui-color-background) inset`. AI agents either omit focus styles entirely (accessibility failure) or use a simple outline (inconsistent with the brand's inset ring pattern). **Every interactive element must implement the extracted focus style using the inset double box-shadow pattern.**
10. **Dynamic Tailwind class construction → Why it fails → Classes are purged**
Tailwind purges classes that aren't statically detectable in source files. Code like `` `bg-${colorVar}` `` or `` `text-${sizeVar}` `` generates classes at runtime that Tailwind has already removed from the CSS bundle. This silently removes all styling with no error. **Never construct Tailwind class names with string interpolation. Use static class strings or `style={}` with CSS variables for dynamic values.**
11. **Using `!important` for state overrides → Why it fails → Breaks the state layer system**
Revolut's state system works by injecting `--rui-color-state-overlay` as a CSS variable that cascade updates on hover/active. Adding `!important` to background or colour properties prevents this cascade from reaching the component, breaking all interactive states silently. **Never use `!important`. Use CSS specificity and the `--rui-color-state-overlay` pattern for interactive states.**
---
## 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 (12) */
--brand-primary-cta: rgb(141, 150, 158); /* Primary CTA background, dominant on 3 buttons — e.g. "button" /* mined from computed styles */ */
--rui-color-background: rgb(255, 255, 255);
--rui-color-foreground: rgb(25, 28, 31);
--color-text-primary: rgb(25, 28, 31);
--color-text-secondary: rgb(80, 90, 99);
--color-text-inverse: rgb(255, 255, 255);
--rui-color-grey-tone-2: rgb(141, 150, 158);
--rui-color-grey-50: [TBD];
--rui-color-disabled: [TBD];
--rui-color-action-background-neutral: [TBD];
--color-modal-overlay: rgba(0, 0, 0, 0.4);
--rui-color-state-overlay: rgb(255 255 255 / 0.08);
/* Typography (96) */
--rui-easing-default: cubic-bezier(0.15,0.5,0.5,1);
--rui-easing-shadow: cubic-bezier(0.4,0.3,0.8,0.6);
--rui-easing-toast: cubic-bezier(0.175,0.885,0.21,1.65);
--rui-font-default: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Helvetica Neue", Helvetica, Arial, Arimo, sans-serif;
--rui-font-system: var(--rui-font-default);
--rui-font-brand: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--rui-font-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
--rui-font-marketing: "Aeonik Pro", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--rui-font-size-display1: 3.5rem;
--rui-size-button-sm: 2.5rem;
--rui-font-size-heading1: 2rem;
--rui-font-size-display2: 2.75rem;
--rui-font-size-heading2: 1.5rem;
--rui-font-size-heading3: 1.25rem;
--rui-font-size-body1: 1rem;
--rui-font-size-body2: 0.875rem;
--rui-font-size-body3: 0.75rem;
--rui-font-size-emphasis4: 0.6875rem;
--rui-font-size-sm-h6: 1.375rem;
--rui-font-weight-marketing-display1: 900;
--rui-font-weight-display1: 700;
--rui-font-weight-sm-h6: 500;
--rui-font-weight-body1: 400;
--rui-line-height-marketing-display1: 1;
--rui-line-height-sm-h6: 1.182;
--rui-line-height-heading1: 1.1875;
--rui-line-height-heading2: 1.25;
--rui-line-height-heading3: 1.4;
--rui-line-height-body1: 1.375;
--rui-line-height-body2: 1.429;
--rui-line-height-body3: 1.5;
--rui-line-height-emphasis4: 1.273;
--rui-letter-spacing-normal: normal;
--rui-letter-spacing-marketing-display1: calc(0.005em / 56);
--rui-letter-spacing-marketing-display2: calc(0.005em / 40);
--rui-letter-spacing-marketing-display3: calc(0.005em / 32);
--rui-letter-spacing-display1: calc(-0.56em / 56);
--rui-letter-spacing-display2: calc(-0.56em / 44);
--rui-letter-spacing-heading1: calc(-0.35em / 32);
--rui-letter-spacing-heading2: calc(-0.31em / 24);
--rui-letter-spacing-heading3: calc(-0.27em / 20);
--rui-letter-spacing-emphasis1: calc(-0.18em / 16);
--rui-letter-spacing-emphasis2: calc(-0.1em / 14);
--rui-letter-spacing-emphasis3: 0;
--rui-letter-spacing-emphasis4: calc(0.16em / 11);
--rui-letter-spacing-sm-h6: calc(-0.01em / 22);
--rui-letter-spacing-sm-subtitle-2: calc(1em / 16);
--rui-size-button-xs: 1.875rem;
--rui-size-button-md: 3.25rem;
--rui-size-layout: 94.375rem;
--rui-size-layout-menu-md: 5.5rem;
--rui-size-layout-menu-lg: 6.5rem;
--rui-size-layout-menu-xl: 15.5rem;
--rui-size-layout-menu-xxl: 25rem;
--rui-size-layout-side-wide: 36.5rem;
--rui-shadow-side: 0 0.125rem 0.25rem rgb(var(--rui-color-channel-black)/0.05),0 0.1875rem 1rem rgb(var(--rui-color-channel-black)/0.1);
--font-size-xs: 12px; /* 65 elements — e.g. h2 "Global Finances", h2 "Investments", h2 "Help" /* mined from computed styles */ */
--font-size-sm: 14px; /* 6 elements — e.g. p "For the financial ba", p "For the smart spende", p "For elevating every " /* mined from computed styles */ */
--font-size-md: 16px; /* 74 elements — e.g. span "Germany", span "Personal", span "Business" /* mined from computed styles */ */
--font-size-lg: 18px; /* 12 elements — e.g. h3 "Free", h3 "£3.99/month", h3 "£7.99/month" /* mined from computed styles */ */
--font-size-xl: 24px; /* 5 elements — e.g. h2 "Standard", h2 "Plus", h2 "Premium" /* mined from computed styles */ */
--font-size-2xl: 52.608px; /* 9 elements — e.g. h2 "Your salary, reimagi", h2 "Elevate your spend", h2 "Go virtual" /* mined from computed styles */ */
--font-size-3xl: 89.216px; /* 1 element — e.g. h1 "Banking & Beyond" /* mined from computed styles */ */
--font-weight-semibold: 600; /* 11 elements — e.g. h2 "Global Finances", h2 "Investments", h2 "Help" /* mined from computed styles */ */
--line-height-normal: 22px; /* 40 elements — e.g. p "For the financial ba", p "For the smart spende", p "For elevating every " /* mined from computed styles */ */
--rui-font-size-marketing-display1: 3.5rem;
--rui-font-size-marketing-display2: 2.5rem;
--rui-font-size-marketing-display3: 2rem;
--rui-font-size-emphasis1: 1rem;
--rui-font-size-emphasis2: 0.875rem;
--rui-font-size-emphasis3: 0.75rem;
--rui-font-size-sm-subtitle-2: 1rem;
--rui-font-weight-marketing-display2: 900;
--rui-font-weight-marketing-display3: 900;
--rui-font-weight-display2: 700;
--rui-font-weight-heading1: 700;
--rui-font-weight-heading2: 700;
--rui-font-weight-heading3: 700;
--rui-font-weight-emphasis1: 500;
--rui-font-weight-emphasis2: 500;
--rui-font-weight-emphasis3: 500;
--rui-font-weight-emphasis4: 500;
--rui-font-weight-body2: 400;
--rui-font-weight-body3: 400;
--rui-font-weight-subheader: 500;
--rui-font-weight-sm-subtitle-2: 500;
--rui-line-height-marketing-display2: 1;
--rui-line-height-marketing-display3: 1;
--rui-line-height-display1: 1.182;
--rui-line-height-emphasis1: 1.375;
--rui-line-height-emphasis2: 1.429;
--rui-line-height-emphasis3: 1.5;
--rui-line-height-sm-subtitle-2: 1.375;
--rui-letter-spacing-body1: calc(-0.18em / 16);
--rui-letter-spacing-body2: calc(-0.1em / 14);
--rui-letter-spacing-body3: 0;
/* Spacing (36) */
--rui-space-none: 0;
--rui-focus-outline-offset: 0.125rem;
--rui-space-s4: 0.25rem;
--rui-space-s6: 0.375rem;
--rui-space-s8: 0.5rem;
--rui-space-s12: 0.75rem;
--rui-space-s16: 1rem;
--rui-space-s20: 1.25rem;
--rui-space-s24: 1.5rem;
--rui-space-s32: 2rem;
--rui-space-s40: 2.5rem;
--rui-space-s48: 3rem;
--rui-space-s56: 3.5rem;
--rui-space-s64: 4rem;
--rui-space-s72: 4.5rem;
--space-xs: 24px; /* 4 elements — e.g. section .Box-rui__sc-1g1k12l-0, section .Box-rui__sc-1g1k12l-0, section .Box-rui__sc-1g1k12l-0 /* mined from computed styles */ */
--space-sm: 40px; /* 2 elements — e.g. footer .Box-rui__sc-1g1k12l-0, footer .Box-rui__sc-1g1k12l-0 /* mined from computed styles */ */
--space-md: 48px; /* 1 element — e.g. section .Box-rui__sc-1g1k12l-0 /* mined from computed styles */ */
--space-lg: 120px; /* 1 element — e.g. section .Box-rui__sc-1g1k12l-0 /* mined from computed styles */ */
--space-xl: 220px; /* 2 elements — e.g. footer .Box-rui__sc-1g1k12l-0, footer .Box-rui__sc-1g1k12l-0 /* mined from computed styles */ */
--rui-size-layout: 94.375rem;
--rui-space-s2: 0.125rem;
--revolut-space-xs: 24px;
--revolut-space-sm: 40px;
--revolut-space-md: 48px;
--revolut-space-lg: 120px;
--revolut-space-xl: 220px;
--rui-size-layout-menu-md: 5.5rem;
--rui-size-layout-menu-lg: 6.5rem;
--rui-size-layout-menu-xl: 15.5rem;
--rui-size-layout-menu-xxl: 25rem;
--rui-size-layout-side-medium: 25rem;
--rui-size-layout-side-wide: 36.5rem;
--rui-size-button-xs: 1.875rem;
--rui-size-button-sm: 2.5rem;
--rui-size-button-md: 3.25rem;
/* Radius (14) */
--rui-radius-none: unset;
--rui-radius-r2: 0.125rem;
--rui-radius-r4: 0.25rem;
--rui-radius-r6: 0.375rem;
--rui-radius-r8: 0.5rem;
--rui-radius-r12: 0.75rem;
--rui-radius-r16: 1rem;
--rui-radius-r24: 1.5rem;
--rui-radius-r32: 2rem;
--rui-radius-round: 9999px;
--rui-radius-widget: var(--rui-radius-r16);
--rui-radius-popup: var(--rui-radius-r24);
--rui-radius-status-popup: 2.75rem;
--radius-sm: 12px; /* 10 elements — e.g. button .ButtonBase__ButtonBaseWrapper-rui__sc-1aqanxw-1, button .ButtonBase__ButtonBaseWrapper-rui__sc-1aqanxw-1 "Germany", button .ButtonBase__ButtonBaseWrapper-rui__sc-1aqanxw-1 /* mined from computed styles */ */
/* Effects (20) */
--rui-duration-xs: 100ms;
--rui-duration-sm: 200ms;
--rui-duration-md: 300ms;
--rui-duration-lg: 450ms;
--rui-duration-xl: 900ms;
--rui-duration-skeleton: 1500ms;
--rui-easing-linear: "linear";
--rui-timing-sm: var(--rui-duration-sm) var(--rui-easing-default);
--rui-timing-md: var(--rui-duration-md) var(--rui-easing-default);
--rui-timing-lg: var(--rui-duration-lg) var(--rui-easing-default);
--rui-timing-xl: var(--rui-duration-xl) var(--rui-easing-default);
--rui-timing-shadow: var(--rui-duration-sm) var(--rui-easing-shadow);
--rui-shadow-none: none;
--rui-shadow-level1: 0 0.125rem 0.1875rem
rgb(var(--rui-color-channel-black) / 0.05);
--rui-shadow-level2: 0 0.1875rem 0.5rem
rgb(var(--rui-color-channel-black) / 0.1);
--rui-shadow-level3: 0 0.1875rem 1.875rem
rgb(var(--rui-color-channel-black) / 0.08);
--rui-shadow-level4: 0 1rem 4rem
rgb(var(--rui-color-channel-black) / 0.12);
--rui-focus-outline: 0.125rem solid rgb(var(--rui-color-channel-accent)/0.5);
--rui-shadow-side: 0 0.125rem 0.25rem rgb(var(--rui-color-channel-black) / 0.05),
0 0.1875rem 1rem rgb(var(--rui-color-channel-black) / 0.1);
--rui-easing-shadow: cubic-bezier(0.4, 0.3, 0.8, 0.6);
/* Motion (3) */
--duration-fast: 0.2s; /* 53 elements — e.g. button, button, button /* mined from computed styles */ */
--duration-base: 0.3s; /* 53 elements — e.g. button, button, button /* mined from computed styles */ */
--ease-default: 0.5; /* 106 elements — e.g. button, button, button /* mined from computed styles */ */
```
More from the gallery
Browse all kits →You may also like

Vercel
MITClean, minimal developer-focused design system built on Geist typography with light surfaces, precise spacing, and fast interactions for modern web applications
00
lightminimaldeveloper-toolsaas

Sentry
MITDark, minimal design system for developer tools with a sophisticated purple palette and refined typography, tailored for error monitoring and performance platforms
00
darkminimaldeveloper-toolsaas

OpenAI
MITClean, minimal design system for AI-native products built on Next.js and Tailwind, featuring a light monochromatic palette with precise token-based spacing and typography
02
lightminimalsaasdeveloper-tool