All Components
Complete component library with live, interactive demos and copy-ready code. Every component follows Apple HIG principles adapted for web.
Cards
Cards group related content. Choose variant based on context: Default for lists, Glass for overlaying content, Elevated for focus, Interactive for clickable items.
<!-- Interactive card with spring lift --> <div style="background:var(--color-bg-primary);border-radius:16px;padding:20px; box-shadow:0 2px 8px rgba(0,0,0,.08);cursor:pointer; transition:transform .25s cubic-bezier(.34,1.56,.64,1),box-shadow .25s;" onmouseenter="this.style.transform='translateY(-6px)';this.style.boxShadow='0 16px 40px rgba(0,0,0,.18)'" onmouseleave="this.style.transform='';this.style.boxShadow='0 2px 8px rgba(0,0,0,.08)'"> Card content here </div> <!-- Glass card (must be placed over colorful background) --> <div style="background:rgba(255,255,255,.18);backdrop-filter:blur(20px); -webkit-backdrop-filter:blur(20px);border-radius:16px;padding:20px; border:1px solid rgba(255,255,255,.35); box-shadow:0 4px 20px rgba(0,0,0,.15),inset 0 1px 0 rgba(255,255,255,.4);"> Glass content </div>
Badges
Badges communicate status, category, or count. Use color semantically — green for success, red for errors, orange for warnings, blue for informational.
<!-- Semantic badge --> <span style="display:inline-flex;align-items:center;padding:4px 10px; border-radius:999px;font-size:12px;font-weight:600; background:rgba(52,199,89,.1);color:#34C759;"> Active </span> <!-- Notification count badge --> <span style="display:inline-flex;align-items:center;justify-content:center; width:20px;height:20px;border-radius:50%;font-size:11px;font-weight:700; background:#FF3B30;color:white;"> 3 </span>
Form Inputs
Inputs should be clearly labeled, have visible focus states, and provide inline validation feedback. Never rely on placeholder alone as a label.
<!-- Labeled input with focus ring -->
<label style="display:block;font-size:13px;font-weight:600;margin-bottom:6px;">
Full Name
</label>
<input type="text" placeholder="Jane Doe"
style="width:100%;padding:10px 14px;border-radius:10px;border:1px solid var(--color-border);
background:var(--color-bg-primary);font-size:15px;outline:none;box-sizing:border-box;"
onfocus="this.style.borderColor='var(--color-accent)';this.style.boxShadow='0 0 0 3px var(--color-accent-tint)'"
onblur="this.style.borderColor='var(--color-border)';this.style.boxShadow='none'">
<!-- Error state -->
<input type="email" value="invalid"
style="border:1.5px solid #FF3B30;background:rgba(255,59,48,.04);">
<div style="font-size:12px;color:#FF3B30;margin-top:5px;">
Enter a valid email address
</div>
Segmented Control
Use for filtering or switching between 2–5 peer views. Not for navigation between different pages or screens.
<div style="display:inline-flex;padding:3px;background:var(--color-fill-tertiary);border-radius:12px;" id="seg1">
<button onclick="segClick(this,'seg1')"
style="padding:8px 20px;border-radius:9px;font-size:15px;font-weight:500;
border:none;cursor:pointer;background:var(--color-bg-primary);
color:var(--color-text-primary);box-shadow:0 1px 3px rgba(0,0,0,.1);">All</button>
<button onclick="segClick(this,'seg1')"
style="padding:8px 20px;border-radius:9px;font-size:15px;font-weight:500;
border:none;cursor:pointer;background:transparent;color:var(--color-text-secondary);">Active</button>
</div>
<script>
function segClick(el, parentId) {
document.querySelectorAll('#' + parentId + ' button').forEach(b => {
b.style.background = 'transparent';
b.style.color = 'var(--color-text-secondary)';
b.style.boxShadow = 'none';
});
el.style.background = 'var(--color-bg-primary)';
el.style.color = 'var(--color-text-primary)';
el.style.boxShadow = '0 1px 3px rgba(0,0,0,.1)';
}
</script>
Switches
Toggles for boolean settings. Always show state change immediately — no submit button required. The toggle should feel instantaneous.
<!-- CSS required in <style> or design-system.css -->
.toggle { position:relative; width:51px; height:31px; display:inline-block; }
.toggle input { opacity:0; width:0; height:0; }
.slider { position:absolute; cursor:pointer; inset:0; background:#E5E5EA;
border-radius:31px; transition:.3s; }
.slider:before { content:""; position:absolute; width:27px; height:27px;
left:2px; bottom:2px; background:white; border-radius:50%; transition:.3s;
box-shadow:0 2px 4px rgba(0,0,0,.3); }
input:checked+.slider { background:#34C759; }
input:checked+.slider:before { transform:translateX(20px); }
<!-- Usage -->
<label class="toggle">
<input type="checkbox" checked>
<div class="slider"></div>
</label>
Progress
Show determinate progress when completion percentage is known. Use indeterminate animation when duration is unknown. Always label what is being loaded.
<!-- Determinate bar -->
<div style="height:6px;background:var(--color-fill-tertiary);
border-radius:999px;overflow:hidden;">
<div style="height:100%;width:67%;background:var(--color-accent);
border-radius:999px;"></div>
</div>
<!-- Indeterminate (CSS animation required) -->
<div style="height:6px;background:var(--color-fill-tertiary);
border-radius:999px;overflow:hidden;position:relative;">
<div style="height:100%;width:40%;background:var(--color-accent);
border-radius:999px;position:absolute;
animation:indeterminate 1.5s ease-in-out infinite;"></div>
</div>
@keyframes indeterminate { 0%{left:-40%} 100%{left:100%} }
Lists
Lists present structured content in scannable rows. Use icons and clear labels. Minimum 44pt touch targets. Group related rows and separate groups with section headers.
<!-- Settings list row with icon, label, and chevron -->
<div style="display:flex;align-items:center;padding:13px 16px;gap:14px;
border-bottom:1px solid var(--color-separator);cursor:pointer;transition:background .15s;"
onmouseenter="this.style.background='var(--color-fill-quaternary)'"
onmouseleave="this.style.background=''">
<div style="width:32px;height:32px;border-radius:8px;
background:rgba(0,122,255,.12);display:flex;align-items:center;justify-content:center;">
<!-- icon -->
</div>
<div style="flex:1;font-size:17px;">Profile</div>
<!-- chevron -->
<svg width="8" height="13" viewBox="0 0 8 13" fill="none">
<path d="M1 1l6 5.5L1 12" stroke="var(--color-text-tertiary)" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</div>
Modals
Modals interrupt the current flow for critical decisions. Use sparingly — prefer sheets or inline actions when possible. Always provide a clear way to dismiss.
<!-- Trigger -->
<button onclick="document.getElementById('myModal').style.display='flex'">
Open Modal
</button>
<!-- Modal overlay (click outside to dismiss) -->
<div id="myModal" style="display:none;position:fixed;inset:0;
background:rgba(0,0,0,.45);backdrop-filter:blur(8px);z-index:9999;
align-items:center;justify-content:center;"
onclick="if(event.target===this)this.style.display='none'">
<div style="background:var(--color-bg-primary);border-radius:20px;padding:32px;
max-width:420px;width:90%;box-shadow:0 24px 64px rgba(0,0,0,.25);
animation:scaleIn .25s cubic-bezier(.34,1.56,.64,1);">
<!-- content -->
<button onclick="document.getElementById('myModal').style.display='none'">
Cancel
</button>
</div>
</div>
Bottom Sheet
Sheets present supplementary actions or content from the bottom of the screen. Less disruptive than modals — ideal for contextual actions and share flows.
<!-- Bottom sheet trigger -->
<button onclick="document.getElementById('mySheet').style.display='flex'">
Open Sheet
</button>
<!-- Sheet overlay -->
<div id="mySheet" style="display:none;position:fixed;inset:0;
background:rgba(0,0,0,.35);backdrop-filter:blur(4px);z-index:9999;
align-items:flex-end;justify-content:center;"
onclick="if(event.target===this)this.style.display='none'">
<div style="background:var(--color-bg-primary);border-radius:20px 20px 0 0;
padding:8px 24px 40px;width:100%;max-width:540px;
animation:slideUp .35s cubic-bezier(.34,1.2,.64,1);">
<!-- drag handle -->
<div style="width:36px;height:4px;background:var(--color-fill-secondary);
border-radius:2px;margin:0 auto 24px;"></div>
<!-- content -->
</div>
</div>
Toast Notifications
Toasts provide brief, non-blocking feedback about an operation's outcome. Auto-dismiss after 3 seconds. Stack multiple toasts vertically.
function showToast(msg, color) {
const stack = document.getElementById('toastStack');
const t = document.createElement('div');
t.style.cssText = `
background:var(--color-bg-primary);
border:1px solid var(--color-border);
border-left:3px solid ${color};
border-radius:12px;
padding:12px 16px;
font-size:14px;font-weight:500;
box-shadow:0 4px 16px rgba(0,0,0,.12);
pointer-events:auto;
animation:slideToast .3s cubic-bezier(.34,1.56,.64,1);
max-width:300px;
color:var(--color-text-primary);
`;
t.textContent = msg;
stack.appendChild(t);
setTimeout(() => {
t.style.opacity = '0';
t.style.transition = 'opacity .3s';
setTimeout(() => t.remove(), 300);
}, 3000);
}
Tables
Tables display structured data clearly. Keep columns minimal. Use badges for status values. Always include row hover states to aid scanability.
<table style="width:100%;border-collapse:collapse;">
<thead>
<tr style="background:var(--color-bg-secondary);">
<th style="padding:11px 16px;text-align:left;font-size:12px;font-weight:600;
color:var(--color-text-secondary);text-transform:uppercase;letter-spacing:.05em;
border-bottom:1px solid var(--color-border);">Name</th>
</tr>
</thead>
<tbody>
<tr style="border-bottom:1px solid var(--color-separator);transition:background .15s;"
onmouseenter="this.style.background='var(--color-fill-quaternary)'"
onmouseleave="this.style.background=''">
<td style="padding:14px 16px;font-size:15px;font-weight:500;">Sarah Johnson</td>
</tr>
</tbody>
</table>
Chips & Tags
Chips represent compact, dismissible elements like selected filters or applied tags. Tap the × to remove. Use color to communicate category.
<!-- Dismissible chip -->
<span style="display:inline-flex;align-items:center;gap:6px;
padding:6px 10px 6px 12px;border-radius:999px;
font-size:13px;font-weight:500;
background:rgba(0,122,255,.1);color:#007AFF;">
iOS
<button onclick="this.parentElement.style.display='none'"
style="background:none;border:none;cursor:pointer;
color:#007AFF;font-size:15px;opacity:.7;
display:flex;align-items:center;">
×
</button>
</span>
Glass Surfaces
Glass surfaces establish depth hierarchy and belong to Apple's Liquid Glass design language. They must be placed over colorful or textured content — glass on solid white is invisible and meaningless.
<!-- Glass card — must be placed over colorful/textured content -->
<div style="background:linear-gradient(135deg,#667eea,#764ba2);
border-radius:20px;padding:24px;">
<div style="backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);
background:rgba(255,255,255,.18);
border:1px solid rgba(255,255,255,.35);
border-radius:16px;padding:20px;
box-shadow:inset 0 1px 0 rgba(255,255,255,.5),0 4px 20px rgba(0,0,0,.1);">
Glass content here
</div>
</div>
<!-- Recipe -->
<!-- Light glass: rgba(255,255,255,.18) blur(20px) -->
<!-- Heavy glass: rgba(255,255,255,.08) blur(40px) -->
<!-- Dark glass: rgba(255,255,255,.06) blur(20px) -->
<!-- Tinted glass: rgba(0,0,0,.12) blur(16px) -->