Apple Design System
Apple's web design is not "minimalism." It is a specific, reproducible system: a near-monochrome palette with one accent, a large optically-tuned type scale, generous whitespace, hairline borders instead of shadows, and motion that is short and eased. Copying the feel without the values produces something that looks almost-Apple and reads as cheap. Use the exact numbers below.
Values marked [verified] were read from Apple's live CSS. Values marked [measured] come from teardowns of rendered apple.com pages. Values marked [derived] are consistent extensions of the system where Apple publishes nothing. Prefer verified values; never invent new ones when a verified one exists.
The three rules that matter most
1. Deference — the UI never competes with the content. Per Apple's HIG, the interface helps users understand content but never competes with it. In practice: no decorative gradients, no borders around everything, no drop shadows on cards, no colored section backgrounds beyond white / #f5f5f7 / black. If a visual element is not content and not a control, delete it.
2. Whitespace is the layout system. Apple separates sections with space, not dividers. A section gets 80–120px of vertical padding on desktop before it gets a border. When something looks wrong, add space before adding any other element.
3. One accent color, reserved for interaction. Blue means "you can act on this." Never use the accent blue for decoration, headings, icons, or emphasis. Everything else is black, near-black, gray, or white.
Two corollaries that catch most mistakes: text is #1d1d1f, never #000 (pure black on white is harsh and un-Apple); and hierarchy comes from size and weight, never from color. A de-emphasized heading is smaller, not gray-blue.
Design tokens
Drop this in as the foundation of any project. It is the whole system.
:root {
/* ---- Color: text ---- */
--ink: #1d1d1f; /* [measured] primary text & headings */
--ink-secondary: #6e6e73; /* [measured] supporting copy */
--ink-tertiary: #86868b; /* [measured] captions, fine print */
/* ---- Color: surface ---- */
--surface: #ffffff; /* page canvas */
--surface-alt: #f5f5f7; /* [measured] alternating sections, cards */
--surface-raised: #fafafc; /* [measured] containers on alt surfaces */
--surface-dark: #000000; /* full-bleed dark sections */
--hairline: #d2d2d7; /* [measured] 1px borders & dividers */
/* ---- Color: accent (interaction only) ---- */
--accent-fill: #0071e3; /* [measured] filled button background */
--accent-fill-hover:#0077ed; /* [measured] */
--accent-link: #0066cc; /* [measured] text links on light */
--accent-link-dark: #2997ff; /* [measured] text links on dark */
/* ---- Type ---- hybrid stack: real SF on Apple devices, Inter everywhere else */
--font-display: -apple-system, BlinkMacSystemFont, "SF Pro Display",
"Inter var", Inter, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-text: -apple-system, BlinkMacSystemFont, "SF Pro Text",
"Inter var", Inter, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
--font-mono: ui-monospace, "SF Mono", SFMono-Regular, "JetBrains Mono",
Menlo, Consolas, monospace;
/* ---- Radius ---- [verified, apps.apple.com] */
--radius-xs: 5px;
--radius-sm: 9px;
--radius-md: 12px;
--radius-lg: 17px;
--radius-xl: 24px;
--radius-pill: 980px; /* [verified] Apple's signature CTA radius */
/* ---- Spacing (4pt base) ---- [derived from measured rhythm] */
--space-1: 4px; --space-2: 8px; --space-3: 12px;
--space-4: 16px; --space-5: 22px; --space-6: 32px;
--space-7: 40px; --space-8: 60px; --space-9: 80px; --space-10: 120px;
/* ---- Layout ---- */
--container: 980px; /* [verified] classic apple.com content width */
--container-wide: 1200px; /* [derived] for data-dense UI */
--gutter: 22px; /* [verified] nav/content inset */
--nav-height: 44px; /* [verified] also the min tap target */
/* ---- Motion ---- */
--ease-default: cubic-bezier(0.25, 0.1, 0.25, 1); /* [verified] */
--ease-smooth: cubic-bezier(0.16, 1, 0.3, 1); /* [derived] long reveals */
--dur-fast: 160ms;
--dur-base: 300ms;
--dur-slow: 350ms; /* [verified] nav expand */
}
Dark mode
Do not hand-write a second stylesheet. Redefine the same tokens and let everything follow.
@media (prefers-color-scheme: dark) {
:root {
--ink: #f5f5f7;
--ink-secondary: #a1a1a6;
--ink-tertiary: #86868b;
--surface: #000000;
--surface-alt: #1d1d1f;
--surface-raised: #2c2c2e;
--hairline: #424245;
--accent-link: var(--accent-link-dark);
}
}
Apple's own approach on apps.apple.com is alpha-based rather than fixed hex — --systemPrimary: rgba(0,0,0,.85) in light, rgba(255,255,255,.85) in dark [verified], stepping down through .5, .25, .1, .05 for secondary/tertiary/quaternary/quinary. Use the alpha approach when text sits on photography or colored panels, since it stays legible against any backdrop.
Typography
Font selection rule
Apple's HIG is explicit: SF Pro Text for 19px and smaller, SF Pro Display for 20px and larger. The two are optically different cuts, not the same face at different sizes. Honor the split — it is the single biggest contributor to "this looks Apple."
SF Pro itself is licensed for Apple platforms and cannot be served as a webfont, so use the hybrid stack in the tokens above. -apple-system resolves to genuine San Francisco on macOS and iOS at zero bytes and zero licensing risk — it references the installed OS font rather than serving it. Since Apple-styled sites skew heavily toward Apple hardware, a large share of visitors get the real thing, and everyone else falls through to Inter, which is the closest open substitute in proportion, x-height, and neo-grotesque character.
Never substitute Roboto, Open Sans, Poppins, or Montserrat. The proportions are wrong and it reads as generic immediately.
Setting up Inter correctly
Two settings do most of the work:
@font-face {
font-family: "Inter var";
src: url("/fonts/Inter-roman-subset.woff2") format("woff2-variations");
font-weight: 100 900;
font-display: swap;
font-style: normal;
}
html {
font-optical-sizing: auto; /* Inter 4.x opsz axis = SF's Text/Display split, automatic */
font-feature-settings: "calt" 1;
}
font-optical-sizing: auto with Inter 4.x variable is the important one — its optical-size axis reproduces exactly the mechanism behind SF Pro Text vs. Display, so the ≤19px / ≥20px split happens without you managing two families.
Do not enable cv11 (single-storey a) — SF Pro uses a double-storey a, so that stylistic set moves you away from the target, not toward it.
Self-host a subset woff2 rather than linking Google Fonts: fewer round trips, no third-party request, and font-display: swap avoids invisible text on first paint.
Handling the two-font seam
The hybrid stack's one real cost is that SF and Inter have different metrics — Inter runs slightly wider with a larger x-height — so a headline breaking across two lines on macOS may break across three on Windows. Two mitigations:
- Add roughly another −0.005em of tracking to display sizes when Inter is the resolved face, then adjust by eye against a macOS render. Treat that figure as a starting point, not a verified value.
- Use
size-adjustandascent-overridein the@font-faceblock to bring Inter's metrics closer to SF's, which stabilizes line breaks across platforms.
If you need pixel-identical rendering — design QA, marketing screenshots, anything compared side by side across machines — drop -apple-system and BlinkMacSystemFont from the front of both stacks and run Inter everywhere. You trade a little authenticity for complete determinism. For normal product work, keep the hybrid.
The display scale [verified]
All display styles are weight 600, SF Pro Display. Apple's actual breakpoint steps:
| Style | ≥1069px | ≤1068px | ≤734px |
|---|---|---|---|
| Headline standalone | 96px / 1.0417 / −0.015em | 80px / 1.05 / −0.015em | 48px / 1.0835 / −0.003em |
| Headline super | 80px / 1.05 / −0.015em | 64px / 1.0625 / −0.009em | 48px / 1.0835 / −0.003em |
| Headline elevated | 64px / 1.0625 / −0.009em | 48px / 1.0835 / −0.003em | 40px / 1.1 / 0 |
| Headline | 48px / 1.0835 / −0.003em | 40px / 1.1 / 0 | 32px / 1.125 / 0 [derived] |
Read the pattern, because it generalizes: as type gets larger, line-height tightens toward 1.0 and letter-spacing goes more negative. Large type set at 1.5 line-height with default tracking is the most common tell of a non-Apple site.
Body and small text [measured]
Body text does not scale fluidly. It steps, then caps.
| Style | Size / line-height | Weight | Tracking |
|---|---|---|---|
| Body (≥744px) | 17px / 1.47 (25px) | 400 | −0.022em |
| Body (<744px) | 16px / 1.5 (24px) | 400 | −0.022em |
| Body compact | 14px / 1.43 (20px) | 400 | −0.016em |
| Caption | 12px / 1.33 (16px) | 400 | −0.01em |
| Legal / footer | 12px / 1.33 | 400 | 0 |
| Eyebrow | 12–14px | 600 | +0.01em, often colored --ink-secondary |
| Button label | 17px / 1.18 | 400 | −0.022em |
Note the tracking direction: negative for body and headlines, positive only for small uppercase eyebrows. Apple applies subtle negative tracking even at 17px.
Implementation
Use clamp() for display type (fluid, feels intentional at every width) and discrete steps for body (predictable, protects line length). This mirrors Apple's own hybrid approach.
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
font-synthesis: none; }
.t-hero {
font-family: var(--font-display);
font-size: clamp(3rem, 1.52vw + 2.63rem, 6rem); /* 48px → 96px */
font-weight: 600; line-height: 1.05; letter-spacing: -0.015em;
}
.t-h2 {
font-family: var(--font-display);
font-size: clamp(2rem, 1.6vw + 1.6rem, 3rem); /* 32px → 48px */
font-weight: 600; line-height: 1.0835; letter-spacing: -0.003em;
}
.t-body {
font-family: var(--font-text);
font-size: 16px; line-height: 1.5; letter-spacing: -0.022em;
color: var(--ink);
}
@media (min-width: 744px) { .t-body { font-size: 17px; line-height: 1.47; } }
Keep measure between 50 and 75 characters — Apple centers body copy in a narrow column (max-width: 60ch) even inside wide sections. Wide full-bleed paragraphs are never Apple.
Layout
Breakpoints [verified]
Apple's marketing site steps at three points. Do not invent a four-tier Bootstrap ladder.
/* base: ≤734px — phone */
@media (min-width: 735px) { /* ≤1068px — tablet / small laptop */ }
@media (min-width: 1069px) { /* desktop */ }
Secondary thresholds Apple uses for navigation behavior: 1023px (desktop nav → compact), 767px (mobile menu), 419px (extra-small).
Containers
.container {
max-width: var(--container);
margin-inline: auto;
padding-inline: max(var(--gutter), env(safe-area-inset-left));
}
The env(safe-area-inset-*) handling is [verified] in Apple's nav CSS and matters on notched devices. Include it.
Section rhythm
Sections alternate --surface and --surface-alt. That alternation is the separation — do not also add a border between them.
| Viewport | Section padding-block |
|---|---|
| ≤734px | 60px |
| 735–1068px | 80px |
| ≥1069px | 100–120px |
Components
Buttons
Two variants, that's it. A third button style is almost always a mistake.
.btn {
display: inline-flex; align-items: center; justify-content: center;
min-height: 44px; /* HIG minimum tap target */
padding: 11px 21px; /* [measured] */
border: 1px solid transparent;
border-radius: var(--radius-pill); /* 980px */
font: 400 17px/1.18 var(--font-text);
letter-spacing: -0.022em;
cursor: pointer;
transition: background-color var(--dur-fast) var(--ease-default),
color var(--dur-fast) var(--ease-default);
}
.btn--primary { background: var(--accent-fill); color: #fff; }
.btn--primary:hover { background: var(--accent-fill-hover); }
.btn--secondary { background: transparent; color: var(--accent-link);
border-color: var(--accent-link); }
.btn--secondary:hover { background: var(--accent-link); color: #fff; }
Apple's buttons are narrow, sized to their label plus 21px of side padding. Full-width buttons appear only in mobile checkout flows. Never stretch a CTA across a desktop section.
Chevron text link
The most-used interactive element on apple.com, and the cheapest way to make a page read as Apple.
.link-chevron {
color: var(--accent-link);
font-size: 17px; letter-spacing: -0.022em;
text-decoration: none;
}
.link-chevron::after { content: "\203A"; margin-left: 0.25em; } /* › */
.link-chevron:hover { text-decoration: underline; }
Use these instead of secondary buttons wherever the action is navigational ("Learn more", "See all specs", "Compare models").
Translucent navigation [verified]
.nav {
position: sticky; top: 0; z-index: 9999;
height: var(--nav-height);
background: rgba(255, 255, 255, 0.8);
-webkit-backdrop-filter: saturate(180%) blur(20px);
backdrop-filter: saturate(180%) blur(20px);
font-size: 12px; /* Apple's nav type is small */
}
@media (prefers-color-scheme: dark) { .nav { background: rgba(0, 0, 0, 0.8); } }
saturate(180%) blur(20px) is Apple's literal published value and predates the Liquid Glass era. The saturation boost is what keeps content beneath from going milky — blur alone looks wrong.
Cards
Hairline border, generous radius, no shadow.
.card {
background: var(--surface-alt);
border-radius: var(--radius-lg); /* 17px */
padding: var(--space-6);
}
If a card needs separation on an already-gray section, use background: var(--surface) with border: 1px solid var(--hairline). Reach for box-shadow only for genuinely floating layers (menus, modals, toasts), and keep it soft: 0 4px 24px rgba(0,0,0,0.08).
Motion
Apple's motion is short, eased, and limited to transform and opacity. Typical durations are 160ms for hovers and state changes, 300–350ms for entrances and expansions [verified]. Anything over 500ms feels sluggish; anything that animates width, height, top, or left will jank.
.reveal {
opacity: 0; transform: translateY(24px);
transition: opacity var(--dur-base) var(--ease-smooth),
transform var(--dur-base) var(--ease-smooth);
}
.reveal.is-visible { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important; animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important; scroll-behavior: auto !important;
}
}
Scroll-triggered reveals should use IntersectionObserver, fire once, and stagger siblings by 60–80ms. Never bind animation directly to scroll position for content — it hurts readability and accessibility.
Liquid Glass (2025–2026)
Apple introduced Liquid Glass at WWDC 2025 — a translucent material that refracts its surroundings — and revised it at WWDC 2026 after readability complaints, adding a user-facing transparency slider and reducing default translucency. The lesson for web work is the revision, not the launch: translucency must never cost legibility.
CSS can approximate the material but not true refraction:
.glass {
background: rgba(255, 255, 255, 0.45);
-webkit-backdrop-filter: saturate(180%) blur(24px);
backdrop-filter: saturate(180%) blur(24px);
border: 1px solid rgba(255, 255, 255, 0.25);
border-radius: var(--radius-xl);
}
@supports not (backdrop-filter: blur(1px)) {
.glass { background: rgba(255, 255, 255, 0.92); } /* opaque fallback */
}
Apply it to floating chrome only — nav bars, toolbars, overlays. Never to body text containers, forms, or data tables. Always verify the text on top still clears 4.5:1 contrast against the worst-case backdrop, and always ship the @supports fallback.
Accessibility
These are part of the design, not a later pass.
- Tap targets ≥ 44×44px, per the HIG. This is why Apple's nav and buttons are 44px tall.
- Contrast: body text ≥ 4.5:1, large text (≥24px or ≥19px bold) ≥ 3:1.
#1d1d1fon#fffand#0071e3on#fffboth pass.--ink-tertiary(#86868b) on white is ~3.5:1 — use it only for 19px+ text. - Focus: style
:focus-visible, never remove the ring.outline: 2px solid var(--accent-link); outline-offset: 2px; - Type sizing in
remso browser zoom works (WCAG 1.4.4). - Honor
prefers-reduced-motionandprefers-color-scheme.
Self-check before shipping
Run through this; each item corresponds to a mistake that makes work read as generic rather than Apple.
- Font stack leads with
-apple-systemand falls back to Inter, withfont-optical-sizing: autoset - Body text is
#1d1d1for its dark-mode token — not#000, not#333 - Blue appears only on interactive elements
- Display headings are weight 600 with negative letter-spacing and line-height near 1.05
- Buttons are pill-radius (980px), 44px tall, sized to content
- Section separation comes from whitespace and surface alternation, not borders
- No drop shadows on static cards
- Body copy is capped at ~60–75 characters per line
- Breakpoints are 735px and 1069px, not a generic 768/1024/1280 ladder
-
prefers-reduced-motion,prefers-color-scheme, and:focus-visibleare all handled - Transitions animate only
transformandopacity, at 160–350ms
Anti-patterns
Purple-to-blue gradient heroes · rounded-rectangle cards with shadows in a 3-column grid · full-width gradient CTA buttons · icon-plus-heading feature rows in a boxed grid · letter-spacing: 0 on 72px headings · Poppins or Montserrat · emoji as iconography · more than one accent color · borders around every element.
Adapting to other stacks
Tailwind: map the tokens into theme.extend — colors.ink: '#1d1d1f', colors.accent: '#0071e3', borderRadius.pill: '980px', fontFamily.sans set to the hybrid stack, and override screens to { md: '735px', lg: '1069px' }. Do not use Tailwind's default font stack, gray scale, or breakpoints; all three are the wrong values.
Component libraries (shadcn, MUI, Chakra): override the theme tokens rather than restyling components individually. Most of the Apple look comes from the token layer — typography, radius, and the removal of shadows and borders.
Existing codebases: apply in this order, since each step is visible on its own — (1) fonts and type scale, (2) color tokens, (3) spacing and section rhythm, (4) button and link shapes, (5) motion. Retrofitting all five at once usually stalls; the type scale alone gets you most of the way.