Agent load: opt-in only. Use only when the human asked for Apple / iOS / macOS / liquid glass aesthetic. Do not apply this skill to generic web, Material, or flat designs. Section-load: philosophy + material stack + the component patterns you need. Pair with project
AGENTS.md. Not a default for every UI task.
Apple Design System: Complete Reference
You are implementing UI in Apple's aesthetic language. Every decision, blur radius, spring curve, border opacity, corner radius, type weight, should feel like it shipped on a real Apple platform. Ship nothing that Apple's HIG would call "inconsistent with the system." Before writing a single line of code, internalize this document fully.
1. The philosophy behind the aesthetic
Apple's UI is a material system, not a color system. The goal is to simulate real-world physical materials, glass, aluminum, fabric, frosted acrylic, rendered with digital light. Every surface has:
- Translucency, the background bleeds through, tinted and blurred
- Depth, foreground, midground, and background exist simultaneously
- Light response, surfaces have highlights (top edge), fill (tint), and shadow (bottom/rim)
- Physics, motion follows spring dynamics, not linear easing
If any component looks flat, opaque, or snaps to its final position, it is wrong.
2. The material stack
Apple uses four named materials. Implement all four and choose the right one per context.
Thick, primary surfaces (cards, modals, sidebars)
background: rgba(255, 255, 255, 0.72); /* light mode */
background: rgba(28, 28, 36, 0.72); /* dark mode */
backdrop-filter: blur(40px) saturate(180%);
-webkit-backdrop-filter: blur(40px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.55); /* light */
border: 1px solid rgba(255, 255, 255, 0.10); /* dark */
Regular, standard panels, popovers, sheets
background: rgba(255, 255, 255, 0.55);
backdrop-filter: blur(28px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.45);
Thin, pill controls, tab bars, toolbars
background: rgba(255, 255, 255, 0.28);
backdrop-filter: blur(16px) saturate(160%);
border: 1px solid rgba(255, 255, 255, 0.38);
Ultra-thin, hover overlays, transient chrome
background: rgba(255, 255, 255, 0.12);
backdrop-filter: blur(10px) saturate(140%);
border: 1px solid rgba(255, 255, 255, 0.22);
Liquid Glass (iOS 26 / visionOS), new pill + button material
The newest Apple material. Use for floating controls, pills, and action buttons.
background: rgba(255, 255, 255, 0.18);
backdrop-filter: blur(24px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.52);
/* Required specular highlight, simulates curved glass catching light */
background: linear-gradient(
180deg,
rgba(255, 255, 255, 0.30) 0%, /* top highlight */
rgba(255, 255, 255, 0.08) 55%, /* fade */
rgba(255, 255, 255, 0.00) 100% /* transparent base */
);
Stack these: the backdrop-filter div beneath, the gradient div above it (via ::after), the content on top.
Dark glass, Control Center, Now Playing, Lock Screen widgets
background: rgba(18, 18, 28, 0.72);
backdrop-filter: blur(30px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.10);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.45), inset 0 1px 0 rgba(255,255,255,0.08);
3. Corner radius, the Apple radius scale
Never use arbitrary values. Apple uses a specific radius vocabulary.
| Token | Value | Use case |
|---|---|---|
--r-xs |
8px |
small badges, tags, input fields |
--r-sm |
12px |
buttons, chips, small cards |
--r-md |
16px |
standard cards, sheets, popovers |
--r-lg |
20px |
large cards, modal sheets, widgets |
--r-xl |
28px |
floating panels, iPadOS split views |
--r-2xl |
36px |
app icon shape (only for icon containers) |
--r-pill |
999px |
toggles, segmented controls, Dynamic Island |
:root {
--r-xs: 8px; --r-sm: 12px; --r-md: 16px; --r-lg: 20px;
--r-xl: 28px; --r-2xl: 36px; --r-pill: 999px;
}
4. Motion, spring physics only
Apple does not use ease, ease-in-out, or linear curves for interactive elements. Every motion is spring-based.
The four Apple spring presets
/* 1. Default spring, general interactions, card lifts */
--spring-default: cubic-bezier(0.34, 1.56, 0.64, 1);
/* 2. Snappy spring, buttons, toggles, taps */
--spring-snappy: cubic-bezier(0.36, 1.4, 0.64, 1);
/* 3. Gentle spring, sheet presentation, modals */
--spring-gentle: cubic-bezier(0.16, 1.0, 0.3, 1);
/* 4. Bounce spring, notifications, playful elements */
--spring-bounce: cubic-bezier(0.22, 1.6, 0.36, 1);
The key is the y-values exceeding 1.0, this is what creates the overshoot/bounce that Apple's interfaces are famous for. A spring that never exceeds 1.0 is not a spring.
Duration guidelines
| Interaction type | Duration |
|---|---|
| Button press / tap | 280-320ms |
| Toggle on/off | 360-400ms |
| Card hover lift | 300-380ms |
| Sheet / modal present | 420-500ms |
| Context menu pop-in | 200-260ms |
| Dynamic Island expand | 380-440ms |
| Page transition | 480-560ms |
Always respect reduced motion
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
Press states, the haptic illusion
Every interactive element must have:
- Press down:
scale(0.93-0.96)+filter: brightness(0.9), fast (120ms, ease-in) - Release: spring back past 1.0 before settling, uses
--spring-snappy - Hover:
scale(1.02-1.05)+translateY(-2px)with subtle shadow increase
.apple-btn {
transition: transform 360ms var(--spring-snappy),
box-shadow 200ms ease,
filter 120ms ease;
}
.apple-btn:hover { transform: scale(1.04) translateY(-2px); }
.apple-btn:active { transform: scale(0.94); filter: brightness(0.88); transition-duration: 120ms; }
5. Typography, SF Pro system
Apple's type system is precise. Use system fonts and follow the exact scale.
/* Always use system font stack, SF Pro on Apple devices, Inter fallback */
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text',
'Inter', system-ui, sans-serif;
/* For rounded/friendly contexts (SF Pro Rounded) */
font-variation-settings: 'ROND' 1;
The iOS/macOS type scale
| Role | Size | Weight | Line height | Letter spacing |
|---|---|---|---|---|
| Large Title | 34px | 700 | 1.2 | -0.4px |
| Title 1 | 28px | 700 | 1.25 | -0.3px |
| Title 2 | 22px | 700 | 1.3 | -0.2px |
| Title 3 | 20px | 600 | 1.3 | -0.1px |
| Headline | 17px | 600 | 1.4 | -0.05px |
| Body | 17px | 400 | 1.5 | 0 |
| Callout | 16px | 400 | 1.5 | 0 |
| Subheadline | 15px | 400 | 1.45 | 0 |
| Footnote | 13px | 400 | 1.4 | 0 |
| Caption 1 | 12px | 400 | 1.35 | +0.03px |
| Caption 2 | 11px | 400 | 1.3 | +0.06px |
Rules:
- Negative letter spacing on large text (
-0.3pxto-0.5px), Apple condenses large type - Positive letter spacing on tiny text (captions, labels), Apple opens small type
- Never use
font-weight: 300, Apple does not use light weight in UI - Sentence case everywhere. Never ALL CAPS in body text.
6. Color, system palette and vibrancy
Semantic colors (auto dark/light via CSS)
:root {
--label: rgba(0, 0, 0, 1.0);
--secondary-label: rgba(60, 60, 67, 0.6);
--tertiary-label: rgba(60, 60, 67, 0.3);
--quaternary-label: rgba(60, 60, 67, 0.18);
--separator: rgba(60, 60, 67, 0.29);
--bg-primary: rgba(255, 255, 255, 1);
--bg-secondary: rgba(242, 242, 247, 1); /* iOS grouped bg */
--bg-tertiary: rgba(255, 255, 255, 1);
--fill-primary: rgba(120, 120, 128, 0.2);
--fill-secondary: rgba(120, 120, 128, 0.16);
--fill-tertiary: rgba(118, 118, 128, 0.12);
--fill-quaternary: rgba(116, 116, 128, 0.08);
}
@media (prefers-color-scheme: dark) {
:root {
--label: rgba(255, 255, 255, 1.0);
--secondary-label: rgba(235, 235, 245, 0.6);
--tertiary-label: rgba(235, 235, 245, 0.3);
--quaternary-label: rgba(235, 235, 245, 0.18);
--separator: rgba(84, 84, 88, 0.65);
--bg-primary: rgba(0, 0, 0, 1);
--bg-secondary: rgba(28, 28, 30, 1);
--bg-tertiary: rgba(44, 44, 46, 1);
--fill-primary: rgba(120, 120, 128, 0.36);
--fill-secondary: rgba(120, 120, 128, 0.32);
--fill-tertiary: rgba(118, 118, 128, 0.24);
--fill-quaternary: rgba(116, 116, 128, 0.18);
}
}
Apple system accent colors (exact hex)
--apple-blue: #007AFF;
--apple-green: #34C759;
--apple-indigo: #5856D6;
--apple-orange: #FF9500;
--apple-pink: #FF2D55;
--apple-purple: #AF52DE;
--apple-red: #FF3B30;
--apple-teal: #5AC8FA;
--apple-yellow: #FFCC00;
--apple-mint: #00C7BE;
--apple-cyan: #32ADE6;
--apple-brown: #A2845E;
Vibrancy, how Apple applies color over glass
Never use solid system accent colors over a glass background. Use their alpha versions:
/* Blue tinted glass pill */
background: rgba(0, 122, 255, 0.15);
border: 1px solid rgba(0, 122, 255, 0.35);
color: #007AFF;
/* Green tinted glass badge */
background: rgba(52, 199, 89, 0.15);
border: 1px solid rgba(52, 199, 89, 0.30);
color: #34C759;
Alpha values for fills: 0.08 (ultra-thin) → 0.12 (thin) → 0.18 (regular) → 0.24 (thick)
7. Shadows, the Apple shadow vocabulary
Apple shadows are subtle and directional, never harsh.
/* Elevation 0, flush, no shadow */
box-shadow: none;
/* Elevation 1, cards, list rows */
box-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 1px 2px rgba(0,0,0,0.06);
/* Elevation 2, popovers, context menus */
box-shadow: 0 4px 16px rgba(0,0,0,0.12), 0 2px 4px rgba(0,0,0,0.08);
/* Elevation 3, modals, sheets */
box-shadow: 0 10px 40px rgba(0,0,0,0.18), 0 4px 12px rgba(0,0,0,0.10);
/* Elevation 4, floating panels, widgets */
box-shadow: 0 20px 60px rgba(0,0,0,0.22), 0 8px 20px rgba(0,0,0,0.12);
/* Colored shadow, Apple tints shadows with the element's color */
/* e.g. blue button: */
box-shadow: 0 6px 20px rgba(0, 122, 255, 0.38);
8. Component patterns
Toggle (iOS Switch)
<button class="ios-toggle" role="switch" aria-checked="true">
<span class="ios-thumb"></span>
</button>
<style>
.ios-toggle {
width: 51px; height: 31px; border-radius: 999px;
background: var(--apple-green); border: none; cursor: pointer;
position: relative; padding: 0;
transition: background 300ms ease;
}
.ios-toggle[aria-checked="false"] { background: var(--fill-primary); }
.ios-thumb {
position: absolute; top: 2px; left: 2px;
width: 27px; height: 27px; border-radius: 50%;
background: #fff;
box-shadow: 0 2px 6px rgba(0,0,0,0.22), 0 1px 2px rgba(0,0,0,0.14);
transition: transform 380ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
.ios-toggle[aria-checked="true"] .ios-thumb { transform: translateX(20px); }
</style>
Segmented Control
<div class="seg-ctrl" role="tablist">
<button class="seg-opt active" role="tab">Day</button>
<button class="seg-opt" role="tab">Week</button>
<button class="seg-opt" role="tab">Month</button>
</div>
<style>
.seg-ctrl {
display: inline-flex;
background: var(--fill-secondary);
border-radius: var(--r-sm); padding: 2px; gap: 2px;
}
.seg-opt {
padding: 6px 16px; border-radius: calc(var(--r-sm) - 2px); border: none;
font-size: 13px; font-weight: 500; cursor: pointer; background: transparent;
color: var(--label); transition: all 320ms var(--spring-snappy);
}
.seg-opt.active {
background: #fff; color: var(--label);
box-shadow: 0 2px 8px rgba(0,0,0,0.10), 0 1px 2px rgba(0,0,0,0.06);
}
</style>
Context Menu (popover)
.ctx-menu {
background: rgba(255,255,255,0.82);
backdrop-filter: blur(40px) saturate(200%);
-webkit-backdrop-filter: blur(40px) saturate(200%);
border-radius: var(--r-md);
border: 0.5px solid rgba(0,0,0,0.08);
box-shadow: 0 20px 60px rgba(0,0,0,0.20), 0 4px 12px rgba(0,0,0,0.10);
padding: 6px;
min-width: 180px;
animation: ctxPopIn 240ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes ctxPopIn {
from { opacity: 0; transform: scale(0.86) translateY(-6px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
.ctx-row {
display: flex; align-items: center; gap: 8px;
padding: 8px 10px; border-radius: calc(var(--r-md) - 4px);
font-size: 13px; cursor: pointer;
transition: background 120ms ease;
}
.ctx-row:hover { background: rgba(0, 122, 255, 0.10); color: var(--apple-blue); }
.ctx-row.destructive { color: var(--apple-red); }
.ctx-row.destructive:hover { background: rgba(255, 59, 48, 0.10); }
.ctx-sep { height: 0.5px; background: var(--separator); margin: 4px 0; }
Dynamic Island pattern
.dynamic-island {
background: #000; border-radius: 999px;
padding: 10px 18px;
display: inline-flex; align-items: center; gap: 10px;
box-shadow: 0 8px 32px rgba(0,0,0,0.5);
transition:
padding 440ms cubic-bezier(0.16, 1, 0.3, 1),
min-width 440ms cubic-bezier(0.16, 1, 0.3, 1),
border-radius 440ms cubic-bezier(0.16, 1, 0.3, 1);
cursor: pointer; min-width: 120px;
}
.dynamic-island:hover {
padding: 16px 28px;
min-width: 300px;
border-radius: 38px;
}
Notification / Alert banner
.banner {
background: rgba(255,255,255,0.82);
backdrop-filter: blur(28px) saturate(180%);
border-radius: var(--r-lg);
border: 0.5px solid rgba(0,0,0,0.08);
box-shadow: 0 16px 48px rgba(0,0,0,0.18);
padding: 12px 16px;
display: flex; gap: 12px; align-items: flex-start;
animation: bannerSlide 500ms cubic-bezier(0.34, 1.56, 0.64, 1);
}
@keyframes bannerSlide {
from { opacity: 0; transform: translateY(-20px) scale(0.95); }
to { opacity: 1; transform: translateY(0) scale(1); }
}
SF Symbol-style icon container
Apple's icons sit in rounded square containers ("app icon shape" for apps, --r-sm for UI actions).
<div class="sf-icon-wrap" style="--accent: #007AFF;">
<i class="ti ti-message"></i>
</div>
<style>
.sf-icon-wrap {
width: 36px; height: 36px;
border-radius: var(--r-sm);
background: color-mix(in srgb, var(--accent) 15%, transparent);
display: flex; align-items: center; justify-content: center;
color: var(--accent); font-size: 18px;
}
</style>
9. Interaction recipe, complete button
A fully correct Apple-style button covers all five states:
<button class="apple-cta">Continue</button>
<style>
.apple-cta {
padding: 14px 28px; border-radius: var(--r-sm); border: none;
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
font-size: 17px; font-weight: 600; cursor: pointer;
background: var(--apple-blue); color: #fff;
box-shadow: 0 4px 14px rgba(0, 122, 255, 0.38);
position: relative; overflow: hidden;
/* specular highlight */
background-image: linear-gradient(180deg,
rgba(255,255,255,0.18) 0%,
rgba(255,255,255,0.00) 60%);
transition:
transform 340ms cubic-bezier(0.34, 1.56, 0.64, 1),
box-shadow 200ms ease,
filter 120ms ease;
}
/* hover, slight lift */
.apple-cta:hover {
transform: scale(1.03) translateY(-2px);
box-shadow: 0 8px 24px rgba(0, 122, 255, 0.44);
}
/* press, haptic compression */
.apple-cta:active {
transform: scale(0.94);
filter: brightness(0.88);
transition-duration: 110ms;
}
/* focus, accessibility ring */
.apple-cta:focus-visible {
outline: none;
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.45);
}
/* ripple layer */
.apple-cta .ripple {
position: absolute; border-radius: 50%;
background: rgba(255,255,255,0.3);
transform: scale(0);
animation: ripple 550ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
pointer-events: none;
}
@keyframes ripple {
to { transform: scale(4); opacity: 0; }
}
</style>
<script>
document.querySelector('.apple-cta').addEventListener('click', function(e) {
const r = this.getBoundingClientRect();
const d = Math.max(r.width, r.height);
const rip = document.createElement('div');
rip.className = 'ripple';
rip.style.cssText = `width:${d}px;height:${d}px;left:${e.clientX-r.left-d/2}px;top:${e.clientY-r.top-d/2}px`;
this.appendChild(rip);
setTimeout(() => rip.remove(), 600);
});
</script>
10. Layout principles from Apple HIG
Spacing, 8pt grid (Apple's base unit)
Apple designs on an 8-point grid. All spacing is multiples of 8.
4px, within a single element (icon gap, tight pair)
8px, between related items (label + input)
12px, compact list row padding
16px, standard content padding (HIG minimum)
20px, section breathing room
24px, between major components
32px, section separation
44px, minimum touch target (absolute requirement)
Content width
- iPhone SE width: 320px minimum
- Standard iPhone: 390px
- iPad: use
max-width: 768pxwith centered content - macOS sidebar: 220-260px fixed
- macOS content: fluid with
max-width: 960px
The safe area, always respect it
padding-top: env(safe-area-inset-top);
padding-bottom: max(env(safe-area-inset-bottom), 20px);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
11. The five things that immediately break Apple feel
Avoid these at all costs:
Hard borders instead of glass edges,
border: 1px solid #cccis wrong. Borders must bergba(255,255,255,0.4-0.6)on glass orvar(--separator)on solid surfaces.Linear/ease animation,
transition: all 0.3s easeis wrong for interactions. Springs only.Wrong corner radius,
border-radius: 4pxon a card is wrong. Cards are--r-lg(20px). Buttons are--r-sm(12px). Pills are999px.Opaque backgrounds where glass should be, any element that floats over content (modal, popover, sheet, toolbar, context menu) must be translucent glass, not
background: white.No specular highlight, any glass element without a
::beforeor::aftertop-edge gradient (rgba(255,255,255,0.25-0.35)) looks flat and cheap. The highlight is what makes it look like glass vs. just a colored div.
12. Dark mode, mandatory
Apple dark mode is not just "invert the colors." Dark glass is darker-tinted, not lighter. Follow these rules:
- Light glass →
rgba(255,255,255, 0.55-0.72)tint - Dark glass →
rgba(18,18,28, 0.65-0.78)tint, never pure black - Dark mode borders →
rgba(255,255,255, 0.08-0.14), very subtle white edge - Dark mode text hierarchy: primary
rgba(255,255,255,1.0), secondaryrgba(235,235,245,0.6), tertiaryrgba(235,235,245,0.3) - Colored accents stay the same hex, Apple does not shift accent colors in dark mode
- Dark mode shadows are invisible on dark bg, increase opacity to compensate:
rgba(0,0,0,0.5-0.6)
Always implement both:
@media (prefers-color-scheme: light) { /* light rules */ }
@media (prefers-color-scheme: dark) { /* dark rules */ }
13. Complete CSS variable reference
Paste this into any Apple-style project as the foundation:
:root {
/* Spring curves */
--spring-default: cubic-bezier(0.34, 1.56, 0.64, 1);
--spring-snappy: cubic-bezier(0.36, 1.4, 0.64, 1);
--spring-gentle: cubic-bezier(0.16, 1.0, 0.3, 1);
--spring-bounce: cubic-bezier(0.22, 1.6, 0.36, 1);
/* Corner radii */
--r-xs: 8px; --r-sm: 12px; --r-md: 16px; --r-lg: 20px;
--r-xl: 28px; --r-2xl: 36px; --r-pill: 999px;
/* System accent colors */
--blue: #007AFF; --green: #34C759; --indigo: #5856D6;
--orange: #FF9500; --pink: #FF2D55; --purple: #AF52DE;
--red: #FF3B30; --teal: #5AC8FA; --yellow: #FFCC00;
--mint: #00C7BE; --cyan: #32ADE6; --brown: #A2845E;
/* Typography */
--font-sf: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Inter', sans-serif;
/* Touch target */
--touch-target: 44px;
/* 8pt grid */
--s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px;
--s5: 20px; --s6: 24px; --s8: 32px; --s11: 44px;
}
14. Self-check before shipping
Before calling a component done, verify:
- Every floating element uses
backdrop-filter, no opaque backgrounds on overlays - Every interactive element has hover + press states with spring animation
- Every glass surface has a specular
::aftertop-edge highlight - Corner radii come from the scale, no arbitrary values
- Typography uses negative letter spacing on large text, positive on captions
- Touch targets are ≥ 44px
- Dark mode is implemented and tested
-
prefers-reduced-motiondisables all animations - Borders on glass use
rgba(255,255,255, …), never solid hex colors - Spring curves, not
ease, notease-in-out