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:
Z User committed 2026-10-27 22:50:50 +00:00
1 parent 7acc0c5451
commit e45b68bb8b
1089 files changed
+260614

No files matched your search

@@ -0,0 +1,482 @@
---
version: alpha
name: Slack
description: "An inspired interpretation of Slack's design language — a workplace messaging brand built on a deep aubergine primary, with cream-lavender hero gradients, blue inline links, and pill CTAs. The system pairs a proprietary humanist sans for display with a separate utility sans for body, and stages product UI mockups inside soft pastel-mesh hero composites that act as both decoration and feature explanation."
colors:
primary: "#4a154b"
primary-deep: "#481a54"
primary-press: "#611f69"
primary-tint: "#592466"
on-primary: "#ffffff"
ink: "#1d1d1d"
ink-mute: "#696969"
link-blue: "#1264a3"
link-hover: "#3860be"
canvas: "#ffffff"
canvas-cream: "#f4ede4"
canvas-lavender: "#f9f0ff"
surface-elev: "#ffffff"
surface-aubergine: "#4a154b"
hairline: "#e6e6e6"
hairline-strong: "#000000"
semantic-error: "#cc4117"
semantic-success: "#007a5a"
on-aubergine-mute: "#d9bdde"
typography:
display-xxl:
fontFamily: "Salesforce-Avant-Garde, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
fontSize: 64px
fontWeight: 700
lineHeight: 1.12
letterSpacing: -0.768px
display-xl:
fontFamily: "Salesforce-Avant-Garde, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
fontSize: 58px
fontWeight: 600
lineHeight: 1.25
letterSpacing: -0.464px
display-lg:
fontFamily: "Salesforce-Avant-Garde, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
fontSize: 50px
fontWeight: 700
lineHeight: 1.12
letterSpacing: -0.6px
display-md:
fontFamily: "Salesforce-Avant-Garde, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
fontSize: 32px
fontWeight: 700
lineHeight: 1.25
letterSpacing: -0.256px
heading-lg:
fontFamily: "Salesforce-Avant-Garde, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
fontSize: 24px
fontWeight: 700
lineHeight: 1.33
letterSpacing: -0.096px
heading-md:
fontFamily: "Salesforce-Avant-Garde, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
fontSize: 22px
fontWeight: 600
lineHeight: 1.4
letterSpacing: 0
heading-sm:
fontFamily: "Salesforce-Avant-Garde, system-ui, -apple-system, BlinkMacSystemFont, sans-serif"
fontSize: 18px
fontWeight: 600
lineHeight: 1.56
letterSpacing: -0.0216px
body-lg:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 18px
fontWeight: 400
lineHeight: 1.55
letterSpacing: -0.0216px
body-md:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 16px
fontWeight: 400
lineHeight: 1.55
letterSpacing: 0
body-strong:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 16px
fontWeight: 700
lineHeight: 1.5
letterSpacing: 0.16px
button-lg:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 18px
fontWeight: 700
lineHeight: 1.0
letterSpacing: 0
button-md:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 16px
fontWeight: 700
lineHeight: 1.38
letterSpacing: 0.2px
button-cap:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 14.4px
fontWeight: 700
lineHeight: 1.0
letterSpacing: 0.144px
caption:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 14px
fontWeight: 400
lineHeight: 1.43
letterSpacing: 0.1px
micro-cap:
fontFamily: "Salesforce-Sans, system-ui, -apple-system, sans-serif"
fontSize: 12px
fontWeight: 700
lineHeight: 1.0
letterSpacing: 0.96px
rounded:
xs: 2px
sm: 4px
md: 8px
lg: 12px
xl: 16px
xxl: 48px
pill: 90px
spacing:
xs: 4px
sm: 8px
md: 12px
lg: 16px
xl: 20px
xxl: 24px
huge: 28px
components:
button-primary-pill:
backgroundColor: "{colors.primary}"
textColor: "{colors.on-primary}"
typography: "{typography.button-md}"
rounded: "{rounded.pill}"
padding: 14px 28px
button-primary-pill-pressed:
backgroundColor: "{colors.primary-press}"
textColor: "{colors.on-primary}"
typography: "{typography.button-md}"
rounded: "{rounded.pill}"
padding: 14px 28px
button-secondary-pill:
backgroundColor: "{colors.canvas-lavender}"
textColor: "{colors.ink}"
typography: "{typography.button-md}"
rounded: "{rounded.pill}"
padding: 10px 30px
button-outline-aubergine:
backgroundColor: "{colors.canvas}"
textColor: "{colors.primary}"
typography: "{typography.button-md}"
rounded: "{rounded.pill}"
padding: 14px 28px
button-outline-on-aubergine:
backgroundColor: "{colors.surface-aubergine}"
textColor: "{colors.on-primary}"
typography: "{typography.button-md}"
rounded: "{rounded.pill}"
padding: 14px 28px
text-input:
backgroundColor: "{colors.canvas}"
textColor: "{colors.ink}"
typography: "{typography.body-md}"
rounded: "{rounded.sm}"
padding: 10px 12px
pill-cap-shade:
backgroundColor: "{colors.canvas-cream}"
textColor: "{colors.ink}"
typography: "{typography.micro-cap}"
rounded: "{rounded.pill}"
padding: 4px 12px
card-pricing:
backgroundColor: "{colors.canvas}"
textColor: "{colors.ink}"
typography: "{typography.body-md}"
rounded: "{rounded.xl}"
padding: 32px
card-pricing-featured:
backgroundColor: "{colors.surface-aubergine}"
textColor: "{colors.on-primary}"
typography: "{typography.body-md}"
rounded: "{rounded.xl}"
padding: 32px
card-feature-cream:
backgroundColor: "{colors.canvas-cream}"
textColor: "{colors.ink}"
typography: "{typography.body-md}"
rounded: "{rounded.xl}"
padding: 32px
card-aubergine-band:
backgroundColor: "{colors.surface-aubergine}"
textColor: "{colors.on-primary}"
typography: "{typography.body-lg}"
rounded: "{rounded.xl}"
padding: 48px
card-stat:
backgroundColor: "{colors.canvas}"
textColor: "{colors.primary}"
typography: "{typography.display-lg}"
rounded: "{rounded.xl}"
padding: 32px
nav-bar-light:
backgroundColor: "{colors.canvas}"
textColor: "{colors.ink}"
typography: "{typography.body-md}"
rounded: "{rounded.xs}"
padding: 16px 24px
link-on-light:
backgroundColor: "{colors.canvas}"
textColor: "{colors.link-blue}"
typography: "{typography.body-md}"
rounded: "{rounded.xs}"
padding: 0px
link-on-aubergine:
backgroundColor: "{colors.surface-aubergine}"
textColor: "{colors.on-primary}"
typography: "{typography.body-md}"
rounded: "{rounded.xs}"
padding: 0px
footer-aubergine:
backgroundColor: "{colors.surface-aubergine}"
textColor: "{colors.on-primary}"
typography: "{typography.caption}"
rounded: "{rounded.xs}"
padding: 32px 24px
---
## Overview
Slack's design language centers on a deep aubergine primary (`{colors.primary}`) — the brand's most enduring visual asset — applied as the dominant button color, the footer band, the featured pricing tier, and the brand wordmark. Around that aubergine the system stages an unusually delicate ecosystem: cream-lavender hero canvases with soft pastel-mesh gradients (peachy oranges, lavenders, dusty greens) that pulse behind floating product UI mockups, with the actual interface chrome rendered in fine detail at 3:2 aspect.
Typography splits between two proprietary humanist sans families. The display tier runs at 700 weight at sizes 32–64px with negative letter-spacing for tight optical density on hero headlines. The UI tier uses the second family at 400–700 with slightly relaxed leading (1.55) — the brand's body copy reads quietly without competing with the aubergine moments.
Buttons are pill-shaped at 90px radius with an unusual amount of horizontal padding (28–30px), giving them a distinctly comfortable, almost over-padded feel. The primary aubergine pill is the only filled button in most contexts; secondary actions use a soft lavender pill (`{colors.canvas-lavender}`) which reads as a gentler echo of the primary surface. Inline links shift to a saturated blue (`{colors.link-blue}`) — the brand's only chromatic departure from the aubergine-and-cream world.
**Key Characteristics:**
- Single aubergine primary (`{colors.primary}`) reused across CTAs, the featured pricing tier, the footer band, and the wordmark — the brand's chromatic monotheism.
- Cream-lavender hero canvas (`{colors.canvas-cream}` / `{colors.canvas-lavender}`) with diffused pastel-mesh atmospheric gradients and floating UI mockups composited above.
- Pill buttons at `{rounded.pill}` (90px radius) with generous 28–30px horizontal padding — over-padded by SaaS-default standards, deliberately so.
- Tight negative letter-spacing on display sizes (-0.768px on 64px hero) for editorial-density headlines.
- Blue inline links (`{colors.link-blue}`) — the only non-aubergine chromatic accent in body type.
- Pastel-mesh gradient atmospherics: every hero band has a subtle peach-lavender-dusty-green wash behind it; product UI sits on top, never inside, the gradient.
- Statistics cards rendered in massive aubergine display type (90% / 43 / 87%) on white — quantitative emphasis through scale alone.
## Colors
> **Source pages:** home (`/`), `/features/channels`, `/pricing`, `/contact-sales`.
### Brand & Accent
- **Aubergine** (`{colors.primary}` — `#4a154b`): The brand's primary surface and CTA color. Deep, warm purple with a hint of ruby — used on filled buttons, the featured pricing tier, the footer band, and the brand wordmark.
- **Aubergine Deep** (`{colors.primary-deep}` — `#481a54`): A near-identical sibling of `{colors.primary}` extracted from a different surface; treat as functionally equivalent.
- **Aubergine Press** (`{colors.primary-press}` — `#611f69`): Pressed-state lift of the primary, slightly lighter and warmer.
- **Aubergine Tint** (`{colors.primary-tint}` — `#592466`): Border accent on aubergine-on-aubergine surfaces.
- **Link Blue** (`{colors.link-blue}` — `#1264a3`): Inline link color — saturated, slightly warm blue. The only chromatic alternative to aubergine in body type.
- **Link Hover** (`{colors.link-hover}` — `#3860be`): A more saturated blue used on link hover state.
### Surface
- **Canvas White** (`{colors.canvas}` — `#ffffff`): Default content surface.
- **Canvas Cream** (`{colors.canvas-cream}` — `#f4ede4`): Warm off-white used on hero gradients and feature bands. Adds editorial warmth.
- **Canvas Lavender** (`{colors.canvas-lavender}` — `#f9f0ff`): Pale lavender tint used as the secondary-button surface and as a soft section band.
- **Surface Aubergine** (`{colors.surface-aubergine}` — `#4a154b`): The primary aubergine reused as a surface — featured pricing tier, footer, dark feature bands.
- **Hairline** (`{colors.hairline}` — `#e6e6e6`): 1px borders on cards and table dividers.
### Text
- **Ink** (`{colors.ink}` — `#1d1d1d`): Primary body text on light surfaces. Just shy of pure black.
- **Ink Mute** (`{colors.ink-mute}` — `#696969`): Secondary text, captions, helper copy.
- **On Primary** (`{colors.on-primary}` — `#ffffff`): Text on aubergine surfaces and filled CTAs.
- **On Aubergine Mute** (`{colors.on-aubergine-mute}` — `#d9bdde`): Secondary text on aubergine surfaces — a desaturated mauve that reads as muted-light.
### Semantic
- **Error** (`{colors.semantic-error}` — `#cc4117`): Form error and destructive-action color.
- **Success** (`{colors.semantic-success}` — `#007a5a`): Inline success indicators.
## Typography
### Font Family
The display tier is **Salesforce Avant Garde** — a proprietary humanist sans with broad apertures and a slightly geometric character. When unavailable, fall back to the system font stack (`system-ui, -apple-system, BlinkMacSystemFont`).
The UI tier is **Salesforce Sans** — a separate proprietary face used for body, captions, and button labels. Same fallback chain.
Both faces are proprietary and not freely available. Substitute with **Inter** (open-source via Google Fonts) at matching weights for both display and body — Inter is the closest open analogue across both tiers.
### Hierarchy
| Token | Size | Weight | Line Height | Letter Spacing | Use |
|---|---|---|---|---|---|
| `{typography.display-xxl}` | 64px | 700 | 1.12 | -0.768px | Marketing hero headline |
| `{typography.display-xl}` | 58px | 600 | 1.25 | -0.464px | Section openers |
| `{typography.display-lg}` | 50px | 700 | 1.12 | -0.6px | Statistics callouts |
| `{typography.display-md}` | 32px | 700 | 1.25 | -0.256px | Card / feature titles |
| `{typography.heading-lg}` | 24px | 700 | 1.33 | -0.096px | Pricing tier names |
| `{typography.heading-md}` | 22px | 600 | 1.4 | 0 | Sub-section heading |
| `{typography.heading-sm}` | 18px | 600 | 1.56 | -0.0216px | Compact card title |
| `{typography.body-lg}` | 18px | 400 | 1.55 | -0.0216px | Marketing body lead |
| `{typography.body-md}` | 16px | 400 | 1.55 | 0 | Default UI body |
| `{typography.body-strong}` | 16px | 700 | 1.5 | 0.16px | Emphasized body |
| `{typography.button-lg}` | 18px | 700 | 1.0 | 0 | Hero pill button label |
| `{typography.button-md}` | 16px | 700 | 1.38 | 0.2px | Standard pill button label |
| `{typography.button-cap}` | 14.4px | 700 | 1.0 | 0.144px | Compact pill label |
| `{typography.caption}` | 14px | 400 | 1.43 | 0.1px | Helper, footnote |
| `{typography.micro-cap}` | 12px | 700 | 1.0 | 0.96px | All-caps eyebrow |
### Principles
- **Tight tracking on display.** Negative letter-spacing across 32–64px sizes; the proprietary face is wide by default, the negative tracking pulls it into editorial density.
- **Body at 1.55 leading.** Slightly relaxed for marketing readability without crossing into airy / 1.7+ territory.
- **Caps for eyebrows.** All eyebrows render uppercase with positive 0.96–0.144px tracking depending on size.
### Note on Font Substitutes
Use **Inter** (open-source Google Fonts) for both display and UI tiers — Inter at 700 weight with `-0.768px` letter-spacing closely approximates the brand's display behavior. For maximum brand fidelity, **Lato** is a softer humanist alternative that pairs well at body sizes. Avoid System UI fonts on the body — the brand's subtle warmth disappears at default weights.
## Layout
### Spacing System
- **Base unit**: 8px (with 4 / 12 / 16 / 20 / 24 / 28 sub-tokens for fine vertical rhythm).
- **Tokens**: `{spacing.xs}` 4px · `{spacing.sm}` 8px · `{spacing.md}` 12px · `{spacing.lg}` 16px · `{spacing.xl}` 20px · `{spacing.xxl}` 24px · `{spacing.huge}` 28px.
- **Section padding**: 64–96px on marketing surfaces; tightens to 48px on transactional pages.
- **Card internal padding**: 32px on pricing cards; 48px on aubergine band cards.
### Grid & Container
- Marketing pages center in a ~1240px container with edge-bleeding pastel-mesh gradients escaping the container.
- Pricing collapses 4-up → 2-up → 1-up at 992 / 768 breakpoints.
- Statistics row: 3-column grid with massive 50px aubergine display numerals.
### Whitespace Philosophy
The pastel-mesh gradients fill most of the negative space on marketing pages — sections feel expansive without being literally empty. On transactional pages the gradients drop, and whitespace reverts to traditional 48px-section breathing room.
## Elevation & Depth
| Level | Treatment | Use |
|---|---|---|
| 0 | Flat | Default surface |
| 1 | `box-shadow: rgba(0,0,0,0.1) 0 5px 20px 0` | Floating buttons on hero |
| 2 | `box-shadow: rgba(0,0,0,0.1) 0 0 32px 0` | Product UI mockup composites |
| 3 | `box-shadow: rgba(0,0,0,0.2) 0 1px 10px 0` | Toast / notification chrome |
| 4 | `box-shadow: rgb(97,31,105) 0 0 0 1px inset` | Aubergine inset border (button focus, special chrome) |
### Decorative Depth
The brand's depth language is the **pastel-mesh gradient** — peach, lavender, dusty green stops blurred together at large radii to create soft atmospheric backdrops behind product UI screenshots. The gradient is the brand's flavor of "depth without shadows": the eye perceives the product mockup as floating above a luminous backdrop without any literal lift.
## Shapes
### Border Radius Scale
| Token | Value | Use |
|---|---|---|
| `{rounded.xs}` | 2px | Hairline tags, status pills (rare) |
| `{rounded.sm}` | 4px | Form inputs |
| `{rounded.md}` | 8px | Compact card chrome, video frames |
| `{rounded.lg}` | 12px | Mid-size cards, secondary surface |
| `{rounded.xl}` | 16px | Pricing cards, feature cards |
| `{rounded.xxl}` | 48px | Stat badge backdrops |
| `{rounded.pill}` | 90px | All buttons |
### Photography Geometry
The brand uses **product UI screenshots** more than photography. UI mockups sit on top of pastel-mesh gradients at roughly 4:3 aspect, with no shadow but with the gradient providing the "lift" the eye expects. Real photography appears in customer-logo strips and the occasional case-study card, treated as full-bleed inside `{rounded.xl}` containers.
## Components
### Buttons
**`button-primary-pill`** — the dominant CTA system-wide.
- Background `{colors.primary}`, text `{colors.on-primary}`, type `{typography.button-md}`, padding `14px 28px`, rounded `{rounded.pill}` 90px.
- Pressed state `button-primary-pill-pressed` shifts background to `{colors.primary-press}`.
**`button-secondary-pill`** — the soft lavender alternative.
- Background `{colors.canvas-lavender}`, text `{colors.ink}`, padding `10px 30px`, same pill geometry. Used as the second action beside the primary aubergine pill.
**`button-outline-aubergine`** — outline variant on white surfaces.
- Background `{colors.canvas}`, text `{colors.primary}`, 2px solid `{colors.primary}` border, same pill shape.
**`button-outline-on-aubergine`** — outline on aubergine canvas.
- Background `{colors.surface-aubergine}` (transparent over the surface), text `{colors.on-primary}`, 2px solid `{colors.on-primary}` border, same pill shape.
### Cards & Containers
**`card-pricing`** — standard pricing tier card.
- Background `{colors.canvas}`, padding `{spacing.xxl}+` (32px), rounded `{rounded.xl}` 16px, 1px `{colors.hairline}` border. Title in `{typography.heading-lg}`, price in `{typography.display-md}`, body in `{typography.body-md}`, CTA pinned to bottom as `button-primary-pill`.
**`card-pricing-featured`** — the inverted aubergine featured tier.
- Background `{colors.surface-aubergine}`, text `{colors.on-primary}`, otherwise identical to `card-pricing`. The aubergine fill is the brand's signature featured-tier choice.
**`card-feature-cream`** — feature explanation card on the cream track.
- Background `{colors.canvas-cream}`, text `{colors.ink}`, rounded `{rounded.xl}`, padding 32px.
**`card-aubergine-band`** — large horizontal band card with aubergine fill, often containing the closing CTA of a marketing page.
- Background `{colors.surface-aubergine}`, text `{colors.on-primary}`, padding 48px, rounded `{rounded.xl}` 16px.
**`card-stat`** — statistics callout card.
- Background `{colors.canvas}`, text `{colors.primary}` rendered in `{typography.display-lg}` (50px aubergine numeral). Holds a single percentage/number with a small caption underneath.
### Inputs & Forms
**`text-input`** — standard form field.
- Background `{colors.canvas}`, text `{colors.ink}`, type `{typography.body-md}`, padding `10px 12px`, rounded `{rounded.sm}` 4px, 1px `{colors.hairline}` border.
### Navigation
**`nav-bar-light`** — top nav across all marketing pages.
- Background `{colors.canvas}`, text `{colors.ink}`, padding `{spacing.lg} {spacing.xxl}`. Logo wordmark on the left, nav items center, two pill buttons on the right (`button-secondary-pill` for "Sign In", `button-primary-pill` for "Try For Free").
### Pills, Tags, and Chips
**`pill-cap-shade`** — small all-caps pill used as eyebrow above pricing-tier titles.
- Background `{colors.canvas-cream}`, text `{colors.ink}`, type `{typography.micro-cap}`, padding `4px 12px`, rounded `{rounded.pill}`.
### Signature Components
**Pastel-Mesh Gradient Backdrop** — peach (`#fff0e6`-ish) + lavender (`#e9d8ff`) + dusty green stops blurred together behind hero bands. Implemented as a CSS radial-gradient stack, not a single image. Provides the brand's depth/luminosity without literal shadows.
**Floating Product UI Mockup** — product screenshots framed in `{rounded.lg}` (12px) containers, positioned above the pastel-mesh gradient with no border or shadow. The gradient does the lifting.
**Aubergine Footer Band** — every marketing page closes with a full-bleed `card-aubergine-band` containing a closing CTA in white type. The band height is generous (~480–600px on desktop) and reads as the page's signature.
**`link-on-light`** — inline links in body copy on light surfaces.
- Text `{colors.link-blue}` rendered in `{typography.body-md}`. No underline by default; underline appears on hover via the link-hover behavior.
**`link-on-aubergine`** — links inside aubergine surfaces.
- Text `{colors.on-primary}` with persistent underline.
**`footer-aubergine`** — site-wide footer.
- Background `{colors.surface-aubergine}`, text `{colors.on-primary}` rendered in `{typography.caption}`, padding `{spacing.huge}+ {spacing.xxl}` (32px 24px). Holds 4–5 columns of `{colors.on-aubergine-mute}` link groups, social icons, and a small legal/copyright row at the bottom.
## Do's and Don'ts
### Do
- Reserve `{colors.primary}` aubergine for filled CTAs, the featured pricing tier, and the closing aubergine band — it's the brand's chromatic monotheism.
- Use `{rounded.pill}` (90px) for every button across the system — never a rounded-rectangle button.
- Pair display tiers with negative letter-spacing (`-0.768px` at 64px); the proprietary face needs the tracking pull.
- Compose hero bands with pastel-mesh gradient backdrop + floating product UI mockup; the gradient is the depth.
- Use `{colors.link-blue}` for inline links — it's the only chromatic departure from aubergine and is part of the brand voice.
### Don't
- Don't add a third accent color to the system — the aubergine + blue link combination is exhaustive.
- Don't shrink button padding below `14px 28px` — the over-padded pill is part of the brand feel.
- Don't render display tiers at default tracking (0) — without negative letter-spacing the headlines read loose and unedited.
- Don't put product UI screenshots inside cards — they sit ABOVE the pastel-mesh gradient, never inside chrome.
- Don't use aubergine for body text — it's a surface and CTA color, not a type color at body sizes.
- Don't replace the pill shape with a square button anywhere.
## Responsive Behavior
### Breakpoints
| Name | Width | Key Changes |
|---|---|---|
| Wide | ≥ 1440px | Full-bleed pastel-mesh hero; pricing 4-up |
| Desktop | 1024–1440px | Default content max-width; pricing 4-up |
| Tablet | 768–1023px | Pricing 2-up; product UI mockups crop to focal panel |
| Mobile | < 768px | Pricing 1-up; hamburger nav; display-xxl drops 64 → 40px |
### Touch Targets
- Pill buttons hit ≥ 48×48px due to the over-padded geometry. WCAG AAA compliant.
- Form fields stay at the 44px minimum height.
### Collapsing Strategy
- Display tiers stair-step 64 → 50 → 32 → 28 → 24 across breakpoints.
- Pastel-mesh gradients re-tile on mobile to prevent the wash from disappearing entirely.
- Floating product UI mockups crop to the most actionable inner panel on mobile.
- Pricing tiers stair-step 4 → 2 → 1; aubergine featured tier stays distinguished.
- Top nav collapses to hamburger below 768px; menu inherits canvas color.
### Image Behavior
Product UI mockups use `srcset` for desktop / tablet / mobile crops; the mobile crop centers on the most actionable inner panel rather than scaling the whole composite down.
## 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}`; reserve `{typography.body-lg}` for marketing leads.
6. Keep aubergine scarce — one filled aubergine button per viewport.
7. Pair every hero band with the pastel-mesh gradient backdrop; bare-canvas heroes read as off-brand.
@@ -0,0 +1,387 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Slack — reference components</title>
<meta
name="description"
content="Reference fixture for design-systems/slack. Every visible
value comes from tokens.css. Slack's signature moves: aubergine
sidebar (#4A154B), white content surfaces, Larsseit display,
4px button radius, logo-color semantic palette."
/>
<style>
:root {
--bg: #ffffff;
--surface: #f8f8f8;
--surface-warm: var(--surface);
--fg: #1d1c1d;
--fg-2: #1d1c1d;
--muted: #616061;
--meta: #616061;
--border: #dddddd;
--border-soft: #f1f1f1;
--accent: #4a154b;
--accent-on: #ffffff;
--accent-hover: #611f69;
--accent-active: #350d36;
--success: #2eb67d;
--warn: #ecb22e;
--danger: #e01e5a;
--font-display: "Larsseit", "Helvetica Neue", Arial, sans-serif;
--font-body: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
--font-mono: "Monaco", "Menlo", "Courier New", Courier, monospace;
--text-xs: 12px;
--text-sm: 13px;
--text-base: 15px;
--text-lg: 16px;
--text-xl: 22px;
--text-2xl: 28px;
--text-3xl: 36px;
--text-4xl: 48px;
--leading-body: 1.5;
--leading-tight: 1.10;
--tracking-display: -0.021em;
--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-y-desktop: 80px;
--section-y-tablet: 48px;
--section-y-phone: 32px;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-pill: 9999px;
--elev-flat: none;
--elev-ring: 0 0 0 1px var(--border);
--elev-raised: 0 4px 12px rgba(0, 0, 0, 0.12);
--focus-ring: 0 0 0 2px rgba(18, 100, 163, 0.25);
--motion-fast: 150ms;
--motion-base: 200ms;
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
--container-max: 1200px;
--container-gutter-desktop: 24px;
--container-gutter-tablet: 16px;
--container-gutter-phone: 16px;
}
*, *::before, *::after { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--fg);
font-family: var(--font-body);
font-size: var(--text-base);
line-height: var(--leading-body);
-webkit-font-smoothing: antialiased;
}
.container {
max-width: var(--container-max);
margin-inline: auto;
padding-inline: var(--container-gutter-desktop);
}
section { padding-block: var(--section-y-desktop); }
section + section { border-top: 1px solid var(--border-soft); }
@media (max-width: 1023px) {
.container { padding-inline: var(--container-gutter-tablet); }
section { padding-block: var(--section-y-tablet); }
}
@media (max-width: 639px) {
.container { padding-inline: var(--container-gutter-phone); }
section { padding-block: var(--section-y-phone); }
}
/* ─── Typography ─────────────────────────── */
h1, h2, h3 {
font-family: var(--font-display);
font-weight: 700;
margin: 0;
line-height: var(--leading-tight);
color: var(--fg);
}
h1 {
font-size: var(--text-4xl);
letter-spacing: var(--tracking-display);
}
h2 {
font-size: var(--text-3xl);
/* 36px section hero: line-height 1.15, tracking -0.5px (-0.014em). */
line-height: 1.15;
letter-spacing: -0.014em;
}
h3 {
font-size: var(--text-2xl);
/* 28px heading 1: line-height 1.25 per DESIGN.md.
Overrides the shared 1.10 set above. */
line-height: 1.25;
}
p { margin: 0; }
.lead {
font-size: var(--text-lg);
line-height: 1.5;
color: var(--muted);
}
.body-muted { color: var(--muted); }
.body-sm { font-size: var(--text-sm); }
.stack-3 > * + * { margin-block-start: var(--space-3); }
.stack-4 > * + * { margin-block-start: var(--space-4); }
.stack-6 > * + * { margin-block-start: var(--space-6); }
/* ─── Buttons ─────────────────────────────── */
.btn {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: 0 var(--space-4);
height: 36px;
border-radius: var(--radius-sm);
font-family: var(--font-body);
font-size: var(--text-base);
font-weight: 700;
line-height: 1;
cursor: pointer;
border: none;
transition: background-color var(--motion-fast) var(--ease-standard);
text-decoration: none;
}
.btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }
.btn-primary {
background: var(--accent);
color: var(--accent-on);
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-secondary {
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
}
.btn-secondary:hover { background: var(--surface); }
.btn-danger {
background: var(--danger);
color: #ffffff;
}
/* ─── Sidebar ─────────────────────────────── */
.sidebar {
background: var(--accent);
border-radius: var(--radius-md);
padding: var(--space-4);
color: rgba(255, 255, 255, 0.9);
font-size: var(--text-base);
display: flex;
flex-direction: column;
gap: var(--space-1);
min-width: 220px;
}
.sidebar-title {
font-weight: 700;
color: #ffffff;
font-size: var(--text-base);
padding: var(--space-2) var(--space-4);
margin-bottom: var(--space-2);
}
.channel-item {
display: flex;
align-items: center;
gap: var(--space-2);
height: 28px;
padding: 0 var(--space-4);
border-radius: 6px;
color: rgba(255, 255, 255, 0.7);
font-size: var(--text-base);
font-weight: 400;
cursor: pointer;
transition: background-color var(--motion-fast) var(--ease-standard),
color var(--motion-fast) var(--ease-standard);
}
.channel-item:hover {
background: rgba(255, 255, 255, 0.1);
color: #ffffff;
}
.channel-item.active {
background: rgba(255, 255, 255, 0.2);
color: #ffffff;
}
.channel-item.unread {
color: #ffffff;
font-weight: 700;
}
/* ─── Reaction ───────────────────────────── */
.reaction {
display: inline-flex;
align-items: center;
gap: var(--space-1);
border: 1px solid var(--border);
border-radius: 24px;
background: var(--surface);
padding: 2px 8px;
font-size: var(--text-sm);
cursor: pointer;
transition: background-color var(--motion-fast) var(--ease-standard);
}
.reaction:hover { background: var(--border-soft); }
.reaction.active {
background: rgba(18, 100, 163, 0.1);
border-color: #1264a3;
}
/* ─── Input ──────────────────────────────── */
.field { display: flex; flex-direction: column; gap: var(--space-2); }
.field label { font-size: var(--text-base); font-weight: 700; color: var(--fg); }
.field input {
background: var(--bg);
color: var(--fg);
border: 1px solid var(--border);
border-radius: var(--radius-sm);
padding: 8px 12px;
font-size: var(--text-base);
height: 36px;
outline: none;
transition: border-color var(--motion-fast) var(--ease-standard),
box-shadow var(--motion-fast) var(--ease-standard);
}
.field input::placeholder { color: var(--muted); }
.field input:focus {
border-color: #1264a3;
box-shadow: var(--focus-ring);
}
/* ─── Layout ─────────────────────────────── */
.hero-grid {
display: grid;
grid-template-columns: 1.3fr 1fr;
gap: var(--space-12);
align-items: center;
}
@media (max-width: 1023px) { .hero-grid { grid-template-columns: 1fr; } }
.features-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-5);
}
@media (max-width: 1023px) { .features-grid { grid-template-columns: 1fr 1fr; } }
@media (max-width: 639px) { .features-grid { grid-template-columns: 1fr; } }
.hero-actions { display: flex; gap: var(--space-3); margin-block-start: var(--space-6); flex-wrap: wrap; }
</style>
</head>
<body>
<main class="container">
<!-- HERO -->
<section data-od-id="hero">
<div class="hero-grid">
<div class="stack-4">
<h1>Made for people. Built for productivity.</h1>
<p class="lead" style="max-width:48ch">
Slack brings your team's communication and tools together in one
place. Warm, human, and structured around the aubergine sidebar
that's become a workplace icon.
</p>
<div class="hero-actions">
<a href="./tokens.css" class="btn btn-primary">Try Slack for free</a>
<a href="./DESIGN.md" class="btn btn-secondary">Read the spec</a>
</div>
</div>
<aside>
<div class="sidebar">
<div class="sidebar-title">open-design ▼</div>
<div class="channel-item"># threads</div>
<div class="channel-item"># all-dms</div>
<div class="channel-item active"># general</div>
<div class="channel-item unread"># design ●</div>
<div class="channel-item"># engineering</div>
<div class="channel-item"># random</div>
</div>
</aside>
</div>
</section>
<!-- FEATURES -->
<section data-od-id="features">
<h2 style="max-width:28ch;margin-bottom:var(--space-6)">Warm, professional, human.</h2>
<div class="features-grid">
<article style="border:1px solid var(--border);border-radius:var(--radius-md);padding:var(--space-6)" class="stack-3">
<h3>Aubergine sidebar</h3>
<p class="body-muted body-sm">
#4A154B — the most recognizable UI element in workplace software.
Channel items: 28px height, 6px radius, bold when unread.
</p>
</article>
<article style="border:1px solid var(--border);border-radius:var(--radius-md);padding:var(--space-6)" class="stack-3">
<h3>Logo palette</h3>
<p class="body-muted body-sm">
Blue (#36C5F0), green (#2EB67D), yellow (#ECB22E), red (#E01E5A) —
strictly semantic. Info, success, warning, danger. Never decorative.
</p>
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3)">
<span style="display:inline-block;width:20px;height:20px;border-radius:4px;background:#36c5f0"></span>
<span style="display:inline-block;width:20px;height:20px;border-radius:4px;background:#2eb67d"></span>
<span style="display:inline-block;width:20px;height:20px;border-radius:4px;background:#ecb22e"></span>
<span style="display:inline-block;width:20px;height:20px;border-radius:4px;background:#e01e5a"></span>
</div>
</article>
<article style="border:1px solid var(--border);border-radius:var(--radius-md);padding:var(--space-6)" class="stack-3">
<h3>Reactions</h3>
<p class="body-muted body-sm">
Pill border-radius 24px, 1px border, surface background. Active
state: tinted blue. Timestamps show on hover only.
</p>
<div style="display:flex;gap:var(--space-2);margin-top:var(--space-3);flex-wrap:wrap">
<span class="reaction active">👍 3</span>
<span class="reaction">🎉 1</span>
<span class="reaction">❤️ 2</span>
</div>
</article>
</div>
</section>
<!-- FORM -->
<section data-od-id="form">
<div style="display:grid;grid-template-columns:1.2fr 1fr;gap:var(--space-12);align-items:start">
<div class="stack-4">
<h2>Inputs and buttons.</h2>
<p class="body-muted" style="max-width:44ch">
4px radius on buttons and inputs — not cartoonish.
Focus ring: 2px solid #1264A3. Height 36px, font-size 15px.
</p>
</div>
<form style="display:flex;flex-direction:column;gap:var(--space-4);max-width:400px"
onsubmit="event.preventDefault()">
<div class="field">
<label for="email">Work email</label>
<input id="email" type="email" placeholder="you@company.com" />
</div>
<div style="display:flex;gap:var(--space-3);margin-top:var(--space-2)">
<button type="submit" class="btn btn-primary">Get started</button>
<button type="button" class="btn btn-secondary">Skip</button>
</div>
</form>
</div>
</section>
</main>
</body>
</html>
@@ -0,0 +1,127 @@
/* ─────────────────────────────────────────────────────────────────────────
* design-systems/slack/tokens.css
*
* Structured token bindings for the "Slack" brand — aubergine sidebar,
* light content surfaces, warm and human workplace communication.
*
* Brand identity in three sentences:
* 1. Light-first content surfaces (white #FFFFFF / near-white #F8F8F8)
* anchored by the iconic deep aubergine (#4A154B) sidebar — the most
* recognizable UI element in workplace software.
* 2. Larsseit for marketing display headings; system-ui for all product
* UI at 15px base (slightly below 16px for density); bold (700)
* channel names are the primary unread indicator.
* 3. Four logo accent colors (blue, green, yellow, red) are strictly
* semantic (info/success/warn/danger) — never decorative accents;
* #1264A3 for links and focus rings.
*
* Schema decisions:
* - --bg: #FFFFFF; --surface: #F8F8F8 (thread panels, secondary).
* - --surface-warm aliases --surface (no warm tier in Slack's system).
* - --fg: #1D1C1D (near-black — not pure black per brand spec).
* - --accent: #4A154B (aubergine — primary CTAs and the sidebar).
* - --accent-hover: #611f69 (active aubergine).
* - --leading-tight: 1.10 (display compression at 48px).
* - h2 (36px) → 1.15; h3 (28px) → 1.25 per DESIGN.md type table.
* - --tracking-display: -0.021em (-1px / 48px normalized).
* - --radius-sm: 4px (buttons/inputs — Slack uses conservative radius).
* ─────────────────────────────────────────────────────────────────── */
:root {
/* ─── Surface ──────────────────────────────────────────────────── */
--bg: #ffffff;
--surface: #f8f8f8; /* thread panels, secondary surfaces */
--surface-warm: var(--surface); /* no warm tier in Slack's system */
/* ─── Foreground ───────────────────────────────────────────────── */
--fg: #1d1c1d; /* near-black — not pure black */
--fg-2: #1d1c1d; /* same ink; no split tier */
--muted: #616061; /* timestamps, muted labels */
--meta: #616061; /* same muted tier */
/* ─── Border ───────────────────────────────────────────────────── */
--border: #dddddd; /* standard dividers, card borders */
--border-soft: #f1f1f1; /* subtle row separators */
/* ─── Accent ───────────────────────────────────────────────────── */
--accent: #4a154b; /* aubergine — primary CTAs, sidebar bg */
--accent-on: #ffffff;
--accent-hover: #611f69; /* active/hover aubergine */
--accent-active: #350d36; /* deep aubergine — pressed */
/* ─── Semantic ─────────────────────────────────────────────────── */
/* Logo palette: these ARE the semantic colors (info/success/warn/danger). */
--success: #2eb67d; /* Slack teal green — online, success */
--warn: #ecb22e; /* Slack gold — away, warning */
--danger: #e01e5a; /* Slack ruby — notifications, errors */
/* ─── Typography ───────────────────────────────────────────────── */
--font-display: "Larsseit", "Helvetica Neue", Arial, sans-serif;
--font-body: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
--font-mono: "Monaco", "Menlo", "Courier New", Courier, monospace;
/* Type scale — DESIGN.md §Type Scale.
* Slack base size is 15px (density). Display: 48px marketing hero.
* Schema maps: xs=12, sm=15, base=15(=body), lg=18, xl=22, 2xl=28,
* 3xl=36, 4xl=48. */
--text-xs: 12px; /* caption / timestamp */
--text-sm: 13px; /* body SM — secondary metadata */
--text-base: 15px; /* default UI text (Slack's base) */
--text-lg: 16px; /* body L — messages, descriptions */
--text-xl: 22px; /* heading 2 — card titles */
--text-2xl: 28px; /* heading 1 — modal titles */
--text-3xl: 36px; /* display L — section heroes */
--text-4xl: 48px; /* display XL — marketing hero */
/* Leading + tracking.
* --leading-tight=1.10: display hero compression.
* h2 (36px) → 1.15; h3 (28px) → 1.25 per DESIGN.md.
* --tracking-display: -0.021em = -1px / 48px normalized. */
--leading-body: 1.5;
--leading-tight: 1.10;
--tracking-display: -0.021em;
/* ─── Spacing ──────────────────────────────────────────────────── */
/* 4px base grid per DESIGN.md §Spacing System. */
--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. */
--section-y-desktop: 80px;
--section-y-tablet: 48px;
--section-y-phone: 32px;
/* ─── Radius ───────────────────────────────────────────────────── */
/* 4px is Slack's standard for buttons and inputs — not cartoonish. */
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-pill: 9999px;
/* ─── Elevation ────────────────────────────────────────────────── */
/* Light shadows on a light surface. */
--elev-flat: none;
--elev-ring: 0 0 0 1px var(--border);
--elev-raised: 0 4px 12px rgba(0, 0, 0, 0.12);
/* ─── Focus ────────────────────────────────────────────────────── */
/* Slack focus: blue ring on #1264A3. */
--focus-ring: 0 0 0 2px rgba(18, 100, 163, 0.25);
/* ─── Motion ───────────────────────────────────────────────────── */
--motion-fast: 150ms;
--motion-base: 200ms;
--ease-standard: cubic-bezier(0.2, 0, 0, 1);
/* ─── Layout ───────────────────────────────────────────────────── */
--container-max: 1200px;
--container-gutter-desktop: 24px;
--container-gutter-tablet: 16px;
--container-gutter-phone: 16px;
}