| name | stripe-design |
| description | Create clean, professional, accessible frontend interfaces following Stripe's design principles applied to the project's own color palette (color-agnostic). Use when building SaaS dashboards, fintech apps, landing pages, or interfaces needing Stripe's polish - precise typography, layered shadows, subtle animations - without Stripe's brand colors. Triggers on "Stripe-style", "fintech design", "professional SaaS UI". |
Stripe Design System (Color-Agnostic)
This skill implements Stripe's design philosophy: clarity, precision, accessibility, and subtle delight. It deliberately does NOT define brand colors. Instead, it defines semantic color roles that must be mapped to the project's existing palette before applying any component pattern.
Core Design Philosophy
Principles:
- Clarity over decoration - Every element serves a purpose
- Accessibility by default - WCAG 2.1 AA compliant color contrast (4.5:1 for text, 3.0:1 for large text)
- Consistent visual weight - Colors at each level have equal perceptual lightness
- Subtle delight - Micro-interactions that feel responsive without being distracting
- Professional trust - Design that conveys security and reliability
Two Modes: Product UI vs Marketing
Stripe's aesthetic has two distinct faces. Before styling, determine which mode applies to the surface being built. When unclear, ask the user. A single app can use both (marketing site in Marketing mode, logged-in app in Product mode).
Product UI mode (dashboards, admin panels, internal tools, logged-in app)
The Stripe Dashboard look: sober, dense, functional. Default to this mode for anything behind a login.
- Density: tighter spacing. Prefer --space-2 to --space-4 (4-16px) inside components; --space-5/6 between sections, not 64-96px.
- Color: neutral surfaces dominate. Brand color appears ONLY on primary actions, links, active states, and data highlights. No decorative color.
- Shadows: minimal. Prefer 1px borders (--color-border) over shadows for cards and containers; reserve --shadow-md/lg for popovers, dropdowns, and modals only.
- NO decorative gradients, NO skewed sections, NO hero animations.
- Typography: smaller scale in practice. Body at --text-sm (14px), section titles at --text-lg/xl. Tabular numbers (font-variant-numeric: tabular-nums) for data columns.
- Tables and forms are first-class citizens: row height 40-48px, clear column alignment (numbers right-aligned), visible focus states, inline validation.
- Motion: restricted to feedback (hover, press, focus, loading states, toasts). Duration --duration-fast or less. Nothing animates on page load.
- Empty states, loading skeletons, and zero-data views get deliberate design; they are most of what new users see.
Marketing mode (landing pages, public site, pricing pages)
The stripe.com look: expressive, spacious, memorable.
- Density: generous. 64-96px between sections, large heroes.
- Gradients: yes; hero gradients and animated mesh backgrounds built from brand hues.
- Skewed sections (transform: skewY(-6deg) on the background, content unskewed) for visual rhythm.
- Typography: large scale (--text-4xl/5xl for heroes), tight leading.
- Motion: staggered entrance animations, parallax accents, spring-based micro-interactions. Still respect prefers-reduced-motion.
- Shadows: full layered shadows for floating cards and screenshots.
Shared across both modes
Typography stack, 4px spacing base, radius scale, tinted (never pure black) shadows when used, focus rings, accessibility rules, and button/input micro-interactions are identical in both modes.
Color Roles (map to the project's palette)
Before styling anything, determine which situation applies:
Case A: The project already has colors
Identify the existing palette (Tailwind config, CSS variables, logo, current UI) and assign it to the semantic roles below. Never invent new brand colors; derive every value from the palette the app already uses.
Case B: New design, no palette exists
STOP before styling. Do not improvise brand colors and do not default to Stripe-like purples/blues. Ask the user first; one seed color is enough. Ask for:
- A primary/seed color, or failing that, references to derive one from: a logo, the client's industry, a website whose colors they like.
- The desired personality: sober/corporate vs vibrant/friendly (this decides saturation levels and how much color appears outside of actions).
From a single seed color, derive the full palette using the rules in "How to derive missing values" below: the darkest hue for text and shadows, tinted surfaces at 2-4%, darkened hover, and accessible semantic colors.
Then, BEFORE writing any CSS, present the proposed palette to the user as a table: role, hex value, where it's used, and the measured contrast ratio of each critical pairing (already passing WCAG AA). Wait for approval or adjustments. Only after approval, apply the palette to the semantic roles and proceed.
Semantic roles
:root {
--color-primary: ;
--color-primary-hover: ;
--color-text: ;
--color-text-secondary: ;
--color-text-placeholder: ;
--color-background: ;
--color-background-container: ;
--color-background-input: ;
--color-success: ;
--color-danger: ;
--color-warning: ;
--color-info: ;
--color-border: ;
--color-border-focus: ;
--shadow-hue: ;
--focus-hue: ;
}
How to derive missing values
- Hover states: darken the base color 8-12% in lightness, keep hue and saturation.
- Tinted surfaces: mix 2-4% of a brand hue into white instead of using pure gray.
- Text colors: instead of pure black/gray, tint neutrals slightly toward the brand's darkest hue. This is what makes an interface feel cohesive rather than templated.
- Dark theme: invert lightness relationships, keep the same hue family. Text becomes light, surfaces become the brand's darkest hue and one step above it.
Accessibility Audit (mandatory after mapping)
Being color-agnostic does NOT mean accepting the palette blindly. After mapping the app's colors to the semantic roles, audit every pairing and propose fixes for anything that fails. Never silently replace a brand color; always flag the issue, explain why it fails, and suggest the closest accessible adjustment.
Contrast requirements (WCAG 2.1 AA):
- Body text on its background: minimum 4.5:1
- Large text (18px+ bold, or 24px+ regular) and icons: minimum 3.0:1
- UI component boundaries (input borders, focus indicators) against adjacent colors: minimum 3.0:1
- Text on --color-primary buttons: minimum 4.5:1 (check white AND the dark text color; pick whichever passes)
Audit checklist per role:
- --color-text on --color-background and on --color-background-container
- --color-text-secondary on both surfaces (this is the most common failure)
- --color-text-placeholder on --color-background-input (may fall below 4.5:1 since placeholders are not content, but keep at least 3.0:1)
- Button label on --color-primary and on --color-primary-hover
- --color-danger / --color-success / --color-warning as text on white (warnings and light greens usually fail)
- --color-border against --color-background (3.0:1 for meaningful boundaries)
- Focus ring visibility against every surface it can appear on
How to suggest fixes (preserve brand identity):
- Adjust lightness only; keep the hue and saturation of the brand color. A darkened variant of the brand color reads as the same brand.
- For text roles that fail: darken the color until it passes, and propose it as a "text-safe" variant alongside the original (original stays for large text, fills, and decorative use).
- For semantic colors that fail as text (typical with warning/amber): keep the original for backgrounds and icons, add a darker "on-white" variant for text.
- Never rely on color alone to convey state: pair semantic colors with icons or text labels.
- Report findings to the user as a short table: role, pairing, measured ratio, pass/fail, suggested value. Let the user approve changes to brand colors; apply non-brand fixes (grays, borders) directly.
Additional non-color accessibility rules:
- Never use light yellows or light greens for small text on white backgrounds
Typography
Geometric sans-serif fonts optimized for screen readability.
Font Stack
:root {
--font-family: "Inter", -apple-system, BlinkMacSystemFont,
"Segoe UI", Roboto, Oxygen, Ubuntu, sans-serif;
--font-mono: "JetBrains Mono", "Fira Code", monospace;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
}
Type Scale
:root {
--text-xs: 0.75rem;
--text-sm: 0.875rem;
--text-base: 1rem;
--text-lg: 1.125rem;
--text-xl: 1.25rem;
--text-2xl: 1.5rem;
--text-3xl: 1.875rem;
--text-4xl: 2.25rem;
--text-5xl: 3rem;
--leading-tight: 1.25;
--leading-normal: 1.5;
--leading-relaxed: 1.625;
--font-normal: 400;
--font-medium: 500;
--font-semibold: 600;
--font-bold: 700;
}
Typography Hierarchy
- Headings: Semi-bold (600) or Bold (700), tight line-height (1.25)
- Body: Regular (400) or Medium (500), normal line-height (1.5)
- Captions/Labels: Regular (400), uppercase optional for labels
- Minimum mobile input font: 16px (prevents zoom on iOS)
Spacing System
Based on 4px base unit for pixel-perfect alignment.
:root {
--space-0: 0;
--space-1: 0.125rem;
--space-2: 0.25rem;
--space-3: 0.5rem;
--space-4: 1rem;
--space-5: 1.5rem;
--space-6: 2rem;
--space-7: 3rem;
--space-8: 4rem;
}
Border Radius
Consistent rounded corners create cohesion.
:root {
--radius-none: 0;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-full: 9999px;
}
Shadows
Subtle, layered shadows for depth without heaviness. Key rule: shadows are never pure black. They are tinted with the brand's darkest hue (--shadow-hue), which is what makes cards feel integrated with the palette instead of pasted on top.
:root {
--shadow-sm: 0 1px 2px 0 rgba(var(--shadow-hue), 0.08);
--shadow-md:
0 1px 3px rgba(var(--shadow-hue), 0.1),
0 1px 2px rgba(var(--shadow-hue), 0.06);
--shadow-lg:
0 7px 14px rgba(var(--shadow-hue), 0.1),
0 3px 6px rgba(var(--shadow-hue), 0.08);
--shadow-xl:
0 15px 35px rgba(var(--shadow-hue), 0.1),
0 5px 15px rgba(var(--shadow-hue), 0.07);
--shadow-focus: 0 0 0 3px rgba(var(--focus-hue), 0.25);
}
Animation Principles
Fast, responsive animations that respect user preferences.
:root {
--duration-instant: 50ms;
--duration-fast: 150ms;
--duration-normal: 250ms;
--duration-slow: 400ms;
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
--ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1);
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Animation Patterns
- Hover states: translateY(-1px) + shadow increase
- Button press: scale(0.98)
- Page load: staggered fade-in with subtle translateY (Marketing mode only — in Product UI nothing animates on page load)
- Modals: fade + scale from 0.95
- Focus: ring with 0.25 opacity of primary color
- In React: prefer spring physics (framer-motion:
type: "spring", stiffness: 300, damping: 25) over linear easings for interactive elements
Component Patterns
All component examples use only semantic tokens; they inherit whatever palette was mapped above.
Buttons
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 10px 16px;
font-size: var(--text-sm);
font-weight: var(--font-medium);
line-height: 1;
border-radius: var(--radius-sm);
transition: all var(--duration-fast) var(--ease-out);
cursor: pointer;
}
.btn-primary {
background: var(--color-primary);
color: white;
border: none;
box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
background: var(--color-primary-hover);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-primary:active {
transform: translateY(0) scale(0.98);
}
Inputs
.input {
width: 100%;
padding: 10px 12px;
font-size: var(--text-base);
line-height: 1.5;
color: var(--color-text);
background: var(--color-background-input);
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
transition: border-color var(--duration-fast),
box-shadow var(--duration-fast);
}
.input:focus {
outline: none;
border-color: var(--color-border-focus);
box-shadow: var(--shadow-focus);
}
.input::placeholder {
color: var(--color-text-placeholder);
}
Cards
.card {
background: var(--color-background);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-5);
}
.card-elevated {
background: var(--color-background);
border-radius: var(--radius-md);
padding: var(--space-5);
box-shadow: var(--shadow-md);
transition: box-shadow var(--duration-normal) var(--ease-out);
}
.card-elevated:hover {
box-shadow: var(--shadow-lg);
}
Layout Guidelines
- Max content width: 1080px - 1200px for readability
- Grid: CSS Grid for layout, Flexbox for alignment
- Container padding: 16px mobile, 24px tablet, 32px+ desktop
- Section spacing: 64px - 96px between major sections (Marketing) / --space-5 to --space-6 (24-32px) in Product UI
Gradient Backgrounds
Marketing mode only — never use decorative gradients in Product UI. Build gradients from the app's own palette: pick 2-3 adjacent hues from the brand colors (or lighter/darker variants of the primary), never arbitrary hex values.
.gradient-bg {
background: linear-gradient(
135deg,
var(--gradient-stop-1) 0%,
var(--gradient-stop-2) 50%,
var(--gradient-stop-3) 100%
);
}
.section-gradient {
background: linear-gradient(
180deg,
var(--color-background) 0%,
var(--color-background-container) 100%
);
}
Implementation Checklist
When applying this skill to a project: