
Apple Hig Designer
- 63 installs
- 138 repo stars
- Updated February 17, 2026
- axiaoge2/apple-hig-designer
Apple HIG Designer is an agent skill that produces Human Interface Guidelines–compliant iOS and macOS interface and component specifications.
About
Apple HIG Designer is an agent skill for solo and indie builders who want Apple-native look and feel without guessing platform rules. It focuses on Human Interface Guidelines compliance: layout hierarchy, navigation patterns, materials, and motion that read as intentional on iPhone, iPad, and Mac. The skill steers conversations toward SF Symbols, San Francisco type ramps, spacing and touch targets, and accessibility affordances such as labels, traits, and contrast—so generated UI specs are implementable in SwiftUI or exported design tokens for hybrid stacks. Use it when mocking new screens, refactoring a cluttered settings flow, or aligning a side project with App Store expectations before review. It is a specification and pattern skill, not a Figma plugin; pairing it with your frontend implementation skill keeps agents from inventing non-standard chrome. Optional modern effects are framed so they do not fight clarity or HIG defaults.
- Maps UI work to Apple Human Interface Guidelines patterns for iOS and macOS
- Specifies San Francisco typography scale and SF Symbols usage
- Embeds VoiceOver and Dynamic Type accessibility expectations in component guidance
- Supports optional modern visual effects without breaking HIG structure
- Produces design-system-ready component descriptions agents can implement
Apple Hig Designer by the numbers
- 63 all-time installs (skills.sh)
- Ranked #1,198 of 1,880 Design & UI/UX skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/axiaoge2/apple-hig-designer --skill apple-hig-designerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 63 |
|---|---|
| repo stars | ★ 138 |
| Security audit | 3 / 3 scanners passed |
| Last updated | February 17, 2026 |
| Repository | axiaoge2/apple-hig-designer ↗ |
What it does
Ship iOS and macOS screens that follow Apple Human Interface Guidelines with the right typography, symbols, and accessibility defaults.
Who is it for?
Best when you're building or polishing iOS/macOS apps and need consistent HIG language in agent-generated UI work.
Skip if: Skip if you're targeting only web or Android Material UI, or when you already have a locked, audited design system with no Apple surface.
When should I use this skill?
Designing Apple-style UI, iOS/macOS interfaces, HIG-compliant components, or implementing design system specifications.
What you get
You get HIG-aligned screen and component specs with typography, SF Symbols, and accessibility notes ready to hand to implementation.
- HIG-oriented screen or component specification
- Typography and symbol usage notes
- Accessibility checklist for the proposed UI
Files
Apple HIG Designer
A professional-grade frontend design skill that enables Claude Code to create interfaces following Apple's Human Interface Guidelines (HIG), achieving the quality standards of Apple's design team.
When to Use This Skill
Activate this skill when users request:
- Apple-style or iOS/macOS-style interfaces
- HIG-compliant UI components
- SF Symbols integration
- San Francisco typography implementation
- Modern glass effects (optional, user must explicitly request)
Trigger phrases:
- "Design an Apple-style..."
- "Create a HIG-compliant..."
- "iOS/macOS style component"
- "苹果风格的界面"
- "符合 HIG 的设计"
---
Core Design Principles
The Four Pillars of Apple Design
1. Clarity (清晰)
- Every element has a purpose
- Eliminate unnecessary complexity
- Users understand immediately without instructions
- Use clear visual hierarchy
2. Deference (尊重内容)
- UI elements support, not compete with content
- Minimize chrome and visual noise
- Let content be the hero
- Use subtle backgrounds and borders
3. Depth (层次)
- Create clear visual hierarchy through layers
- Use shadows, blur, and translucency purposefully
- Motion reinforces spatial relationships
- Z-axis communicates importance
4. Consistency (一致性)
- Familiar patterns across platforms
- Predictable interactions
- Unified visual language
- Respect platform conventions
---
Design System Specifications
Typography System
Font Family: San Francisco (SF Pro)
/* System Font Stack for Web */
:root {
--font-system: -apple-system, BlinkMacSystemFont, 'SF Pro Display',
'SF Pro Text', 'Helvetica Neue', Arial, sans-serif;
--font-mono: 'SF Mono', SFMono-Regular, Menlo, Monaco, monospace;
}
/* Font Size Scale (iOS) */
--text-caption2: 11px; /* Caption 2 */
--text-caption1: 12px; /* Caption 1 */
--text-footnote: 13px; /* Footnote */
--text-subhead: 15px; /* Subheadline */
--text-body: 17px; /* Body - Default */
--text-headline: 17px; /* Headline (semibold) */
--text-title3: 20px; /* Title 3 */
--text-title2: 22px; /* Title 2 */
--text-title1: 28px; /* Title 1 */
--text-large-title: 34px; /* Large Title */Typography Rules:
- Use SF Pro Display for sizes ≥ 20pt
- Use SF Pro Text for sizes < 20pt
- Maintain consistent line-height (1.2-1.5)
- Use weight for hierarchy, not just size
Color System
/* Apple System Colors - Light Mode */
:root {
/* Primary Colors */
--system-blue: #007AFF;
--system-green: #34C759;
--system-indigo: #5856D6;
--system-orange: #FF9500;
--system-pink: #FF2D55;
--system-purple: #AF52DE;
--system-red: #FF3B30;
--system-teal: #5AC8FA;
--system-yellow: #FFCC00;
/* Gray Scale */
--system-gray: #8E8E93;
--system-gray2: #AEAEB2;
--system-gray3: #C7C7CC;
--system-gray4: #D1D1D6;
--system-gray5: #E5E5EA;
--system-gray6: #F2F2F7;
/* Semantic Colors */
--label-primary: #000000;
--label-secondary: rgba(60, 60, 67, 0.6);
--label-tertiary: rgba(60, 60, 67, 0.3);
--label-quaternary: rgba(60, 60, 67, 0.18);
/* Backgrounds */
--bg-primary: #FFFFFF;
--bg-secondary: #F2F2F7;
--bg-tertiary: #FFFFFF;
/* Separators */
--separator: rgba(60, 60, 67, 0.29);
--separator-opaque: #C6C6C8;
}
/* Dark Mode */
@media (prefers-color-scheme: dark) {
:root {
--system-blue: #0A84FF;
--system-green: #30D158;
--system-indigo: #5E5CE6;
--system-orange: #FF9F0A;
--system-pink: #FF375F;
--system-purple: #BF5AF2;
--system-red: #FF453A;
--system-teal: #64D2FF;
--system-yellow: #FFD60A;
--system-gray: #8E8E93;
--system-gray2: #636366;
--system-gray3: #48484A;
--system-gray4: #3A3A3C;
--system-gray5: #2C2C2E;
--system-gray6: #1C1C1E;
--label-primary: #FFFFFF;
--label-secondary: rgba(235, 235, 245, 0.6);
--label-tertiary: rgba(235, 235, 245, 0.3);
--label-quaternary: rgba(235, 235, 245, 0.18);
--bg-primary: #000000;
--bg-secondary: #1C1C1E;
--bg-tertiary: #2C2C2E;
--separator: rgba(84, 84, 88, 0.6);
--separator-opaque: #38383A;
}
}Spacing System
8pt Grid System:
:root {
--space-1: 4px; /* Extra small */
--space-2: 8px; /* Small */
--space-3: 12px; /* Medium-small */
--space-4: 16px; /* Medium */
--space-5: 20px; /* Medium-large */
--space-6: 24px; /* Large */
--space-8: 32px; /* Extra large */
--space-10: 40px; /* 2X large */
--space-12: 48px; /* 3X large */
}Touch Target Requirements:
- iOS: Minimum 44×44 points
- visionOS: Minimum 60 points tap area
- Always add sufficient padding for small visual elements
Border Radius (Concentric Design)
:root {
/* Base radius values */
--radius-sm: 8px;
--radius-md: 12px;
--radius-lg: 16px;
--radius-xl: 20px;
--radius-2xl: 24px;
--radius-full: 9999px; /* Capsule */
}
/* Concentric Rule: inner_radius + padding = outer_radius */
/* Example: 8px inner + 8px padding = 16px outer */---
Component Patterns
The following components should follow Apple HIG conventions. For complete CSS implementations, see resources/design-tokens.css and resources/ui-patterns.md. For React component examples, see resources/components.jsx.
Buttons
- Primary: Capsule shape (
border-radius: 9999px),min-height: 44px, system-blue background, white text, 600 weight - Secondary: Same capsule shape, system-blue text on
rgba(0, 122, 255, 0.1)background - Press feedback:
scale(0.98)on:active
Glass Effect (Optional - Only When Requested)
- Only use when user explicitly requests glass/frosted effects
- Default designs should use solid backgrounds for readability and performance
backdrop-filter: blur(20px) saturate(180%)with semi-transparent background- Always provide non-glass fallback
Cards
- Default: Solid background (
--bg-tertiary),border-radius: 16px, subtle box-shadow - Grouped style:
--bg-secondaryouter with--bg-tertiaryinner items, separated by--separator
Input Fields
min-height: 44px,--bg-secondarybackground,border-radius: 12px- Focus state:
box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.3)
Tab Bars
- Position fixed at bottom,
height: 49px+ safe-area padding - Minimum 44x44pt touch targets per tab
- Always display text labels (never icon-only)
- Use filled SF Symbols for selected state, outline for unselected
- Support badge for notifications (red oval, white text)
Toolbars
- Three sections: leading (back/title), center (tools), trailing (actions)
- Maximum 3 item groups
- Use SF Symbols without borders
- Primary action uses
.prominentstyle on trailing side - Back button: circular with symbol only (no text)
Sheets
- Detents:
large(100%),medium(50%) - Include grabber indicator (36x5px, centered)
- Done button: top-right; Cancel button: top-left
- Semi-transparent backdrop
rgba(0,0,0,0.4)
Alerts
- Use only for critical information (sparingly)
- Maximum 3 buttons; use specific verb titles, avoid "OK"
- Cancel: leading side; Destructive: only for unintended destructive actions
- Width: 270px,
border-radius: 14px
Lists and Tables
- Minimum row height: 44px
- Info button: reveals details without navigation
- Disclosure indicator (>): navigates to subview
- Grouped style: 10px border-radius, inset separators start at 60px
---
Animation Guidelines
Timing Functions
:root {
/* Apple's preferred easing */
--ease-default: cubic-bezier(0.25, 0.1, 0.25, 1);
--ease-in: cubic-bezier(0.42, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.58, 1);
--ease-in-out: cubic-bezier(0.42, 0, 0.58, 1);
/* Spring-like easing */
--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
}Duration Scale
:root {
--duration-instant: 100ms; /* Micro-interactions */
--duration-fast: 200ms; /* Hover, focus states */
--duration-normal: 300ms; /* Standard transitions */
--duration-slow: 500ms; /* Complex animations */
}Interaction Feedback
/* Press feedback */
.interactive {
transition: transform var(--duration-instant) var(--ease-out);
}
.interactive:active {
transform: scale(0.97);
}
/* Hover glow */
.interactive:hover {
box-shadow: 0 0 0 4px rgba(0, 122, 255, 0.15);
}---
SF Symbols Integration
Rendering Modes
1. Monochrome: Single color for all layers 2. Hierarchical: Opacity variations for depth 3. Palette: Custom colors per layer 4. Multicolor: Apple's intrinsic symbol colors
Usage Guidelines
- Toolbar/Navigation: Use outline variant
- Tab Bar: Use fill variant
- Match text size: Symbols auto-align with SF font
- Provide text alternatives: Always include aria-label
Web Implementation (Icon Font Alternative)
<!-- Using SF Symbols via system font -->
<span class="sf-symbol" aria-label="Settings"></span>
<!-- Or use equivalent system icons -->
<svg class="icon" aria-hidden="true">
<use href="#icon-gear"></use>
</svg>---
Accessibility Requirements
Color Contrast
- Normal text: 4.5:1 minimum (WCAG AA)
- Large text: 3:1 minimum
- Interactive elements: Clearly distinguishable states
Motion
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}Semantic HTML
<!-- Always use semantic elements -->
<button type="button">Action</button>
<nav aria-label="Main navigation">...</nav>
<main role="main">...</main>---
Output Format
When generating Apple-style UI code, always include:
1. Complete, runnable code (CSS/React/Vue) 2. Light/Dark mode support via CSS custom properties 3. Design rationale explaining HIG compliance 4. Responsive breakpoints for different devices 5. Accessibility attributes (aria-*, role, etc.)
Default Behavior
- Use solid backgrounds by default for better readability
- Glass/blur effects only when user explicitly requests
- Always provide non-glass fallback for browsers without backdrop-filter support
Example Output Structure
/**
* Apple HIG Compliant Component
*
* Design Decisions:
* - Uses SF Pro system font stack for native feel
* - 44pt minimum touch target for accessibility
* - Capsule shape for primary actions (HIG recommendation)
* - Solid background for optimal readability
* - Supports prefers-color-scheme for auto theming
*/
const AppleButton = ({ children, variant = 'primary', ...props }) => {
return (
<button
className={`btn btn-${variant}`}
{...props}
>
{children}
</button>
);
};---
Best Practices Checklist
Before finalizing any design output, verify:
- [ ] Typography: Using SF Pro with correct size thresholds
- [ ] Colors: System colors with Light/Dark variants
- [ ] Spacing: Following 8pt grid
- [ ] Touch targets: Minimum 44×44pt
- [ ] Border radius: Concentric relationships maintained
- [ ] Animations: Using Apple-standard easing curves
- [ ] Accessibility: WCAG AA compliant, reduced motion support
- [ ] Consistency: Matches platform conventions
- [ ] Backgrounds: Solid by default, glass only when requested
---
Additional Resources
Local References (read these for detailed implementations)
- For complete design patterns and real-world examples, see REFERENCE.md
- For CSS custom properties and design tokens, see resources/design-tokens.css
- For React component templates, see resources/components.jsx
- For detailed UI pattern documentation, see resources/ui-patterns.md
External References
---
This skill ensures Claude Code produces interfaces that meet Apple's exacting design standards, creating cohesive, accessible, and beautiful user experiences.
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
# IDE and editor files
.idea/
.vscode/
*.swp
*.swo
*~
*.sublime-project
*.sublime-workspace
# Temporary files
*.tmp
*.temp
*.log
# Node modules (if any)
node_modules/
# Python
__pycache__/
*.py[cod]
*$py.class
.Python
*.so
.env
.venv
venv/
# Build outputs
dist/
build/
*.min.js
*.min.css
# Claude Code local configuration
.claude/
# Development and research files
research/
test-examples/
# Spec workflow templates (optional)
.spec-workflow/
# Project architecture index (for AI use)
CLAUDE.md
resources/CLAUDE.md
MIT License
Copyright (c) 2024 Apple HIG Designer Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
<h1 align="center">Apple HIG 设计器</h1>
<div align="center">
Apple-Hig-Designer,用于创建符合 Apple HIG以底层原则与各种风格融合的界面
English | 简体中文
</div>
---
📸 基础效果展示:
<div align="center">
单页面设计 “创建 iOS 页面转场动画”
<!-- Page Transitions --> !Page Transitions
"设计一个Web端的Mac风格的聊天页面"
<!-- macOS Chat Page --> !macOS Chat
</div>
<div align="center"> 📸风格融合设计: “请运用您高超的苹果设计师技能,选择一个合适的框架,并开发一个完整的响应式前端项目,融入奢侈品牌的美学理念。”
Vercel:https://fashion-editorial.vercel.app/ <img width="2075" height="1206" alt="image" src="https://github.com/user-attachments/assets/605fa6b0-51a9-440d-b0b0-a3578988ecf3" />
</div>
🎯 概述
这是一个 Claude Code Skill,用于创建符合 Apple 人机界面指南的专业界面设计。包含以下知识:
- SF Pro 字体系统
- Apple 系统色彩 (支持亮色/暗色模式)
- 8pt 网格间距系统
- 组件模式 (按钮、卡片、输入框等)
- 动画指南 (Apple 标准缓动曲线)
📦 安装方法
方法一:用户级安装(推荐)
将 Skill 复制到 Claude Code 技能目录:
# Windows
xcopy /E /I "apple-hig-designer" "%USERPROFILE%\.claude\skills\apple-hig-designer"
# macOS / Linux
cp -r apple-hig-designer ~/.claude/skills/方法二:项目级安装
复制到项目的 .claude/skills 目录:
mkdir -p .claude/skills
cp -r apple-hig-designer .claude/skills/🚀 使用方法
安装后,当您进行以下操作时,Claude Code 会自动激活此 Skill:
初级用法:请告诉claude code,你要使用apple-hig-designer 示例
- "设计一个苹果风格的..."
- "创建一个符合 HIG 的..."
- "iOS 风格的组件"
进阶用法:使用apple-hig-designer与其他风格进行融合,以apple-hig-designer设计为基础框架
示例 -"使用apple-hig-designer技能融合赛博朋克风格,帮我开发一个机器人展示页面?"
-"使用apple-hig-designer技能融合其他风格,开发一个博客主题的网站,你有什么推荐的风格吗?"
📁 文件结构
apple-hig-designer/
├── SKILL.md # 主技能定义文件
├── REFERENCE.md # 详细 HIG 参考文档
├── README.md # 英文文档
├── README_CN.md # 中文文档
├── LICENSE # MIT 许可证
└── resources/
├── components.jsx # React 组件示例
├── design-tokens.css # CSS 自定义属性
└── ui-patterns.md # UI 模式文档🎨 功能特性
| 功能 | 描述 |
|---|---|
| 字体排版 | SF Pro 字体系统,正确的尺寸阈值 |
| 色彩系统 | 完整的 Apple 系统色彩调色板 |
| 间距系统 | 8pt 网格系统实现 |
| 组件库 | 按钮、卡片、输入框、毛玻璃面板 |
| 动画效果 | Apple 标准三次贝塞尔缓动 |
| 无障碍 | WCAG AA 合规,减少动效支持 |
| 深色模式 | 完整的亮色/暗色模式支持 |
📚 参考资源
🤝 贡献
欢迎提交 Issue 和 Pull Request!
📄 许可证
本项目采用 MIT 许可证 - 详见 LICENSE 文件。
---
<h1 align="center">Apple-Hig-Designer</h1>
<div align="center">
Apple-Hig-Designer is used to create interfaces that conform to Apple Hig principles and blend with various styles.
English | 简体中文
</div>
---
📸 Basic effect demonstration:
<div align="center">
Single page design
"Creating iOS Page Transition Animations"
<!-- Page Transitions --> !Page Transitions
"Design a Mac-style chat page for web use." <!-- macOS Chat Page --> !macOS Chat
</div>
<div align="center"> 📸Style fusion design:
"Please use your apple-hig-designer skills to choose a suitable framework and develop a complete responsive front-end project that incorporates a luxury brand aesthetic."
Vercel: https://fashion-editorial.vercel.app/ <img width="2075" height="1206" alt="image" src="https://github.com/user-attachments/assets/605fa6b0-51a9-440d-b0b0-a3578988ecf3" />
</div>
🎯 Overview
This apple-hig-designer enables you to design professional web and mobile interfaces that follow Apple's Human Interface Guidelines. It provides comprehensive knowledge about:
- SF Pro Typography system
- Apple System Colors with light/dark mode support
- 8pt Grid Spacing System
- Component Patterns (buttons, cards, inputs, etc.)
- Animation Guidelines with Apple-standard easing curves
📦 Installation
Method 1: User-level Installation (Recommended)
Copy the skill to your Claude Code skills directory:
# Windows
xcopy /E /I "apple-hig-designer" "%USERPROFILE%\.claude\skills\apple-hig-designer"
# macOS / Linux
cp -r apple-hig-designer ~/.claude/skills/Method 2: Project-level Installation
Copy to your project's .claude/skills directory:
mkdir -p .claude/skills
cp -r apple-hig-designer .claude/skills/🚀 Usage
After installation, Claude Code will automatically activate this skill when you perform the following actions:
Basic usage: Please tell Claude Code that you want to use apple-hig-designer.
Example
- "Design an Apple-style..."
- "Create a HIG-compliant..."
- "iOS-style components"
Advanced usage: Integrate apple-hig-designer with other styles, using apple-hig-designer as the basic design framework.
Example
"Using your apple-hig-designer skills and a cyberpunk aesthetic, could you help me develop a robot showcase page?"
"Using apple-hig-designer skills to blend other styles, how would you recommend a style for developing a blog-themed website?"
📁 File Structure
apple-hig-designer/
├── SKILL.md # Main skill definition
├── REFERENCE.md # Detailed HIG reference documentation
├── README.md # English documentation
├── README_CN.md # Chinese documentation
├── LICENSE # MIT License
└── resources/
├── components.jsx # React component examples
├── design-tokens.css # CSS custom properties
└── ui-patterns.md # UI pattern documentation🎨 Features
| Feature | Description |
|---|---|
| Typography | SF Pro font system with proper size thresholds |
| Colors | Complete Apple system color palette |
| Spacing | 8pt grid system implementation |
| Components | Buttons, cards, inputs, glass panels |
| Animations | Apple-standard cubic-bezier easing |
| Accessibility | WCAG AA compliant, reduced motion support |
| Dark Mode | Full light/dark mode support |
📚 Resources
🤝 Contributing
Contributions are welcome! Feel free to submit Issues and Pull Requests.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
---
<div align="center">
</div>
Apple HIG Design Reference Guide
This document provides comprehensive design patterns, real-world examples, and detailed specifications for creating Apple-quality interfaces.
---
Table of Contents
1. Page Layout Patterns 2. Navigation Patterns 3. Form Design Patterns 4. Data Display Patterns 5. Modal & Dialog Patterns 6. Empty States & Error Handling 7. Loading States 8. Responsive Design Breakpoints 9. Platform-Specific Adaptations 10. Complete Component Examples
---
Page Layout Patterns
Hero Section (Landing Page)
<section class="hero">
<div class="hero-content">
<h1 class="hero-title">Welcome to Innovation</h1>
<p class="hero-subtitle">
Experience the future of technology with our revolutionary platform.
</p>
<div class="hero-actions">
<button class="btn-primary">Get Started</button>
<button class="btn-secondary">Learn More</button>
</div>
</div>
</section>.hero {
display: flex;
align-items: center;
justify-content: center;
min-height: 80vh;
padding: var(--space-8) var(--space-4);
text-align: center;
background: linear-gradient(
180deg,
var(--bg-primary) 0%,
var(--bg-secondary) 100%
);
}
.hero-content {
max-width: 680px;
}
.hero-title {
font-family: var(--font-system);
font-size: clamp(40px, 8vw, 80px);
font-weight: 700;
letter-spacing: -0.02em;
line-height: 1.05;
color: var(--label-primary);
margin-bottom: var(--space-4);
}
.hero-subtitle {
font-size: clamp(17px, 3vw, 24px);
font-weight: 400;
line-height: 1.4;
color: var(--label-secondary);
margin-bottom: var(--space-6);
}
.hero-actions {
display: flex;
gap: var(--space-3);
justify-content: center;
flex-wrap: wrap;
}Content Section with Cards
<section class="content-section">
<header class="section-header">
<h2 class="section-title">Features</h2>
<p class="section-description">Everything you need to succeed.</p>
</header>
<div class="card-grid">
<article class="feature-card">
<div class="feature-icon">
<span class="sf-symbol"></span>
</div>
<h3 class="feature-title">Lightning Fast</h3>
<p class="feature-description">
Optimized performance that keeps you moving forward.
</p>
</article>
<!-- More cards... -->
</div>
</section>.content-section {
padding: var(--space-12) var(--space-4);
max-width: 1200px;
margin: 0 auto;
}
.section-header {
text-align: center;
margin-bottom: var(--space-8);
}
.section-title {
font-size: 48px;
font-weight: 700;
letter-spacing: -0.01em;
color: var(--label-primary);
margin-bottom: var(--space-2);
}
.section-description {
font-size: 21px;
color: var(--label-secondary);
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--space-4);
}
.feature-card {
background: var(--bg-tertiary);
border-radius: var(--radius-xl);
padding: var(--space-6);
text-align: center;
transition: transform var(--duration-normal) var(--ease-out),
box-shadow var(--duration-normal) var(--ease-out);
}
.feature-card:hover {
transform: translateY(-4px);
box-shadow: 0 12px 40px rgba(0, 0, 0, 0.12);
}
.feature-icon {
width: 56px;
height: 56px;
margin: 0 auto var(--space-4);
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
background: linear-gradient(135deg, var(--system-blue), var(--system-purple));
border-radius: var(--radius-lg);
color: white;
}
.feature-title {
font-size: 21px;
font-weight: 600;
color: var(--label-primary);
margin-bottom: var(--space-2);
}
.feature-description {
font-size: 15px;
color: var(--label-secondary);
line-height: 1.5;
}---
Navigation Patterns
Top Navigation Bar
<nav class="navbar" role="navigation" aria-label="Main navigation">
<div class="navbar-container">
<a href="/" class="navbar-brand" aria-label="Home">
<span class="brand-logo"></span>
<span class="brand-name">AppName</span>
</a>
<ul class="navbar-menu">
<li><a href="/features" class="nav-link">Features</a></li>
<li><a href="/pricing" class="nav-link">Pricing</a></li>
<li><a href="/about" class="nav-link">About</a></li>
</ul>
<div class="navbar-actions">
<button class="btn-text">Sign In</button>
<button class="btn-primary btn-sm">Get Started</button>
</div>
<button class="navbar-toggle" aria-label="Toggle menu" aria-expanded="false">
<span class="hamburger-line"></span>
<span class="hamburger-line"></span>
</button>
</div>
</nav>.navbar {
position: sticky;
top: 0;
z-index: 100;
background: rgba(255, 255, 255, 0.72);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-bottom: 1px solid var(--separator);
}
@media (prefers-color-scheme: dark) {
.navbar {
background: rgba(29, 29, 31, 0.72);
}
}
.navbar-container {
display: flex;
align-items: center;
justify-content: space-between;
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--space-4);
height: 52px;
}
.navbar-brand {
display: flex;
align-items: center;
gap: var(--space-2);
text-decoration: none;
color: var(--label-primary);
font-weight: 600;
font-size: 17px;
}
.brand-logo {
font-size: 24px;
}
.navbar-menu {
display: flex;
list-style: none;
gap: var(--space-6);
margin: 0;
padding: 0;
}
.nav-link {
font-size: 14px;
font-weight: 400;
color: var(--label-primary);
text-decoration: none;
padding: var(--space-2) 0;
transition: color var(--duration-fast) var(--ease-out);
}
.nav-link:hover {
color: var(--system-blue);
}
.navbar-actions {
display: flex;
align-items: center;
gap: var(--space-3);
}
.btn-text {
font-size: 14px;
font-weight: 400;
color: var(--system-blue);
background: transparent;
border: none;
cursor: pointer;
padding: var(--space-2) var(--space-3);
}
.btn-sm {
font-size: 14px;
padding: 8px 16px;
min-height: 32px;
}
/* Mobile Navigation Toggle */
.navbar-toggle {
display: none;
flex-direction: column;
justify-content: center;
gap: 4px;
width: 44px;
height: 44px;
background: transparent;
border: none;
cursor: pointer;
}
.hamburger-line {
width: 20px;
height: 2px;
background: var(--label-primary);
border-radius: 1px;
transition: transform var(--duration-fast) var(--ease-out);
}
@media (max-width: 768px) {
.navbar-menu,
.navbar-actions {
display: none;
}
.navbar-toggle {
display: flex;
}
}Tab Bar (iOS Style)
<nav class="tab-bar" role="tablist" aria-label="Main sections">
<a href="/" class="tab-item active" role="tab" aria-selected="true">
<span class="tab-icon"></span>
<span class="tab-label">Home</span>
</a>
<a href="/search" class="tab-item" role="tab" aria-selected="false">
<span class="tab-icon"></span>
<span class="tab-label">Search</span>
</a>
<a href="/library" class="tab-item" role="tab" aria-selected="false">
<span class="tab-icon"></span>
<span class="tab-label">Library</span>
</a>
<a href="/profile" class="tab-item" role="tab" aria-selected="false">
<span class="tab-icon"></span>
<span class="tab-label">Profile</span>
</a>
</nav>.tab-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
display: flex;
justify-content: space-around;
background: rgba(255, 255, 255, 0.85);
backdrop-filter: blur(20px) saturate(180%);
-webkit-backdrop-filter: blur(20px) saturate(180%);
border-top: 1px solid var(--separator);
padding-bottom: env(safe-area-inset-bottom);
z-index: 100;
}
@media (prefers-color-scheme: dark) {
.tab-bar {
background: rgba(29, 29, 31, 0.85);
}
}
.tab-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
min-width: 64px;
min-height: 49px;
padding: var(--space-1) var(--space-3);
text-decoration: none;
color: var(--label-secondary);
transition: color var(--duration-fast) var(--ease-out);
}
.tab-item.active {
color: var(--system-blue);
}
.tab-icon {
font-size: 24px;
margin-bottom: 2px;
}
.tab-label {
font-size: 10px;
font-weight: 500;
}---
Form Design Patterns
Login Form
<form class="auth-form" autocomplete="on">
<header class="form-header">
<h1 class="form-title">Sign In</h1>
<p class="form-subtitle">Welcome back! Please enter your details.</p>
</header>
<div class="form-group">
<label for="email" class="form-label">Email</label>
<input
type="email"
id="email"
class="input-field"
placeholder="Enter your email"
autocomplete="email"
required
>
</div>
<div class="form-group">
<label for="password" class="form-label">Password</label>
<div class="input-wrapper">
<input
type="password"
id="password"
class="input-field"
placeholder="Enter your password"
autocomplete="current-password"
required
>
<button type="button" class="input-action" aria-label="Show password">
<span class="sf-symbol"></span>
</button>
</div>
</div>
<div class="form-options">
<label class="checkbox-label">
<input type="checkbox" class="checkbox" name="remember">
<span class="checkbox-custom"></span>
<span class="checkbox-text">Remember me</span>
</label>
<a href="/forgot-password" class="link-text">Forgot password?</a>
</div>
<button type="submit" class="btn-primary btn-full">Sign In</button>
<div class="form-divider">
<span>or continue with</span>
</div>
<div class="social-buttons">
<button type="button" class="btn-social">
<span class="social-icon"></span>
Apple
</button>
<button type="button" class="btn-social">
<span class="social-icon">G</span>
Google
</button>
</div>
<p class="form-footer">
Don't have an account? <a href="/signup" class="link-text">Sign up</a>
</p>
</form>.auth-form {
max-width: 400px;
margin: 0 auto;
padding: var(--space-8) var(--space-4);
}
.form-header {
text-align: center;
margin-bottom: var(--space-8);
}
.form-title {
font-size: 28px;
font-weight: 700;
color: var(--label-primary);
margin-bottom: var(--space-2);
}
.form-subtitle {
font-size: 15px;
color: var(--label-secondary);
}
.form-group {
margin-bottom: var(--space-4);
}
.form-label {
display: block;
font-size: 13px;
font-weight: 600;
color: var(--label-primary);
margin-bottom: var(--space-2);
}
.input-wrapper {
position: relative;
}
.input-field {
width: 100%;
min-height: 44px;
padding: 12px 16px;
font-family: var(--font-system);
font-size: 17px;
color: var(--label-primary);
background: var(--bg-secondary);
border: 1px solid transparent;
border-radius: var(--radius-md);
outline: none;
transition: border-color var(--duration-fast) var(--ease-out),
box-shadow var(--duration-fast) var(--ease-out);
}
.input-field:focus {
border-color: var(--system-blue);
box-shadow: 0 0 0 3px rgba(0, 122, 255, 0.2);
}
.input-field::placeholder {
color: var(--label-tertiary);
}
.input-action {
position: absolute;
right: 8px;
top: 50%;
transform: translateY(-50%);
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
color: var(--label-secondary);
cursor: pointer;
}
.form-options {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-6);
}
.checkbox-label {
display: flex;
align-items: center;
gap: var(--space-2);
cursor: pointer;
}
.checkbox {
position: absolute;
opacity: 0;
cursor: pointer;
}
.checkbox-custom {
width: 20px;
height: 20px;
border: 2px solid var(--system-gray3);
border-radius: 6px;
transition: all var(--duration-fast) var(--ease-out);
}
.checkbox:checked + .checkbox-custom {
background: var(--system-blue);
border-color: var(--system-blue);
}
.checkbox:checked + .checkbox-custom::after {
content: '✓';
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
font-weight: 700;
}
.checkbox-text {
font-size: 14px;
color: var(--label-primary);
}
.link-text {
font-size: 14px;
color: var(--system-blue);
text-decoration: none;
}
.link-text:hover {
text-decoration: underline;
}
.btn-full {
width: 100%;
}
.form-divider {
display: flex;
align-items: center;
margin: var(--space-6) 0;
color: var(--label-tertiary);
font-size: 13px;
}
.form-divider::before,
.form-divider::after {
content: '';
flex: 1;
height: 1px;
background: var(--separator);
}
.form-divider span {
padding: 0 var(--space-3);
}
.social-buttons {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
margin-bottom: var(--space-6);
}
.btn-social {
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-2);
min-height: 44px;
font-family: var(--font-system);
font-size: 15px;
font-weight: 500;
color: var(--label-primary);
background: var(--bg-tertiary);
border: 1px solid var(--separator);
border-radius: var(--radius-md);
cursor: pointer;
transition: background var(--duration-fast) var(--ease-out);
}
.btn-social:hover {
background: var(--bg-secondary);
}
.form-footer {
text-align: center;
font-size: 14px;
color: var(--label-secondary);
}Settings Form (Grouped Style)
<div class="settings-container">
<section class="settings-group">
<h3 class="settings-group-title">Account</h3>
<div class="settings-card">
<div class="settings-item">
<div class="settings-item-content">
<span class="settings-icon" style="background: var(--system-blue);"></span>
<div class="settings-text">
<span class="settings-label">Profile</span>
<span class="settings-value">John Doe</span>
</div>
</div>
<span class="settings-chevron"></span>
</div>
<div class="settings-item">
<div class="settings-item-content">
<span class="settings-icon" style="background: var(--system-gray);"></span>
<div class="settings-text">
<span class="settings-label">Email</span>
<span class="settings-value">john@example.com</span>
</div>
</div>
<span class="settings-chevron"></span>
</div>
</div>
</section>
<section class="settings-group">
<h3 class="settings-group-title">Preferences</h3>
<div class="settings-card">
<div class="settings-item">
<div class="settings-item-content">
<span class="settings-icon" style="background: var(--system-orange);"></span>
<span class="settings-label">Notifications</span>
</div>
<label class="toggle">
<input type="checkbox" checked>
<span class="toggle-slider"></span>
</label>
</div>
<div class="settings-item">
<div class="settings-item-content">
<span class="settings-icon" style="background: var(--system-indigo);"></span>
<span class="settings-label">Dark Mode</span>
</div>
<label class="toggle">
<input type="checkbox">
<span class="toggle-slider"></span>
</label>
</div>
</div>
</section>
</div>.settings-container {
max-width: 600px;
margin: 0 auto;
padding: var(--space-4);
}
.settings-group {
margin-bottom: var(--space-6);
}
.settings-group-title {
font-size: 13px;
font-weight: 400;
color: var(--label-secondary);
text-transform: uppercase;
letter-spacing: 0.02em;
margin-left: var(--space-4);
margin-bottom: var(--space-2);
}
.settings-card {
background: var(--bg-tertiary);
border-radius: var(--radius-lg);
overflow: hidden;
}
.settings-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--separator);
cursor: pointer;
transition: background var(--duration-fast) var(--ease-out);
}
.settings-item:last-child {
border-bottom: none;
}
.settings-item:hover {
background: var(--bg-secondary);
}
.settings-item-content {
display: flex;
align-items: center;
gap: var(--space-3);
}
.settings-icon {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
font-size: 16px;
color: white;
border-radius: 6px;
}
.settings-text {
display: flex;
flex-direction: column;
}
.settings-label {
font-size: 17px;
color: var(--label-primary);
}
.settings-value {
font-size: 13px;
color: var(--label-secondary);
}
.settings-chevron {
font-size: 14px;
color: var(--label-tertiary);
}
/* Toggle Switch */
.toggle {
position: relative;
width: 51px;
height: 31px;
cursor: pointer;
}
.toggle input {
opacity: 0;
width: 0;
height: 0;
}
.toggle-slider {
position: absolute;
inset: 0;
background: var(--system-gray4);
border-radius: 31px;
transition: background var(--duration-fast) var(--ease-out);
}
.toggle-slider::before {
content: '';
position: absolute;
width: 27px;
height: 27px;
left: 2px;
top: 2px;
background: white;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: transform var(--duration-fast) var(--ease-out);
}
.toggle input:checked + .toggle-slider {
background: var(--system-green);
}
.toggle input:checked + .toggle-slider::before {
transform: translateX(20px);
}---
Data Display Patterns
List View
<ul class="list-view" role="list">
<li class="list-item">
<img src="avatar1.jpg" alt="" class="list-avatar">
<div class="list-content">
<span class="list-title">Sarah Johnson</span>
<span class="list-subtitle">Product Designer</span>
</div>
<span class="list-accessory"></span>
</li>
<li class="list-item">
<img src="avatar2.jpg" alt="" class="list-avatar">
<div class="list-content">
<span class="list-title">Mike Chen</span>
<span class="list-subtitle">Engineer</span>
</div>
<span class="list-accessory"></span>
</li>
</ul>.list-view {
list-style: none;
margin: 0;
padding: 0;
background: var(--bg-tertiary);
border-radius: var(--radius-lg);
overflow: hidden;
}
.list-item {
display: flex;
align-items: center;
padding: var(--space-3) var(--space-4);
border-bottom: 1px solid var(--separator);
cursor: pointer;
transition: background var(--duration-fast) var(--ease-out);
}
.list-item:last-child {
border-bottom: none;
}
.list-item:hover {
background: var(--bg-secondary);
}
.list-item:active {
background: var(--system-gray5);
}
.list-avatar {
width: 44px;
height: 44px;
border-radius: 50%;
object-fit: cover;
margin-right: var(--space-3);
}
.list-content {
flex: 1;
min-width: 0;
}
.list-title {
display: block;
font-size: 17px;
font-weight: 400;
color: var(--label-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.list-subtitle {
display: block;
font-size: 14px;
color: var(--label-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.list-accessory {
font-size: 14px;
color: var(--label-tertiary);
margin-left: var(--space-2);
}Data Table
<div class="table-container">
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Date</th>
<th>Amount</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<div class="table-cell-main">
<img src="avatar.jpg" alt="" class="table-avatar">
<span>Sarah Johnson</span>
</div>
</td>
<td><span class="badge badge-success">Active</span></td>
<td>Dec 12, 2024</td>
<td>$2,400.00</td>
</tr>
</tbody>
</table>
</div>.table-container {
overflow-x: auto;
border-radius: var(--radius-lg);
background: var(--bg-tertiary);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: 14px;
}
.data-table th {
padding: var(--space-3) var(--space-4);
text-align: left;
font-weight: 600;
color: var(--label-secondary);
background: var(--bg-secondary);
border-bottom: 1px solid var(--separator);
}
.data-table td {
padding: var(--space-3) var(--space-4);
color: var(--label-primary);
border-bottom: 1px solid var(--separator);
}
.data-table tr:last-child td {
border-bottom: none;
}
.data-table tr:hover td {
background: var(--bg-secondary);
}
.table-cell-main {
display: flex;
align-items: center;
gap: var(--space-3);
}
.table-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
}
.badge {
display: inline-flex;
align-items: center;
padding: 4px 10px;
font-size: 12px;
font-weight: 500;
border-radius: var(--radius-full);
}
.badge-success {
color: var(--system-green);
background: rgba(52, 199, 89, 0.12);
}
.badge-warning {
color: var(--system-orange);
background: rgba(255, 149, 0, 0.12);
}
.badge-error {
color: var(--system-red);
background: rgba(255, 59, 48, 0.12);
}---
Modal & Dialog Patterns
Alert Dialog
<div class="modal-overlay" role="dialog" aria-modal="true" aria-labelledby="modal-title">
<div class="modal-content modal-alert">
<div class="modal-icon modal-icon-warning">
<span></span>
</div>
<h2 id="modal-title" class="modal-title">Delete Item?</h2>
<p class="modal-message">
This action cannot be undone. Are you sure you want to delete this item?
</p>
<div class="modal-actions">
<button class="btn-secondary">Cancel</button>
<button class="btn-destructive">Delete</button>
</div>
</div>
</div>.modal-overlay {
position: fixed;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(4px);
z-index: 1000;
animation: fadeIn var(--duration-fast) var(--ease-out);
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
.modal-content {
background: var(--bg-tertiary);
border-radius: var(--radius-xl);
padding: var(--space-6);
max-width: 320px;
width: 90%;
text-align: center;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
animation: slideUp var(--duration-normal) var(--ease-spring);
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(20px) scale(0.95);
}
to {
opacity: 1;
transform: translateY(0) scale(1);
}
}
.modal-icon {
width: 56px;
height: 56px;
margin: 0 auto var(--space-4);
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
border-radius: 50%;
}
.modal-icon-warning {
color: var(--system-orange);
background: rgba(255, 149, 0, 0.12);
}
.modal-icon-error {
color: var(--system-red);
background: rgba(255, 59, 48, 0.12);
}
.modal-icon-success {
color: var(--system-green);
background: rgba(52, 199, 89, 0.12);
}
.modal-title {
font-size: 17px;
font-weight: 600;
color: var(--label-primary);
margin-bottom: var(--space-2);
}
.modal-message {
font-size: 13px;
color: var(--label-secondary);
line-height: 1.5;
margin-bottom: var(--space-6);
}
.modal-actions {
display: flex;
gap: var(--space-3);
}
.modal-actions button {
flex: 1;
}
.btn-destructive {
min-height: 44px;
padding: 12px 24px;
font-family: var(--font-system);
font-size: 17px;
font-weight: 600;
color: white;
background: var(--system-red);
border: none;
border-radius: var(--radius-full);
cursor: pointer;
}Bottom Sheet
<div class="sheet-overlay">
<div class="sheet-container">
<div class="sheet-handle"></div>
<div class="sheet-content">
<h3 class="sheet-title">Share</h3>
<div class="sheet-grid">
<button class="sheet-action">
<span class="sheet-action-icon" style="background: var(--system-green);"></span>
<span class="sheet-action-label">Messages</span>
</button>
<button class="sheet-action">
<span class="sheet-action-icon" style="background: var(--system-blue);"></span>
<span class="sheet-action-label">Mail</span>
</button>
<button class="sheet-action">
<span class="sheet-action-icon" style="background: #1DA1F2;"></span>
<span class="sheet-action-label">Twitter</span>
</button>
<button class="sheet-action">
<span class="sheet-action-icon" style="background: var(--system-gray);"></span>
<span class="sheet-action-label">Copy Link</span>
</button>
</div>
<button class="sheet-cancel">Cancel</button>
</div>
</div>
</div>.sheet-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
z-index: 1000;
display: flex;
align-items: flex-end;
}
.sheet-container {
width: 100%;
background: var(--bg-secondary);
border-radius: var(--radius-xl) var(--radius-xl) 0 0;
padding-bottom: env(safe-area-inset-bottom);
animation: slideUpSheet var(--duration-normal) var(--ease-out);
}
@keyframes slideUpSheet {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.sheet-handle {
width: 36px;
height: 5px;
background: var(--system-gray4);
border-radius: 2.5px;
margin: var(--space-2) auto var(--space-4);
}
.sheet-content {
padding: 0 var(--space-4) var(--space-4);
}
.sheet-title {
font-size: 13px;
font-weight: 600;
color: var(--label-secondary);
text-align: center;
margin-bottom: var(--space-4);
}
.sheet-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-4);
margin-bottom: var(--space-4);
}
.sheet-action {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-2);
padding: var(--space-2);
background: transparent;
border: none;
cursor: pointer;
}
.sheet-action-icon {
width: 60px;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
color: white;
border-radius: var(--radius-lg);
}
.sheet-action-label {
font-size: 11px;
color: var(--label-primary);
}
.sheet-cancel {
width: 100%;
min-height: 56px;
font-family: var(--font-system);
font-size: 17px;
font-weight: 600;
color: var(--system-blue);
background: var(--bg-tertiary);
border: none;
border-radius: var(--radius-lg);
cursor: pointer;
}---
Empty States & Error Handling
Empty State
<div class="empty-state">
<div class="empty-icon">
<span></span>
</div>
<h3 class="empty-title">No Messages</h3>
<p class="empty-description">
Your inbox is empty. Start a conversation to see messages here.
</p>
<button class="btn-primary">Compose Message</button>
</div>.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: var(--space-12) var(--space-4);
text-align: center;
min-height: 400px;
}
.empty-icon {
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
color: var(--label-tertiary);
background: var(--bg-secondary);
border-radius: 50%;
margin-bottom: var(--space-4);
}
.empty-title {
font-size: 21px;
font-weight: 600;
color: var(--label-primary);
margin-bottom: var(--space-2);
}
.empty-description {
font-size: 15px;
color: var(--label-secondary);
max-width: 280px;
margin-bottom: var(--space-6);
line-height: 1.5;
}Error State
<div class="error-state">
<div class="error-icon">
<span></span>
</div>
<h3 class="error-title">Something Went Wrong</h3>
<p class="error-description">
We couldn't load this content. Please check your connection and try again.
</p>
<button class="btn-secondary">
<span></span>
Try Again
</button>
</div>.error-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: var(--space-12) var(--space-4);
text-align: center;
}
.error-icon {
width: 80px;
height: 80px;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
color: var(--system-red);
background: rgba(255, 59, 48, 0.12);
border-radius: 50%;
margin-bottom: var(--space-4);
}
.error-title {
font-size: 21px;
font-weight: 600;
color: var(--label-primary);
margin-bottom: var(--space-2);
}
.error-description {
font-size: 15px;
color: var(--label-secondary);
max-width: 280px;
margin-bottom: var(--space-6);
line-height: 1.5;
}---
Loading States
Skeleton Loading
<div class="skeleton-card">
<div class="skeleton skeleton-avatar"></div>
<div class="skeleton-content">
<div class="skeleton skeleton-title"></div>
<div class="skeleton skeleton-text"></div>
<div class="skeleton skeleton-text skeleton-text-short"></div>
</div>
</div>.skeleton {
background: linear-gradient(
90deg,
var(--system-gray5) 25%,
var(--system-gray6) 50%,
var(--system-gray5) 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
border-radius: var(--radius-sm);
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.skeleton-card {
display: flex;
gap: var(--space-3);
padding: var(--space-4);
background: var(--bg-tertiary);
border-radius: var(--radius-lg);
}
.skeleton-avatar {
width: 48px;
height: 48px;
border-radius: 50%;
flex-shrink: 0;
}
.skeleton-content {
flex: 1;
}
.skeleton-title {
height: 20px;
width: 60%;
margin-bottom: var(--space-2);
}
.skeleton-text {
height: 14px;
width: 100%;
margin-bottom: var(--space-1);
}
.skeleton-text-short {
width: 40%;
}Spinner
<div class="spinner-container">
<div class="spinner"></div>
<span class="spinner-text">Loading...</span>
</div>.spinner-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: var(--space-8);
gap: var(--space-3);
}
.spinner {
width: 32px;
height: 32px;
border: 3px solid var(--system-gray5);
border-top-color: var(--system-blue);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.spinner-text {
font-size: 15px;
color: var(--label-secondary);
}---
Responsive Design Breakpoints
/* Apple-style Breakpoints */
:root {
/* Compact (iPhone Portrait) */
--breakpoint-compact: 430px;
/* Regular (iPhone Landscape, iPad) */
--breakpoint-regular: 744px;
/* Large (iPad Landscape, Desktop) */
--breakpoint-large: 1024px;
/* Extra Large (Large Displays) */
--breakpoint-xl: 1280px;
}
/* Mobile First Approach */
/* Base: iPhone Portrait (compact) */
.container {
padding: var(--space-4);
max-width: 100%;
}
/* iPhone Landscape / iPad Portrait */
@media (min-width: 744px) {
.container {
padding: var(--space-6);
max-width: 720px;
margin: 0 auto;
}
}
/* iPad Landscape / Desktop */
@media (min-width: 1024px) {
.container {
padding: var(--space-8);
max-width: 980px;
}
}
/* Large Displays */
@media (min-width: 1280px) {
.container {
max-width: 1200px;
}
}
/* Dynamic Type Support */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
/* High Contrast Mode */
@media (prefers-contrast: high) {
:root {
--separator: rgba(0, 0, 0, 0.5);
}
.btn-primary {
border: 2px solid currentColor;
}
}---
Platform-Specific Adaptations
iOS Adaptations
/* Safe Area Insets for iOS */
.app-container {
padding-top: env(safe-area-inset-top);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
padding-right: env(safe-area-inset-right);
}
/* iOS Scroll Behavior */
.scrollable {
-webkit-overflow-scrolling: touch;
overscroll-behavior: contain;
}
/* iOS Tap Highlight */
button, a {
-webkit-tap-highlight-color: transparent;
}
/* iOS Input Styling */
input, textarea, select {
-webkit-appearance: none;
appearance: none;
}macOS Adaptations
/* macOS Window Controls Spacing */
.titlebar {
padding-left: 78px; /* Space for traffic lights */
}
/* macOS Sidebar */
.sidebar {
width: 240px;
background: var(--bg-secondary);
border-right: 1px solid var(--separator);
}
/* macOS uses smaller touch targets */
@media (pointer: fine) {
.btn-primary {
min-height: 28px;
padding: 6px 12px;
font-size: 13px;
}
.input-field {
min-height: 24px;
padding: 4px 8px;
font-size: 13px;
}
}---
Complete Component Examples
Profile Card Component
/**
* Profile Card - Apple HIG Compliant
*
* Design Decisions:
* - Uses SF Pro system font for native feel
* - Circular avatar with subtle shadow
* - Grouped card style for settings context
* - Supports Light/Dark mode automatically
*/
const ProfileCard = ({ user }) => {
return (
<div className="profile-card">
<div className="profile-header">
<img
src={user.avatar}
alt={`${user.name}'s avatar`}
className="profile-avatar"
/>
<div className="profile-info">
<h2 className="profile-name">{user.name}</h2>
<p className="profile-email">{user.email}</p>
</div>
</div>
<div className="profile-stats">
<div className="stat-item">
<span className="stat-value">{user.posts}</span>
<span className="stat-label">Posts</span>
</div>
<div className="stat-item">
<span className="stat-value">{user.followers}</span>
<span className="stat-label">Followers</span>
</div>
<div className="stat-item">
<span className="stat-value">{user.following}</span>
<span className="stat-label">Following</span>
</div>
</div>
<button className="btn-primary btn-full">
Edit Profile
</button>
</div>
);
};.profile-card {
background: var(--bg-tertiary);
border-radius: var(--radius-xl);
padding: var(--space-6);
max-width: 360px;
}
.profile-header {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin-bottom: var(--space-6);
}
.profile-avatar {
width: 96px;
height: 96px;
border-radius: 50%;
object-fit: cover;
margin-bottom: var(--space-4);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.profile-name {
font-size: 22px;
font-weight: 600;
color: var(--label-primary);
margin-bottom: var(--space-1);
}
.profile-email {
font-size: 15px;
color: var(--label-secondary);
}
.profile-stats {
display: flex;
justify-content: space-around;
padding: var(--space-4) 0;
border-top: 1px solid var(--separator);
border-bottom: 1px solid var(--separator);
margin-bottom: var(--space-6);
}
.stat-item {
display: flex;
flex-direction: column;
align-items: center;
}
.stat-value {
font-size: 20px;
font-weight: 600;
color: var(--label-primary);
}
.stat-label {
font-size: 13px;
color: var(--label-secondary);
}---
This reference guide provides comprehensive patterns for building Apple-quality interfaces. Always refer to the official Apple Human Interface Guidelines for the most up-to-date specifications.
/**
* Apple HIG React Component Library
*
* A collection of React components following Apple Human Interface Guidelines.
* Use these components as templates for building Apple-quality interfaces.
*
* @requires React 18+
* @requires CSS Variables from design-tokens.css
*/
import React, { forwardRef, useState, useEffect, useRef } from 'react';
// ============================================================================
// BUTTON COMPONENTS
// ============================================================================
/**
* Primary Button - Capsule style for main actions
*
* @example
* <Button variant="primary" onClick={handleClick}>Get Started</Button>
* <Button variant="secondary" size="sm">Learn More</Button>
* <Button variant="destructive" loading>Deleting...</Button>
*/
export const Button = forwardRef(({
children,
variant = 'primary',
size = 'md',
loading = false,
disabled = false,
fullWidth = false,
icon,
iconPosition = 'left',
className = '',
...props
}, ref) => {
const baseStyles = {
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
gap: '8px',
fontFamily: 'var(--font-system)',
fontWeight: 600,
border: 'none',
borderRadius: 'var(--radius-full)',
cursor: disabled || loading ? 'not-allowed' : 'pointer',
opacity: disabled ? 0.5 : 1,
transition: 'transform 0.1s ease, opacity 0.1s ease, background 0.2s ease',
width: fullWidth ? '100%' : 'auto',
};
const sizeStyles = {
sm: { minHeight: '32px', padding: '8px 16px', fontSize: '14px' },
md: { minHeight: '44px', padding: '12px 24px', fontSize: '17px' },
lg: { minHeight: '56px', padding: '16px 32px', fontSize: '19px' },
};
const variantStyles = {
primary: {
color: '#FFFFFF',
background: 'var(--system-blue)',
},
secondary: {
color: 'var(--system-blue)',
background: 'rgba(0, 122, 255, 0.1)',
},
tertiary: {
color: 'var(--system-blue)',
background: 'transparent',
},
destructive: {
color: '#FFFFFF',
background: 'var(--system-red)',
},
ghost: {
color: 'var(--label-primary)',
background: 'transparent',
},
};
const handleMouseDown = (e) => {
if (!disabled && !loading) {
e.currentTarget.style.transform = 'scale(0.98)';
}
};
const handleMouseUp = (e) => {
e.currentTarget.style.transform = 'scale(1)';
};
return (
<button
ref={ref}
disabled={disabled || loading}
style={{
...baseStyles,
...sizeStyles[size],
...variantStyles[variant],
}}
className={`apple-button apple-button--${variant} ${className}`}
onMouseDown={handleMouseDown}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
{...props}
>
{loading && <Spinner size="sm" />}
{icon && iconPosition === 'left' && !loading && <span className="button-icon">{icon}</span>}
{children}
{icon && iconPosition === 'right' && <span className="button-icon">{icon}</span>}
</button>
);
});
Button.displayName = 'Button';
// ============================================================================
// INPUT COMPONENTS
// ============================================================================
/**
* Text Input - HIG compliant text field
*
* @example
* <Input
* label="Email"
* type="email"
* placeholder="Enter your email"
* error="Invalid email format"
* />
*/
export const Input = forwardRef(({
label,
error,
helper,
icon,
iconPosition = 'left',
fullWidth = true,
className = '',
...props
}, ref) => {
const [isFocused, setIsFocused] = useState(false);
const containerStyles = {
display: 'flex',
flexDirection: 'column',
gap: '8px',
width: fullWidth ? '100%' : 'auto',
};
const labelStyles = {
fontSize: '13px',
fontWeight: 600,
color: 'var(--label-primary)',
fontFamily: 'var(--font-system)',
};
const inputWrapperStyles = {
position: 'relative',
display: 'flex',
alignItems: 'center',
};
const inputStyles = {
width: '100%',
minHeight: '44px',
padding: icon ? (iconPosition === 'left' ? '12px 16px 12px 44px' : '12px 44px 12px 16px') : '12px 16px',
fontFamily: 'var(--font-system)',
fontSize: '17px',
color: 'var(--label-primary)',
background: 'var(--bg-secondary)',
border: `1px solid ${error ? 'var(--system-red)' : isFocused ? 'var(--system-blue)' : 'transparent'}`,
borderRadius: 'var(--radius-md)',
outline: 'none',
transition: 'border-color 0.2s ease, box-shadow 0.2s ease',
boxShadow: isFocused ? '0 0 0 3px rgba(0, 122, 255, 0.2)' : 'none',
};
const iconStyles = {
position: 'absolute',
[iconPosition]: '12px',
color: 'var(--label-tertiary)',
fontSize: '17px',
pointerEvents: 'none',
};
const helperStyles = {
fontSize: '13px',
color: error ? 'var(--system-red)' : 'var(--label-secondary)',
fontFamily: 'var(--font-system)',
};
return (
<div style={containerStyles} className={className}>
{label && <label style={labelStyles}>{label}</label>}
<div style={inputWrapperStyles}>
{icon && <span style={iconStyles}>{icon}</span>}
<input
ref={ref}
style={inputStyles}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
{...props}
/>
</div>
{(error || helper) && <span style={helperStyles}>{error || helper}</span>}
</div>
);
});
Input.displayName = 'Input';
/**
* Textarea - Multi-line text input
*/
export const Textarea = forwardRef(({
label,
error,
helper,
rows = 4,
className = '',
...props
}, ref) => {
const [isFocused, setIsFocused] = useState(false);
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px', width: '100%' }} className={className}>
{label && (
<label style={{ fontSize: '13px', fontWeight: 600, color: 'var(--label-primary)' }}>
{label}
</label>
)}
<textarea
ref={ref}
rows={rows}
style={{
width: '100%',
padding: '12px 16px',
fontFamily: 'var(--font-system)',
fontSize: '17px',
color: 'var(--label-primary)',
background: 'var(--bg-secondary)',
border: `1px solid ${error ? 'var(--system-red)' : isFocused ? 'var(--system-blue)' : 'transparent'}`,
borderRadius: 'var(--radius-md)',
outline: 'none',
resize: 'vertical',
transition: 'border-color 0.2s ease, box-shadow 0.2s ease',
boxShadow: isFocused ? '0 0 0 3px rgba(0, 122, 255, 0.2)' : 'none',
}}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
{...props}
/>
{(error || helper) && (
<span style={{ fontSize: '13px', color: error ? 'var(--system-red)' : 'var(--label-secondary)' }}>
{error || helper}
</span>
)}
</div>
);
});
Textarea.displayName = 'Textarea';
// ============================================================================
// TOGGLE / SWITCH COMPONENT
// ============================================================================
/**
* Toggle Switch - iOS style toggle
*
* @example
* <Toggle checked={isEnabled} onChange={setIsEnabled} label="Notifications" />
*/
export const Toggle = ({ checked, onChange, label, disabled = false, className = '' }) => {
const toggleStyles = {
position: 'relative',
width: '51px',
height: '31px',
cursor: disabled ? 'not-allowed' : 'pointer',
opacity: disabled ? 0.5 : 1,
};
const sliderStyles = {
position: 'absolute',
inset: 0,
background: checked ? 'var(--system-green)' : 'var(--system-gray4)',
borderRadius: '31px',
transition: 'background 0.2s ease',
};
const knobStyles = {
position: 'absolute',
width: '27px',
height: '27px',
left: '2px',
top: '2px',
background: 'white',
borderRadius: '50%',
boxShadow: '0 2px 4px rgba(0, 0, 0, 0.2)',
transform: checked ? 'translateX(20px)' : 'translateX(0)',
transition: 'transform 0.2s ease',
};
return (
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }} className={className}>
<button
type="button"
role="switch"
aria-checked={checked}
disabled={disabled}
onClick={() => !disabled && onChange(!checked)}
style={toggleStyles}
>
<span style={sliderStyles} />
<span style={knobStyles} />
</button>
{label && <span style={{ fontSize: '17px', color: 'var(--label-primary)' }}>{label}</span>}
</div>
);
};
// ============================================================================
// CARD COMPONENTS
// ============================================================================
/**
* Card - Container with Apple styling
*
* @example
* <Card variant="elevated">
* <Card.Header>Title</Card.Header>
* <Card.Body>Content here</Card.Body>
* </Card>
*/
export const Card = ({ children, variant = 'default', padding = 'md', className = '', style = {}, ...props }) => {
const paddingValues = {
none: '0',
sm: 'var(--space-3)',
md: 'var(--space-4)',
lg: 'var(--space-6)',
};
const variantStyles = {
default: {
background: 'var(--bg-tertiary)',
boxShadow: 'none',
},
elevated: {
background: 'var(--bg-tertiary)',
boxShadow: '0 1px 3px rgba(0, 0, 0, 0.04), 0 4px 12px rgba(0, 0, 0, 0.04)',
},
glass: {
background: 'rgba(255, 255, 255, 0.7)',
backdropFilter: 'blur(20px) saturate(180%)',
WebkitBackdropFilter: 'blur(20px) saturate(180%)',
border: '1px solid rgba(255, 255, 255, 0.3)',
},
};
return (
<div
style={{
borderRadius: 'var(--radius-xl)',
padding: paddingValues[padding],
...variantStyles[variant],
...style,
}}
className={`apple-card apple-card--${variant} ${className}`}
{...props}
>
{children}
</div>
);
};
Card.Header = ({ children, className = '', ...props }) => (
<div
style={{
fontSize: '17px',
fontWeight: 600,
color: 'var(--label-primary)',
marginBottom: 'var(--space-3)',
}}
className={className}
{...props}
>
{children}
</div>
);
Card.Body = ({ children, className = '', ...props }) => (
<div
style={{
fontSize: '15px',
color: 'var(--label-secondary)',
lineHeight: 1.5,
}}
className={className}
{...props}
>
{children}
</div>
);
// ============================================================================
// MODAL COMPONENTS
// ============================================================================
/**
* Modal - Alert dialog or sheet
*
* @example
* <Modal isOpen={showModal} onClose={() => setShowModal(false)}>
* <Modal.Header>Confirm Action</Modal.Header>
* <Modal.Body>Are you sure?</Modal.Body>
* <Modal.Actions>
* <Button variant="secondary">Cancel</Button>
* <Button variant="destructive">Delete</Button>
* </Modal.Actions>
* </Modal>
*/
export const Modal = ({ isOpen, onClose, children, variant = 'alert', className = '' }) => {
const overlayRef = useRef(null);
useEffect(() => {
const handleEscape = (e) => {
if (e.key === 'Escape') onClose();
};
if (isOpen) {
document.addEventListener('keydown', handleEscape);
document.body.style.overflow = 'hidden';
}
return () => {
document.removeEventListener('keydown', handleEscape);
document.body.style.overflow = '';
};
}, [isOpen, onClose]);
if (!isOpen) return null;
const handleOverlayClick = (e) => {
if (e.target === overlayRef.current) onClose();
};
return (
<div
ref={overlayRef}
onClick={handleOverlayClick}
style={{
position: 'fixed',
inset: 0,
display: 'flex',
alignItems: variant === 'sheet' ? 'flex-end' : 'center',
justifyContent: 'center',
background: 'rgba(0, 0, 0, 0.4)',
backdropFilter: 'blur(4px)',
zIndex: 1000,
animation: 'fadeIn 0.2s ease',
}}
role="dialog"
aria-modal="true"
className={className}
>
<div
style={{
background: 'var(--bg-tertiary)',
borderRadius: variant === 'sheet' ? 'var(--radius-xl) var(--radius-xl) 0 0' : 'var(--radius-xl)',
padding: 'var(--space-6)',
maxWidth: variant === 'sheet' ? '100%' : '320px',
width: variant === 'sheet' ? '100%' : '90%',
textAlign: 'center',
boxShadow: '0 20px 60px rgba(0, 0, 0, 0.3)',
animation: variant === 'sheet' ? 'slideUp 0.3s ease' : 'scaleIn 0.2s ease',
}}
>
{children}
</div>
</div>
);
};
Modal.Header = ({ children, icon, iconVariant = 'default' }) => {
const iconColors = {
default: { color: 'var(--system-blue)', bg: 'rgba(0, 122, 255, 0.12)' },
warning: { color: 'var(--system-orange)', bg: 'rgba(255, 149, 0, 0.12)' },
error: { color: 'var(--system-red)', bg: 'rgba(255, 59, 48, 0.12)' },
success: { color: 'var(--system-green)', bg: 'rgba(52, 199, 89, 0.12)' },
};
return (
<>
{icon && (
<div
style={{
width: '56px',
height: '56px',
margin: '0 auto var(--space-4)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '28px',
color: iconColors[iconVariant].color,
background: iconColors[iconVariant].bg,
borderRadius: '50%',
}}
>
{icon}
</div>
)}
<h2 style={{ fontSize: '17px', fontWeight: 600, color: 'var(--label-primary)', marginBottom: 'var(--space-2)' }}>
{children}
</h2>
</>
);
};
Modal.Body = ({ children }) => (
<p style={{ fontSize: '13px', color: 'var(--label-secondary)', lineHeight: 1.5, marginBottom: 'var(--space-6)' }}>
{children}
</p>
);
Modal.Actions = ({ children }) => (
<div style={{ display: 'flex', gap: 'var(--space-3)' }}>
{React.Children.map(children, (child) =>
React.cloneElement(child, { style: { ...child.props.style, flex: 1 } })
)}
</div>
);
// ============================================================================
// LOADING COMPONENTS
// ============================================================================
/**
* Spinner - Loading indicator
*/
export const Spinner = ({ size = 'md', color = 'var(--system-blue)' }) => {
const sizes = { sm: 16, md: 32, lg: 48 };
return (
<div
style={{
width: sizes[size],
height: sizes[size],
border: `3px solid var(--system-gray5)`,
borderTopColor: color,
borderRadius: '50%',
animation: 'spin 0.8s linear infinite',
}}
role="status"
aria-label="Loading"
/>
);
};
/**
* Skeleton - Loading placeholder
*/
export const Skeleton = ({ width = '100%', height = '20px', variant = 'text', className = '' }) => {
const variantStyles = {
text: { borderRadius: 'var(--radius-sm)' },
circular: { borderRadius: '50%' },
rectangular: { borderRadius: 'var(--radius-md)' },
};
return (
<div
style={{
width,
height,
background: 'linear-gradient(90deg, var(--system-gray5) 25%, var(--system-gray6) 50%, var(--system-gray5) 75%)',
backgroundSize: '200% 100%',
animation: 'shimmer 1.5s infinite',
...variantStyles[variant],
}}
className={className}
/>
);
};
// ============================================================================
// LIST COMPONENTS
// ============================================================================
/**
* List - iOS style list view
*/
export const List = ({ children, className = '' }) => (
<ul
style={{
listStyle: 'none',
margin: 0,
padding: 0,
background: 'var(--bg-tertiary)',
borderRadius: 'var(--radius-lg)',
overflow: 'hidden',
}}
role="list"
className={className}
>
{children}
</ul>
);
List.Item = ({ children, onClick, accessory, avatar, className = '' }) => (
<li
onClick={onClick}
style={{
display: 'flex',
alignItems: 'center',
padding: 'var(--space-3) var(--space-4)',
borderBottom: '1px solid var(--separator)',
cursor: onClick ? 'pointer' : 'default',
transition: 'background 0.1s ease',
}}
className={className}
>
{avatar && (
<img
src={avatar}
alt=""
style={{
width: '44px',
height: '44px',
borderRadius: '50%',
objectFit: 'cover',
marginRight: 'var(--space-3)',
}}
/>
)}
<div style={{ flex: 1, minWidth: 0 }}>{children}</div>
{accessory && <span style={{ color: 'var(--label-tertiary)', marginLeft: 'var(--space-2)' }}>{accessory}</span>}
</li>
);
// ============================================================================
// BADGE COMPONENT
// ============================================================================
/**
* Badge - Status indicator
*/
export const Badge = ({ children, variant = 'default', size = 'md' }) => {
const variantStyles = {
default: { color: 'var(--label-secondary)', background: 'var(--system-gray6)' },
success: { color: 'var(--system-green)', background: 'rgba(52, 199, 89, 0.12)' },
warning: { color: 'var(--system-orange)', background: 'rgba(255, 149, 0, 0.12)' },
error: { color: 'var(--system-red)', background: 'rgba(255, 59, 48, 0.12)' },
info: { color: 'var(--system-blue)', background: 'rgba(0, 122, 255, 0.12)' },
};
const sizeStyles = {
sm: { padding: '2px 6px', fontSize: '11px' },
md: { padding: '4px 10px', fontSize: '12px' },
lg: { padding: '6px 12px', fontSize: '14px' },
};
return (
<span
style={{
display: 'inline-flex',
alignItems: 'center',
fontWeight: 500,
borderRadius: 'var(--radius-full)',
fontFamily: 'var(--font-system)',
...variantStyles[variant],
...sizeStyles[size],
}}
>
{children}
</span>
);
};
// ============================================================================
// AVATAR COMPONENT
// ============================================================================
/**
* Avatar - User profile image
*/
export const Avatar = ({ src, alt = '', size = 'md', fallback }) => {
const [error, setError] = useState(false);
const sizes = {
sm: 32,
md: 44,
lg: 64,
xl: 96,
};
const dimension = sizes[size];
if (error || !src) {
return (
<div
style={{
width: dimension,
height: dimension,
borderRadius: '50%',
background: 'var(--system-gray5)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: dimension * 0.4,
fontWeight: 600,
color: 'var(--label-secondary)',
}}
>
{fallback || alt.charAt(0).toUpperCase()}
</div>
);
}
return (
<img
src={src}
alt={alt}
onError={() => setError(true)}
style={{
width: dimension,
height: dimension,
borderRadius: '50%',
objectFit: 'cover',
}}
/>
);
};
// ============================================================================
// EMPTY STATE COMPONENT
// ============================================================================
/**
* EmptyState - Placeholder for empty content
*/
export const EmptyState = ({ icon, title, description, action, className = '' }) => (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
padding: 'var(--space-12) var(--space-4)',
textAlign: 'center',
}}
className={className}
>
{icon && (
<div
style={{
width: '80px',
height: '80px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '40px',
color: 'var(--label-tertiary)',
background: 'var(--bg-secondary)',
borderRadius: '50%',
marginBottom: 'var(--space-4)',
}}
>
{icon}
</div>
)}
<h3 style={{ fontSize: '21px', fontWeight: 600, color: 'var(--label-primary)', marginBottom: 'var(--space-2)' }}>
{title}
</h3>
<p style={{ fontSize: '15px', color: 'var(--label-secondary)', maxWidth: '280px', marginBottom: 'var(--space-6)', lineHeight: 1.5 }}>
{description}
</p>
{action}
</div>
);
// ============================================================================
// CSS KEYFRAMES (inject into document)
// ============================================================================
const injectGlobalStyles = () => {
const styleId = 'apple-hig-animations';
if (document.getElementById(styleId)) return;
const style = document.createElement('style');
style.id = styleId;
style.textContent = `
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
`;
document.head.appendChild(style);
};
// Auto-inject animations
if (typeof document !== 'undefined') {
injectGlobalStyles();
}
// ============================================================================
// EXPORTS
// ============================================================================
export default {
Button,
Input,
Textarea,
Toggle,
Card,
Modal,
Spinner,
Skeleton,
List,
Badge,
Avatar,
EmptyState,
};
/**
* Apple HIG Design Tokens
*
* A comprehensive CSS custom properties system following Apple Human Interface Guidelines.
* Import this file first to enable all Apple-style components.
*
* Usage:
* @import 'design-tokens.css';
* or
* <link rel="stylesheet" href="design-tokens.css">
*/
/* ============================================================================
ROOT VARIABLES - LIGHT MODE (DEFAULT)
============================================================================ */
:root {
/* ==========================================================================
TYPOGRAPHY
========================================================================== */
/* Font Families */
--font-system: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text',
'Helvetica Neue', Arial, sans-serif;
--font-mono: 'SF Mono', SFMono-Regular, ui-monospace, Menlo, Monaco,
'Cascadia Code', 'Consolas', monospace;
--font-rounded: -apple-system-rounded, 'SF Pro Rounded', 'Helvetica Neue Rounded',
var(--font-system);
/* Font Sizes - iOS Typography Scale */
--text-caption2: 11px;
--text-caption1: 12px;
--text-footnote: 13px;
--text-subhead: 15px;
--text-callout: 16px;
--text-body: 17px;
--text-headline: 17px;
--text-title3: 20px;
--text-title2: 22px;
--text-title1: 28px;
--text-large-title: 34px;
/* Display Sizes - For Hero Sections */
--text-display-sm: 40px;
--text-display-md: 56px;
--text-display-lg: 80px;
--text-display-xl: 96px;
/* Font Weights */
--font-weight-ultralight: 100;
--font-weight-thin: 200;
--font-weight-light: 300;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-heavy: 800;
--font-weight-black: 900;
/* Line Heights */
--line-height-tight: 1.1;
--line-height-snug: 1.2;
--line-height-normal: 1.4;
--line-height-relaxed: 1.5;
--line-height-loose: 1.75;
/* Letter Spacing */
--tracking-tighter: -0.03em;
--tracking-tight: -0.02em;
--tracking-normal: 0;
--tracking-wide: 0.02em;
--tracking-wider: 0.04em;
/* ==========================================================================
COLORS - LIGHT MODE
========================================================================== */
/* Primary System Colors */
--system-blue: #007AFF;
--system-green: #34C759;
--system-indigo: #5856D6;
--system-orange: #FF9500;
--system-pink: #FF2D55;
--system-purple: #AF52DE;
--system-red: #FF3B30;
--system-teal: #5AC8FA;
--system-yellow: #FFCC00;
--system-cyan: #32ADE6;
--system-mint: #00C7BE;
--system-brown: #A2845E;
/* Gray Scale */
--system-gray: #8E8E93;
--system-gray2: #AEAEB2;
--system-gray3: #C7C7CC;
--system-gray4: #D1D1D6;
--system-gray5: #E5E5EA;
--system-gray6: #F2F2F7;
/* Label Colors (Text) */
--label-primary: #000000;
--label-secondary: rgba(60, 60, 67, 0.6);
--label-tertiary: rgba(60, 60, 67, 0.3);
--label-quaternary: rgba(60, 60, 67, 0.18);
/* Fill Colors (Backgrounds for thin overlays) */
--fill-primary: rgba(120, 120, 128, 0.2);
--fill-secondary: rgba(120, 120, 128, 0.16);
--fill-tertiary: rgba(120, 120, 128, 0.12);
--fill-quaternary: rgba(120, 120, 128, 0.08);
/* Background Colors */
--bg-primary: #FFFFFF;
--bg-secondary: #F2F2F7;
--bg-tertiary: #FFFFFF;
--bg-grouped-primary: #F2F2F7;
--bg-grouped-secondary: #FFFFFF;
--bg-grouped-tertiary: #F2F2F7;
/* Separator Colors */
--separator: rgba(60, 60, 67, 0.29);
--separator-opaque: #C6C6C8;
/* ==========================================================================
SPACING - 8PT GRID SYSTEM
========================================================================== */
--space-0: 0;
--space-px: 1px;
--space-0-5: 2px;
--space-1: 4px;
--space-1-5: 6px;
--space-2: 8px;
--space-2-5: 10px;
--space-3: 12px;
--space-3-5: 14px;
--space-4: 16px;
--space-5: 20px;
--space-6: 24px;
--space-7: 28px;
--space-8: 32px;
--space-9: 36px;
--space-10: 40px;
--space-11: 44px;
--space-12: 48px;
--space-14: 56px;
--space-16: 64px;
--space-20: 80px;
--space-24: 96px;
--space-28: 112px;
--space-32: 128px;
/* ==========================================================================
BORDER RADIUS - CONCENTRIC DESIGN
========================================================================== */
--radius-none: 0;
--radius-sm: 4px;
--radius-md: 8px;
--radius-lg: 12px;
--radius-xl: 16px;
--radius-2xl: 20px;
--radius-3xl: 24px;
--radius-4xl: 32px;
--radius-full: 9999px; /* Capsule */
/* Platform-Specific Radius */
--radius-button: var(--radius-full);
--radius-card: var(--radius-xl);
--radius-modal: var(--radius-2xl);
--radius-input: var(--radius-md);
/* ==========================================================================
SHADOWS
========================================================================== */
/* Elevation Levels */
--shadow-none: none;
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.04);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.04), 0 2px 4px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.04), 0 4px 6px rgba(0, 0, 0, 0.08);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.06), 0 8px 10px rgba(0, 0, 0, 0.1);
--shadow-2xl: 0 25px 50px rgba(0, 0, 0, 0.15);
/* Focus Ring */
--shadow-focus: 0 0 0 4px rgba(0, 122, 255, 0.3);
--shadow-focus-error: 0 0 0 4px rgba(255, 59, 48, 0.3);
/* Inset Shadow */
--shadow-inset: inset 0 2px 4px rgba(0, 0, 0, 0.05);
/* ==========================================================================
ANIMATION & TRANSITIONS
========================================================================== */
/* Durations */
--duration-instant: 50ms;
--duration-faster: 100ms;
--duration-fast: 150ms;
--duration-normal: 200ms;
--duration-slow: 300ms;
--duration-slower: 400ms;
--duration-slowest: 500ms;
/* Easing Functions - Apple's Preferred */
--ease-default: cubic-bezier(0.25, 0.1, 0.25, 1);
--ease-linear: linear;
--ease-in: cubic-bezier(0.42, 0, 1, 1);
--ease-out: cubic-bezier(0, 0, 0.58, 1);
--ease-in-out: cubic-bezier(0.42, 0, 0.58, 1);
/* Spring Easings */
--ease-spring: cubic-bezier(0.175, 0.885, 0.32, 1.275);
--ease-bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
--ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
/* ==========================================================================
LAYOUT
========================================================================== */
/* Container Widths */
--container-xs: 320px;
--container-sm: 480px;
--container-md: 640px;
--container-lg: 768px;
--container-xl: 1024px;
--container-2xl: 1280px;
--container-max: 1440px;
/* Breakpoints */
--breakpoint-compact: 430px;
--breakpoint-regular: 744px;
--breakpoint-large: 1024px;
--breakpoint-xl: 1280px;
/* Z-Index Scale */
--z-base: 0;
--z-dropdown: 100;
--z-sticky: 200;
--z-fixed: 300;
--z-modal-backdrop: 400;
--z-modal: 500;
--z-popover: 600;
--z-tooltip: 700;
--z-toast: 800;
--z-max: 9999;
/* ==========================================================================
TOUCH TARGETS
========================================================================== */
--touch-target-min: 44px; /* iOS minimum */
--touch-target-comfortable: 48px;
--touch-target-spacious: 56px;
--touch-target-vision: 60px; /* visionOS minimum */
/* ==========================================================================
BLUR VALUES
========================================================================== */
--blur-none: 0;
--blur-sm: 4px;
--blur-md: 8px;
--blur-lg: 16px;
--blur-xl: 24px;
--blur-2xl: 40px;
--blur-3xl: 64px;
/* Backdrop Blur for Glass Effects */
--backdrop-blur: blur(20px) saturate(180%);
}
/* ============================================================================
DARK MODE
============================================================================ */
@media (prefers-color-scheme: dark) {
:root {
/* System Colors - Adjusted for Dark Mode */
--system-blue: #0A84FF;
--system-green: #30D158;
--system-indigo: #5E5CE6;
--system-orange: #FF9F0A;
--system-pink: #FF375F;
--system-purple: #BF5AF2;
--system-red: #FF453A;
--system-teal: #64D2FF;
--system-yellow: #FFD60A;
--system-cyan: #64D2FF;
--system-mint: #63E6E2;
--system-brown: #AC8E68;
/* Gray Scale - Dark Mode */
--system-gray: #8E8E93;
--system-gray2: #636366;
--system-gray3: #48484A;
--system-gray4: #3A3A3C;
--system-gray5: #2C2C2E;
--system-gray6: #1C1C1E;
/* Label Colors - Dark Mode */
--label-primary: #FFFFFF;
--label-secondary: rgba(235, 235, 245, 0.6);
--label-tertiary: rgba(235, 235, 245, 0.3);
--label-quaternary: rgba(235, 235, 245, 0.18);
/* Fill Colors - Dark Mode */
--fill-primary: rgba(120, 120, 128, 0.36);
--fill-secondary: rgba(120, 120, 128, 0.32);
--fill-tertiary: rgba(120, 120, 128, 0.24);
--fill-quaternary: rgba(120, 120, 128, 0.18);
/* Background Colors - Dark Mode */
--bg-primary: #000000;
--bg-secondary: #1C1C1E;
--bg-tertiary: #2C2C2E;
--bg-grouped-primary: #000000;
--bg-grouped-secondary: #1C1C1E;
--bg-grouped-tertiary: #2C2C2E;
/* Separator Colors - Dark Mode */
--separator: rgba(84, 84, 88, 0.6);
--separator-opaque: #38383A;
/* Shadows - Dark Mode (more subtle) */
--shadow-xs: 0 1px 2px rgba(0, 0, 0, 0.3);
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.3), 0 1px 2px rgba(0, 0, 0, 0.4);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.3), 0 2px 4px rgba(0, 0, 0, 0.4);
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.3), 0 4px 6px rgba(0, 0, 0, 0.4);
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.4), 0 8px 10px rgba(0, 0, 0, 0.5);
--shadow-2xl: 0 25px 50px rgba(0, 0, 0, 0.5);
}
}
/* ============================================================================
HIGH CONTRAST MODE
============================================================================ */
@media (prefers-contrast: high) {
:root {
--label-secondary: rgba(60, 60, 67, 0.8);
--label-tertiary: rgba(60, 60, 67, 0.6);
--separator: rgba(60, 60, 67, 0.5);
}
}
@media (prefers-contrast: high) and (prefers-color-scheme: dark) {
:root {
--label-secondary: rgba(235, 235, 245, 0.8);
--label-tertiary: rgba(235, 235, 245, 0.6);
--separator: rgba(84, 84, 88, 0.8);
}
}
/* ============================================================================
REDUCED MOTION
============================================================================ */
@media (prefers-reduced-motion: reduce) {
:root {
--duration-instant: 0ms;
--duration-faster: 0ms;
--duration-fast: 0ms;
--duration-normal: 0ms;
--duration-slow: 0ms;
--duration-slower: 0ms;
--duration-slowest: 0ms;
}
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
/* ============================================================================
BASE RESET & DEFAULTS
============================================================================ */
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-family: var(--font-system);
font-size: 16px;
line-height: var(--line-height-normal);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-rendering: optimizeLegibility;
-webkit-text-size-adjust: 100%;
tab-size: 4;
}
body {
font-family: var(--font-system);
font-size: var(--text-body);
color: var(--label-primary);
background-color: var(--bg-primary);
min-height: 100vh;
min-height: 100dvh;
}
/* Remove default tap highlight on iOS */
button,
a,
input,
textarea,
select {
-webkit-tap-highlight-color: transparent;
}
/* Remove default appearance for form elements */
input,
textarea,
select,
button {
font-family: inherit;
font-size: inherit;
-webkit-appearance: none;
appearance: none;
}
/* ============================================================================
UTILITY CLASSES
============================================================================ */
/* Text Colors */
.text-primary { color: var(--label-primary); }
.text-secondary { color: var(--label-secondary); }
.text-tertiary { color: var(--label-tertiary); }
.text-blue { color: var(--system-blue); }
.text-green { color: var(--system-green); }
.text-red { color: var(--system-red); }
.text-orange { color: var(--system-orange); }
.text-purple { color: var(--system-purple); }
/* Background Colors */
.bg-primary { background-color: var(--bg-primary); }
.bg-secondary { background-color: var(--bg-secondary); }
.bg-tertiary { background-color: var(--bg-tertiary); }
/* Font Sizes */
.text-caption2 { font-size: var(--text-caption2); }
.text-caption1 { font-size: var(--text-caption1); }
.text-footnote { font-size: var(--text-footnote); }
.text-subhead { font-size: var(--text-subhead); }
.text-body { font-size: var(--text-body); }
.text-headline { font-size: var(--text-headline); font-weight: var(--font-weight-semibold); }
.text-title3 { font-size: var(--text-title3); font-weight: var(--font-weight-semibold); }
.text-title2 { font-size: var(--text-title2); font-weight: var(--font-weight-bold); }
.text-title1 { font-size: var(--text-title1); font-weight: var(--font-weight-bold); }
.text-large-title { font-size: var(--text-large-title); font-weight: var(--font-weight-bold); }
/* Font Weights */
.font-regular { font-weight: var(--font-weight-regular); }
.font-medium { font-weight: var(--font-weight-medium); }
.font-semibold { font-weight: var(--font-weight-semibold); }
.font-bold { font-weight: var(--font-weight-bold); }
/* Spacing */
.p-0 { padding: var(--space-0); }
.p-1 { padding: var(--space-1); }
.p-2 { padding: var(--space-2); }
.p-3 { padding: var(--space-3); }
.p-4 { padding: var(--space-4); }
.p-6 { padding: var(--space-6); }
.p-8 { padding: var(--space-8); }
.m-0 { margin: var(--space-0); }
.m-1 { margin: var(--space-1); }
.m-2 { margin: var(--space-2); }
.m-3 { margin: var(--space-3); }
.m-4 { margin: var(--space-4); }
.m-auto { margin: auto; }
.gap-1 { gap: var(--space-1); }
.gap-2 { gap: var(--space-2); }
.gap-3 { gap: var(--space-3); }
.gap-4 { gap: var(--space-4); }
.gap-6 { gap: var(--space-6); }
/* Border Radius */
.rounded-none { border-radius: var(--radius-none); }
.rounded-sm { border-radius: var(--radius-sm); }
.rounded-md { border-radius: var(--radius-md); }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-xl { border-radius: var(--radius-xl); }
.rounded-full { border-radius: var(--radius-full); }
/* Shadows */
.shadow-none { box-shadow: var(--shadow-none); }
.shadow-sm { box-shadow: var(--shadow-sm); }
.shadow-md { box-shadow: var(--shadow-md); }
.shadow-lg { box-shadow: var(--shadow-lg); }
.shadow-xl { box-shadow: var(--shadow-xl); }
/* Display */
.flex { display: flex; }
.inline-flex { display: inline-flex; }
.grid { display: grid; }
.hidden { display: none; }
/* Flex */
.flex-row { flex-direction: row; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.items-start { align-items: flex-start; }
.items-end { align-items: flex-end; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.justify-start { justify-content: flex-start; }
.justify-end { justify-content: flex-end; }
.flex-1 { flex: 1; }
.flex-wrap { flex-wrap: wrap; }
/* Width */
.w-full { width: 100%; }
.w-auto { width: auto; }
.max-w-sm { max-width: var(--container-sm); }
.max-w-md { max-width: var(--container-md); }
.max-w-lg { max-width: var(--container-lg); }
.max-w-xl { max-width: var(--container-xl); }
/* Text Alignment */
.text-left { text-align: left; }
.text-center { text-align: center; }
.text-right { text-align: right; }
/* Overflow */
.overflow-hidden { overflow: hidden; }
.overflow-auto { overflow: auto; }
.overflow-scroll { overflow: scroll; }
/* Cursor */
.cursor-pointer { cursor: pointer; }
.cursor-not-allowed { cursor: not-allowed; }
/* Opacity */
.opacity-0 { opacity: 0; }
.opacity-50 { opacity: 0.5; }
.opacity-100 { opacity: 1; }
/* Transitions */
.transition-all {
transition-property: all;
transition-duration: var(--duration-normal);
transition-timing-function: var(--ease-out);
}
.transition-colors {
transition-property: color, background-color, border-color;
transition-duration: var(--duration-fast);
transition-timing-function: var(--ease-out);
}
.transition-transform {
transition-property: transform;
transition-duration: var(--duration-fast);
transition-timing-function: var(--ease-out);
}
/* ============================================================================
KEYFRAME ANIMATIONS
============================================================================ */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes slideUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
@keyframes slideDown {
from { transform: translateY(-100%); }
to { transform: translateY(0); }
}
@keyframes scaleIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
@keyframes scaleOut {
from { opacity: 1; transform: scale(1); }
to { opacity: 0; transform: scale(0.95); }
}
@keyframes spin {
to { transform: rotate(360deg); }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
/* Animation Classes */
.animate-fade-in { animation: fadeIn var(--duration-normal) var(--ease-out); }
.animate-fade-out { animation: fadeOut var(--duration-normal) var(--ease-out); }
.animate-slide-up { animation: slideUp var(--duration-normal) var(--ease-out); }
.animate-slide-down { animation: slideDown var(--duration-normal) var(--ease-out); }
.animate-scale-in { animation: scaleIn var(--duration-normal) var(--ease-spring); }
.animate-spin { animation: spin 1s linear infinite; }
.animate-pulse { animation: pulse 2s ease-in-out infinite; }
.animate-shimmer { animation: shimmer 1.5s infinite; }
/* ============================================================================
GLASS EFFECT (LIQUID GLASS 2025)
============================================================================ */
.glass {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: var(--backdrop-blur);
-webkit-backdrop-filter: var(--backdrop-blur);
border: 1px solid rgba(255, 255, 255, 0.3);
}
.glass-dark {
background: rgba(28, 28, 30, 0.7);
backdrop-filter: var(--backdrop-blur);
-webkit-backdrop-filter: var(--backdrop-blur);
border: 1px solid rgba(255, 255, 255, 0.1);
}
@media (prefers-color-scheme: dark) {
.glass {
background: rgba(28, 28, 30, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
}
}
/* ============================================================================
SAFE AREA INSETS (iOS)
============================================================================ */
.safe-area-inset {
padding-top: env(safe-area-inset-top);
padding-right: env(safe-area-inset-right);
padding-bottom: env(safe-area-inset-bottom);
padding-left: env(safe-area-inset-left);
}
.safe-area-bottom {
padding-bottom: env(safe-area-inset-bottom);
}
.safe-area-top {
padding-top: env(safe-area-inset-top);
}
/* ============================================================================
SCROLLBAR STYLING (WEBKIT)
============================================================================ */
::-webkit-scrollbar {
width: 8px;
height: 8px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--system-gray4);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--system-gray3);
}
/* ============================================================================
SELECTION STYLING
============================================================================ */
::selection {
background: rgba(0, 122, 255, 0.3);
color: inherit;
}
::-moz-selection {
background: rgba(0, 122, 255, 0.3);
color: inherit;
}
/* ============================================================================
FOCUS VISIBLE
============================================================================ */
:focus-visible {
outline: none;
box-shadow: var(--shadow-focus);
}
/* ============================================================================
PRINT STYLES
============================================================================ */
@media print {
:root {
--bg-primary: #FFFFFF;
--bg-secondary: #FFFFFF;
--bg-tertiary: #FFFFFF;
--label-primary: #000000;
--label-secondary: #333333;
}
.no-print {
display: none !important;
}
}
Related skills
How it compares
Use for Apple platform HIG fidelity instead of generic "make it pretty" UI prompts that ignore SF Symbols and accessibility.
FAQ
Who is apple-hig-designer for?
Developers shipping on Apple platforms who want agent help that respects Human Interface Guidelines rather than generic web UI patterns.
When should I use apple-hig-designer?
During Build frontend work when designing new screens, navigation, or components; also when validating that a prototype matches HIG before Ship and App Store review prep.
Is apple-hig-designer safe to install?
It is design guidance only—review the Security Audits panel on this Prism page and the skill source before installing any third-party skill in your agent.