COMPONENTS

All Components

Complete component library with live, interactive demos and copy-ready code. Every component follows Apple HIG principles adapted for web.

Buttons

Buttons communicate actions. Use hierarchy to guide users: Filled → Tinted → Gray → Borderless. Only one filled button per view.

<!-- Filled -->
<button style="padding:10px 20px;border-radius:980px;font-size:15px;font-weight:600;
  border:none;background:var(--color-accent);color:#fff;cursor:pointer;">
  Save Changes
</button>

<!-- Tinted (secondary) -->
<button style="padding:10px 20px;border-radius:980px;font-size:15px;font-weight:600;
  border:none;background:var(--color-accent-tint);color:var(--color-accent);cursor:pointer;">
  Learn More
</button>

<!-- Danger -->
<button style="padding:10px 20px;border-radius:980px;font-size:15px;font-weight:600;
  border:none;background:rgba(255,59,48,.1);color:#FF3B30;cursor:pointer;">
  Delete
</button>

Cards

Cards group related content. Choose variant based on context: Default for lists, Glass for overlaying content, Elevated for focus, Interactive for clickable items.

Default
Card Title
Standard surface for grouped content.
Glass
Card Title
Translucent, adapts to background.
Elevated
Card Title
High elevation for floating panels.
Interactive ↗
Card Title
Hover to see the spring lift effect.
<!-- 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.

Blue Green Red Orange Purple Teal Pink Gray Medium Large Outline Online 3
<!-- 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.

Visible to other members
⚠ Enter a valid email address
0 / 500
<!-- 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.

Connectivity
Wi-Fi
HomeNetwork_5G
Bluetooth
Airplane Mode
Notifications
VPN
Not configured
<!-- 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.

Uploading project.zip 67%
Training complete 100%
Failed — retry? 42%
Processing...
Interactive — drag to set 45%
<!-- 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.

Account
Profile
jane@example.com
Notifications
On
Privacy & Security
Sign Out
<!-- 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.

Name Role Status Actions
SJ
Sarah Johnson
Design Lead Active
MR
Marcus Rivera
Engineer Active
PN
Priya Nakamura
Product Manager Away
TC
Tom Chen
Marketing Inactive
<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.

Design iOS Accessibility Motion Liquid Glass Figma HIG
<!-- 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;">
    &times;
  </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.

Light Glass
Translucent Surface
20px blur, 18% white fill
Heavy Blur
Prominent Material
40px blur, 8% white fill
Dark Glass
Dark Surface
20px blur, 6% white on dark
Tinted Glass
Colored Tint
Dark tint, picks up green hue
<!-- 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) -->