assets/README.md
# Modern Web Design Assets
## Overview
This directory contains design system templates, starter files, and reusable assets for modern web development. All assets follow accessibility, performance, and modern design best practices.
## Available Assets
### 1. Design Tokens
**Location**: Would be in `tokens/` subdirectory
**What's included**:
- CSS custom properties (variables) for:
- Colors (OKLCH color space, AAA compliant)
- Typography scales (fluid sizing with clamp())
- Spacing scales (consistent rhythm)
- Shadows (elevation system)
- Border radius (consistent rounding)
- Animation timing (duration & easing)
**Usage**:
```css
/* Import design tokens */
@import url('tokens/colors.css');
@import url('tokens/typography.css');
@import url('tokens/spacing.css');
/* Use in your styles */
.button {
background: var(--color-primary);
padding: var(--space-md) var(--space-lg);
font-size: var(--font-size-base);
transition: transform var(--duration-fast) var(--ease-out);
}
```
### 2. Component Library
**Location**: Would be in `components/` subdirectory
**What's included**:
- Button variants (primary, secondary, ghost, danger)
- Form components (inputs, textareas, select, checkbox, radio)
- Card layouts
- Navigation components
- Modal/Dialog components
- Toast notifications
- Loading states (spinners, skeletons, progress bars)
**Features**:
- Fully accessible (WCAG AAA where possible)
- Keyboard navigable
- Screen reader friendly
- Dark mode variants
- Responsive by default
### 3. Layout Templates
**Location**: Would be in `layouts/` subdirectory
**What's included**:
- Landing page template
- Portfolio/Agency template
- Blog/Article template
- Dashboard/App template
- E-commerce template
**Features**:
- Modern CSS Grid & Flexbox
- Responsive breakpoints
- Print stylesheets
- Semantic HTML5
### 4. Animation Presets
**Location**: Would be in `animations/` subdirectory
**What's included**:
- CSS keyframe animations
- Transition presets
- Easing functions
- GSAP animation snippets
- Framer Motion variants
**Categories**:
- Fade (in/out, up/down/left/right)
- Slide (all directions)
- Scale (grow/shrink)
- Rotate
- Bounce
- Elastic
- Custom easings
### 5. Icon System
**Location**: Would be in `icons/` subdirectory
**What's included**:
- SVG icon set (inline-ready)
- Icon sprite sheet
- Icon font (optional)
**Features**:
- Accessibility ready (proper aria-hidden/labels)
- Consistent sizing
- Color customizable via CSS
### 6. Utility Classes
**Location**: Would be in `utilities/` subdirectory
**What's included**:
- Display utilities (flex, grid, block, inline, none)
- Spacing utilities (margin, padding)
- Typography utilities (size, weight, align)
- Color utilities (text, background)
- Layout utilities (container, aspect-ratio)
- Accessibility utilities (sr-only, focus-visible)
**Example**:
```html
<div class="flex items-center gap-md p-lg bg-primary text-white rounded-lg">
Content
</div>
```
### 7. Reset & Base Styles
**Location**: Would be in `base/` subdirectory
**What's included**:
- Modern CSS reset (based on modern-normalize)
- Base typography styles
- Accessible focus styles
- Print styles
---
## How to Use These Assets
### Option 1: Copy Individual Files
Copy only the files you need into your project:
```bash
# Copy design tokens
cp assets/tokens/colors.css src/styles/
cp assets/tokens/typography.css src/styles/
# Copy specific components
cp assets/components/button.css src/components/
```
### Option 2: Import Everything
Import the complete design system:
```html
<link rel="stylesheet" href="assets/design-system.css">
```
Or in your CSS:
```css
@import url('../assets/design-system.css');
```
### Option 3: Use as Reference
Browse the assets to understand patterns and adapt them to your own design system.
---
## Customization Guide
### Colors
Modify color tokens to match your brand:
```css
:root {
/* Update these values */
--color-primary: oklch(50% 0.2 250); /* Your brand blue */
--color-accent: oklch(65% 0.25 30); /* Your accent color */
/* System will generate variants automatically */
--color-primary-hover: oklch(45% 0.2 250);
--color-primary-active: oklch(40% 0.2 250);
}
```
### Typography
Adjust type scale for your design:
```css
:root {
/* Base size (1rem = 16px by default) */
--font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
/* Adjust ratio for scale */
--font-scale-ratio: 1.25; /* Minor third */
/* Or use pre-calculated values */
--font-size-sm: clamp(0.875rem, ...);
--font-size-lg: clamp(1.25rem, ...);
}
```
### Spacing
Customize spacing scale:
```css
:root {
/* 8px base unit */
--space-unit: 0.5rem;
/* Scale multiples */
--space-xs: calc(var(--space-unit) * 1); /* 8px */
--space-sm: calc(var(--space-unit) * 2); /* 16px */
--space-md: calc(var(--space-unit) * 4); /* 32px */
--space-lg: calc(var(--space-unit) * 6); /* 48px */
--space-xl: calc(var(--space-unit) * 8); /* 64px */
}
```
---
## Design System Principles
### 1. Mobile-First
All components are designed mobile-first with progressive enhancement for larger screens.
```css
/* Mobile first */
.component {
font-size: 1rem;
}
/* Enhance for larger screens */
@media (min-width: 768px) {
.component {
font-size: 1.25rem;
}
}
```
### 2. Accessibility First
- AAA color contrast ratios where possible (7:1)
- Keyboard navigable (visible focus states)
- Screen reader friendly (proper ARIA labels)
- Respects motion preferences
### 3. Performance Optimized
- CSS-only animations where possible
- GPU-accelerated transforms
- Minimal JavaScript
- Lazy-loadable components
### 4. Design Token Driven
Everything is configurable via design tokens:
- Easy rebranding
- Consistent visual language
- Dark mode support built-in
---
## File Structure
```
assets/
├── README.md (this file)
├── tokens/
│ ├── colors.css
│ ├── typography.css
│ ├── spacing.css
│ ├── shadows.css
│ └── animation.css
├── base/
│ ├── reset.css
│ ├── typography.css
│ └── print.css
├── components/
│ ├── button.css
│ ├── card.css
│ ├── form.css
│ ├── modal.css
│ └── ...
├── layouts/
│ ├── landing.html
│ ├── dashboard.html
│ └── ...
├── animations/
│ ├── fade.css
│ ├── slide.css
│ └── gsap-presets.js
├── utilities/
│ └── utilities.css
└── design-system.css (imports all)
```
---
## Browser Support
**Modern Browsers** (recommended):
- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
**Fallbacks provided for**:
- CSS Grid → Flexbox
- CSS custom properties → Fallback values
- Modern CSS functions → Static values
**Progressive Enhancement**:
```css
/* Fallback */
.container {
padding: 2rem; /* Static value */
}
/* Enhanced */
@supports (padding: clamp(1rem, 5vw, 3rem)) {
.container {
padding: clamp(1rem, 5vw, 3rem);
}
}
```
---
## Contributing
When adding new assets:
1. **Follow naming conventions**: kebab-case for files, BEM for CSS classes
2. **Include accessibility**: ARIA labels, keyboard support, focus states
3. **Document usage**: Add examples and notes in component files
4. **Test thoroughly**: Cross-browser, keyboard, screen reader
5. **Optimize**: Remove unused code, minimize file size
---
## Related Skills
For implementation guidance, reference:
- `gsap-scrolltrigger` - Animation patterns
- `motion-framer` - React component animations
- `animated-component-libraries` - Pre-built components
- `threejs-webgl` - 3D backgrounds and effects
---
## License
All assets follow the Apache 2.0 license (same as the skill).
---
*Last updated: 2024*
references/accessibility_guide.md
# Web Accessibility Guide (WCAG AAA Compliance)
## Overview
Comprehensive guide to web accessibility compliance following WCAG 2.1/2.2 Level AAA standards. This guide covers the most critical accessibility requirements for modern web design.
**Target Standard**: WCAG 2.1 Level AAA (where practical) with Level AA as minimum baseline.
---
## Table of Contents
1. [Color & Contrast](#color--contrast)
2. [Typography & Readability](#typography--readability)
3. [Keyboard Navigation](#keyboard-navigation)
4. [Screen Reader Support](#screen-reader-support)
5. [Motion & Animation](#motion--animation)
6. [Touch Targets & Mobile](#touch-targets--mobile)
7. [Forms & Input](#forms--input)
8. [Focus Management](#focus-management)
9. [Semantic HTML](#semantic-html)
10. [Testing & Auditing](#testing--auditing)
---
## Color & Contrast
### WCAG Contrast Requirements
**Level AA (Minimum)**:
- Normal text (< 18pt): 4.5:1 contrast ratio
- Large text (≥ 18pt or 14pt bold): 3:1 contrast ratio
**Level AAA (Enhanced)**:
- Normal text: **7:1 contrast ratio**
- Large text: **4.5:1 contrast ratio**
### Checking Contrast
**Manual Calculation**:
```javascript
function getLuminance(r, g, b) {
const [rs, gs, bs] = [r, g, b].map(c => {
c = c / 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * rs + 0.7152 * gs + 0.0722 * bs;
}
function getContrastRatio(rgb1, rgb2) {
const l1 = getLuminance(...rgb1);
const l2 = getLuminance(...rgb2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
// Example usage
const textColor = [0, 0, 0]; // Black
const bgColor = [255, 255, 255]; // White
const ratio = getContrastRatio(textColor, bgColor);
console.log(ratio); // 21:1 (AAA compliant)
```
**Tools**:
- WebAIM Contrast Checker: https://webaim.org/resources/contrastchecker/
- Stark (Figma plugin)
- Chrome DevTools Accessibility Panel
### Color System (AAA Compliant)
**Using OKLCH Color Space**:
```css
:root {
/* Text on white background (AAA compliant) */
--color-text-primary: oklch(20% 0 0); /* #1a1a1a, 11.6:1 */
--color-text-secondary: oklch(40% 0 0); /* #666666, 7.3:1 */
/* Backgrounds */
--color-bg-primary: oklch(98% 0 0); /* Near white */
--color-bg-secondary: oklch(95% 0 0); /* Light gray */
/* Accent colors (AAA on white) */
--color-accent-blue: oklch(45% 0.2 250); /* 7.1:1 */
--color-accent-green: oklch(50% 0.15 140); /* 7.2:1 */
/* Interactive states */
--color-link: oklch(40% 0.2 250); /* Blue, 8.3:1 */
--color-link-hover: oklch(35% 0.25 250); /* Darker blue, 10.5:1 */
}
```
### Color Blindness Considerations
**Avoid**:
- Red-green as only distinguisher (affects 8% of males)
- Relying on color alone for meaning
**Best Practices**:
- Use patterns, shapes, or icons alongside color
- Test with color blindness simulators
- Provide high-contrast mode
**Simulation Tools**:
- Chrome DevTools: Rendering > Emulate vision deficiencies
- Colorblindly (browser extension)
- Stark (Figma)
---
## Typography & Readability
### Font Size Requirements
**WCAG Guidelines**:
- Body text: Minimum 16px (1rem)
- Small text: Minimum 14px (0.875rem)
- Users must be able to zoom to 200% without loss of functionality
**Recommended Sizes**:
```css
:root {
/* Fluid typography */
--font-size-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
--font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
--font-size-lg: clamp(1.25rem, 1.1rem + 0.75vw, 1.75rem);
--font-size-xl: clamp(1.75rem, 1.5rem + 1.25vw, 2.5rem);
}
body {
font-size: var(--font-size-base);
line-height: 1.5; /* WCAG minimum for body text */
}
```
### Line Height & Spacing
**WCAG 1.4.12 (Level AAA)**:
- Line height: Minimum 1.5× font size
- Paragraph spacing: Minimum 2× font size
- Letter spacing: Minimum 0.12× font size
- Word spacing: Minimum 0.16× font size
**Implementation**:
```css
p {
font-size: 1rem;
line-height: 1.6; /* 1.5× minimum */
margin-bottom: 2rem; /* 2× font size */
letter-spacing: 0.01em; /* Slight increase */
word-spacing: 0.05em;
}
```
### Font Choices
**Accessible Fonts**:
- Sans-serif: Inter, Roboto, Open Sans, Atkinson Hyperlegible
- Serif: Georgia, Merriweather, Lora
- Avoid: Overly decorative, thin weights (< 300), all-caps for long text
**Dyslexia-Friendly**:
- OpenDyslexic
- Atkinson Hyperlegible
- Comic Sans (surprisingly good for dyslexia)
---
## Keyboard Navigation
### Requirements
**All interactive elements must be**:
- Focusable with Tab key
- Activatable with Enter or Space
- Dismissible with Escape (modals, dropdowns)
- Navigable with arrow keys (where appropriate)
### Tab Order
**Logical Tab Order**:
```html
<!-- Natural DOM order is best -->
<button tabindex="0">First</button>
<button tabindex="0">Second</button>
<button tabindex="0">Third</button>
<!-- Avoid tabindex > 0 (creates unpredictable order) -->
<button tabindex="3">Third</button> <!-- BAD -->
<button tabindex="1">First</button> <!-- BAD -->
```
**Skip Links**:
```html
<a href="#main-content" class="skip-link">
Skip to main content
</a>
<nav>...</nav>
<main id="main-content">
<!-- Main content -->
</main>
```
```css
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
text-decoration: none;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
```
### Focus Indicators
**WCAG 2.4.7 (Level AA)**:
- Focus indicator must be visible
- Minimum 2px thick
- Sufficient contrast (3:1 against background)
**Modern Focus Styles**:
```css
/* Remove default outline */
*:focus {
outline: none;
}
/* Custom focus indicator */
:focus-visible {
outline: 3px solid var(--color-accent);
outline-offset: 2px;
border-radius: 4px;
}
/* Remove focus ring for mouse users */
:focus:not(:focus-visible) {
outline: none;
}
/* Button focus */
button:focus-visible {
outline: 3px solid #3b82f6;
outline-offset: 2px;
box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.2);
}
```
### Keyboard Patterns
**Modal Dialogs**:
```javascript
function trapFocus(element) {
const focusableElements = element.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
const firstFocusable = focusableElements[0];
const lastFocusable = focusableElements[focusableElements.length - 1];
element.addEventListener('keydown', (e) => {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstFocusable) {
e.preventDefault();
lastFocusable.focus();
} else if (!e.shiftKey && document.activeElement === lastFocusable) {
e.preventDefault();
firstFocusable.focus();
}
}
if (e.key === 'Escape') {
closeModal();
}
});
firstFocusable.focus();
}
```
**Dropdown Menus**:
```javascript
function handleDropdownKeys(e) {
const items = Array.from(dropdown.querySelectorAll('[role="menuitem"]'));
const currentIndex = items.indexOf(document.activeElement);
switch (e.key) {
case 'ArrowDown':
e.preventDefault();
const nextIndex = (currentIndex + 1) % items.length;
items[nextIndex].focus();
break;
case 'ArrowUp':
e.preventDefault();
const prevIndex = (currentIndex - 1 + items.length) % items.length;
items[prevIndex].focus();
break;
case 'Home':
e.preventDefault();
items[0].focus();
break;
case 'End':
e.preventDefault();
items[items.length - 1].focus();
break;
case 'Escape':
closeDropdown();
triggerButton.focus();
break;
}
}
```
---
## Screen Reader Support
### ARIA Attributes
**Essential ARIA**:
**Landmarks**:
```html
<header role="banner">
<nav role="navigation" aria-label="Main">
<!-- Navigation -->
</nav>
</header>
<main role="main">
<!-- Main content -->
</main>
<aside role="complementary" aria-label="Related articles">
<!-- Sidebar -->
</aside>
<footer role="contentinfo">
<!-- Footer -->
</footer>
```
**Buttons**:
```html
<!-- Icon button needs label -->
<button aria-label="Close menu">
<svg>...</svg>
</button>
<!-- Button with visible text -->
<button>
<svg aria-hidden="true">...</svg>
Submit
</button>
```
**Live Regions**:
```html
<!-- Announce loading state -->
<div role="status" aria-live="polite" aria-atomic="true">
Loading content...
</div>
<!-- Announce errors -->
<div role="alert" aria-live="assertive">
Error: Form submission failed.
</div>
```
**Custom Components**:
```html
<!-- Accordion -->
<div class="accordion">
<h3>
<button
aria-expanded="false"
aria-controls="panel-1"
id="button-1"
>
Section 1
</button>
</h3>
<div
id="panel-1"
role="region"
aria-labelledby="button-1"
hidden
>
Content...
</div>
</div>
<!-- Tab panel -->
<div role="tablist" aria-label="Content sections">
<button role="tab" aria-selected="true" aria-controls="panel-1">
Tab 1
</button>
<button role="tab" aria-selected="false" aria-controls="panel-2">
Tab 2
</button>
</div>
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
Panel content...
</div>
```
### Alt Text Guidelines
**Images**:
```html
<!-- Informative image -->
<img src="chart.png" alt="Bar chart showing 50% increase in sales">
<!-- Decorative image -->
<img src="decorative.png" alt="" role="presentation">
<!-- Complex image -->
<img src="infographic.png" alt="Sales data for Q4" longdesc="sales-data.html">
```
**Best Practices**:
- Describe the content/function, not "image of..."
- Keep under 150 characters (screen readers pause)
- Use empty alt (`alt=""`) for decorative images
- Provide long descriptions for complex images
---
## Motion & Animation
### Reduced Motion Preference
**WCAG 2.3.3 (Level AAA)**:
Users must be able to disable non-essential motion.
**Implementation**:
```css
/* Default: animations enabled */
.animated-element {
animation: slide-in 0.5s ease;
}
/* Respect user preference */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
/* Keep essential animations but reduce */
.loading-spinner {
animation-duration: 1s; /* Slower */
}
}
```
**JavaScript Detection**:
```javascript
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches;
if (prefersReducedMotion) {
// Disable or simplify animations
gsap.config({ nullTargetWarn: false, force3D: false });
// Skip scroll animations
ScrollTrigger.getAll().forEach(st => st.kill());
}
```
### Animation Guidelines
**Safe Animations**:
- Fade in/out (opacity)
- Slide short distances (< 50px)
- Scale small amounts (0.95-1.05)
- Avoid: flashing, rapid movement, parallax
**Vestibular Disorders**:
Avoid animations that can cause dizziness or nausea:
- Large parallax effects
- Continuous rotation
- Zoom effects
- Perspective transforms
---
## Touch Targets & Mobile
### WCAG 2.5.5 (Level AAA)
**Target Size**: Minimum 44×44px (iOS) or 48×48px (Android)
**Spacing**: Minimum 8px between targets
**Implementation**:
```css
button, a {
min-height: 44px;
min-width: 44px;
padding: 12px 24px;
/* Touch target includes padding */
}
/* Increase tap target without changing visual size */
button::before {
content: '';
position: absolute;
inset: -8px; /* Extends tap target by 8px all sides */
}
```
### Mobile Considerations
**Viewport**:
```html
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=5">
```
*Note*: Allow zoom up to 5× (WCAG requirement)
**Touch Gestures**:
- Avoid complex gestures (multi-finger, long-press)
- Provide alternative interactions
- Don't rely on hover states
---
## Forms & Input
### Labels & Instructions
**WCAG 3.3.2 (Level A)**:
Every input must have a visible label.
**Implementation**:
```html
<!-- Explicit label -->
<label for="email">Email address</label>
<input id="email" type="email" required aria-describedby="email-help">
<small id="email-help">We'll never share your email.</small>
<!-- Error state -->
<input
id="email"
type="email"
aria-invalid="true"
aria-describedby="email-error"
>
<div id="email-error" role="alert">
Please enter a valid email address.
</div>
```
### Form Validation
**Accessible Validation**:
```javascript
function validateEmail(input) {
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(input.value);
if (isValid) {
input.setAttribute('aria-invalid', 'false');
input.removeAttribute('aria-describedby');
removeError(input);
} else {
input.setAttribute('aria-invalid', 'true');
input.setAttribute('aria-describedby', `${input.id}-error`);
showError(input, 'Please enter a valid email address.');
}
}
function showError(input, message) {
const errorElement = document.getElementById(`${input.id}-error`) ||
createErrorElement(input.id);
errorElement.textContent = message;
errorElement.setAttribute('role', 'alert'); // Announces to screen readers
}
```
### Required Fields
**Indication**:
```html
<label for="name">
Full name
<abbr title="required" aria-label="required">*</abbr>
</label>
<input id="name" type="text" required aria-required="true">
```
**Visual + Programmatic**:
- Visual indicator (asterisk, "required")
- `required` attribute
- `aria-required="true"`
---
## Focus Management
### Managing Focus States
**After Modal Opens**:
```javascript
function openModal() {
modal.style.display = 'block';
modal.setAttribute('aria-hidden', 'false');
// Focus first focusable element
const firstFocusable = modal.querySelector('button, [href], input');
firstFocusable.focus();
// Trap focus
trapFocus(modal);
}
```
**After Modal Closes**:
```javascript
function closeModal() {
modal.style.display = 'none';
modal.setAttribute('aria-hidden', 'true');
// Return focus to trigger button
triggerButton.focus();
}
```
### Focus Order
**Best Practices**:
- Follow visual order (top to bottom, left to right)
- Group related elements
- Use `tabindex="-1"` for programmatic focus only
- Avoid `tabindex > 0` (creates unpredictable order)
---
## Semantic HTML
### Heading Hierarchy
**WCAG 1.3.1 (Level A)**:
Use proper heading levels (h1-h6) without skipping.
**Correct**:
```html
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h3>Another Subsection</h3>
<h2>Another Section</h2>
```
**Incorrect**:
```html
<h1>Page Title</h1>
<h3>Section</h3> <!-- Skipped h2 -->
```
### Landmark Regions
**Essential Landmarks**:
```html
<header>
<nav aria-label="Main navigation">
<!-- Primary nav -->
</nav>
</header>
<main>
<article>
<header>
<h1>Article Title</h1>
</header>
<!-- Article content -->
</article>
<aside aria-label="Related links">
<!-- Sidebar -->
</aside>
</main>
<footer>
<nav aria-label="Footer navigation">
<!-- Footer nav -->
</nav>
</footer>
```
### Lists & Tables
**Lists**:
```html
<!-- Use proper list elements -->
<ul>
<li>Item 1</li>
<li>Item 2</li>
</ul>
<!-- Not divs styled as lists -->
```
**Tables**:
```html
<table>
<caption>Sales Data Q4 2024</caption>
<thead>
<tr>
<th scope="col">Month</th>
<th scope="col">Sales</th>
</tr>
</thead>
<tbody>
<tr>
<td>October</td>
<td>$50,000</td>
</tr>
</tbody>
</table>
```
---
## Testing & Auditing
### Automated Testing Tools
**Browser Extensions**:
- axe DevTools (Chrome/Firefox)
- WAVE (Web Accessibility Evaluation Tool)
- Lighthouse (Chrome DevTools)
**Command Line**:
```bash
# pa11y
npm install -g pa11y
pa11y https://example.com
# axe-core CLI
npm install -g @axe-core/cli
axe https://example.com
```
### Manual Testing Checklist
**Keyboard Navigation**:
- [ ] Tab through all interactive elements
- [ ] Activate with Enter/Space
- [ ] Close modals with Escape
- [ ] Navigate dropdowns with arrow keys
**Screen Reader Testing**:
- [ ] Test with NVDA (Windows), VoiceOver (Mac), JAWS
- [ ] Check heading structure
- [ ] Verify alt text
- [ ] Test form labels and errors
**Visual Testing**:
- [ ] Zoom to 200%
- [ ] Test color contrast
- [ ] Check focus indicators
- [ ] Review with color blindness simulator
**Motion Testing**:
- [ ] Enable `prefers-reduced-motion`
- [ ] Verify animations are disabled/reduced
- [ ] Check for essential animations that remain
### Screen Reader Testing Commands
**NVDA (Windows)**:
- Toggle: Caps Lock or Insert
- Next heading: H
- Next landmark: D
- Read all: Caps Lock + Down Arrow
**VoiceOver (Mac)**:
- Toggle: Cmd + F5
- Rotor: Ctrl + Option + U
- Next heading: Ctrl + Option + Cmd + H
**JAWS (Windows)**:
- List headings: Insert + F6
- List links: Insert + F7
- Next heading: H
---
## Common Accessibility Patterns
### Accessible Modal
```jsx
function AccessibleModal({ isOpen, onClose, children }) {
const modalRef = useRef(null);
const triggerRef = useRef(document.activeElement);
useEffect(() => {
if (isOpen) {
// Trap focus
trapFocus(modalRef.current);
// Focus first element
const firstFocusable = modalRef.current.querySelector('button, [href], input');
firstFocusable?.focus();
// Prevent body scroll
document.body.style.overflow = 'hidden';
} else {
// Return focus
triggerRef.current?.focus();
document.body.style.overflow = '';
}
// Listen for Escape key
const handleEscape = (e) => {
if (e.key === 'Escape') onClose();
};
document.addEventListener('keydown', handleEscape);
return () => document.removeEventListener('keydown', handleEscape);
}, [isOpen, onClose]);
if (!isOpen) return null;
return (
<div
className="modal-backdrop"
onClick={onClose}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<div
ref={modalRef}
className="modal"
onClick={(e) => e.stopPropagation()}
>
<h2 id="modal-title">Modal Title</h2>
{children}
<button onClick={onClose} aria-label="Close modal">
×
</button>
</div>
</div>
);
}
```
### Accessible Accordion
```html
<div class="accordion">
<h3>
<button
aria-expanded="false"
aria-controls="panel-1"
id="accordion-button-1"
>
<span class="accordion-title">Section 1</span>
<svg class="accordion-icon" aria-hidden="true">...</svg>
</button>
</h3>
<div
id="panel-1"
role="region"
aria-labelledby="accordion-button-1"
hidden
>
<p>Content for section 1...</p>
</div>
</div>
```
```javascript
function initAccordion() {
const buttons = document.querySelectorAll('.accordion button');
buttons.forEach(button => {
button.addEventListener('click', () => {
const expanded = button.getAttribute('aria-expanded') === 'true';
const panel = document.getElementById(button.getAttribute('aria-controls'));
button.setAttribute('aria-expanded', !expanded);
panel.hidden = expanded;
});
});
}
```
---
## WCAG Quick Reference
**Level A (Must Have)**:
- Text alternatives (alt text)
- Keyboard accessible
- Color not only means of conveying info
- Labeled form inputs
- Proper heading hierarchy
**Level AA (Should Have)**:
- 4.5:1 contrast for normal text
- 3:1 contrast for large text
- Resize text to 200%
- Focus visible
- Multiple ways to find pages
**Level AAA (Nice to Have)**:
- 7:1 contrast for normal text
- 4.5:1 contrast for large text
- Target size 44×44px minimum
- No timing restrictions
- Help available
---
*Last updated: 2024*
*Based on WCAG 2.1 and WCAG 2.2 standards*
references/design_trends_2024.md
# Web Design Trends 2024-2025
## Overview
This document catalogs the dominant web design trends, aesthetic movements, and interaction paradigms shaping modern web experiences in 2024-2025. Updated regularly based on industry analysis, award-winning sites, and emerging patterns.
## Visual Design Trends
### 1. Bold Minimalism
**Definition**: Minimalist foundations enhanced with bold typography, intentional color, and confident white space.
**Key Characteristics**:
- Ultra-large typography (80px-200px+ headlines)
- Limited color palettes (2-4 colors max)
- Generous white space (breathing room)
- High contrast (black/white, bold accent colors)
- Geometric shapes and clean lines
- Sans-serif dominance (Inter, Space Grotesk, Sora)
**Examples**:
- Apple product pages
- Linear.app
- Stripe documentation
- Vercel website
**Implementation Tips**:
```css
/* Fluid typography for bold headlines */
h1 {
font-size: clamp(3rem, 8vw, 10rem);
font-weight: 700;
line-height: 0.95;
letter-spacing: -0.03em;
}
/* Generous spacing */
section {
padding: clamp(3rem, 10vw, 8rem) 0;
}
```
**Why It Works**: Reduces cognitive load, focuses attention, loads fast, works across devices.
---
### 2. Kinetic Typography
**Definition**: Text that moves, transforms, and responds to user interaction.
**Patterns**:
**a) Variable Font Animations**:
- Weight transitions (100 → 900)
- Width morphing (condensed → expanded)
- Slant/italic transformations
- Multiple axes animating simultaneously
**Implementation**:
```css
@font-face {
font-family: 'Inter';
src: url('Inter-Variable.woff2') format('woff2-variations');
font-weight: 100 900;
}
h1 {
font-variation-settings: 'wght' 300;
transition: font-variation-settings 0.3s;
}
h1:hover {
font-variation-settings: 'wght' 900;
}
```
**b) Text Split & Stagger**:
- Reveal by character, word, or line
- Staggered entrance animations
- 3D text transformations
**c) Scroll-Linked Text**:
- Text color change on scroll
- Weight increase/decrease
- Size transformations
- Parallax text layers
**Related Skills**: `gsap-scrolltrigger` for text animations, `motion-framer` for React text effects
---
### 3. Glassmorphism 2.0
**Evolution**: Refined frosted-glass aesthetic with better accessibility and performance.
**Modern Approach** (2024):
```css
.glass-card {
/* More subtle, accessible backgrounds */
background: rgba(255, 255, 255, 0.08);
backdrop-filter: blur(12px) saturate(150%);
/* Higher contrast borders */
border: 1px solid rgba(255, 255, 255, 0.18);
/* Softer, more realistic shadows */
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.12),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
border-radius: 16px;
}
/* Dark mode variant */
@media (prefers-color-scheme: dark) {
.glass-card {
background: rgba(0, 0, 0, 0.4);
border: 1px solid rgba(255, 255, 255, 0.1);
}
}
```
**Accessibility Considerations**:
- Ensure text has 4.5:1+ contrast on glass backgrounds
- Test with screen readers (ensure content is readable)
- Provide high-contrast mode alternative
**Performance**:
- `backdrop-filter` can be expensive on mobile
- Use sparingly (2-3 glass elements max per viewport)
- Consider `will-change: backdrop-filter` during animations only
---
### 4. Gradient Mesh Backgrounds
**Definition**: Complex, multi-color gradients with organic blob shapes.
**Tools**:
- CSS: `background: radial-gradient()` layering
- SVG: Gradient mesh filters
- Canvas/WebGL: Shader-based gradients (Vanta.js)
**Modern Gradient System**:
```css
:root {
/* Define gradient stops */
--gradient-1: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--gradient-2: radial-gradient(circle at 20% 50%, #f093fb 0%, #f5576c 100%);
--gradient-mesh:
radial-gradient(at 27% 37%, hsla(215, 98%, 61%, 1) 0px, transparent 50%),
radial-gradient(at 97% 21%, hsla(125, 98%, 72%, 1) 0px, transparent 50%),
radial-gradient(at 52% 99%, hsla(354, 98%, 61%, 1) 0px, transparent 50%),
radial-gradient(at 10% 29%, hsla(256, 96%, 67%, 1) 0px, transparent 50%);
}
.hero {
background: var(--gradient-mesh);
background-size: 400% 400%;
animation: gradient-shift 15s ease infinite;
}
@keyframes gradient-shift {
0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
}
```
**Animated Gradients** (WebGL approach with Vanta.js):
- WAVES effect with custom colors
- CELLS effect for organic feel
- FOG for atmospheric backgrounds
**Related Skills**: `lightweight-3d-effects` (Vanta.js backgrounds)
---
### 5. Neumorphism (Evolved)
**Status**: Declining but still used for specific contexts (iOS apps, dashboards).
**Modern Implementation** (Improved contrast):
```css
.neomorphic-card {
background: #e0e5ec;
box-shadow:
/* Light shadow */
8px 8px 16px rgba(163, 177, 198, 0.6),
/* Dark shadow */
-8px -8px 16px rgba(255, 255, 255, 0.8);
border-radius: 16px;
padding: 2rem;
/* CRITICAL: Ensure contrast for accessibility */
color: #1a1a1a; /* 11.6:1 contrast ratio */
}
/* Interactive state */
.neomorphic-card:active {
box-shadow:
inset 4px 4px 8px rgba(163, 177, 198, 0.6),
inset -4px -4px 8px rgba(255, 255, 255, 0.8);
}
```
**Why It's Declining**: Accessibility concerns (low contrast), limited color palette, heavy shadows impact performance.
---
### 6. Dark Mode as Default
**Trend**: Many sites now default to dark mode with light mode as alternative.
**Best Practices** (2024):
```css
/* Use system preference as default */
:root {
color-scheme: light dark;
}
/* Dark mode first */
:root {
--color-bg: #0a0a0a;
--color-text: #e5e5e5;
--color-accent: #3b82f6;
}
/* Light mode override */
@media (prefers-color-scheme: light) {
:root {
--color-bg: #ffffff;
--color-text: #171717;
--color-accent: #2563eb;
}
}
/* Manual toggle support */
[data-theme="light"] {
--color-bg: #ffffff;
--color-text: #171717;
}
[data-theme="dark"] {
--color-bg: #0a0a0a;
--color-text: #e5e5e5;
}
```
**Color Adjustments for Dark Mode**:
- Reduce saturation by 10-20%
- Lower brightness for vibrant colors
- Use warmer blacks (#0a0a0a, not pure #000)
- Increase contrast for text (7:1+ ratio)
---
### 7. Asymmetric Layouts
**Definition**: Breaking the grid for visual interest and hierarchy.
**Patterns**:
- Split-screen layouts (60/40, 70/30)
- Overlapping elements with z-index
- Diagonal sections
- Off-grid text positioning
- Broken grid systems
**CSS Grid Implementation**:
```css
.asymmetric-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 2rem;
}
.feature-1 {
/* Spans 7 columns, offset by 1 */
grid-column: 2 / 9;
grid-row: 1;
}
.feature-2 {
/* Overlaps with feature-1 */
grid-column: 8 / 13;
grid-row: 1;
margin-top: 4rem; /* Offset vertically */
z-index: 2;
}
```
**Examples**:
- Awwwards winners (70%+ use asymmetry)
- Design agency portfolios
- Product launch pages
---
## Interaction Trends
### 8. Micro-Interactions Everywhere
**Definition**: Small, delightful animations that provide feedback and enhance usability.
**Categories & Examples**:
**a) Button Interactions**:
- Hover lift (transform: translateY(-2px))
- Press down (scale: 0.98)
- Ripple effect on click
- Color shift transitions
**b) Form Feedback**:
- Input focus animations (border glow)
- Success/error state transitions
- Character count with progress ring
- Password strength indicator
**c) Loading States**:
- Skeleton screens (better than spinners)
- Progress indicators
- Optimistic UI updates
- Staggered content reveals
**d) Navigation**:
- Hamburger to X transitions
- Dropdown reveals with stagger
- Active state indicators
- Scroll progress bars
**Implementation** (Framer Motion):
```jsx
const buttonVariants = {
idle: { scale: 1, y: 0 },
hover: { scale: 1.05, y: -2 },
tap: { scale: 0.98, y: 0 }
};
<motion.button
variants={buttonVariants}
initial="idle"
whileHover="hover"
whileTap="tap"
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
Click me
</motion.button>
```
**Related Skills**: `motion-framer`, `react-spring-physics`, `animejs`
---
### 9. Scroll-Driven Storytelling
**Definition**: Content reveals, transforms, and tells a story as the user scrolls.
**Techniques**:
**a) Pinned Sections with Scrubbing**:
```javascript
// Pin section while content scrubs through
gsap.to(".content", {
scrollTrigger: {
trigger: ".section",
start: "top top",
end: "+=3000", // 3000px of scroll
scrub: 1,
pin: true
},
opacity: 1,
scale: 1.2
});
```
**b) Horizontal Scroll Galleries**:
- Card-based portfolios
- Timeline experiences
- Product showcases
**c) Parallax Layers**:
- Foreground, midground, background at different speeds
- 3D depth illusion
- Hero section parallax
**d) Progress Indicators**:
- Reading progress bars
- Section progress dots
- Animated SVG paths
**Examples**:
- Apple product pages (iPhone, MacBook)
- Stripe's annual reports
- Web agency showcases
**Related Skills**: `gsap-scrolltrigger`, `locomotive-scroll`
---
### 10. Cursor Effects & Custom Cursors
**Trend**: Custom cursors that enhance interaction and provide visual delight.
**Patterns**:
**a) Dot Follower**:
```javascript
const cursor = { x: 0, y: 0 };
const follower = { x: 0, y: 0 };
document.addEventListener('mousemove', (e) => {
cursor.x = e.clientX;
cursor.y = e.clientY;
});
function updateCursor() {
// Smooth follow with easing
follower.x += (cursor.x - follower.x) * 0.1;
follower.y += (cursor.y - follower.y) * 0.1;
cursorDot.style.transform = `translate(${follower.x}px, ${follower.y}px)`;
requestAnimationFrame(updateCursor);
}
```
**b) Contextual Cursors**:
- "View" on images
- "Drag" on sliders
- "Play" on videos
- Magnetic attraction to buttons
**c) Blend Mode Cursors**:
```css
.cursor {
mix-blend-mode: difference;
/* Inverts colors underneath */
}
```
**Accessibility**:
- Hide on touch devices
- Respect `prefers-reduced-motion`
- Don't hide native cursor completely (layer on top)
---
### 11. 3D Elements & WebGL
**Status**: Increasingly mainstream with improved performance and tooling.
**Use Cases**:
- Hero backgrounds (Vanta.js waves, particles)
- Product viewers (rotate, zoom, configure)
- Data visualization (3D charts, globes)
- Immersive experiences (games, virtual tours)
**Performance-First Approach**:
```javascript
// Lazy load 3D content
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Load 3D scene
loadThreeJSScene();
observer.unobserve(entry.target);
}
});
});
observer.observe(document.querySelector('.3d-container'));
```
**Lightweight 3D Options**:
- Vanta.js for backgrounds (uses Three.js)
- Zdog for flat-style 3D illustrations
- Spline for designer-friendly 3D
**Full 3D Engines**:
- Three.js for custom scenes
- React Three Fiber for React apps
- Babylon.js for physics and VR
**Related Skills**: `threejs-webgl`, `react-three-fiber`, `lightweight-3d-effects`
---
### 12. Voice & Conversational UI
**Emerging Trend**: Voice commands and conversational interfaces.
**Patterns**:
- Voice search integration
- Chatbot interfaces (natural language)
- Voice-controlled navigation
- Audio feedback for interactions
**Implementation Considerations**:
- Web Speech API for voice input
- Text-to-speech for feedback
- Fallback to text input
- Accessibility: don't require voice-only
---
## Technical Trends
### 13. Performance-First Design
**Core Web Vitals as Design Constraints**:
**LCP (Largest Contentful Paint) < 2.5s**:
- Optimize hero images (WebP/AVIF)
- Inline critical CSS
- Preload key resources
- Avoid layout shifts
**FID (First Input Delay) < 100ms**:
- Defer non-critical JavaScript
- Split code bundles
- Use passive event listeners
- Minimize main thread work
**CLS (Cumulative Layout Shift) < 0.1**:
- Reserve space for images (aspect-ratio)
- Avoid inserting content above existing content
- Use CSS transforms, not position changes
**Design Implications**:
- Avoid web fonts that cause FOIT/FOUT
- Use system fonts for body text
- Lazy load below-fold content
- Progressive enhancement mindset
---
### 14. Component-Driven Design Systems
**Approach**: Design and build in reusable components.
**Tools**:
- Figma with variants and auto-layout
- Storybook for component documentation
- Design tokens (JSON format)
- Automated design-to-code (Figma plugins)
**Component Architecture**:
```
Design System/
├── Foundations/
│ ├── Colors (tokens)
│ ├── Typography (scales)
│ ├── Spacing (scales)
│ └── Shadows (elevation)
├── Components/
│ ├── Atoms (Button, Input, Icon)
│ ├── Molecules (SearchBar, Card)
│ ├── Organisms (Header, Footer)
│ └── Templates (PageLayout)
└── Patterns/
├── Navigation patterns
├── Form patterns
└── Animation patterns
```
**Related Skills**: `animated-component-libraries`
---
### 15. AI-Enhanced Design
**Applications**:
**a) Content Generation**:
- AI-written copy (with human editing)
- Image generation (Midjourney, DALL-E)
- Icon generation
- Design variations
**b) Personalization**:
- Dynamic layouts based on user behavior
- A/B testing with AI optimization
- Adaptive color schemes
- Content recommendations
**c) Accessibility**:
- AI-generated alt text
- Automatic color contrast adjustments
- Smart keyboard navigation
- Content simplification
**Implementation**:
- Edge functions for fast personalization
- Client-side ML models (TensorFlow.js)
- Privacy-preserving personalization
---
### 16. Progressive Web Apps (PWAs)
**Status**: Mainstream adoption, especially for mobile-first products.
**Key Features**:
- Offline functionality
- Install to home screen
- Push notifications
- Background sync
- Native-like performance
**Design Considerations**:
- Mobile-first responsive design
- Touch-friendly interactions (44px+ targets)
- Offline-first content strategy
- Loading states for sync
- Native-feeling animations
---
## Color Trends
### 17. Color Palette Trends (2024-2025)
**Trending Color Schemes**:
**a) High Contrast Duotone**:
- Black + bright accent (electric blue, hot pink, lime green)
- Minimal, punchy, attention-grabbing
**b) Muted Earth Tones**:
- Terracotta, sage green, warm beige
- Natural, calming, sustainable aesthetic
**c) Neon Gradients**:
- Bright, saturated gradient meshes
- Cyberpunk aesthetic
- Gaming and tech brands
**d) Monochromatic + Accent**:
- Single hue with varying lightness
- One bold accent color
- Sophisticated, cohesive
**Color Space Evolution**:
```css
/* OKLCH for perceptual uniformity */
:root {
--color-primary: oklch(60% 0.2 250);
/* 60% lightness, 0.2 chroma, 250° hue */
/* Benefits: */
/* - Perceptually uniform lightness */
/* - Wider color gamut than RGB */
/* - Easier to create accessible palettes */
}
```
---
## Typography Trends
### 18. Variable Fonts Mainstream
**Adoption**: Major sites now using variable fonts by default.
**Popular Variable Fonts**:
- Inter (UI text)
- Space Grotesk (headlines)
- Recursive (code & UI)
- Fraunces (display serif)
- Outfit (geometric sans)
**Advantages**:
- Single file, multiple weights/styles
- Animation possibilities
- Fine-grained control (weight: 347)
- Smaller file size than multiple weights
**Implementation**:
```css
@font-face {
font-family: 'Inter';
src: url('Inter-Variable.woff2') format('woff2-variations');
font-weight: 100 900;
font-display: swap;
}
h1 {
font-family: 'Inter', sans-serif;
font-weight: 750; /* Any value 100-900 */
}
```
---
### 19. Oversized Typography
**Trend**: Headlines at 100px-300px for desktop.
**Fluid Typography System**:
```css
:root {
--font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
--font-size-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
--font-size-xl: clamp(2rem, 1.5rem + 2.5vw, 4rem);
--font-size-2xl: clamp(3rem, 2rem + 5vw, 8rem);
--font-size-3xl: clamp(4rem, 2rem + 10vw, 12rem);
}
h1 {
font-size: var(--font-size-3xl);
line-height: 0.95;
letter-spacing: -0.03em;
}
```
**Design Considerations**:
- Tight line-height (0.9-1.0)
- Negative letter-spacing (-0.02 to -0.04em)
- Strong font weight (700-900)
- Responsive scaling with clamp()
---
## Layout Trends
### 20. Broken Grid Layouts
**Definition**: Intentionally breaking traditional grid systems for visual interest.
**Techniques**:
- Overlapping elements
- Off-grid positioning
- Diagonal layouts
- Negative space as design element
- Z-axis layering
**CSS Grid + Subgrid**:
```css
.parent-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 2rem;
}
.child {
/* Subgrid inherits parent's columns */
display: grid;
grid-template-columns: subgrid;
grid-column: span 6;
}
```
---
### 21. Single-Page Experiences
**Trend**: Entire site on one scrollable page.
**Patterns**:
- Section-based navigation (anchor links)
- Full-screen sections
- Scroll-driven reveals
- Smooth scrolling between sections
**Benefits**:
- Cohesive narrative flow
- No page load transitions
- Mobile-friendly (swipe to scroll)
- Performance (single bundle)
**Challenges**:
- SEO (use meaningful sections with h2-h6)
- Deep linking (use hash routing)
- Back button behavior
- Large bundle size
---
## Emerging & Experimental
### 22. AI-Generated Visuals
**Tools**:
- Midjourney for illustrations
- DALL-E 3 for specific images
- Stable Diffusion for customization
**Use Cases**:
- Hero backgrounds
- Blog post headers
- Icon generation
- Abstract patterns
**Ethical Considerations**:
- Disclose AI-generated content
- Review for bias and appropriateness
- Ensure licensing rights
- Don't replace human designers for key visuals
---
### 23. Web3 Design Patterns
**Characteristics**:
- Wallet connection UIs
- NFT galleries
- Token-gated content
- Blockchain transaction states
- Decentralized identity
**Design Challenges**:
- Explaining complex concepts simply
- Transaction loading states (slow blockchains)
- Error handling (failed transactions)
- Gas fee transparency
---
### 24. Spatial Computing & 3D Interfaces
**Future Trend**: Preparing for AR/VR mainstream adoption.
**Patterns**:
- Depth-based layering
- 3D navigation
- Gesture controls
- Spatial audio cues
**Implementation**: WebXR API, A-Frame, Babylon.js
**Related Skills**: `aframe-webxr`, `babylonjs-engine`
---
## Anti-Trends (What to Avoid)
### Declining Patterns:
1. **Carousels**: Low engagement, poor accessibility
2. **Auto-Playing Videos**: Annoying, data-hungry
3. **Hamburger Menus Only**: Hide navigation
4. **Stock Photos**: Generic, unauthentic
5. **Neumorphism**: Accessibility issues
6. **Long Scroll Animations**: Frustrating on mobile
7. **Overuse of Parallax**: Motion sickness, poor performance
8. **Chatbots as First Contact**: Often unhelpful, annoying
9. **Cookie Banners That Block Content**: Poor UX
10. **Infinite Scroll Without Pagination**: SEO and UX issues
---
## Resources & Inspiration
**Award Sites**:
- Awwwards.com (daily winners)
- CSS Design Awards
- The FWA
**Trend Reports**:
- Webflow's Annual Design Report
- Dribbble Year in Review
- Behance Featured Projects
**Design Systems**:
- Material Design 3 (Google)
- Fluent 2 (Microsoft)
- Polaris (Shopify)
- Carbon (IBM)
**Tools**:
- Figma (design)
- Webflow (visual development)
- Framer (design + code)
- Spline (3D design)
---
## Forecasts (2025+)
**Emerging Trends to Watch**:
1. **AI-Personalized Experiences**: Every user sees a unique layout
2. **Voice-First Interfaces**: Beyond chatbots to full voice navigation
3. **Spatial Web**: 3D interfaces for AR/VR headsets
4. **Sustainability Indicators**: Carbon footprint of web experiences
5. **Ethical Design Standards**: Privacy, accessibility, inclusivity as default
6. **Real-Time Collaboration**: Multiplayer web experiences
7. **Generative Art**: Unique visuals for each visitor
8. **Biometric Interfaces**: Face/voice recognition for auth and personalization
**Technology Shifts**:
- WebGPU mainstream adoption (faster 3D)
- Container queries (component-based responsive design)
- View Transitions API (smooth SPA transitions)
- CSS Houdini (custom CSS magic)
---
*Last updated: 2024*
*Review quarterly for updates*
references/interaction_patterns.md
# Micro-Interaction Patterns Catalog
## Overview
Comprehensive catalog of micro-interactions for modern web interfaces. Each pattern includes implementation code, accessibility considerations, and performance notes.
Micro-interactions are small, purposeful animations that provide feedback, guide users, and enhance perceived performance.
---
## Table of Contents
1. [Button Interactions](#button-interactions)
2. [Form & Input Interactions](#form--input-interactions)
3. [Loading States](#loading-states)
4. [Navigation Interactions](#navigation-interactions)
5. [Hover Effects](#hover-effects)
6. [Toggle & Switch Interactions](#toggle--switch-interactions)
7. [Card Interactions](#card-interactions)
8. [Scroll Interactions](#scroll-interactions)
9. [Modal & Dialog Interactions](#modal--dialog-interactions)
10. [Toast & Notification Interactions](#toast--notification-interactions)
---
## Button Interactions
### 1.1 Hover Lift
**Effect**: Button lifts up slightly on hover, creating depth.
**Implementation** (CSS):
```css
.button-lift {
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.button-lift:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.button-lift:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
```
**Framer Motion**:
```jsx
<motion.button
whileHover={{ y: -2, boxShadow: "0 4px 12px rgba(0,0,0,0.15)" }}
whileTap={{ y: 0, boxShadow: "0 2px 4px rgba(0,0,0,0.1)" }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
Click me
</motion.button>
```
**Accessibility**: Preserve for keyboard focus states.
---
### 1.2 Ripple Effect
**Effect**: Material Design-style ripple on click.
**Implementation** (JavaScript):
```javascript
function createRipple(event) {
const button = event.currentTarget;
const ripple = document.createElement('span');
const rect = button.getBoundingClientRect();
const size = Math.max(rect.width, rect.height);
const x = event.clientX - rect.left - size / 2;
const y = event.clientY - rect.top - size / 2;
ripple.style.width = ripple.style.height = `${size}px`;
ripple.style.left = `${x}px`;
ripple.style.top = `${y}px`;
ripple.classList.add('ripple');
button.appendChild(ripple);
setTimeout(() => ripple.remove(), 600);
}
document.querySelectorAll('.button-ripple').forEach(button => {
button.addEventListener('click', createRipple);
});
```
**CSS**:
```css
.button-ripple {
position: relative;
overflow: hidden;
}
.ripple {
position: absolute;
border-radius: 50%;
background: rgba(255, 255, 255, 0.6);
transform: scale(0);
animation: ripple-animation 0.6s ease-out;
}
@keyframes ripple-animation {
to {
transform: scale(4);
opacity: 0;
}
}
```
**Performance**: Use `will-change: transform` sparingly.
---
### 1.3 Magnetic Button
**Effect**: Button magnetically attracts cursor on proximity.
**Implementation** (GSAP):
```javascript
const button = document.querySelector('.magnetic-button');
const magneticStrength = 0.3;
button.addEventListener('mousemove', (e) => {
const rect = button.getBoundingClientRect();
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const distanceX = (e.clientX - centerX) * magneticStrength;
const distanceY = (e.clientY - centerY) * magneticStrength;
gsap.to(button, {
x: distanceX,
y: distanceY,
duration: 0.3,
ease: "power2.out"
});
});
button.addEventListener('mouseleave', () => {
gsap.to(button, {
x: 0,
y: 0,
duration: 0.5,
ease: "elastic.out(1, 0.3)"
});
});
```
**Accessibility**: Disable on mobile/touch devices.
---
### 1.4 Loading Button State
**Effect**: Button morphs into loading spinner.
**Framer Motion**:
```jsx
function LoadingButton() {
const [isLoading, setIsLoading] = useState(false);
return (
<motion.button
onClick={() => setIsLoading(true)}
animate={isLoading ? "loading" : "idle"}
disabled={isLoading}
style={{ position: 'relative', overflow: 'hidden' }}
>
<motion.span
variants={{
idle: { opacity: 1 },
loading: { opacity: 0 }
}}
>
Submit
</motion.span>
<motion.div
className="spinner"
variants={{
idle: { opacity: 0, scale: 0 },
loading: { opacity: 1, scale: 1 }
}}
style={{ position: 'absolute', inset: 0 }}
>
<Spinner />
</motion.div>
</motion.button>
);
}
```
**Accessibility**: Announce loading state to screen readers.
---
## Form & Input Interactions
### 2.1 Floating Label
**Effect**: Label floats up when input is focused or filled.
**Implementation**:
```css
.input-wrapper {
position: relative;
margin: 1rem 0;
}
.input-field {
width: 100%;
padding: 1rem;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 1rem;
}
.input-field:focus {
outline: none;
border-color: #3b82f6;
}
.input-label {
position: absolute;
left: 1rem;
top: 1rem;
pointer-events: none;
transition: all 0.2s ease;
color: #666;
}
.input-field:focus + .input-label,
.input-field:not(:placeholder-shown) + .input-label {
top: -0.5rem;
left: 0.75rem;
font-size: 0.75rem;
background: white;
padding: 0 0.25rem;
color: #3b82f6;
}
```
**HTML**:
```html
<div class="input-wrapper">
<input
class="input-field"
type="text"
placeholder=" "
required
/>
<label class="input-label">Email address</label>
</div>
```
---
### 2.2 Input Validation Feedback
**Effect**: Instant feedback on validation state.
**React + Framer Motion**:
```jsx
function ValidatedInput({ label, validator }) {
const [value, setValue] = useState('');
const [isValid, setIsValid] = useState(null);
const handleChange = (e) => {
const newValue = e.target.value;
setValue(newValue);
setIsValid(validator(newValue));
};
return (
<div className="input-wrapper">
<motion.input
value={value}
onChange={handleChange}
animate={{
borderColor: isValid === null ? '#ccc' :
isValid ? '#10b981' : '#ef4444'
}}
transition={{ duration: 0.2 }}
/>
<AnimatePresence>
{isValid === true && (
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
className="success-icon"
>
✓
</motion.div>
)}
{isValid === false && (
<motion.div
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
exit={{ opacity: 0, x: -10 }}
className="error-icon"
>
✗
</motion.div>
)}
</AnimatePresence>
</div>
);
}
```
**Accessibility**: Provide error messages, not just icons.
---
### 2.3 Password Strength Indicator
**Effect**: Visual indicator of password strength.
**Implementation**:
```jsx
function PasswordStrengthIndicator({ password }) {
const strength = calculateStrength(password);
const variants = {
weak: { width: '33%', backgroundColor: '#ef4444' },
medium: { width: '66%', backgroundColor: '#f59e0b' },
strong: { width: '100%', backgroundColor: '#10b981' }
};
return (
<div className="strength-bar-container">
<motion.div
className="strength-bar"
variants={variants}
animate={strength}
transition={{ duration: 0.3 }}
/>
<p className="strength-label">
Password strength: <strong>{strength}</strong>
</p>
</div>
);
}
function calculateStrength(password) {
if (password.length < 6) return 'weak';
if (password.length < 10) return 'medium';
return 'strong';
}
```
---
### 2.4 Character Count with Progress
**Effect**: Shows remaining characters with visual indicator.
**Implementation**:
```jsx
function TextareaWithCount({ maxLength = 280 }) {
const [text, setText] = useState('');
const remaining = maxLength - text.length;
const percentage = (text.length / maxLength) * 100;
return (
<div>
<textarea
value={text}
onChange={(e) => setText(e.target.value)}
maxLength={maxLength}
/>
<div className="count-indicator">
<motion.svg
width="40"
height="40"
style={{ transform: 'rotate(-90deg)' }}
>
<circle
cx="20"
cy="20"
r="16"
stroke="#e5e7eb"
strokeWidth="3"
fill="none"
/>
<motion.circle
cx="20"
cy="20"
r="16"
stroke={remaining < 20 ? '#ef4444' : '#3b82f6'}
strokeWidth="3"
fill="none"
strokeDasharray={`${2 * Math.PI * 16}`}
animate={{
strokeDashoffset: (1 - percentage / 100) * (2 * Math.PI * 16)
}}
/>
</motion.svg>
<span className="count-text">{remaining}</span>
</div>
</div>
);
}
```
---
## Loading States
### 3.1 Skeleton Screen
**Effect**: Content placeholder that mimics layout.
**Implementation**:
```css
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: skeleton-loading 1.5s ease-in-out infinite;
border-radius: 4px;
}
@keyframes skeleton-loading {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
.skeleton-text {
height: 1rem;
margin-bottom: 0.5rem;
}
.skeleton-title {
height: 2rem;
width: 60%;
margin-bottom: 1rem;
}
.skeleton-image {
width: 100%;
aspect-ratio: 16 / 9;
}
```
**React Component**:
```jsx
function SkeletonCard() {
return (
<div className="card">
<div className="skeleton skeleton-image" />
<div className="skeleton skeleton-title" />
<div className="skeleton skeleton-text" />
<div className="skeleton skeleton-text" style={{ width: '80%' }} />
</div>
);
}
```
---
### 3.2 Progress Bar
**Effect**: Linear progress indicator.
**Framer Motion**:
```jsx
function ProgressBar({ progress }) {
return (
<div className="progress-container">
<motion.div
className="progress-bar"
initial={{ width: 0 }}
animate={{ width: `${progress}%` }}
transition={{ duration: 0.5, ease: "easeOut" }}
/>
</div>
);
}
```
**CSS**:
```css
.progress-container {
width: 100%;
height: 4px;
background: #e5e7eb;
border-radius: 9999px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background: linear-gradient(90deg, #3b82f6, #8b5cf6);
border-radius: 9999px;
}
```
---
### 3.3 Spinner
**Effect**: Rotating loading indicator.
**CSS**:
```css
.spinner {
width: 40px;
height: 40px;
border: 4px solid rgba(59, 130, 246, 0.1);
border-top-color: #3b82f6;
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
```
**Framer Motion (Advanced)**:
```jsx
function Spinner() {
return (
<motion.div
className="spinner-container"
initial={{ opacity: 0, scale: 0.8 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.8 }}
>
<motion.div
className="spinner"
animate={{ rotate: 360 }}
transition={{
duration: 1,
repeat: Infinity,
ease: "linear"
}}
/>
</motion.div>
);
}
```
---
## Navigation Interactions
### 4.1 Hamburger Menu Animation
**Effect**: Hamburger transforms into X on click.
**CSS**:
```css
.hamburger {
width: 30px;
height: 20px;
position: relative;
cursor: pointer;
}
.hamburger span {
display: block;
position: absolute;
height: 3px;
width: 100%;
background: #333;
border-radius: 3px;
transition: all 0.3s ease;
}
.hamburger span:nth-child(1) { top: 0; }
.hamburger span:nth-child(2) { top: 50%; transform: translateY(-50%); }
.hamburger span:nth-child(3) { bottom: 0; }
.hamburger.active span:nth-child(1) {
top: 50%;
transform: translateY(-50%) rotate(45deg);
}
.hamburger.active span:nth-child(2) {
opacity: 0;
}
.hamburger.active span:nth-child(3) {
bottom: 50%;
transform: translateY(50%) rotate(-45deg);
}
```
---
### 4.2 Dropdown Menu Reveal
**Effect**: Dropdown slides down with stagger.
**Framer Motion**:
```jsx
const menuVariants = {
closed: {
opacity: 0,
y: -20,
transition: { staggerChildren: 0.05, staggerDirection: -1 }
},
open: {
opacity: 1,
y: 0,
transition: { staggerChildren: 0.07, delayChildren: 0.1 }
}
};
const itemVariants = {
closed: { opacity: 0, x: -20 },
open: { opacity: 1, x: 0 }
};
function DropdownMenu({ isOpen }) {
return (
<AnimatePresence>
{isOpen && (
<motion.ul
className="dropdown"
variants={menuVariants}
initial="closed"
animate="open"
exit="closed"
>
{items.map((item) => (
<motion.li key={item} variants={itemVariants}>
{item}
</motion.li>
))}
</motion.ul>
)}
</AnimatePresence>
);
}
```
---
### 4.3 Active Link Indicator
**Effect**: Animated underline follows active link.
**Framer Motion**:
```jsx
function TabNavigation() {
const [activeTab, setActiveTab] = useState(0);
return (
<nav className="tabs">
{tabs.map((tab, index) => (
<button
key={tab}
onClick={() => setActiveTab(index)}
className={activeTab === index ? 'active' : ''}
>
{tab}
{activeTab === index && (
<motion.div
className="active-indicator"
layoutId="activeTab"
transition={{ type: "spring", stiffness: 300, damping: 30 }}
/>
)}
</button>
))}
</nav>
);
}
```
**CSS**:
```css
.tabs {
display: flex;
gap: 2rem;
border-bottom: 2px solid #e5e7eb;
}
.tabs button {
position: relative;
padding: 1rem 0;
background: none;
border: none;
cursor: pointer;
}
.active-indicator {
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
background: #3b82f6;
}
```
---
## Hover Effects
### 5.1 Image Zoom on Hover
**Effect**: Image scales up slightly on hover.
**CSS**:
```css
.image-container {
overflow: hidden;
border-radius: 8px;
}
.image-container img {
transition: transform 0.3s ease;
display: block;
width: 100%;
}
.image-container:hover img {
transform: scale(1.05);
}
```
---
### 5.2 Tilt Effect
**Effect**: Card tilts based on mouse position.
**Vanilla-Tilt**:
```javascript
VanillaTilt.init(document.querySelectorAll(".tilt-card"), {
max: 15,
speed: 400,
glare: true,
"max-glare": 0.3
});
```
**Manual Implementation**:
```javascript
const card = document.querySelector('.tilt-card');
card.addEventListener('mousemove', (e) => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const rotateX = (y - centerY) / 10;
const rotateY = (centerX - x) / 10;
card.style.transform = `perspective(1000px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`;
});
card.addEventListener('mouseleave', () => {
card.style.transform = 'perspective(1000px) rotateX(0) rotateY(0)';
});
```
---
### 5.3 Reveal on Hover
**Effect**: Content reveals on hover with slide/fade.
**CSS**:
```css
.card {
position: relative;
overflow: hidden;
}
.card-overlay {
position: absolute;
inset: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform: translateY(100%);
transition: all 0.3s ease;
}
.card:hover .card-overlay {
opacity: 1;
transform: translateY(0);
}
```
---
## Toggle & Switch Interactions
### 6.1 Toggle Switch
**Effect**: Smooth toggle with spring animation.
**Framer Motion**:
```jsx
function ToggleSwitch({ enabled, setEnabled }) {
return (
<motion.div
className="switch"
data-enabled={enabled}
onClick={() => setEnabled(!enabled)}
animate={{ backgroundColor: enabled ? '#3b82f6' : '#d1d5db' }}
>
<motion.div
className="switch-handle"
layout
transition={{ type: "spring", stiffness: 500, damping: 30 }}
style={{ justifyContent: enabled ? 'flex-end' : 'flex-start' }}
/>
</motion.div>
);
}
```
**CSS**:
```css
.switch {
width: 50px;
height: 28px;
border-radius: 14px;
padding: 4px;
cursor: pointer;
display: flex;
align-items: center;
}
.switch-handle {
width: 20px;
height: 20px;
background: white;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
```
---
## Card Interactions
### 7.1 Card Expand
**Effect**: Card expands to full screen on click.
**Framer Motion with `layout`**:
```jsx
function ExpandableCard() {
const [isExpanded, setIsExpanded] = useState(false);
return (
<motion.div
layout
className={isExpanded ? 'card-expanded' : 'card'}
onClick={() => setIsExpanded(!isExpanded)}
transition={{ type: "spring", stiffness: 300, damping: 30 }}
>
<motion.h2 layout="position">Title</motion.h2>
<motion.p layout="position">Content</motion.p>
{isExpanded && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Additional content...
</motion.div>
)}
</motion.div>
);
}
```
---
## Scroll Interactions
### 8.1 Fade In on Scroll
**Effect**: Elements fade in as they enter viewport.
**Framer Motion + `useInView`**:
```jsx
function FadeInSection({ children }) {
const ref = useRef(null);
const isInView = useInView(ref, { once: true, amount: 0.3 });
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 50 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
>
{children}
</motion.div>
);
}
```
---
### 8.2 Scroll Progress Indicator
**Effect**: Shows reading progress.
**Implementation**:
```jsx
function ScrollProgress() {
const [scrollProgress, setScrollProgress] = useState(0);
useEffect(() => {
const updateProgress = () => {
const scrollHeight = document.documentElement.scrollHeight - window.innerHeight;
const progress = (window.scrollY / scrollHeight) * 100;
setScrollProgress(progress);
};
window.addEventListener('scroll', updateProgress);
return () => window.removeEventListener('scroll', updateProgress);
}, []);
return (
<motion.div
className="progress-bar"
style={{ scaleX: scrollProgress / 100, transformOrigin: 'left' }}
/>
);
}
```
---
## Modal & Dialog Interactions
### 9.1 Modal Fade & Scale
**Effect**: Modal fades in and scales up.
**Framer Motion**:
```jsx
function Modal({ isOpen, onClose, children }) {
return (
<AnimatePresence>
{isOpen && (
<>
<motion.div
className="modal-backdrop"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
onClick={onClose}
/>
<motion.div
className="modal"
initial={{ opacity: 0, scale: 0.8, y: 50 }}
animate={{ opacity: 1, scale: 1, y: 0 }}
exit={{ opacity: 0, scale: 0.8, y: 50 }}
transition={{ type: "spring", stiffness: 300, damping: 25 }}
>
{children}
</motion.div>
</>
)}
</AnimatePresence>
);
}
```
**Accessibility**: Trap focus, close on Escape key, focus first element.
---
## Toast & Notification Interactions
### 10.1 Toast Notification
**Effect**: Toast slides in from top/bottom with auto-dismiss.
**Framer Motion**:
```jsx
function Toast({ message, type, onDismiss }) {
useEffect(() => {
const timer = setTimeout(onDismiss, 3000);
return () => clearTimeout(timer);
}, [onDismiss]);
return (
<motion.div
className={`toast toast-${type}`}
initial={{ opacity: 0, y: -50, scale: 0.8 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: -50, scale: 0.8 }}
transition={{ type: "spring", stiffness: 400, damping: 25 }}
>
{message}
<button onClick={onDismiss}>×</button>
</motion.div>
);
}
```
---
## Accessibility Checklist
For all micro-interactions:
- [ ] **Respect `prefers-reduced-motion`**: Disable or simplify animations
- [ ] **Keyboard accessible**: All interactions work without mouse
- [ ] **Focus indicators**: Visible focus states
- [ ] **Screen reader friendly**: Announce state changes with `aria-live`
- [ ] **Touch targets**: Minimum 44x44px for mobile
- [ ] **Escape key**: Close modals/overlays
- [ ] **Loading states**: Indicate when actions are processing
- [ ] **Error states**: Clear error messages, not just visual indicators
---
## Performance Checklist
- [ ] **GPU-accelerated properties**: Use `transform` and `opacity` only
- [ ] **Avoid layout thrash**: Batch DOM reads/writes
- [ ] **RequestAnimationFrame**: For JavaScript animations
- [ ] **Will-change sparingly**: Only during active animations
- [ ] **Debounce scroll/resize**: Avoid excessive repaints
- [ ] **Lazy load**: Defer non-critical animations
- [ ] **Test on low-end devices**: Ensure 60 FPS
---
*This catalog is a living document. Patterns are updated based on modern web standards and user research.*
references/performance_checklist.md
# Web Performance Optimization Checklist
## Overview
Comprehensive performance optimization guide targeting Core Web Vitals and 60 FPS experiences. This checklist covers measurement, optimization strategies, and modern best practices.
**Performance Budget Targets**:
- Largest Contentful Paint (LCP): < 2.5s
- First Input Delay (FID): < 100ms
- Cumulative Layout Shift (CLS): < 0.1
- Interaction to Next Paint (INP): < 200ms
- Time to Interactive (TTI): < 3.8s
- First Contentful Paint (FCP): < 1.8s
---
## Table of Contents
1. [Core Web Vitals](#core-web-vitals)
2. [Animation Performance](#animation-performance)
3. [Image Optimization](#image-optimization)
4. [Font Loading](#font-loading)
5. [JavaScript Optimization](#javascript-optimization)
6. [CSS Optimization](#css-optimization)
7. [3D & WebGL Optimization](#3d--webgl-optimization)
8. [Loading Strategies](#loading-strategies)
9. [Caching & CDN](#caching--cdn)
10. [Monitoring & Measurement](#monitoring--measurement)
---
## Core Web Vitals
### Largest Contentful Paint (LCP)
**Target**: < 2.5 seconds
**What it measures**: Time until largest content element is visible.
**Optimization Checklist**:
- [ ] Optimize hero images (WebP/AVIF, < 100KB)
- [ ] Preload critical resources (`<link rel="preload">`)
- [ ] Eliminate render-blocking resources
- [ ] Use CDN for static assets
- [ ] Implement efficient server response time (< 200ms TTFB)
- [ ] Remove unused CSS/JS
- [ ] Enable compression (Brotli/gzip)
**Implementation**:
```html
<!-- Preload critical resources -->
<link rel="preload" as="image" href="hero.webp" type="image/webp">
<link rel="preload" as="font" href="inter-var.woff2" type="font/woff2" crossorigin>
<!-- Modern image formats -->
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="Hero" loading="eager" fetchpriority="high">
</picture>
```
**Debugging**:
```javascript
// Measure LCP
new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
console.log('LCP:', lastEntry.renderTime || lastEntry.loadTime);
}).observe({ entryTypes: ['largest-contentful-paint'] });
```
---
### First Input Delay (FID) / Interaction to Next Paint (INP)
**Target**: FID < 100ms, INP < 200ms
**What it measures**: Responsiveness to user interactions.
**Optimization Checklist**:
- [ ] Split long tasks (< 50ms each)
- [ ] Defer non-critical JavaScript
- [ ] Use web workers for heavy computation
- [ ] Implement code splitting
- [ ] Avoid long-running event handlers
- [ ] Debounce scroll/resize listeners
- [ ] Use passive event listeners
**Implementation**:
```javascript
// Split long tasks
function yieldToMain() {
return new Promise(resolve => {
setTimeout(resolve, 0);
});
}
async function processLargeArray(array) {
for (let i = 0; i < array.length; i++) {
processItem(array[i]);
// Yield every 50 items
if (i % 50 === 0) {
await yieldToMain();
}
}
}
// Passive event listeners
window.addEventListener('scroll', handleScroll, { passive: true });
// Debounced resize handler
const debouncedResize = debounce(() => {
handleResize();
}, 150);
window.addEventListener('resize', debouncedResize);
```
---
### Cumulative Layout Shift (CLS)
**Target**: < 0.1
**What it measures**: Visual stability during page load.
**Optimization Checklist**:
- [ ] Reserve space for images (use `aspect-ratio`)
- [ ] Reserve space for ads and embeds
- [ ] Avoid inserting content above existing content
- [ ] Use CSS transforms instead of position changes
- [ ] Set explicit dimensions for iframes
- [ ] Avoid web fonts that cause FOUT/FOIT
- [ ] Use `font-display: swap` or `font-display: optional`
**Implementation**:
```css
/* Reserve space for images */
img {
aspect-ratio: attr(width) / attr(height);
width: 100%;
height: auto;
}
/* Or explicit aspect ratio */
.hero-image {
aspect-ratio: 16 / 9;
}
/* Animations: use transform, not position */
.animated {
transform: translateX(0);
transition: transform 0.3s;
}
.animated.active {
transform: translateX(100px); /* Good */
/* left: 100px; Bad - causes layout shift */
}
```
**Debugging**:
```javascript
// Measure CLS
let clsValue = 0;
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (!entry.hadRecentInput) {
clsValue += entry.value;
console.log('CLS:', clsValue);
}
}
}).observe({ entryTypes: ['layout-shift'] });
```
---
## Animation Performance
### 60 FPS Checklist
**Target**: < 16.67ms per frame
**GPU-Accelerated Properties (Use These)**:
- `transform` (translate, scale, rotate)
- `opacity`
- `filter` (some, like blur - be careful)
**Avoid Animating**:
- `top`, `left`, `right`, `bottom`
- `width`, `height`
- `margin`, `padding`
- `border-width`
**Implementation**:
```css
/* Good: GPU-accelerated */
.animated {
transform: translateX(0);
opacity: 1;
transition: transform 0.3s, opacity 0.3s;
}
.animated.active {
transform: translateX(100px) scale(1.1);
opacity: 0.8;
}
/* Bad: triggers layout/paint */
.animated-bad {
left: 0;
width: 100px;
transition: left 0.3s, width 0.3s;
}
```
### Will-Change Optimization
**Use Sparingly**:
```css
/* Only during animation */
.will-animate {
/* Don't set will-change here */
}
.will-animate.animating {
will-change: transform; /* Set on animation start */
}
.will-animate.done {
will-change: auto; /* Remove after animation */
}
```
**JavaScript Control**:
```javascript
element.addEventListener('mouseenter', () => {
element.style.willChange = 'transform';
});
element.addEventListener('mouseleave', () => {
// Remove after animation completes
element.addEventListener('transitionend', () => {
element.style.willChange = 'auto';
}, { once: true });
});
```
### RequestAnimationFrame
**Always use for JavaScript animations**:
```javascript
// Good
function animate() {
element.style.transform = `translateX(${x}px)`;
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
// Bad
setInterval(() => {
element.style.transform = `translateX(${x}px)`;
}, 16); // Not synced with browser paint
```
### Performance Monitoring
**Chrome DevTools**:
```javascript
// Mark start of expensive operation
performance.mark('animation-start');
// ... do animation work ...
// Mark end
performance.mark('animation-end');
// Measure duration
performance.measure('animation-duration', 'animation-start', 'animation-end');
// Get measurement
const measure = performance.getEntriesByName('animation-duration')[0];
console.log(`Animation took ${measure.duration}ms`);
```
---
## Image Optimization
### Format Selection
**Decision Tree**:
1. **Photos**: AVIF > WebP > JPEG
2. **Graphics/logos**: SVG > WebP > PNG
3. **Animations**: WebP animated > GIF
4. **Icons**: SVG or icon fonts
**Implementation**:
```html
<picture>
<!-- Modern browsers: AVIF (best compression) -->
<source srcset="image.avif" type="image/avif">
<!-- Fallback: WebP (good compression) -->
<source srcset="image.webp" type="image/webp">
<!-- Final fallback: JPEG -->
<img src="image.jpg" alt="Description" loading="lazy">
</picture>
```
### Responsive Images
**Checklist**:
- [ ] Use `srcset` for multiple resolutions
- [ ] Define `sizes` attribute
- [ ] Serve appropriately sized images (not full-res everywhere)
- [ ] Use lazy loading for below-fold images
- [ ] Set explicit dimensions to prevent CLS
**Implementation**:
```html
<img
src="image-800.jpg"
srcset="
image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w,
image-1600.jpg 1600w
"
sizes="
(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw
"
alt="Description"
loading="lazy"
width="1200"
height="800"
>
```
### Image Compression
**Tools**:
- **CLI**: ImageMagick, Sharp (Node.js)
- **GUI**: Squoosh (web), ImageOptim (Mac)
- **Build tools**: imagemin, @squoosh/lib
**Example (Sharp)**:
```javascript
const sharp = require('sharp');
await sharp('input.jpg')
.resize(1200, 800, { fit: 'cover' })
.webp({ quality: 80 })
.toFile('output.webp');
await sharp('input.jpg')
.resize(1200, 800, { fit: 'cover' })
.avif({ quality: 70 })
.toFile('output.avif');
```
### Lazy Loading
**Native Lazy Loading**:
```html
<!-- Lazy load below-fold images -->
<img src="image.jpg" loading="lazy" alt="Description">
<!-- Eager load above-fold images -->
<img src="hero.jpg" loading="eager" fetchpriority="high" alt="Hero">
```
**Intersection Observer (Advanced)**:
```javascript
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
```
---
## Font Loading
### Font Display Strategy
**Options**:
- `swap`: Show fallback, swap when loaded (FOUT - Flash of Unstyled Text)
- `optional`: Use cached font if available, otherwise use fallback
- `fallback`: Brief block, swap if fast, fallback if slow
**Recommended**:
```css
@font-face {
font-family: 'Inter';
src: url('/fonts/inter-var.woff2') format('woff2-variations');
font-weight: 100 900;
font-display: swap; /* Show fallback immediately */
}
```
### Font Subsetting
**Reduce file size by including only needed characters**:
**Tools**: `pyftsubset` (part of fonttools)
```bash
# Include only Latin characters
pyftsubset input.ttf \
--output-file=output.woff2 \
--flavor=woff2 \
--layout-features=* \
--unicodes=U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD
```
### Preload Critical Fonts
```html
<link
rel="preload"
as="font"
href="/fonts/inter-var.woff2"
type="font/woff2"
crossorigin
>
```
### System Font Stack
**Performance-first approach**:
```css
body {
font-family:
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
sans-serif;
}
```
---
## JavaScript Optimization
### Code Splitting
**Route-based splitting**:
```javascript
// React Router
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Gallery = lazy(() => import('./pages/Gallery'));
<Suspense fallback={<Loading />}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/gallery" element={<Gallery />} />
</Routes>
</Suspense>
```
**Component-based splitting**:
```javascript
// Load heavy components only when needed
const HeavyChart = lazy(() => import('./components/HeavyChart'));
function Dashboard() {
const [showChart, setShowChart] = useState(false);
return (
<div>
<button onClick={() => setShowChart(true)}>Show Chart</button>
{showChart && (
<Suspense fallback={<Skeleton />}>
<HeavyChart />
</Suspense>
)}
</div>
);
}
```
### Tree Shaking
**Ensure tree shaking works**:
```javascript
// Good: Named imports
import { map, filter } from 'lodash-es';
// Bad: Default import (imports entire library)
import _ from 'lodash';
```
### Bundle Analysis
**Webpack Bundle Analyzer**:
```bash
npm install --save-dev webpack-bundle-analyzer
# Add to webpack.config.js
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
plugins: [
new BundleAnalyzerPlugin()
]
```
**Vite**:
```bash
npm install --save-dev rollup-plugin-visualizer
# vite.config.js
import { visualizer } from 'rollup-plugin-visualizer';
export default {
plugins: [visualizer({ open: true })]
}
```
---
## CSS Optimization
### Critical CSS
**Inline above-the-fold CSS**:
```html
<style>
/* Critical CSS inlined */
body { font-family: sans-serif; }
.hero { height: 100vh; }
</style>
<!-- Defer non-critical CSS -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="styles.css"></noscript>
```
**Tools**:
- Critical (npm package)
- Critters (Vite/Webpack plugin)
### Remove Unused CSS
**PurgeCSS**:
```javascript
// postcss.config.js
module.exports = {
plugins: [
require('@fullhuman/postcss-purgecss')({
content: ['./src/**/*.html', './src/**/*.jsx'],
defaultExtractor: content => content.match(/[\w-/:]+(?<!:)/g) || []
})
]
}
```
### CSS Containment
**Improve rendering performance**:
```css
.card {
/* Isolate layout calculations */
contain: layout style paint;
}
.article {
/* More aggressive containment */
contain: strict;
}
```
---
## 3D & WebGL Optimization
### Loading Strategy
**Checklist**:
- [ ] Show placeholder while loading
- [ ] Load 3D content below fold lazily
- [ ] Use low-poly models initially
- [ ] Progressive enhancement with high-poly
**Implementation**:
```javascript
// Lazy load Three.js scene
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadThreeJSScene();
observer.unobserve(entry.target);
}
});
});
observer.observe(document.querySelector('.3d-container'));
```
### Runtime Performance
**Checklist**:
- [ ] Implement object pooling (reuse objects)
- [ ] Use LOD (Level of Detail)
- [ ] Frustum culling (don't render off-screen objects)
- [ ] Texture compression (Basis Universal, KTX2)
- [ ] Reduce polygon count (< 100k for real-time)
- [ ] Limit draw calls (< 100 per frame)
- [ ] Use instancing for repeated geometry
**Three.js Example**:
```javascript
// Object pooling
const objectPool = [];
function getObject() {
return objectPool.length > 0 ? objectPool.pop() : createNewObject();
}
function releaseObject(obj) {
objectPool.push(obj);
}
// LOD (Level of Detail)
const lod = new THREE.LOD();
lod.addLevel(highPolyMesh, 0); // 0-50m
lod.addLevel(mediumPolyMesh, 50); // 50-100m
lod.addLevel(lowPolyMesh, 100); // 100m+
scene.add(lod);
// Instancing
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshStandardMaterial();
const count = 10000;
const mesh = new THREE.InstancedMesh(geometry, material, count);
scene.add(mesh);
```
### Texture Optimization
**Checklist**:
- [ ] Use power-of-two dimensions (512, 1024, 2048)
- [ ] Compress textures (Basis, KTX2)
- [ ] Use texture atlases
- [ ] Reduce texture resolution (don't use 4K everywhere)
- [ ] Enable mipmaps
---
## Loading Strategies
### Critical Rendering Path
**Optimize the order**:
1. **HTML** (initial)
2. **Critical CSS** (inlined)
3. **Critical JavaScript** (defer rest)
4. **Fonts** (preload, font-display: swap)
5. **Images** (lazy load below-fold)
**Implementation**:
```html
<!DOCTYPE html>
<html>
<head>
<!-- Critical CSS inlined -->
<style>/* Critical styles */</style>
<!-- Preload critical resources -->
<link rel="preload" as="font" href="font.woff2" type="font/woff2" crossorigin>
<link rel="preload" as="image" href="hero.webp">
<!-- Defer non-critical CSS -->
<link rel="preload" href="styles.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<!-- Defer JavaScript -->
<script defer src="main.js"></script>
</head>
<body>
<!-- Content -->
</body>
</html>
```
### Resource Hints
**Preconnect** (DNS, TCP, TLS):
```html
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">
```
**DNS-Prefetch**:
```html
<link rel="dns-prefetch" href="https://analytics.example.com">
```
**Prefetch** (next page):
```html
<link rel="prefetch" href="/next-page.html">
```
---
## Caching & CDN
### HTTP Caching Headers
**Static Assets** (immutable):
```
Cache-Control: public, max-age=31536000, immutable
```
**HTML** (revalidate):
```
Cache-Control: no-cache
```
**API Responses**:
```
Cache-Control: public, max-age=3600, must-revalidate
```
### Service Worker Caching
**Workbox**:
```javascript
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst, NetworkFirst } from 'workbox-strategies';
// Precache build assets
precacheAndRoute(self.__WB_MANIFEST);
// Cache images
registerRoute(
({ request }) => request.destination === 'image',
new CacheFirst({ cacheName: 'images' })
);
// Network-first for HTML
registerRoute(
({ request }) => request.mode === 'navigate',
new NetworkFirst({ cacheName: 'pages' })
);
```
---
## Monitoring & Measurement
### Real User Monitoring (RUM)
**Web Vitals**:
```bash
npm install web-vitals
```
```javascript
import { getCLS, getFID, getFCP, getLCP, getTTFB } from 'web-vitals';
function sendToAnalytics({ name, value, id }) {
// Send to your analytics endpoint
fetch('/analytics', {
method: 'POST',
body: JSON.stringify({ name, value, id })
});
}
getCLS(sendToAnalytics);
getFID(sendToAnalytics);
getFCP(sendToAnalytics);
getLCP(sendToAnalytics);
getTTFB(sendToAnalytics);
```
### Performance Budget
**Lighthouse CI**:
```json
// lighthouserc.json
{
"ci": {
"collect": {
"numberOfRuns": 3
},
"assert": {
"assertions": {
"first-contentful-paint": ["error", {"maxNumericValue": 1800}],
"largest-contentful-paint": ["error", {"maxNumericValue": 2500}],
"cumulative-layout-shift": ["error", {"maxNumericValue": 0.1}],
"total-blocking-time": ["error", {"maxNumericValue": 200}]
}
}
}
}
```
### Chrome DevTools
**Performance Panel**:
1. Open DevTools (F12)
2. Performance tab
3. Click Record
4. Interact with page
5. Stop recording
6. Analyze flame chart
**Look for**:
- Long tasks (> 50ms)
- Excessive layout thrashing
- Forced synchronous layouts
- Paint flashing
---
## Quick Wins Checklist
**Immediate Impact**:
- [ ] Enable Brotli/gzip compression
- [ ] Implement lazy loading for images
- [ ] Add `width` and `height` to images (prevent CLS)
- [ ] Use modern image formats (WebP/AVIF)
- [ ] Preload critical resources
- [ ] Defer non-critical JavaScript
- [ ] Use CDN for static assets
- [ ] Enable HTTP/2 or HTTP/3
- [ ] Minify CSS and JavaScript
- [ ] Remove unused CSS/JS
**High Impact**:
- [ ] Implement code splitting
- [ ] Optimize images (< 100KB)
- [ ] Use system fonts or preload web fonts
- [ ] Inline critical CSS
- [ ] Implement service worker caching
- [ ] Optimize animation performance (use transforms)
- [ ] Reduce JavaScript bundle size (< 200KB)
- [ ] Set up performance monitoring
---
*Last updated: 2024*
*Benchmarks based on median 4G mobile connections*
scripts/design_audit.py
#!/usr/bin/env python3
"""
Modern Web Design Auditor
Audits HTML/CSS code for:
- Accessibility compliance (WCAG)
- Performance best practices
- Modern design pattern usage
- SEO basics
Usage:
./design_audit.py # Interactive mode
./design_audit.py --file index.html # Audit specific file
./design_audit.py --file index.html --report audit.txt # Save report
"""
import sys
import argparse
import re
from pathlib import Path
from collections import defaultdict
class DesignAuditor:
"""Audits web design code for best practices."""
def __init__(self, html_content):
self.html = html_content
self.issues = defaultdict(list)
self.warnings = defaultdict(list)
self.passes = defaultdict(list)
def audit(self):
"""Run all audits."""
self.audit_accessibility()
self.audit_performance()
self.audit_seo()
self.audit_responsive()
self.audit_modern_practices()
def audit_accessibility(self):
"""Audit for accessibility issues."""
category = 'Accessibility'
# Check for alt attributes on images
imgs_without_alt = re.findall(r'<img(?![^>]*alt=)[^>]*>', self.html, re.IGNORECASE)
if imgs_without_alt:
self.issues[category].append(
f"Found {len(imgs_without_alt)} images without alt attributes"
)
else:
self.passes[category].append("All images have alt attributes")
# Check for lang attribute
if not re.search(r'<html[^>]+lang=', self.html, re.IGNORECASE):
self.issues[category].append("Missing lang attribute on <html> element")
else:
self.passes[category].append("HTML lang attribute present")
# Check for semantic HTML
if '<div class="header"' in self.html or '<div id="header"' in self.html:
self.warnings[category].append("Consider using <header> instead of div with class/id 'header'")
if '<div class="nav"' in self.html or '<div id="nav"' in self.html:
self.warnings[category].append("Consider using <nav> instead of div with class/id 'nav'")
if '<div class="footer"' in self.html or '<div id="footer"' in self.html:
self.warnings[category].append("Consider using <footer> instead of div with class/id 'footer'")
# Check for proper heading hierarchy
h1_count = len(re.findall(r'<h1[^>]*>', self.html, re.IGNORECASE))
if h1_count == 0:
self.issues[category].append("No <h1> element found")
elif h1_count > 1:
self.warnings[category].append(f"Multiple <h1> elements found ({h1_count}). Consider using only one per page")
# Check for aria-labels on icon buttons
button_pattern = r'<button[^>]*>[\s]*<(?:svg|i|span)[^>]*>[\s]*</(?:svg|i|span)>[\s]*</button>'
icon_buttons = re.findall(button_pattern, self.html, re.IGNORECASE)
for button in icon_buttons:
if 'aria-label' not in button:
self.warnings[category].append("Icon button found without aria-label")
break
# Check for form labels
inputs = re.findall(r'<input[^>]*>', self.html, re.IGNORECASE)
for input_tag in inputs:
if 'id=' in input_tag:
input_id = re.search(r'id="([^"]+)"', input_tag)
if input_id:
label_pattern = f'for="{input_id.group(1)}"'
if label_pattern not in self.html:
self.warnings[category].append(f"Input with id='{input_id.group(1)}' has no associated label")
# Check for skip links
if not re.search(r'skip to (main |)content', self.html, re.IGNORECASE):
self.warnings[category].append("Consider adding a skip link for keyboard navigation")
def audit_performance(self):
"""Audit for performance issues."""
category = 'Performance'
# Check for inline styles (should be minimal)
inline_styles = re.findall(r'style="[^"]*"', self.html)
if len(inline_styles) > 10:
self.warnings[category].append(
f"Found {len(inline_styles)} inline styles. Consider moving to external CSS."
)
# Check for lazy loading
imgs = re.findall(r'<img[^>]*>', self.html, re.IGNORECASE)
lazy_imgs = [img for img in imgs if 'loading="lazy"' in img or "loading='lazy'" in img]
if imgs and not lazy_imgs:
self.warnings[category].append(
"Consider adding loading='lazy' to below-fold images"
)
# Check for modern image formats
if re.search(r'<img[^>]+src="[^"]+\.(jpg|jpeg|png)"', self.html, re.IGNORECASE):
if not re.search(r'<picture>|<source[^>]+type="image/webp"', self.html, re.IGNORECASE):
self.warnings[category].append(
"Consider using modern image formats (WebP/AVIF) with <picture> element"
)
# Check for preload/prefetch
if re.search(r'<link[^>]+rel="preload"', self.html, re.IGNORECASE):
self.passes[category].append("Using preload for critical resources")
# Check for async/defer on scripts
scripts = re.findall(r'<script[^>]*src="[^"]*"[^>]*>', self.html, re.IGNORECASE)
scripts_without_async_defer = [s for s in scripts if 'async' not in s and 'defer' not in s]
if scripts_without_async_defer:
self.warnings[category].append(
f"Found {len(scripts_without_async_defer)} script tags without async or defer attributes"
)
# Check for width/height on images (prevents CLS)
imgs_without_dimensions = [
img for img in imgs
if not ('width=' in img and 'height=' in img)
and 'aspect-ratio' not in self.html
]
if imgs_without_dimensions:
self.warnings[category].append(
f"Found {len(imgs_without_dimensions)} images without explicit dimensions (width/height). This can cause Cumulative Layout Shift."
)
def audit_seo(self):
"""Audit for SEO basics."""
category = 'SEO'
# Check for title tag
if not re.search(r'<title>[^<]+</title>', self.html, re.IGNORECASE):
self.issues[category].append("Missing <title> tag")
else:
title = re.search(r'<title>([^<]+)</title>', self.html, re.IGNORECASE)
if title and len(title.group(1)) < 10:
self.warnings[category].append("Title tag is too short (< 10 characters)")
else:
self.passes[category].append("Title tag present and sufficient length")
# Check for meta description
if not re.search(r'<meta[^>]+name="description"', self.html, re.IGNORECASE):
self.warnings[category].append("Missing meta description tag")
else:
self.passes[category].append("Meta description present")
# Check for viewport meta tag
if not re.search(r'<meta[^>]+name="viewport"', self.html, re.IGNORECASE):
self.issues[category].append("Missing viewport meta tag (required for mobile)")
else:
self.passes[category].append("Viewport meta tag present")
# Check for charset
if not re.search(r'<meta[^>]+charset=', self.html, re.IGNORECASE):
self.warnings[category].append("Missing charset declaration")
else:
self.passes[category].append("Charset declaration present")
def audit_responsive(self):
"""Audit for responsive design."""
category = 'Responsive Design'
# Check for viewport meta tag
if re.search(r'<meta[^>]+name="viewport"', self.html, re.IGNORECASE):
self.passes[category].append("Viewport meta tag present")
else:
self.issues[category].append("Missing viewport meta tag")
# Check for media queries in CSS
if re.search(r'@media[^{]+{', self.html, re.IGNORECASE):
self.passes[category].append("Media queries found (responsive CSS)")
else:
self.warnings[category].append("No media queries found. Consider adding responsive styles.")
# Check for clamp() or min/max functions (modern responsive approach)
if re.search(r'clamp\(|min\(|max\(', self.html, re.IGNORECASE):
self.passes[category].append("Using modern CSS functions (clamp/min/max) for fluid sizing")
def audit_modern_practices(self):
"""Audit for modern design practices."""
category = 'Modern Practices'
# Check for CSS custom properties
if re.search(r'--[a-z-]+:', self.html, re.IGNORECASE):
self.passes[category].append("Using CSS custom properties (variables)")
# Check for CSS Grid or Flexbox
if re.search(r'display:\s*(grid|flex)', self.html, re.IGNORECASE):
self.passes[category].append("Using modern layout (Grid or Flexbox)")
# Check for prefers-reduced-motion
if re.search(r'prefers-reduced-motion', self.html, re.IGNORECASE):
self.passes[category].append("Respecting prefers-reduced-motion preference")
else:
if re.search(r'animation:|transition:', self.html, re.IGNORECASE):
self.warnings[category].append(
"Animations found but no @media (prefers-reduced-motion) rule detected"
)
# Check for semantic HTML5 elements
semantic_elements = ['header', 'nav', 'main', 'article', 'section', 'aside', 'footer']
found_semantic = [el for el in semantic_elements if f'<{el}' in self.html.lower()]
if found_semantic:
self.passes[category].append(f"Using semantic HTML5 elements: {', '.join(found_semantic)}")
else:
self.warnings[category].append("No semantic HTML5 elements found. Consider using <header>, <nav>, <main>, etc.")
# Check for focus-visible
if ':focus-visible' in self.html:
self.passes[category].append("Using :focus-visible for keyboard navigation")
elif ':focus' in self.html:
self.passes[category].append("Using :focus styles (consider upgrading to :focus-visible)")
def generate_report(self):
"""Generate audit report."""
report = []
report.append("=" * 70)
report.append("Modern Web Design Audit Report")
report.append("=" * 70)
report.append("")
# Summary
total_issues = sum(len(v) for v in self.issues.values())
total_warnings = sum(len(v) for v in self.warnings.values())
total_passes = sum(len(v) for v in self.passes.values())
report.append("SUMMARY")
report.append("-" * 70)
report.append(f"❌ Issues: {total_issues}")
report.append(f"⚠️ Warnings: {total_warnings}")
report.append(f"✅ Passes: {total_passes}")
report.append("")
# Score calculation
score = (total_passes / (total_passes + total_warnings + total_issues)) * 100 if (total_passes + total_warnings + total_issues) > 0 else 0
report.append(f"Overall Score: {score:.1f}/100")
report.append("")
# Detailed results
categories = set(list(self.issues.keys()) + list(self.warnings.keys()) + list(self.passes.keys()))
for category in sorted(categories):
report.append(f"\n{category}")
report.append("-" * 70)
if category in self.issues:
report.append("\n❌ ISSUES (must fix):")
for issue in self.issues[category]:
report.append(f" • {issue}")
if category in self.warnings:
report.append("\n⚠️ WARNINGS (should fix):")
for warning in self.warnings[category]:
report.append(f" • {warning}")
if category in self.passes:
report.append("\n✅ PASSES:")
for passing in self.passes[category]:
report.append(f" • {passing}")
report.append("")
# Recommendations
report.append("\nRECOMMENDATIONS")
report.append("-" * 70)
if total_issues > 0:
report.append("1. Address all ISSUES first (accessibility and critical SEO)")
if total_warnings > 5:
report.append("2. Review WARNINGS and implement fixes where applicable")
if score < 70:
report.append("3. Consider a comprehensive design review")
else:
report.append("Good job! Your design follows many modern best practices.")
report.append("")
report.append("=" * 70)
return "\n".join(report)
def audit_file(filepath, output_file=None):
"""Audit a specific file."""
path = Path(filepath)
if not path.exists():
print(f"Error: File '{filepath}' not found.")
sys.exit(1)
if not path.suffix in ['.html', '.htm']:
print(f"Warning: File '{filepath}' is not an HTML file.")
print(f"Auditing: {path}")
print("Please wait...\n")
html_content = path.read_text()
auditor = DesignAuditor(html_content)
auditor.audit()
report = auditor.generate_report()
if output_file:
output_path = Path(output_file)
output_path.write_text(report)
print(f"✅ Audit complete!")
print(f" Report saved to: {output_path}")
else:
print(report)
def interactive_mode():
"""Interactive audit mode."""
print("\n" + "="*70)
print("Modern Web Design Auditor")
print("="*70)
print("\nThis tool audits HTML files for:")
print(" • Accessibility (WCAG compliance)")
print(" • Performance best practices")
print(" • SEO basics")
print(" • Responsive design")
print(" • Modern CSS/HTML practices")
print("")
filepath = input("Enter path to HTML file: ").strip()
if not filepath:
print("Error: No file path provided.")
sys.exit(1)
save_report = input("\nSave report to file? (y/n): ").strip().lower()
if save_report == 'y':
output_file = input("Enter output filename (e.g., audit-report.txt): ").strip()
if not output_file:
output_file = "audit-report.txt"
audit_file(filepath, output_file)
else:
audit_file(filepath)
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description='Audit web design code for best practices'
)
parser.add_argument(
'--file',
type=str,
help='HTML file to audit'
)
parser.add_argument(
'--report',
type=str,
help='Output file for audit report'
)
args = parser.parse_args()
if args.file:
audit_file(args.file, args.report)
else:
interactive_mode()
if __name__ == '__main__':
main()
scripts/pattern_generator.py
#!/usr/bin/env python3
"""
Modern Web Design Pattern Generator
Generates boilerplate code for common modern web design patterns including:
- Hero sections
- Card layouts
- Navigation patterns
- Form patterns
- Animation patterns
Usage:
./pattern_generator.py # Interactive mode
./pattern_generator.py --pattern hero # Generate specific pattern
./pattern_generator.py --pattern card --output card.html # Output to file
"""
import sys
import argparse
from pathlib import Path
# Pattern templates
PATTERNS = {
'hero': {
'name': 'Immersive Hero Section',
'description': 'Full-viewport hero with animated background',
'html': '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hero Section</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.hero {
position: relative;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.hero__bg {
position: absolute;
inset: 0;
opacity: 0.3;
}
.hero__content {
position: relative;
z-index: 2;
text-align: center;
color: white;
max-width: 800px;
padding: 2rem;
}
.hero__title {
font-size: clamp(3rem, 8vw, 6rem);
font-weight: 700;
line-height: 0.95;
letter-spacing: -0.03em;
margin-bottom: 1.5rem;
animation: fadeInUp 0.8s ease;
}
.hero__subtitle {
font-size: clamp(1.25rem, 2.5vw, 1.75rem);
margin-bottom: 2rem;
opacity: 0.9;
animation: fadeInUp 0.8s ease 0.2s backwards;
}
.hero__cta {
display: inline-block;
padding: 1rem 2.5rem;
background: white;
color: #667eea;
text-decoration: none;
border-radius: 9999px;
font-weight: 600;
font-size: 1.125rem;
transition: transform 0.2s, box-shadow 0.2s;
animation: fadeInUp 0.8s ease 0.4s backwards;
}
.hero__cta:hover {
transform: translateY(-2px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
}
.scroll-indicator {
position: absolute;
bottom: 2rem;
left: 50%;
transform: translateX(-50%);
color: white;
opacity: 0.7;
animation: bounce 2s infinite;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes bounce {
0%, 100% { transform: translateX(-50%) translateY(0); }
50% { transform: translateX(-50%) translateY(-10px); }
}
/* Respect reduced motion preference */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<section class="hero">
<div class="hero__bg" id="bg"></div>
<div class="hero__content">
<h1 class="hero__title">Modern Design</h1>
<p class="hero__subtitle">Performance meets beauty in every pixel</p>
<a href="#content" class="hero__cta">Explore</a>
</div>
<div class="scroll-indicator">
<span>↓ Scroll</span>
</div>
</section>
<section id="content" style="padding: 4rem 2rem; text-align: center;">
<h2>Your content here</h2>
</section>
</body>
</html>'''
},
'card': {
'name': 'Interactive Card Grid',
'description': 'Responsive card grid with hover effects',
'html': '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Card Grid</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f5f7fa;
padding: 4rem 2rem;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.card-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 2rem;
}
.card {
background: white;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.15);
}
.card__image {
width: 100%;
aspect-ratio: 16 / 9;
object-fit: cover;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.card__content {
padding: 1.5rem;
}
.card__title {
font-size: 1.5rem;
font-weight: 600;
margin-bottom: 0.75rem;
color: #1a1a1a;
}
.card__description {
color: #666;
line-height: 1.6;
margin-bottom: 1rem;
}
.card__link {
display: inline-flex;
align-items: center;
color: #667eea;
text-decoration: none;
font-weight: 500;
transition: transform 0.2s;
}
.card__link:hover {
transform: translateX(4px);
}
/* Accessibility */
.card:focus-within {
outline: 3px solid #667eea;
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<div class="container">
<h1 style="margin-bottom: 3rem; font-size: 2.5rem;">Featured Content</h1>
<div class="card-grid">
<article class="card">
<img class="card__image" src="" alt="Card image" aria-hidden="true">
<div class="card__content">
<h2 class="card__title">Card Title 1</h2>
<p class="card__description">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore.
</p>
<a href="#" class="card__link">
Learn more →
</a>
</div>
</article>
<article class="card">
<img class="card__image" src="" alt="Card image" aria-hidden="true">
<div class="card__content">
<h2 class="card__title">Card Title 2</h2>
<p class="card__description">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore.
</p>
<a href="#" class="card__link">
Learn more →
</a>
</div>
</article>
<article class="card">
<img class="card__image" src="" alt="Card image" aria-hidden="true">
<div class="card__content">
<h2 class="card__title">Card Title 3</h2>
<p class="card__description">
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed do eiusmod tempor incididunt ut labore.
</p>
<a href="#" class="card__link">
Learn more →
</a>
</div>
</article>
</div>
</div>
</body>
</html>'''
},
'navigation': {
'name': 'Responsive Navigation',
'description': 'Mobile-friendly navigation with hamburger menu',
'html': '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Navigation</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
}
.header {
position: fixed;
top: 0;
left: 0;
right: 0;
background: white;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 100;
}
.nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
max-width: 1200px;
margin: 0 auto;
}
.nav__logo {
font-size: 1.5rem;
font-weight: 700;
color: #667eea;
text-decoration: none;
}
.nav__toggle {
display: none;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
}
.nav__toggle span {
display: block;
width: 25px;
height: 3px;
background: #333;
margin: 5px 0;
transition: transform 0.3s, opacity 0.3s;
}
.nav__toggle.active span:nth-child(1) {
transform: translateY(8px) rotate(45deg);
}
.nav__toggle.active span:nth-child(2) {
opacity: 0;
}
.nav__toggle.active span:nth-child(3) {
transform: translateY(-8px) rotate(-45deg);
}
.nav__menu {
display: flex;
list-style: none;
gap: 2rem;
}
.nav__link {
color: #333;
text-decoration: none;
font-weight: 500;
transition: color 0.2s;
}
.nav__link:hover {
color: #667eea;
}
.nav__link:focus-visible {
outline: 3px solid #667eea;
outline-offset: 4px;
border-radius: 4px;
}
@media (max-width: 768px) {
.nav__toggle {
display: block;
}
.nav__menu {
position: absolute;
top: 100%;
left: 0;
right: 0;
flex-direction: column;
background: white;
padding: 1rem;
gap: 0;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transform: translateY(-100%);
opacity: 0;
pointer-events: none;
transition: transform 0.3s, opacity 0.3s;
}
.nav__menu.active {
transform: translateY(0);
opacity: 1;
pointer-events: all;
}
.nav__menu li {
border-bottom: 1px solid #eee;
}
.nav__menu li:last-child {
border-bottom: none;
}
.nav__link {
display: block;
padding: 1rem;
}
}
.main {
margin-top: 80px;
padding: 4rem 2rem;
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<header class="header">
<nav class="nav" role="navigation" aria-label="Main">
<a href="#" class="nav__logo">Logo</a>
<button class="nav__toggle" aria-label="Toggle navigation" aria-expanded="false">
<span></span>
<span></span>
<span></span>
</button>
<ul class="nav__menu" id="navMenu">
<li><a href="#" class="nav__link">Home</a></li>
<li><a href="#" class="nav__link">About</a></li>
<li><a href="#" class="nav__link">Services</a></li>
<li><a href="#" class="nav__link">Contact</a></li>
</ul>
</nav>
</header>
<main class="main">
<h1>Main Content</h1>
<p>Your content goes here...</p>
</main>
<script>
const toggle = document.querySelector('.nav__toggle');
const menu = document.querySelector('.nav__menu');
toggle.addEventListener('click', () => {
const isExpanded = toggle.getAttribute('aria-expanded') === 'true';
toggle.classList.toggle('active');
menu.classList.toggle('active');
toggle.setAttribute('aria-expanded', !isExpanded);
});
// Close menu on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape' && menu.classList.contains('active')) {
toggle.classList.remove('active');
menu.classList.remove('active');
toggle.setAttribute('aria-expanded', 'false');
toggle.focus();
}
});
</script>
</body>
</html>'''
},
'form': {
'name': 'Accessible Form',
'description': 'Form with validation and accessibility features',
'html': '''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact Form</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #f5f7fa;
padding: 4rem 2rem;
}
.form-container {
max-width: 600px;
margin: 0 auto;
background: white;
padding: 3rem;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
h1 {
margin-bottom: 2rem;
color: #1a1a1a;
}
.form-group {
margin-bottom: 1.5rem;
}
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
color: #333;
}
.required {
color: #ef4444;
margin-left: 0.25rem;
}
input, textarea {
width: 100%;
padding: 0.75rem;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 1rem;
font-family: inherit;
transition: border-color 0.2s;
}
input:focus, textarea:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
}
input[aria-invalid="true"], textarea[aria-invalid="true"] {
border-color: #ef4444;
}
.help-text {
font-size: 0.875rem;
color: #666;
margin-top: 0.25rem;
}
.error-message {
color: #ef4444;
font-size: 0.875rem;
margin-top: 0.25rem;
display: none;
}
.error-message.visible {
display: block;
}
.success-message {
color: #10b981;
font-size: 0.875rem;
margin-top: 0.25rem;
}
button {
background: #667eea;
color: white;
padding: 0.875rem 2rem;
border: none;
border-radius: 6px;
font-size: 1rem;
font-weight: 600;
cursor: pointer;
transition: background 0.2s, transform 0.2s;
min-height: 44px;
}
button:hover {
background: #5568d3;
transform: translateY(-1px);
}
button:active {
transform: translateY(0);
}
button:focus-visible {
outline: 3px solid #667eea;
outline-offset: 2px;
}
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}
</style>
</head>
<body>
<div class="form-container">
<h1>Contact Us</h1>
<form id="contactForm" novalidate>
<div class="form-group">
<label for="name">
Full Name
<abbr class="required" title="required" aria-label="required">*</abbr>
</label>
<input
type="text"
id="name"
name="name"
required
aria-required="true"
aria-describedby="name-error"
>
<div id="name-error" class="error-message" role="alert">
Please enter your name
</div>
</div>
<div class="form-group">
<label for="email">
Email Address
<abbr class="required" title="required" aria-label="required">*</abbr>
</label>
<input
type="email"
id="email"
name="email"
required
aria-required="true"
aria-describedby="email-help email-error"
>
<p id="email-help" class="help-text">
We'll never share your email
</p>
<div id="email-error" class="error-message" role="alert">
Please enter a valid email address
</div>
</div>
<div class="form-group">
<label for="message">
Message
<abbr class="required" title="required" aria-label="required">*</abbr>
</label>
<textarea
id="message"
name="message"
rows="5"
required
aria-required="true"
aria-describedby="message-error"
></textarea>
<div id="message-error" class="error-message" role="alert">
Please enter a message
</div>
</div>
<button type="submit">Send Message</button>
</form>
</div>
<script>
const form = document.getElementById('contactForm');
form.addEventListener('submit', (e) => {
e.preventDefault();
let isValid = true;
// Validate name
const name = document.getElementById('name');
const nameError = document.getElementById('name-error');
if (!name.value.trim()) {
nameError.classList.add('visible');
name.setAttribute('aria-invalid', 'true');
isValid = false;
} else {
nameError.classList.remove('visible');
name.setAttribute('aria-invalid', 'false');
}
// Validate email
const email = document.getElementById('email');
const emailError = document.getElementById('email-error');
const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
if (!emailRegex.test(email.value)) {
emailError.classList.add('visible');
email.setAttribute('aria-invalid', 'true');
isValid = false;
} else {
emailError.classList.remove('visible');
email.setAttribute('aria-invalid', 'false');
}
// Validate message
const message = document.getElementById('message');
const messageError = document.getElementById('message-error');
if (!message.value.trim()) {
messageError.classList.add('visible');
message.setAttribute('aria-invalid', 'true');
isValid = false;
} else {
messageError.classList.remove('visible');
message.setAttribute('aria-invalid', 'false');
}
if (isValid) {
alert('Form submitted successfully!');
form.reset();
} else {
// Focus first invalid field
const firstInvalid = form.querySelector('[aria-invalid="true"]');
if (firstInvalid) firstInvalid.focus();
}
});
// Real-time validation
['name', 'email', 'message'].forEach(id => {
const field = document.getElementById(id);
field.addEventListener('blur', () => {
// Trigger validation on blur
form.dispatchEvent(new Event('submit'));
});
});
</script>
</body>
</html>'''
}
}
def list_patterns():
"""List all available patterns."""
print("\nAvailable patterns:\n")
for key, pattern in PATTERNS.items():
print(f" {key:<15} - {pattern['name']}")
print(f" {'':<15} {pattern['description']}\n")
def generate_pattern(pattern_key, output_file=None):
"""Generate pattern code."""
if pattern_key not in PATTERNS:
print(f"Error: Pattern '{pattern_key}' not found.")
print("\nRun './pattern_generator.py --list' to see available patterns.")
sys.exit(1)
pattern = PATTERNS[pattern_key]
code = pattern['html']
if output_file:
output_path = Path(output_file)
output_path.write_text(code)
print(f"✅ Generated '{pattern['name']}' pattern")
print(f" Saved to: {output_path}")
else:
print(f"\n{'='*60}")
print(f"{pattern['name']}")
print(f"{'='*60}\n")
print(code)
def interactive_mode():
"""Interactive pattern generation."""
print("\n" + "="*60)
print("Modern Web Design Pattern Generator")
print("="*60)
list_patterns()
pattern_key = input("Select a pattern (or 'q' to quit): ").strip().lower()
if pattern_key == 'q':
print("Goodbye!")
sys.exit(0)
if pattern_key not in PATTERNS:
print(f"Error: '{pattern_key}' is not a valid pattern.")
sys.exit(1)
save = input("\nSave to file? (y/n): ").strip().lower()
if save == 'y':
filename = input("Enter filename (e.g., hero.html): ").strip()
if not filename:
filename = f"{pattern_key}.html"
generate_pattern(pattern_key, filename)
else:
generate_pattern(pattern_key)
def main():
"""Main entry point."""
parser = argparse.ArgumentParser(
description='Generate modern web design pattern boilerplate'
)
parser.add_argument(
'--pattern',
type=str,
help='Pattern to generate (hero, card, navigation, form)'
)
parser.add_argument(
'--output',
type=str,
help='Output file path'
)
parser.add_argument(
'--list',
action='store_true',
help='List all available patterns'
)
args = parser.parse_args()
if args.list:
list_patterns()
sys.exit(0)
if args.pattern:
generate_pattern(args.pattern, args.output)
else:
interactive_mode()
if __name__ == '__main__':
main()
SKILL.md
---
name: modern-web-design
description: Modern web design trends, principles, and implementation patterns for 2024-2025. Use this skill when designing websites, creating interactive experiences, implementing design systems, ensuring accessibility, or building performance-first interfaces. Triggers on tasks involving modern design trends, micro-interactions, scrollytelling, bold minimalism, cursor UX, glassmorphism, accessibility compliance, performance optimization, or design system architecture. References animation skills (GSAP, Framer Motion, React Spring), 3D skills (Three.js, R3F, Babylon.js), and component libraries for implementation guidance.
---
# Modern Web Design
## Overview
Modern web design in 2024-2025 emphasizes performance, accessibility, and meaningful interactions. This skill provides comprehensive guidance on current design trends, implementation patterns, and best practices for creating engaging, accessible, and performant web experiences.
This meta-skill synthesizes knowledge from all animation, interaction, and 3D skills in this repository to provide holistic design guidance.
## Core Design Principles (2024-2025)
### 1. Performance-First Design
**Philosophy**: Design decisions should prioritize Core Web Vitals and user experience on all devices.
**Key Metrics**:
- Largest Contentful Paint (LCP): < 2.5s
- First Input Delay (FID): < 100ms
- Cumulative Layout Shift (CLS): < 0.1
- Interaction to Next Paint (INP): < 200ms
**Implementation Guidelines**:
- Defer non-critical animations until after page load
- Use CSS transforms/opacity for animations (GPU-accelerated)
- Implement lazy loading for images, videos, and 3D content
- Progressive enhancement: core content without JavaScript
**Related Skills**: `gsap-scrolltrigger`, `motion-framer`, `lottie-animations` for optimized animations
### 2. Bold Minimalism
**Characteristics**:
- Large, impactful typography (clamp() for fluid sizing)
- Ample white space (negative space as design element)
- Limited color palettes (3-5 primary colors)
- Intentional use of bold accent colors
- Geometric shapes and clean lines
**Typography Scale** (Modern fluid system):
```css
/* Fluid typography using clamp() */
--font-size-xs: clamp(0.75rem, 0.7rem + 0.25vw, 0.875rem);
--font-size-sm: clamp(0.875rem, 0.8rem + 0.375vw, 1rem);
--font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
--font-size-lg: clamp(1.25rem, 1.1rem + 0.75vw, 1.75rem);
--font-size-xl: clamp(1.75rem, 1.5rem + 1.25vw, 2.5rem);
--font-size-2xl: clamp(2.5rem, 2rem + 2.5vw, 4rem);
--font-size-3xl: clamp(3.5rem, 2.5rem + 5vw, 6rem);
```
**Color System** (Accessibility-first):
```css
/* WCAG AAA compliant color system */
--color-primary: oklch(50% 0.2 250); /* Blue */
--color-accent: oklch(65% 0.25 30); /* Coral */
--color-neutral-50: oklch(98% 0 0);
--color-neutral-900: oklch(20% 0 0);
/* Contrast ratio: minimum 7:1 for text */
```
**Related Skills**: `animated-component-libraries` for UI components
### 3. Micro-Interactions
**Definition**: Small, purposeful animations that provide feedback, guide users, and enhance perceived performance.
**Categories**:
**a) Hover States** (Desktop):
- Scale transformations (1.05-1.1x)
- Color transitions (200-300ms)
- Shadow depth changes
- Cursor transformations
**b) Loading States**:
- Skeleton screens (better than spinners)
- Progressive image loading (blur-up technique)
- Optimistic UI updates
- Staggered content reveals
**c) Interactive Feedback**:
- Button press states (scale down 0.95x)
- Toggle switches with spring physics
- Form field validation (immediate, kind feedback)
- Success/error states with motion
**Implementation Example** (Framer Motion):
```jsx
// Button with micro-interaction
<motion.button
whileHover={{ scale: 1.05, y: -2 }}
whileTap={{ scale: 0.95 }}
transition={{ type: "spring", stiffness: 400, damping: 17 }}
>
Click me
</motion.button>
```
**Related Skills**: `motion-framer`, `react-spring-physics`, `animejs` for micro-interactions
### 4. Scrollytelling
**Definition**: Narrative-driven experiences where content reveals and transforms as the user scrolls.
**Patterns**:
**a) Scroll-Triggered Reveals**:
- Fade-in on scroll entry (with offset)
- Slide-in from sides with stagger
- Scale + opacity transitions
- Clip-path reveals
**b) Scroll-Linked Animations**:
- Parallax layers (different scroll speeds)
- Horizontal scrolling sections
- Pinned sections with scrubbing animations
- 3D object rotation tied to scroll
**c) Progress Indicators**:
- Reading progress bars
- Step-by-step visual guides
- Animated SVG paths following scroll
**Implementation Example** (GSAP ScrollTrigger):
```javascript
// Scroll-linked 3D rotation
gsap.to(".cube", {
scrollTrigger: {
trigger: ".section",
start: "top top",
end: "bottom top",
scrub: 1, // Smooth scrubbing
},
rotationY: 360,
ease: "none"
});
```
**Related Skills**: `gsap-scrolltrigger`, `locomotive-scroll`, `scroll-reveal-libraries`, `react-three-fiber` for 3D scrollytelling
### 5. Cursor UX
**Evolution**: Custom cursors that enhance interaction and provide contextual feedback.
**Patterns**:
**a) Custom Cursor Shapes**:
- Circle/dot followers (delayed with easing)
- Text-based cursors ("View", "Drag", "Click")
- Blend modes for visual interest
- Scale/morph on hover
**b) Contextual Transformations**:
- Expand on links/buttons
- Magnetic attraction to interactive elements
- Color inversion over images
- Custom icons for actions (play, zoom, expand)
**c) Performance Considerations**:
- Use CSS transforms only (no top/left)
- RequestAnimationFrame for JS cursors
- Disable on mobile/touch devices
- Respect `prefers-reduced-motion`
**Implementation Example**:
```javascript
// Simple smooth cursor follower
const cursor = document.querySelector('.cursor');
let mouseX = 0, mouseY = 0;
let cursorX = 0, cursorY = 0;
document.addEventListener('mousemove', (e) => {
mouseX = e.clientX;
mouseY = e.clientY;
});
function updateCursor() {
// Smooth easing
cursorX += (mouseX - cursorX) * 0.1;
cursorY += (mouseY - cursorY) * 0.1;
cursor.style.transform = `translate(${cursorX}px, ${cursorY}px)`;
requestAnimationFrame(updateCursor);
}
updateCursor();
```
**Related Skills**: `gsap-scrolltrigger` (for easing), `motion-framer` (for React cursor components)
### 6. Glassmorphism & Depth
**Characteristics**:
- Frosted glass effect (backdrop-filter)
- Layered UI with depth hierarchy
- Subtle shadows and borders
- Translucent backgrounds
**Modern Glassmorphism** (2024):
```css
.glass-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px) saturate(180%);
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
border-radius: 16px;
}
```
**Depth System** (Layering):
```css
/* Elevation scale */
--elevation-1: 0 1px 3px rgba(0,0,0,0.12);
--elevation-2: 0 4px 8px rgba(0,0,0,0.15);
--elevation-3: 0 8px 16px rgba(0,0,0,0.18);
--elevation-4: 0 16px 32px rgba(0,0,0,0.2);
```
**Related Skills**: `animated-component-libraries` for glassmorphic components
### 7. AI-Enhanced Personalization
**Patterns**:
**a) Adaptive Content**:
- Dynamic layout based on user behavior
- Personalized content recommendations
- Adaptive color schemes (system preference + user history)
- Smart defaults based on context
**b) Intelligent Interactions**:
- Predictive search with instant results
- Smart form completion
- Context-aware suggestions
- Progressive disclosure based on usage
**c) Performance + Privacy**:
- Client-side personalization (localStorage, IndexedDB)
- Edge computing for fast personalization
- Privacy-preserving analytics
- Transparent data usage
**Implementation Considerations**:
- Fallback to default experience
- No layout shift from personalization
- Respect "Do Not Track"
- GDPR/CCPA compliance
## Common Design Patterns
### Pattern 1: Immersive Hero Section
**Use Case**: Landing pages, product launches, portfolio sites
**Characteristics**:
- Full viewport height
- Subtle 3D background or animated gradient
- Large headline with fluid typography
- Smooth scroll indicator
- Parallax on scroll exit
**Implementation** (Combined approach):
**HTML Structure**:
```html
<section class="hero">
<div id="bg-canvas"></div>
<div class="hero__content">
<h1 class="hero__title">Modern Design</h1>
<p class="hero__subtitle">Performance meets beauty</p>
<button class="hero__cta">Explore</button>
</div>
<div class="scroll-indicator">
<span>Scroll</span>
</div>
</section>
```
**Technologies**:
- Background: Vanta.js WAVES effect (`lightweight-3d-effects`)
- Text animation: GSAP SplitText with stagger (`gsap-scrolltrigger`)
- Button: Framer Motion hover states (`motion-framer`)
- Scroll indicator: CSS animation + GSAP fade out on scroll
**Related Skills**: `lightweight-3d-effects`, `gsap-scrolltrigger`, `motion-framer`
### Pattern 2: Horizontal Scroll Gallery
**Use Case**: Portfolio work, product showcases, case studies
**Implementation** (GSAP ScrollTrigger):
```javascript
gsap.to(".gallery__track", {
x: () => -(document.querySelector(".gallery__track").scrollWidth - window.innerWidth),
ease: "none",
scrollTrigger: {
trigger: ".gallery",
pin: true,
scrub: 1,
end: () => "+=" + document.querySelector(".gallery__track").scrollWidth
}
});
```
**Enhancements**:
- Lazy load images as they scroll into view
- Parallax within cards
- Scale transformation on active card
- Smooth momentum scrolling with Locomotive
**Related Skills**: `gsap-scrolltrigger`, `locomotive-scroll`
### Pattern 3: 3D Product Viewer
**Use Case**: E-commerce, product marketing, showcases
**Implementation** (React Three Fiber):
```jsx
import { Canvas } from '@react-three/fiber'
import { OrbitControls, useGLTF } from '@react-three/drei'
function ProductViewer() {
return (
<Canvas camera={{ position: [0, 0, 5], fov: 50 }}>
<ambientLight intensity={0.5} />
<spotLight position={[10, 10, 10]} angle={0.15} />
<Product />
<OrbitControls
enableZoom={false}
autoRotate
autoRotateSpeed={2}
/>
</Canvas>
)
}
```
**Enhancements**:
- Material variants (color picker)
- Hotspots with annotations
- AR mode on mobile
- Screenshot/share functionality
**Related Skills**: `react-three-fiber`, `threejs-webgl`, `model-viewer-component`
### Pattern 4: Animated Data Visualization
**Use Case**: Dashboards, analytics, infographics
**Patterns**:
- Count-up animations on scroll into view
- Animated chart reveals (progress bars, pie charts)
- Staggered grid animations
- Particle backgrounds representing data
**Implementation** (Framer Motion + IntersectionObserver):
```jsx
function AnimatedStat({ end, label }) {
const [count, setCount] = useState(0);
const ref = useRef();
const isInView = useInView(ref, { once: true });
useEffect(() => {
if (isInView) {
// Count up animation
const duration = 2000;
const steps = 60;
const increment = end / steps;
let current = 0;
const timer = setInterval(() => {
current += increment;
if (current >= end) {
setCount(end);
clearInterval(timer);
} else {
setCount(Math.floor(current));
}
}, duration / steps);
}
}, [isInView, end]);
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 20 }}
animate={isInView ? { opacity: 1, y: 0 } : {}}
transition={{ duration: 0.6 }}
>
<h2>{count}+</h2>
<p>{label}</p>
</motion.div>
);
}
```
**Related Skills**: `motion-framer`, `pixijs-2d` for canvas-based visualizations
### Pattern 5: Page Transitions
**Use Case**: Multi-page apps, portfolio sites, storytelling experiences
**Implementation** (Barba.js + GSAP):
```javascript
barba.init({
transitions: [{
name: 'slide',
leave(data) {
return gsap.to(data.current.container, {
xPercent: -100,
duration: 0.5
});
},
enter(data) {
return gsap.from(data.next.container, {
xPercent: 100,
duration: 0.5
});
}
}]
});
```
**Modern Alternatives**:
- View Transitions API (Chrome 111+, progressive enhancement)
- Framer Motion's AnimatePresence for React SPAs
- Shared element transitions
**Related Skills**: `barba-js`, `gsap-scrolltrigger`, `motion-framer`
### Pattern 6: Interactive Cursor Effects
**Use Case**: Creative agencies, portfolios, interactive experiences
**Patterns**:
- Text cursor that follows with delay
- Magnetic buttons (cursor attracted to elements)
- Blend mode cursor (inverts colors)
- Cursor that reveals content
**Implementation** (Vanilla JS + GSAP):
```javascript
const links = document.querySelectorAll('a');
const cursor = document.querySelector('.cursor');
links.forEach(link => {
link.addEventListener('mouseenter', () => {
gsap.to(cursor, {
scale: 2,
duration: 0.3,
ease: "power2.out"
});
});
link.addEventListener('mouseleave', () => {
gsap.to(cursor, {
scale: 1,
duration: 0.3,
ease: "power2.out"
});
});
});
```
**Related Skills**: `gsap-scrolltrigger`, `motion-framer`
### Pattern 7: Staggered Content Reveals
**Use Case**: Feature sections, testimonials, team grids
**Implementation** (Framer Motion variants):
```jsx
const container = {
hidden: { opacity: 0 },
show: {
opacity: 1,
transition: {
staggerChildren: 0.1
}
}
};
const item = {
hidden: { opacity: 0, y: 20 },
show: { opacity: 1, y: 0 }
};
function FeatureGrid() {
return (
<motion.div
variants={container}
initial="hidden"
whileInView="show"
viewport={{ once: true, amount: 0.3 }}
className="grid"
>
{features.map((feature, i) => (
<motion.div key={i} variants={item}>
{feature}
</motion.div>
))}
</motion.div>
);
}
```
**Related Skills**: `motion-framer`, `gsap-scrolltrigger`, `scroll-reveal-libraries`
## Integration with Other Skills
### Animation Skills Integration
**GSAP ScrollTrigger** (`gsap-scrolltrigger`):
- Use for scroll-driven storytelling
- Pin sections for multi-step reveals
- Scrub animations tied to scroll position
- Batch animations for performance
**Framer Motion** (`motion-framer`):
- React component animations
- Page transitions with AnimatePresence
- Gesture-based interactions (drag, hover, tap)
- Layout animations (shared element transitions)
**React Spring** (`react-spring-physics`):
- Physics-based animations (more natural feel)
- Interactive springs (drag, pull)
- Trail animations (sequential reveals)
- Use for UI feedback that feels "real"
**Anime.js** (`animejs`):
- SVG path animations (line drawing)
- SVG morphing transitions
- Stagger grid animations
- Timeline-based sequences
**Lottie** (`lottie-animations`):
- Complex designer-created animations
- Icon animations and micro-interactions
- Loading states
- Scroll-driven playback
### 3D Skills Integration
**Three.js** (`threejs-webgl`):
- Custom 3D scenes and experiences
- Shader effects and post-processing
- WebGL-based particle systems
- Advanced lighting and materials
**React Three Fiber** (`react-three-fiber`):
- 3D in React applications
- Product viewers and configurators
- Interactive 3D UI components
- Scroll-driven 3D animations
**Babylon.js** (`babylonjs-engine`):
- Physics-based 3D experiences
- VR/XR applications
- Game-like interactions
- PBR materials for realism
**Lightweight 3D** (`lightweight-3d-effects`):
- Background effects (Vanta.js)
- Subtle 3D illustrations (Zdog)
- Tilt effects (Vanilla-Tilt)
- Performance-friendly 3D accents
### Component Libraries
**Animated Components** (`animated-component-libraries`):
- Magic UI components (backgrounds, text effects)
- Pre-built interactive components
- Design system foundations
- Rapid prototyping
**Scroll Reveals** (`scroll-reveal-libraries`):
- Simple fade/slide on scroll
- AOS library integration
- Lightweight alternative to ScrollTrigger
- Quick implementation for basic reveals
## Accessibility Best Practices
### 1. Motion & Animation
**Respect User Preferences**:
```css
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
```
**JavaScript Detection**:
```javascript
const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (prefersReducedMotion) {
// Disable or simplify animations
gsap.config({ nullTargetWarn: false });
// Skip scroll animations, use instant reveals
}
```
### 2. Color Contrast
**WCAG AAA Standards**:
- Normal text: 7:1 contrast ratio
- Large text (18pt+): 4.5:1 contrast ratio
- Use OKLCH color space for perceptual uniformity
**Testing**:
```javascript
// Check contrast ratio
function getContrastRatio(color1, color2) {
const l1 = getLuminance(color1);
const l2 = getLuminance(color2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
}
```
### 3. Keyboard Navigation
**Requirements**:
- All interactive elements focusable
- Visible focus indicators (not outline: none)
- Logical tab order
- Skip links for long navigation
- Escape key closes modals/overlays
**Focus Styles** (Modern):
```css
:focus-visible {
outline: 3px solid var(--color-accent);
outline-offset: 2px;
border-radius: 4px;
}
/* Remove focus ring for mouse users */
:focus:not(:focus-visible) {
outline: none;
}
```
### 4. Screen Reader Support
**Semantic HTML**:
- Use proper heading hierarchy (h1-h6)
- Landmark regions (header, nav, main, footer)
- aria-labels for icon buttons
- aria-live regions for dynamic content
**Animation Announcements**:
```html
<!-- Announce when content loads -->
<div role="status" aria-live="polite" aria-atomic="true">
Loading complete. 12 items displayed.
</div>
```
### 5. Touch Targets
**Minimum Size**: 44x44px (iOS), 48x48px (Android)
**Spacing**: Minimum 8px between touch targets
**Implementation**:
```css
.button {
min-height: 44px;
min-width: 44px;
padding: 12px 24px;
/* Tap target includes padding */
}
```
## Performance Optimization
### 1. Animation Performance
**60 FPS Checklist**:
- Use CSS transforms (translateX/Y/Z, scale, rotate) - GPU accelerated
- Use opacity for fades - GPU accelerated
- Avoid: top/left, width/height, margin, padding animations
- Use `will-change` sparingly (memory cost)
- RequestAnimationFrame for JS animations
**GSAP Performance**:
```javascript
// Force GPU acceleration
gsap.set(element, { force3D: true });
// Use will-change during animation only
gsap.to(element, {
x: 100,
onStart: () => element.style.willChange = 'transform',
onComplete: () => element.style.willChange = 'auto'
});
```
### 2. Loading Strategies
**Critical Path**:
- Inline critical CSS (above-the-fold)
- Defer non-critical CSS
- Async JavaScript loading
- Preload fonts and hero images
**Progressive Enhancement**:
```html
<!-- Load essential styles first -->
<style>
/* Critical CSS inlined */
</style>
<!-- Defer non-critical styles -->
<link rel="preload" href="animations.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
<noscript><link rel="stylesheet" href="animations.css"></noscript>
```
### 3. Image Optimization
**Modern Formats**:
```html
<picture>
<source srcset="image.avif" type="image/avif">
<source srcset="image.webp" type="image/webp">
<img src="image.jpg" alt="Description" loading="lazy">
</picture>
```
**Responsive Images**:
```html
<img
srcset="image-400.jpg 400w,
image-800.jpg 800w,
image-1200.jpg 1200w"
sizes="(max-width: 640px) 100vw,
(max-width: 1024px) 50vw,
33vw"
src="image-800.jpg"
alt="Description"
loading="lazy"
>
```
### 4. 3D Content Optimization
**Loading Strategy**:
- Show placeholder while loading
- Load low-poly model first
- Progressive enhancement with high-poly
- Lazy load 3D scenes below fold
**Runtime Performance**:
- Use object pooling
- Implement LOD (Level of Detail)
- Frustum culling
- Texture compression (Basis Universal)
**Related Skills**: `threejs-webgl`, `react-three-fiber`, `babylonjs-engine`
### 5. JavaScript Bundle Size
**Code Splitting**:
```javascript
// Dynamic imports
const AnimationModule = lazy(() => import('./animations'));
// Route-based splitting
const Gallery = lazy(() => import('./pages/Gallery'));
```
**Tree Shaking**:
- Use ES6 imports
- Import only what you need
- Use modern build tools (Vite, esbuild)
## Common Pitfalls
### Pitfall 1: Over-Animation
**Problem**: Too many animations distract from content and hurt performance.
**Solution**:
- Limit animations to meaningful interactions
- Use animation to guide attention, not demand it
- Follow the principle: "Animation with purpose"
- Measure performance impact (Chrome DevTools Performance tab)
**Rule of Thumb**: If you can't explain why an animation exists, remove it.
### Pitfall 2: Ignoring Mobile Performance
**Problem**: Animations work on desktop but lag on mobile devices.
**Solution**:
- Test on real devices (not just simulators)
- Reduce animation complexity on mobile
- Disable expensive effects (parallax, 3D) on low-end devices
- Use `matchMedia` for device-specific experiences
```javascript
const isLowEndDevice = () => {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) &&
navigator.hardwareConcurrency < 4;
};
if (isLowEndDevice()) {
// Simplify or disable animations
}
```
### Pitfall 3: Missing Fallbacks
**Problem**: Experience breaks without JavaScript or on older browsers.
**Solution**:
- Progressive enhancement mindset
- Core content accessible without JS
- Feature detection before using modern APIs
- Polyfills for critical features
```javascript
// Feature detection
if ('IntersectionObserver' in window) {
// Use scroll-triggered animations
} else {
// Show content immediately
}
```
### Pitfall 4: Accessibility Oversight
**Problem**: Forgetting keyboard users, screen readers, or motion-sensitive users.
**Solution**:
- Test with keyboard only
- Test with screen reader (NVDA, VoiceOver)
- Always check `prefers-reduced-motion`
- Use semantic HTML
- Maintain proper focus management
**Checklist**:
- [ ] All interactive elements keyboard accessible
- [ ] Focus indicators visible
- [ ] Color contrast meets WCAG AAA
- [ ] Motion can be disabled
- [ ] Screen reader announcements make sense
### Pitfall 5: Ignoring Loading States
**Problem**: Blank screen or layout shifts during content load.
**Solution**:
- Skeleton screens for predictable layouts
- Smooth loading transitions
- Reserve space for dynamic content
- Show meaningful loading indicators
```jsx
// Skeleton screen pattern
function ProductCard({ loading, data }) {
if (loading) {
return (
<div className="skeleton">
<div className="skeleton__image" />
<div className="skeleton__title" />
<div className="skeleton__price" />
</div>
);
}
return <ProductCardContent data={data} />;
}
```
### Pitfall 6: Scroll Hijacking
**Problem**: Overriding native scroll behavior frustrates users.
**Solution**:
- Preserve browser's native scroll (momentum, keyboard)
- Use ScrollTrigger/Locomotive without changing scroll physics
- Allow users to scroll at their own pace
- Never disable scroll entirely
- Avoid scroll-jacking for full-page sections
**Good Practice**: Enhance scroll, don't replace it.
## Design System Architecture
### Token Structure
**Modern Design Tokens** (CSS Custom Properties):
```css
:root {
/* Colors - OKLCH for perceptual uniformity */
--color-primary: oklch(50% 0.2 250);
--color-accent: oklch(65% 0.25 30);
/* Spacing - Consistent scale */
--space-2xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.375rem);
--space-xs: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
--space-sm: clamp(0.75rem, 0.6rem + 0.75vw, 1.125rem);
--space-md: clamp(1rem, 0.8rem + 1vw, 1.5rem);
--space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2.25rem);
--space-xl: clamp(2rem, 1.6rem + 2vw, 3rem);
--space-2xl: clamp(3rem, 2.4rem + 3vw, 4.5rem);
/* Typography - Fluid scale */
--font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem);
/* Animation - Consistent timing */
--duration-fast: 150ms;
--duration-normal: 250ms;
--duration-slow: 400ms;
/* Easing - Natural motion */
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
--ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}
```
### Component Architecture
**Atomic Design** (Brad Frost):
1. **Atoms**: Buttons, inputs, labels
2. **Molecules**: Form fields, cards
3. **Organisms**: Navigation, hero sections
4. **Templates**: Page layouts
5. **Pages**: Specific instances
**Related Skills**: `animated-component-libraries` for component patterns
## Resources
This skill references the following skills for implementation:
### Animation & Interaction
- `gsap-scrolltrigger` - Scroll-driven animations, pinning, scrubbing
- `motion-framer` - React animations, gestures, layout animations
- `react-spring-physics` - Physics-based animations
- `animejs` - SVG animations, stagger effects
- `lottie-animations` - Designer-created animations
- `scroll-reveal-libraries` - Simple scroll reveals (AOS)
### 3D & WebGL
- `threejs-webgl` - Custom 3D scenes and effects
- `react-three-fiber` - 3D in React applications
- `babylonjs-engine` - Physics-based 3D, VR/XR
- `lightweight-3d-effects` - Vanta.js backgrounds, Zdog illustrations
### Page Transitions & Scroll
- `barba-js` - Page transitions
- `locomotive-scroll` - Smooth scrolling
### Component Libraries
- `animated-component-libraries` - Magic UI, React Bits
- `pixijs-2d` - Canvas-based 2D graphics
### Detailed References
See the `references/` directory for in-depth documentation:
- `design_trends_2024.md` - Current web design trends and forecasts
- `interaction_patterns.md` - Comprehensive micro-interaction catalog
- `accessibility_guide.md` - WCAG compliance patterns and testing
- `performance_checklist.md` - Optimization strategies and metrics
### Scripts
The `scripts/` directory includes tools for implementing design patterns:
- `pattern_generator.py` - Generate design pattern boilerplate
- `design_audit.py` - Audit existing designs for compliance
### Assets
The `assets/` directory contains design system templates and starter files. See `assets/README.md` for details.