feat: IMAP mail sync with thread-to-contact matching (PRD §6.1)
Real IMAP connection via imapflow library: - Connects to IMAP server (IMAPFlow, TLS on port 993) - Fetches recent messages (configurable limit, default 20) - Extracts: from, fromEmail, subject, date, snippet (200 chars) - Thread-to-contact matching (PRD §6.1): 1. Match sender email against persons table 2. If no person match, extract domain and match against company.website 3. Returns matchedCompany + matchedPerson for each email - Audit-logged (action: crm.mailsync:imap) - Zod validated (host, port, user, password, folder, limit) - 502 on IMAP connection failure with detail - 400 on validation failure Tested: invalid creds → 502 'IMAP connection failed', missing host → 400
This commit is contained in:
1 parent
7acc0c5451
commit
e45b68bb8b
1089 files changed
+260614
No files matched your search
@@ -0,0 +1,487 @@
|
||||
---
|
||||
version: alpha
|
||||
name: Stripe
|
||||
description: "An inspired interpretation of Stripe's design language — a financial-infrastructure brand built on a deep navy ink, an electric indigo primary, and a recurring atmospheric gradient mesh that occupies the upper third of nearly every marketing page. The system pairs the proprietary Sohne family at thin (300) weights with negative letter-spacing for editorial-density display headlines, and uses tabular-figure body type where money and numerics matter. Buttons are tight-radius pills, cards live on near-white surfaces, and the dashboard track flips polarity to a familiar dark-app shell."
|
||||
|
||||
colors:
|
||||
primary: "#533afd"
|
||||
primary-deep: "#4434d4"
|
||||
primary-press: "#2e2b8c"
|
||||
primary-soft: "#665efd"
|
||||
primary-bg-subdued-hover: "#b9b9f9"
|
||||
brand-dark-900: "#1c1e54"
|
||||
ink: "#0d253d"
|
||||
ink-secondary: "#273951"
|
||||
ink-mute: "#64748d"
|
||||
ink-mute-2: "#61718a"
|
||||
on-primary: "#ffffff"
|
||||
canvas: "#ffffff"
|
||||
canvas-soft: "#f6f9fc"
|
||||
canvas-cream: "#f5e9d4"
|
||||
hairline: "#e3e8ee"
|
||||
hairline-input: "#a8c3de"
|
||||
ruby: "#ea2261"
|
||||
magenta: "#f96bee"
|
||||
lemon: "#9b6829"
|
||||
shadow-blue: "#003770"
|
||||
|
||||
typography:
|
||||
display-xxl:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 56px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.03
|
||||
letterSpacing: -1.4px
|
||||
fontFeature: ss01
|
||||
display-xl:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 48px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.15
|
||||
letterSpacing: -0.96px
|
||||
fontFeature: ss01
|
||||
display-lg:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 32px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.1
|
||||
letterSpacing: -0.64px
|
||||
fontFeature: ss01
|
||||
display-md:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 26px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.12
|
||||
letterSpacing: -0.26px
|
||||
fontFeature: ss01
|
||||
heading-lg:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 22px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.1
|
||||
letterSpacing: -0.22px
|
||||
fontFeature: ss01
|
||||
heading-md:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 20px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.4
|
||||
letterSpacing: -0.2px
|
||||
fontFeature: ss01
|
||||
heading-sm:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 18px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.4
|
||||
letterSpacing: 0
|
||||
fontFeature: ss01
|
||||
body-lg:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 16px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.4
|
||||
letterSpacing: 0
|
||||
fontFeature: ss01
|
||||
body-md:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 15px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.4
|
||||
letterSpacing: 0
|
||||
fontFeature: ss01
|
||||
body-tabular:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 14px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.4
|
||||
letterSpacing: -0.42px
|
||||
fontFeature: tnum
|
||||
button-md:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 16px
|
||||
fontWeight: 400
|
||||
lineHeight: 1.0
|
||||
letterSpacing: 0
|
||||
fontFeature: ss01
|
||||
button-sm:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 14px
|
||||
fontWeight: 400
|
||||
lineHeight: 1.0
|
||||
letterSpacing: 0
|
||||
fontFeature: ss01
|
||||
caption:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 13px
|
||||
fontWeight: 400
|
||||
lineHeight: 1.4
|
||||
letterSpacing: -0.39px
|
||||
fontFeature: tnum
|
||||
micro:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 11px
|
||||
fontWeight: 300
|
||||
lineHeight: 1.4
|
||||
letterSpacing: 0
|
||||
fontFeature: ss01
|
||||
micro-cap:
|
||||
fontFamily: "sohne-var, 'SF Pro Display', system-ui, -apple-system, sans-serif"
|
||||
fontSize: 10px
|
||||
fontWeight: 400
|
||||
lineHeight: 1.15
|
||||
letterSpacing: 0.1px
|
||||
fontFeature: ss01
|
||||
|
||||
rounded:
|
||||
xs: 4px
|
||||
sm: 6px
|
||||
md: 8px
|
||||
lg: 12px
|
||||
xl: 16px
|
||||
pill: 9999px
|
||||
|
||||
spacing:
|
||||
xxs: 2px
|
||||
xs: 4px
|
||||
sm: 8px
|
||||
md: 12px
|
||||
lg: 16px
|
||||
xl: 24px
|
||||
xxl: 32px
|
||||
huge: 64px
|
||||
|
||||
components:
|
||||
button-primary-pill:
|
||||
backgroundColor: "{colors.primary}"
|
||||
textColor: "{colors.on-primary}"
|
||||
typography: "{typography.button-md}"
|
||||
rounded: "{rounded.pill}"
|
||||
padding: 8px 16px
|
||||
button-primary-pill-pressed:
|
||||
backgroundColor: "{colors.primary-press}"
|
||||
textColor: "{colors.on-primary}"
|
||||
typography: "{typography.button-md}"
|
||||
rounded: "{rounded.pill}"
|
||||
padding: 8px 16px
|
||||
button-secondary:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.primary}"
|
||||
typography: "{typography.button-md}"
|
||||
rounded: "{rounded.pill}"
|
||||
padding: 8px 16px
|
||||
button-on-dark:
|
||||
backgroundColor: "{colors.brand-dark-900}"
|
||||
textColor: "{colors.on-primary}"
|
||||
typography: "{typography.button-md}"
|
||||
rounded: "{rounded.pill}"
|
||||
padding: 8px 16px
|
||||
text-input:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.ink}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.sm}"
|
||||
padding: 8px 12px
|
||||
text-input-focused:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.ink}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.sm}"
|
||||
padding: 8px 12px
|
||||
card-feature-light:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.ink}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.lg}"
|
||||
padding: 32px
|
||||
card-pricing:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.ink}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.lg}"
|
||||
padding: 32px
|
||||
card-pricing-featured:
|
||||
backgroundColor: "{colors.brand-dark-900}"
|
||||
textColor: "{colors.on-primary}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.lg}"
|
||||
padding: 32px
|
||||
card-cream-band:
|
||||
backgroundColor: "{colors.canvas-cream}"
|
||||
textColor: "{colors.ink}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.lg}"
|
||||
padding: 32px
|
||||
card-dashboard-mockup:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.ink}"
|
||||
typography: "{typography.body-tabular}"
|
||||
rounded: "{rounded.lg}"
|
||||
padding: 24px
|
||||
pill-tag-soft:
|
||||
backgroundColor: "{colors.primary-bg-subdued-hover}"
|
||||
textColor: "{colors.primary-deep}"
|
||||
typography: "{typography.micro-cap}"
|
||||
rounded: "{rounded.pill}"
|
||||
padding: 4px 8px
|
||||
nav-bar-on-mesh:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.ink}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.xs}"
|
||||
padding: 16px 24px
|
||||
link-on-light:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.primary}"
|
||||
typography: "{typography.body-md}"
|
||||
rounded: "{rounded.xs}"
|
||||
padding: 0px
|
||||
footer-light:
|
||||
backgroundColor: "{colors.canvas}"
|
||||
textColor: "{colors.ink-mute}"
|
||||
typography: "{typography.caption}"
|
||||
rounded: "{rounded.xs}"
|
||||
padding: 64px 24px
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Stripe's design language opens with the gradient mesh. A wide horizontal band of pastel cream, sherbet orange, lavender, electric indigo, and ruby pink occupies the upper third of nearly every marketing page — the brand's instantly-recognizable atmospheric backdrop. Type and product UI mockups float above it on `{colors.canvas}` (white), with the gradient acting as both decoration and visual anchor. The lower portion of the page returns to white, with feature explanations on `{colors.canvas-soft}` (a barely-tinted cool off-white) and dashboard product mockups composited as faux IDE/console panels in deep navy.
|
||||
|
||||
The color system has two primary roles. **Indigo** (`{colors.primary}` — `#533afd`) is the brand's signature CTA color, used sparingly: one filled pill per band. **Deep navy** (`{colors.ink}` — `#0d253d`) is the universal body text color and the fill of dashboard mockups, the featured pricing tier, and the dark-app surfaces on the dashboard track. Ruby (`{colors.ruby}`) and magenta (`{colors.magenta}`) appear inside the gradient mesh and as accent dots in product UI mockups; they are not used as button colors.
|
||||
|
||||
Typography is built around **Sohne** at weight 300 with negative letter-spacing — the brand's editorial-density display signature. Display sizes (32–56px) use -1.4px to -0.64px tracking; body sizes use 0; tabular caption sizes (where money and numerics matter) use the OpenType `tnum` feature plus a tightening -0.36 to -0.42px tracking. The `ss01` stylistic set is enabled across all roles.
|
||||
|
||||
**Key Characteristics:**
|
||||
- Gradient-mesh backdrop on every marketing hero — cream/orange/lavender/indigo/ruby horizontally washed across the upper third of the page.
|
||||
- Single-indigo CTA hierarchy: filled `{colors.primary}` pill is the only filled button on marketing surfaces.
|
||||
- Sohne thin (weight 300) display tier with negative tracking from -1.4px to -0.2px depending on size.
|
||||
- Tabular-figure body type (`tnum`) for any cell containing money or numerics — the brand's quiet financial-data signal.
|
||||
- Dark-app dashboard track: deep navy product UI mockups sit composited above the white canvas, frequently with rendered code or dashboard tables inside.
|
||||
- Pill-shaped buttons (`{rounded.pill}` 9999px) with tight `8px 16px` padding — short, decisive, transactional.
|
||||
- Cream-band feature cards (`{colors.canvas-cream}`) introduce a warm interlude between blue/white sections without breaking the brand's chromatic logic.
|
||||
|
||||
## Colors
|
||||
|
||||
> **Source pages:** home (`/`), `/payments`, `/pricing`, `dashboard.stripe.com/register/payments`.
|
||||
|
||||
### Brand & Accent
|
||||
- **Indigo** (`{colors.primary}` — `#533afd`): The brand's signature CTA color. Filled-pill button, link emphasis, gradient anchor.
|
||||
- **Indigo Deep** (`{colors.primary-deep}` — `#4434d4`): A deeper indigo used in gradient mid-stops and as the press-state warmer alternative.
|
||||
- **Indigo Press** (`{colors.primary-press}` — `#2e2b8c`): Pressed-state lift of the primary.
|
||||
- **Indigo Soft** (`{colors.primary-soft}` — `#665efd`): A lighter indigo used in product-UI accents and chart highlights.
|
||||
- **Indigo Subdued** (`{colors.primary-bg-subdued-hover}` — `#b9b9f9`): Pale indigo fill used as soft tag background.
|
||||
- **Brand Dark 900** (`{colors.brand-dark-900}` — `#1c1e54`): The deep navy used on the featured pricing tier and dashboard chrome.
|
||||
- **Ruby** (`{colors.ruby}` — `#ea2261`): Gradient accent and chart highlight; never a button.
|
||||
- **Magenta** (`{colors.magenta}` — `#f96bee`): Brighter pink stop in gradient meshes.
|
||||
- **Lemon** (`{colors.lemon}` — `#9b6829`): Warm sherbet stop in gradient backdrops.
|
||||
|
||||
### Surface
|
||||
- **Canvas** (`{colors.canvas}` — `#ffffff`): Default page background.
|
||||
- **Canvas Soft** (`{colors.canvas-soft}` — `#f6f9fc`): Cool-tinted off-white used on feature bands beneath the gradient hero.
|
||||
- **Canvas Cream** (`{colors.canvas-cream}` — `#f5e9d4`): Warm cream used as a feature-band fill — the brand's chromatic interlude.
|
||||
- **Hairline** (`{colors.hairline}` — `#e3e8ee`): 1px borders on cards and tables.
|
||||
- **Hairline Input** (`{colors.hairline-input}` — `#a8c3de`): Slightly cooler hairline used on form inputs.
|
||||
|
||||
### Text
|
||||
- **Ink** (`{colors.ink}` — `#0d253d`): Default body text color across the brand. Deep navy, never pure black.
|
||||
- **Ink Secondary** (`{colors.ink-secondary}` — `#273951`): Secondary text on white.
|
||||
- **Ink Mute** (`{colors.ink-mute}` — `#64748d`): Helper text, captions, table labels.
|
||||
- **Ink Mute 2** (`{colors.ink-mute-2}` — `#61718a`): Near-equivalent to ink-mute used in nav.
|
||||
- **On Primary** (`{colors.on-primary}` — `#ffffff`): Text on indigo / dark-navy surfaces.
|
||||
|
||||
### Semantic
|
||||
The brand does not use a separate semantic color palette in the marketing system — error / success states live in dashboard-product UI specifically.
|
||||
|
||||
## Typography
|
||||
|
||||
### Font Family
|
||||
|
||||
The display and UI tier is **Sohne** (proprietary, licensed from Klim Type Foundry) at weights 300 (thin) and 400 (regular). The variable font (`sohne-var`) is loaded with `font-feature-settings: "ss01"` enabled globally — the stylistic set substitutes a single-story `a` and other character variants that are part of the brand's typographic signature.
|
||||
|
||||
When Sohne is unavailable, fall back to **SF Pro Display** at thin weights, then system-ui. For maximum brand fidelity, **Inter** (open-source) at weight 300 with `font-feature-settings: "ss01"` and `letter-spacing: -1.4px` on display sizes approximates the rhythm closely.
|
||||
|
||||
### Hierarchy
|
||||
|
||||
| Token | Size | Weight | Line Height | Letter Spacing | Use |
|
||||
|---|---|---|---|---|---|
|
||||
| `{typography.display-xxl}` | 56px | 300 | 1.03 | -1.4px | Hero headline |
|
||||
| `{typography.display-xl}` | 48px | 300 | 1.15 | -0.96px | Section opener |
|
||||
| `{typography.display-lg}` | 32px | 300 | 1.1 | -0.64px | Card title / sub-section |
|
||||
| `{typography.display-md}` | 26px | 300 | 1.12 | -0.26px | Compact card title |
|
||||
| `{typography.heading-lg}` | 22px | 300 | 1.1 | -0.22px | Pricing tier name |
|
||||
| `{typography.heading-md}` | 20px | 300 | 1.4 | -0.2px | Section sub-heading |
|
||||
| `{typography.heading-sm}` | 18px | 300 | 1.4 | 0 | Mini-section label |
|
||||
| `{typography.body-lg}` | 16px | 300 | 1.4 | 0 | Marketing body lead |
|
||||
| `{typography.body-md}` | 15px | 300 | 1.4 | 0 | Default UI body |
|
||||
| `{typography.body-tabular}` | 14px | 300 | 1.4 | -0.42px | Money / numeric tables (uses `tnum`) |
|
||||
| `{typography.button-md}` | 16px | 400 | 1.0 | 0 | Pill button label |
|
||||
| `{typography.button-sm}` | 14px | 400 | 1.0 | 0 | Compact pill label |
|
||||
| `{typography.caption}` | 13px | 400 | 1.4 | -0.39px | Helper, table labels |
|
||||
| `{typography.micro}` | 11px | 300 | 1.4 | 0 | Fine print |
|
||||
| `{typography.micro-cap}` | 10px | 400 | 1.15 | 0.1px | All-caps eyebrow |
|
||||
|
||||
### Principles
|
||||
- **Thin weight is the brand.** Display tiers always render at weight 300. Bumping to 400+ removes the brand's editorial air.
|
||||
- **Negative tracking on display.** -1.4px at 56px, scaling proportionally down to -0.2px at 20px. The negative tracking is the brand's typographic signature.
|
||||
- **Tabular figures for money.** Any cell rendering currency, transaction amounts, or numeric counts uses `font-feature-settings: "tnum"` plus a tightening tracking. The brand quietly signals its financial DNA through this micro-detail.
|
||||
- **`ss01` globally.** Apply `font-feature-settings: "ss01"` to the body element so the stylistic-set substitution is on for every text role.
|
||||
|
||||
### Note on Font Substitutes
|
||||
Sohne is proprietary. Use **Inter** (open-source via Google Fonts) at weight 300 with `letter-spacing: -1.4px` and `font-feature-settings: "ss01"` for display tiers — Inter is the closest open-source analogue. For body sizes, Inter at 300 weight with `font-feature-settings: "tnum"` (where applicable) is the canonical substitute. Avoid Helvetica or system-ui defaults — they're heavier than the brand needs.
|
||||
|
||||
## Layout
|
||||
|
||||
### Spacing System
|
||||
- **Base unit**: 8px (with 2 / 4 / 12 sub-tokens for fine work).
|
||||
- **Tokens**: `{spacing.xxs}` 2px · `{spacing.xs}` 4px · `{spacing.sm}` 8px · `{spacing.md}` 12px · `{spacing.lg}` 16px · `{spacing.xl}` 24px · `{spacing.xxl}` 32px · `{spacing.huge}` 64px.
|
||||
- **Section padding**: 64–96px on marketing surfaces; 32–48px on dashboard / product surfaces.
|
||||
- **Card internal padding**: 32px on feature cards; 24px on dashboard mockups.
|
||||
|
||||
### Grid & Container
|
||||
- Marketing pages center in a ~1200px container with the gradient mesh extending edge-to-edge above.
|
||||
- Pricing collapses 4-up → 2-up → 1-up at 1024 / 768 breakpoints.
|
||||
- Dashboard product mockups use their own internal grids (12-col tables, 3-col card grids) rendered as static composites.
|
||||
|
||||
### Whitespace Philosophy
|
||||
The gradient mesh occupies the upper third of the page; the white canvas below is generously padded. Section gaps tend toward 96px, with content tightening to 32px on dashboard / pricing pages where users compare and act.
|
||||
|
||||
## Elevation & Depth
|
||||
|
||||
| Level | Treatment | Use |
|
||||
|---|---|---|
|
||||
| 0 | Flat | Default surface |
|
||||
| 1 | `box-shadow: rgba(0,55,112,0.08) 0 1px 3px` | Card lift on white |
|
||||
| 2 | `box-shadow: rgba(0,55,112,0.08) 0 8px 24px, rgba(0,55,112,0.04) 0 2px 6px` | Floating panels, dashboard mockup chrome |
|
||||
| 3 | Gradient mesh backdrop | The brand's primary depth medium — atmospheric color rather than literal shadow |
|
||||
|
||||
### Decorative Depth
|
||||
The gradient mesh IS the depth system. Implemented as a layered SVG or large background image rather than CSS gradients (the actual mesh has organic blob shapes that aren't CSS-renderable). The mesh provides the brand's signature lift; literal shadows are reserved for product-UI mockups and stay subtle.
|
||||
|
||||
## Shapes
|
||||
|
||||
### Border Radius Scale
|
||||
|
||||
| Token | Value | Use |
|
||||
|---|---|---|
|
||||
| `{rounded.xs}` | 4px | Hairline tags, table chrome |
|
||||
| `{rounded.sm}` | 6px | Form inputs |
|
||||
| `{rounded.md}` | 8px | Compact cards, alerts |
|
||||
| `{rounded.lg}` | 12px | Pricing cards, feature cards |
|
||||
| `{rounded.xl}` | 16px | Dashboard product mockup chrome |
|
||||
| `{rounded.pill}` | 9999px | All buttons, tag pills |
|
||||
|
||||
### Photography Geometry
|
||||
The brand uses **product UI mockups** more than photography. Dashboard composites render as faux IDE/terminal/dashboard chrome inside `{rounded.lg}` 12px containers with a subtle `box-shadow`. Real photography appears in customer logo strips and the rare case-study card; treated as inset 4:3 with no shadow.
|
||||
|
||||
## Components
|
||||
|
||||
### Buttons
|
||||
|
||||
**`button-primary-pill`** — the dominant CTA system-wide.
|
||||
- Background `{colors.primary}`, text `{colors.on-primary}`, type `{typography.button-md}`, padding `{spacing.sm} {spacing.lg}` (8px 16px), rounded `{rounded.pill}` 9999px.
|
||||
- Pressed state `button-primary-pill-pressed` shifts background to `{colors.primary-press}`.
|
||||
|
||||
**`button-secondary`** — outline-style alternative.
|
||||
- Background `{colors.canvas}`, text `{colors.primary}`, 1px solid `{colors.primary}` border, same pill geometry.
|
||||
|
||||
**`button-on-dark`** — used on dashboard / dark surfaces.
|
||||
- Background `{colors.brand-dark-900}`, text `{colors.on-primary}`, same pill geometry.
|
||||
|
||||
### Cards & Containers
|
||||
|
||||
**`card-feature-light`** — feature explanation card on white.
|
||||
- Background `{colors.canvas}`, padding `{spacing.xxl}`, rounded `{rounded.lg}` 12px, 1px `{colors.hairline}` border, optional Level 1 shadow.
|
||||
|
||||
**`card-pricing`** — standard pricing tier.
|
||||
- Background `{colors.canvas}`, padding `{spacing.xxl}`, rounded `{rounded.lg}`, 1px `{colors.hairline}` border. Title `{typography.heading-lg}`, price `{typography.display-md}`, body `{typography.body-md}`, CTA pinned bottom as `button-primary-pill`.
|
||||
|
||||
**`card-pricing-featured`** — the inverted dark featured tier.
|
||||
- Background `{colors.brand-dark-900}`, text `{colors.on-primary}`, otherwise identical structure to `card-pricing`. The deep-navy fill is the brand's distinctive featured-tier choice.
|
||||
|
||||
**`card-cream-band`** — warm interlude card.
|
||||
- Background `{colors.canvas-cream}`, text `{colors.ink}`, padding `{spacing.xxl}`, rounded `{rounded.lg}`. Used to break up the indigo / white rhythm with warmth.
|
||||
|
||||
**`card-dashboard-mockup`** — composited dashboard / product UI screenshot.
|
||||
- Background `{colors.canvas}`, type `{typography.body-tabular}` (with `tnum`), padding `{spacing.xl}` 24px, rounded `{rounded.lg}` 12px, Level 2 shadow. Often contains nested mini-mockups: code preview + dashboard table + chart card.
|
||||
|
||||
### Inputs & Forms
|
||||
|
||||
**`text-input`** — standard form field.
|
||||
- Background `{colors.canvas}`, text `{colors.ink}`, type `{typography.body-md}`, padding `{spacing.sm} {spacing.md}` (8px 12px), rounded `{rounded.sm}` 6px, 1px `{colors.hairline-input}` border.
|
||||
- Focus state `text-input-focused`: border swaps to `{colors.primary}`.
|
||||
|
||||
### Navigation
|
||||
|
||||
**`nav-bar-on-mesh`** — top nav floating over the gradient hero.
|
||||
- Background `{colors.canvas}` (or transparent depending on scroll), text `{colors.ink}`, padding `{spacing.lg} {spacing.xl}`. Logo wordmark on the left, primary nav center, sign-in + filled `button-primary-pill` on the right.
|
||||
|
||||
### Pills, Tags, and Chips
|
||||
|
||||
**`pill-tag-soft`** — subdued indigo tag.
|
||||
- Background `{colors.primary-bg-subdued-hover}`, text `{colors.primary-deep}`, type `{typography.micro-cap}`, padding `4px 8px`, rounded `{rounded.pill}`.
|
||||
|
||||
### Signature Components
|
||||
|
||||
**Gradient Mesh Backdrop** — pastel cream → sherbet orange → lavender → indigo → ruby pink stops blurred horizontally across the upper third of the page. Implemented as SVG or a large background image — not a flat CSS gradient (the real mesh has organic blob shapes).
|
||||
|
||||
**Composited Dashboard Mockup** — multi-layer faux-product-UI compositions: an IDE panel on the left, a dashboard table center, a chart card on the right, all rendered at small scale inside `{rounded.lg}` containers with subtle Level 2 shadows. The composite is the brand's most-photographed feature.
|
||||
|
||||
**Tabular-Figure Money Type** — every number rendering money, count, or transaction value uses `font-feature-settings: "tnum"`. The brand's quiet signal that it's a financial-infrastructure platform.
|
||||
|
||||
**`link-on-light`** — inline links on light surfaces.
|
||||
- Text `{colors.primary}` rendered in `{typography.body-md}`, no underline by default.
|
||||
|
||||
**`footer-light`** — site-wide footer.
|
||||
- Background `{colors.canvas}`, text `{colors.ink-mute}`, type `{typography.caption}`, padding `{spacing.huge} {spacing.xl}` (64px 24px). Holds 4–6 columns of link groups, social icons, and a small legal row.
|
||||
|
||||
## Do's and Don'ts
|
||||
|
||||
### Do
|
||||
- Reserve `{colors.primary}` for filled CTAs and inline link emphasis — it should appear sparingly, one filled button per band.
|
||||
- Apply the gradient mesh to every marketing hero; bare-canvas heroes feel off-brand.
|
||||
- Render display tiers at weight 300 with negative letter-spacing — the thin tracking is the typographic signature.
|
||||
- Use `font-feature-settings: "tnum"` on every money / numeric cell.
|
||||
- Apply `font-feature-settings: "ss01"` globally on the body element.
|
||||
- Pair every feature explanation with a composited product UI mockup; the brand's argument is "look at the actual product."
|
||||
|
||||
### Don't
|
||||
- Don't bump display weight above 300 — at 400 the brand's editorial air collapses.
|
||||
- Don't add new accent colors outside the documented gradient stops (cream / orange / lavender / indigo / ruby / magenta).
|
||||
- Don't use the indigo `{colors.primary}` as a body-text color — it's a CTA and link color, not a type color at body size.
|
||||
- Don't shrink button padding below `8px 16px` — the tight pill is part of the brand's transactional feel.
|
||||
- Don't render money cells without `tnum` — it breaks the quiet financial-data signature.
|
||||
- Don't replace the pill shape with rounded-rectangles for buttons.
|
||||
|
||||
## Responsive Behavior
|
||||
|
||||
### Breakpoints
|
||||
|
||||
| Name | Width | Key Changes |
|
||||
|---|---|---|
|
||||
| Wide | ≥ 1440px | Full gradient mesh edge-to-edge; dashboard composite at full scale |
|
||||
| Desktop | 1024–1440px | Default content max-width; pricing 4-up |
|
||||
| Tablet | 768–1023px | Pricing 2-up; dashboard composite simplifies to 2 panels |
|
||||
| Mobile | < 768px | Pricing 1-up; hamburger nav; display drops 56 → 36px |
|
||||
|
||||
### Touch Targets
|
||||
- Pill buttons hit ≥ 40×40px on mobile via padding scaling. On smaller screens, buttons size up to 44×44px to maintain WCAG AAA.
|
||||
- Form fields stay at 40px minimum height.
|
||||
|
||||
### Collapsing Strategy
|
||||
- Display tiers stair-step 56 → 48 → 32 → 26 → 22px through the breakpoints.
|
||||
- Gradient mesh re-tiles on mobile to preserve the wash without disappearing.
|
||||
- Dashboard composites simplify to single-panel mockups on mobile; the multi-layer composition only renders at desktop+.
|
||||
- Pricing tiers stair-step 4-up → 2-up → 1-up.
|
||||
|
||||
### Image Behavior
|
||||
Product UI composites use `srcset` with art-direction crops at major breakpoints. Mobile crops focus on the most actionable inner panel; desktop crops show the full multi-layer composition.
|
||||
|
||||
## Iteration Guide
|
||||
|
||||
1. Focus on ONE component at a time.
|
||||
2. Reference component names and tokens directly (`{colors.primary}`, `{button-primary-pill}-pressed`, `{rounded.pill}`).
|
||||
3. Run `npx @google/design.md lint DESIGN.md` after edits.
|
||||
4. Add new variants as separate entries.
|
||||
5. Default body to `{typography.body-md}` (15px); use `{typography.body-tabular}` for any money / numeric cell.
|
||||
6. Apply `ss01` globally on the body; apply `tnum` per-element on numeric content.
|
||||
7. The gradient mesh is non-negotiable on marketing heroes — bare-canvas heroes break the brand.
|
||||
@@ -0,0 +1,5 @@
|
||||
# Stripe Inspired Design System
|
||||
|
||||
Design system details have been moved to: https://getdesign.md/stripe/design-md
|
||||
|
||||
You can also view previews, dark mode examples, and download options on getdesign.md.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,295 @@
|
||||
/* ─────────────────────────────────────────────────────────────────────────
|
||||
* design-systems/stripe/tokens.css
|
||||
*
|
||||
* Structured token bindings for "Inspired by Stripe" — fintech
|
||||
* infrastructure dressed in deep navy, signature violet, and the
|
||||
* blue-tinted multi-layer shadow that makes elevation feel branded.
|
||||
*
|
||||
* This file pre-compiles the values described in `DESIGN.md` into
|
||||
* the schema shared with every OD design system. Agents generating a
|
||||
* Stripe-flavored artifact should paste the `:root { … }` block
|
||||
* verbatim into the first `<style>` of the artifact, then reference
|
||||
* every value through `var(--name)` — never re-author the hex
|
||||
* inline. The fixture in `components.html` is the round-trip proof
|
||||
* that the token block alone is sufficient to render a recognisably
|
||||
* Stripe page.
|
||||
*
|
||||
* Why this file exists:
|
||||
* DESIGN.md gives humans context ("Stripe Purple #533afd — primary
|
||||
* CTAs"), but agents have to translate prose names like
|
||||
* "Stripe Purple" into the standard token names the lint enforces
|
||||
* (`--accent`). This file pre-translates the brand once, so agents
|
||||
* copy structure rather than invent it.
|
||||
*
|
||||
* Schema notes (Stripe binds the shared schema in seven brand-
|
||||
* specific ways — see `#Stripe N` tags inline for each decision):
|
||||
* #Stripe 1 — 3-tier surface stops short of a darker third tier;
|
||||
* `--surface-warm` binds to the cool-pale #f6f9fc that
|
||||
* Stripe uses behind nested panels (pricing rows, code
|
||||
* previews) rather than to a warm parchment.
|
||||
* #Stripe 2 — 3-tier foreground (navy heading / dark-slate label /
|
||||
* slate body). No fourth metadata tier — `--meta`
|
||||
* aliases `var(--muted)`.
|
||||
* #Stripe 3 — One border tier. `--border-soft` aliases `--border`
|
||||
* because DESIGN.md never describes a separate
|
||||
* row-separator weight.
|
||||
* #Stripe 4 — `--accent-hover` and `--accent-active` bind to the
|
||||
* hand-picked Purple Hover (#4434d4) and Purple Deep
|
||||
* (#2e2b8c) values from DESIGN.md, not the schema's
|
||||
* black-mix formula. Stripe's saturation needs the
|
||||
* exact shifts.
|
||||
* #Stripe 5 — Type scale ceiling is 56px (`--text-4xl`), not 64px.
|
||||
* DESIGN.md §3 puts the display hero at 56px / weight
|
||||
* 300 with -1.4px tracking — Stripe's "whisper
|
||||
* authority". Adding 64px would push past the brand's
|
||||
* self-imposed ceiling.
|
||||
* #Stripe 6 — `--elev-raised` carries Stripe's trademark
|
||||
* multi-layer blue-tinted shadow
|
||||
* (`rgba(50,50,93,0.25) … , rgba(0,0,0,0.1) …`). This
|
||||
* is the single most brand-distinctive token in the
|
||||
* file; rebinding it to a neutral drop shadow erases
|
||||
* Stripe's atmosphere even when every color is right.
|
||||
* #Stripe 7 — `--focus-ring` is a layered purple halo — a thin
|
||||
* solid edge plus a translucent outer glow. Stripe's
|
||||
* focus state is more visible than the schema default
|
||||
* because keyboard navigation in payment flows is a
|
||||
* first-class concern.
|
||||
*
|
||||
* Brand-specific extensions (Layer C):
|
||||
* None. The dark brand section (`#1c1e54`) and ruby/magenta
|
||||
* gradient accents from DESIGN.md are *decorative* — they exist
|
||||
* only as one-off inline expressions in components.html, derived
|
||||
* from `--accent` via `color-mix(...)`. They are not promoted to
|
||||
* tokens because no cross-component rule needs to reference them
|
||||
* by name; promoting them would force every other brand to declare
|
||||
* matching slots.
|
||||
* ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
:root {
|
||||
/* ─── Surface (3 levels — #Stripe 1) ──────────────────────────────
|
||||
* Stripe sits on pure white. The "warm" tier is a cool-pale
|
||||
* blue-tinted #f6f9fc (DESIGN.md's neutral-pill border colour) —
|
||||
* it reads as a separate elevation under nested panels (pricing
|
||||
* rows, code previews, sticky nav blur) without introducing a
|
||||
* warm cast that would clash with the navy / violet palette.
|
||||
* Brands without a tertiary surface tier should alias
|
||||
* `--surface-warm` to `var(--surface)`; Stripe binds a real value
|
||||
* because its dense data panels need that quiet separation. */
|
||||
--bg: #ffffff;
|
||||
--surface: #ffffff;
|
||||
--surface-warm: #f6f9fc; /* cool-pale blue — nested panels, sticky nav */
|
||||
|
||||
/* ─── Foreground ramp (3 tiers — #Stripe 2) ─────────────────────
|
||||
* Deep Navy (`#061b31`) carries every heading — never pure
|
||||
* black, never gray. DESIGN.md is explicit: "warmth matters".
|
||||
* Dark Slate (`#273951`) is the form-label tier. Slate (`#64748d`)
|
||||
* is the body / caption / placeholder colour.
|
||||
*
|
||||
* Stripe doesn't differentiate a fourth metadata tier, so
|
||||
* `--meta` aliases `var(--muted)`. Brands like kami that need
|
||||
* dates / timestamps / footnote text at a fourth shade may bind
|
||||
* an independent value. */
|
||||
--fg: #061b31; /* deep navy — headings, nav text, strong labels */
|
||||
--fg-2: #273951; /* dark slate — form labels, sub-headings */
|
||||
--muted: #64748d; /* slate — body, placeholders, captions */
|
||||
--meta: var(--muted); /* alias — Stripe has no separate metadata tier */
|
||||
|
||||
/* ─── Border (1 tier — #Stripe 3) ───────────────────────────────
|
||||
* One border weight for everything: card edges, dividers,
|
||||
* containers. DESIGN.md's purple/magenta/dashed border variants
|
||||
* are *decorative* (drop-zones, themed badges), not row
|
||||
* separators — they live inline in components.html and do not
|
||||
* earn a slot in the schema. `--border-soft` aliases. */
|
||||
--border: #e5edf5;
|
||||
--border-soft: var(--border); /* alias — Stripe has one border weight */
|
||||
|
||||
/* ─── Accent ──────────────────────────────────────────────────────
|
||||
* Stripe Purple — primary CTAs, link text, interactive
|
||||
* highlights, the focus ring. A saturated blue-violet that
|
||||
* anchors the entire system. Hard cap of ≤2 visible uses per
|
||||
* screen (lint `accent-overuse` P1 enforces). */
|
||||
--accent: #533afd;
|
||||
--accent-on: #ffffff; /* button label on purple — DESIGN.md §4 */
|
||||
|
||||
/* ─── Accent states (#Stripe 4) ─────────────────────────────────
|
||||
* Hand-picked values from DESIGN.md §2, NOT a black-mix formula.
|
||||
* Stripe Purple is saturated enough that the schema's
|
||||
* `color-mix(in oklab, var(--accent), black 8%)` produces a
|
||||
* muddy violet rather than the deliberate Purple Hover that
|
||||
* Stripe.com ships. Use the brand's own values.
|
||||
*
|
||||
* Schema rule: every brand provides `--accent-hover` and
|
||||
* `--accent-active`. The binding strategy (formula / identity /
|
||||
* hand-picked) is brand-decided. */
|
||||
--accent-hover: #4434d4; /* Purple Hover — DESIGN.md §2 */
|
||||
--accent-active: #2e2b8c; /* Purple Deep — DESIGN.md §2 */
|
||||
|
||||
/* ─── Semantic ───────────────────────────────────────────────────
|
||||
* Status colours pulled from DESIGN.md §2. Stripe's success is a
|
||||
* deliberately vivid green (`#15be53`) because payment-success
|
||||
* confirmations are the system's happy path — that pixel deserves
|
||||
* the saturation. Warn is the muted lemon Stripe uses for
|
||||
* highlight pills; danger is the ruby accent because rejected
|
||||
* cards and disputed charges are the only "danger" surface
|
||||
* Stripe.com renders, and they ship ruby — not the schema's
|
||||
* neutral red — for the alert. */
|
||||
--success: #15be53; /* DESIGN.md success green */
|
||||
--warn: #9b6829; /* lemon — warning / highlight accent */
|
||||
--danger: #ea2261; /* ruby — used for alerts, dispute states */
|
||||
|
||||
/* ─── Typography — fonts ─────────────────────────────────────────
|
||||
* `sohne-var` is the defining element of Stripe's identity
|
||||
* (DESIGN.md §1). It is a custom face and will not load for
|
||||
* most readers; the fallback chain is the closest SF / system
|
||||
* sans we can reach without licensing. Components must always
|
||||
* set `font-feature-settings: "ss01"` on top of these stacks —
|
||||
* the stylistic set is non-negotiable when sohne-var IS loaded.
|
||||
*
|
||||
* `--font-mono` is `SourceCodePro` per DESIGN.md §3 — the
|
||||
* brand's monospace companion, used for code blocks at 12px /
|
||||
* 500 with 2.00 line-height. */
|
||||
--font-display:
|
||||
"sohne-var", "Söhne", "Sohne",
|
||||
"SF Pro Display", -apple-system, BlinkMacSystemFont,
|
||||
system-ui, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-body:
|
||||
"sohne-var", "Söhne", "Sohne",
|
||||
"SF Pro Display", -apple-system, BlinkMacSystemFont,
|
||||
system-ui, "Helvetica Neue", Arial, sans-serif;
|
||||
--font-mono:
|
||||
"SourceCodePro", "Source Code Pro",
|
||||
ui-monospace, "SF Mono", "JetBrains Mono",
|
||||
Menlo, Monaco, Consolas, monospace;
|
||||
|
||||
/* ─── Typography — type scale (#Stripe 5) ───────────────────────
|
||||
* Direct mapping of DESIGN.md §3 hierarchy: 12 / 14 / 16 / 18 /
|
||||
* 22 / 32 / 48 / 56. Stripe's display ceiling is 56px (weight
|
||||
* 300, -1.4px tracking) — adding a 64px tier would push past
|
||||
* the brand's self-imposed limit on hero loudness. The smaller
|
||||
* 8px / 10px / 11px / 13px sizes from DESIGN.md's table are
|
||||
* intentional micro sizes (chart axes, financial fine print) and
|
||||
* are inlined where used rather than promoted to the shared
|
||||
* scale. */
|
||||
--text-xs: 12px; /* caption small, dense labels */
|
||||
--text-sm: 14px; /* button small, navigation links */
|
||||
--text-base: 16px; /* body baseline */
|
||||
--text-lg: 18px; /* body large, feature lede */
|
||||
--text-xl: 22px; /* sub-heading, card title */
|
||||
--text-2xl: 32px; /* section heading */
|
||||
--text-3xl: 48px; /* display large */
|
||||
--text-4xl: 56px; /* display hero — ceiling */
|
||||
|
||||
/* ─── Typography — leading & tracking ───────────────────────────
|
||||
* Reading rhythm is 1.40 — denser than the schema-default 1.5
|
||||
* because Stripe's body type (sohne-var at weight 300) reads
|
||||
* lighter than Inter / system sans, and slightly tighter
|
||||
* leading prevents the page from feeling airy.
|
||||
*
|
||||
* `--leading-tight` is the headline rhythm — 1.10 matches
|
||||
* DESIGN.md's section-heading line-height. The 1.03 / 1.15
|
||||
* variations for individual display sizes are tuned in
|
||||
* components.html overrides; this token is the default.
|
||||
*
|
||||
* `--tracking-display` is brand-defining: Stripe compresses
|
||||
* display sizes with negative letter-spacing (-1.4px at 56px →
|
||||
* -0.025em). The token uses -0.02em so the rule reads
|
||||
* consistently across the display range (48px → -0.96px,
|
||||
* 32px → -0.64px, all close to -0.02em). Components targeting
|
||||
* the 56px hero may override locally for the exact -1.4px. */
|
||||
--leading-body: 1.40;
|
||||
--leading-tight: 1.10;
|
||||
--tracking-display: -0.02em;
|
||||
|
||||
/* ─── Spacing ────────────────────────────────────────────────────
|
||||
* 4px base unit. DESIGN.md §5 calls out a denser scale at the
|
||||
* small end (every 2px from 4 → 12) for precision data displays;
|
||||
* those fine-grained values are inlined inside specific
|
||||
* financial-data components rather than promoted to tokens —
|
||||
* the schema's 4/8/12/16/20/24/32/48 ladder covers the
|
||||
* UI-chrome rhythm Stripe actually uses on hero / nav / card
|
||||
* surfaces. */
|
||||
--space-1: 4px;
|
||||
--space-2: 8px;
|
||||
--space-3: 12px;
|
||||
--space-4: 16px;
|
||||
--space-5: 20px;
|
||||
--space-6: 24px;
|
||||
--space-8: 32px;
|
||||
--space-12: 48px;
|
||||
|
||||
/* ─── Section rhythm ─────────────────────────────────────────────
|
||||
* Generous vertical breathing room desktop-side (96px) so the
|
||||
* 56px hero has air above and below; tightens to 64px on
|
||||
* tablet, 40px on phone per DESIGN.md §8 ("64px → 40px on
|
||||
* mobile"). */
|
||||
--section-y-desktop: 96px;
|
||||
--section-y-tablet: 64px;
|
||||
--section-y-phone: 40px;
|
||||
|
||||
/* ─── Radius ─────────────────────────────────────────────────────
|
||||
* Conservative rounding, 4 → 6 → 8 (DESIGN.md §5). Buttons,
|
||||
* inputs, and badges share the 4px small tier; nav containers
|
||||
* and dropdown shells take 6px; featured cards and hero
|
||||
* elements take 8px. No pill shapes on cards or buttons —
|
||||
* DESIGN.md §7 Don't list. `--radius-pill` is kept as a schema
|
||||
* slot (avatars only) but is never used on interactive
|
||||
* surfaces. */
|
||||
--radius-sm: 4px; /* buttons, inputs, badges, cards */
|
||||
--radius-md: 6px; /* nav container, comfortable cards */
|
||||
--radius-lg: 8px; /* featured cards, hero elements */
|
||||
--radius-pill: 9999px; /* avatars only — never on buttons / cards */
|
||||
|
||||
/* ─── Elevation (3 levels — #Stripe 6) ──────────────────────────
|
||||
* The single most brand-distinctive token in this file.
|
||||
*
|
||||
* Stripe's shadow philosophy is "chromatic depth": the primary
|
||||
* shadow colour is a deep blue-gray (`rgba(50,50,93,0.25)`) that
|
||||
* echoes the navy palette, paired with a pure-black secondary
|
||||
* layer at a different offset for parallax. Negative spread
|
||||
* values (-30px, -18px) keep the shadow vertical so cards don't
|
||||
* fringe sideways.
|
||||
*
|
||||
* Rebinding `--elev-raised` to a neutral drop shadow strips
|
||||
* Stripe's atmosphere even when every other colour is correct —
|
||||
* the shadow IS part of the brand voice, not chrome around it.
|
||||
* Brands forbidding chromatic shadows (kami) override; Stripe
|
||||
* defines. */
|
||||
--elev-flat: none;
|
||||
--elev-ring: 0 0 0 1px var(--border);
|
||||
--elev-raised:
|
||||
rgba(50, 50, 93, 0.25) 0px 30px 45px -30px,
|
||||
rgba(0, 0, 0, 0.10) 0px 18px 36px -18px;
|
||||
|
||||
/* ─── Focus ring (#Stripe 7) ────────────────────────────────────
|
||||
* Layered purple halo — a 2px solid edge in `--accent` plus an
|
||||
* outer translucent glow. DESIGN.md §6 specifies the solid 2px
|
||||
* ring; we add the soft outer halo so keyboard focus is
|
||||
* unambiguously visible against pale surfaces (payment flows
|
||||
* are keyboard-heavy, the brand spec mandates clarity).
|
||||
*
|
||||
* Implemented as box-shadow stack so it layers outside the
|
||||
* element without affecting layout. */
|
||||
--focus-ring:
|
||||
0 0 0 2px var(--accent),
|
||||
0 0 0 5px color-mix(in oklab, var(--accent), transparent 75%);
|
||||
|
||||
/* ─── Motion ─────────────────────────────────────────────────────
|
||||
* Two durations + one easing curve, per the anti-ai-slop
|
||||
* "short, purposeful transitions (150–250ms) with stable
|
||||
* easing" contract. Stripe's hover transitions colour and shadow
|
||||
* but never transform — buttons do not nudge or scale on hover;
|
||||
* cards lift via shadow intensity only. */
|
||||
--motion-fast: 150ms;
|
||||
--motion-base: 200ms;
|
||||
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
|
||||
|
||||
/* ─── Layout ─────────────────────────────────────────────────────
|
||||
* 1080px container ceiling per DESIGN.md §5. Per-breakpoint
|
||||
* gutter compresses 32 → 24 → 16 — generous on desktop because
|
||||
* the white canvas wants air; tight on phone because every
|
||||
* pixel is content. */
|
||||
--container-max: 1080px;
|
||||
--container-gutter-desktop: 32px;
|
||||
--container-gutter-tablet: 24px;
|
||||
--container-gutter-phone: 16px;
|
||||
}
|
||||
Reference in new issue
Block a user