Initial commit — Singular Particular Space v1

Homepage (site/index.html): integration-v14 promoted, Writings section
integrated with 33 pieces clustered by type (stories/essays/miscellany),
Writings welcome lightbox, content frame at 98% opacity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-27 12:09:22 +02:00
commit 5422131782
359 changed files with 117437 additions and 0 deletions

View File

@@ -0,0 +1,428 @@
# Animation & Microinteractions
Guidelines for when and how to animate UI elements effectively.
## The Purpose of Animation
Animation should serve a function, not just look nice.
### Valid Reasons to Animate
| Purpose | Example |
|---------|---------|
| **Feedback** | Button press confirmation, form submission success |
| **Orientation** | Showing where something came from or went to |
| **Focus** | Drawing attention to important changes |
| **Teaching** | Demonstrating how something works |
| **Continuity** | Maintaining context during state changes |
| **Delight** | Occasional surprise (use sparingly) |
### Invalid Reasons to Animate
- "It looks cool"
- "The competition does it"
- "To show off our skills"
- Every state change
- To hide slow performance
---
## Timing & Duration
### Duration Guidelines
| Animation Type | Duration | Rationale |
|----------------|----------|-----------|
| Micro-feedback (hover, press) | 100-150ms | Must feel instant |
| Simple transitions (fade, slide) | 150-250ms | Noticeable but quick |
| Complex transitions (modal, navigation) | 250-350ms | Need time to follow |
| Entrances/Reveals | 200-400ms | Can be slightly longer |
| Decorative/Emphasis | 300-500ms | Purpose is to be noticed |
### The 200ms Rule
Most UI animations should be around 200ms:
- Faster than 100ms → Too fast to perceive
- Slower than 400ms → Feels sluggish, interrupts flow
**Exception:** Loading and progress indicators can be slower because they represent real waiting.
### Duration by Distance
Longer travel distance = longer duration (but not proportionally).
```
Small movement (8-16px): 100-150ms
Medium movement (50-100px): 150-250ms
Large movement (full screen): 250-350ms
```
---
## Easing Functions
Easing makes motion feel natural. Linear motion looks robotic.
### Common Easing Curves
| Easing | Use For | Feel |
|--------|---------|------|
| **ease-out** | Elements entering | Fast start, gentle stop |
| **ease-in** | Elements leaving | Gentle start, fast exit |
| **ease-in-out** | Elements moving within view | Smooth throughout |
| **linear** | Progress indicators, opacity changes | Mechanical (intentional) |
### When to Use Each
**Ease-out (default for entrances):**
```css
transition: transform 200ms ease-out;
```
- Modals appearing
- Notifications sliding in
- Dropdowns opening
- Tooltips appearing
**Ease-in (for exits):**
```css
transition: opacity 150ms ease-in;
```
- Modals dismissing
- Elements fading out
- Notifications leaving
**Ease-in-out (for on-screen movement):**
```css
transition: transform 250ms ease-in-out;
```
- Tab indicators sliding
- Carousel transitions
- Drawer/sidebar toggling
### Custom Cubic Bezier
For more personality, customize curves:
```css
/* Snappy entrance */
transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
/* Smooth overshoot */
transition: transform 300ms cubic-bezier(0.175, 0.885, 0.32, 1.275);
```
---
## Common Animation Patterns
### Button States
**Hover:**
```css
.btn {
transition: background-color 100ms ease-out, transform 100ms ease-out;
}
.btn:hover {
background-color: var(--btn-hover);
}
```
**Active/Pressed:**
```css
.btn:active {
transform: scale(0.97);
}
```
**Loading state:**
```css
.btn.loading {
opacity: 0.7;
pointer-events: none;
}
.btn.loading .spinner {
animation: spin 1s linear infinite;
}
```
### Modal Entrance/Exit
**Enter:**
```css
.modal {
opacity: 0;
transform: scale(0.95) translateY(-10px);
transition: opacity 200ms ease-out, transform 200ms ease-out;
}
.modal.open {
opacity: 1;
transform: scale(1) translateY(0);
}
```
**Exit:**
```css
.modal.closing {
opacity: 0;
transform: scale(0.95);
transition: opacity 150ms ease-in, transform 150ms ease-in;
}
```
### Dropdown/Menu
```css
.dropdown {
opacity: 0;
transform: translateY(-8px);
pointer-events: none;
transition: opacity 150ms ease-out, transform 150ms ease-out;
}
.dropdown.open {
opacity: 1;
transform: translateY(0);
pointer-events: auto;
}
```
### Toast/Notification
```css
.toast {
transform: translateX(100%);
transition: transform 300ms ease-out;
}
.toast.visible {
transform: translateX(0);
}
.toast.exiting {
transform: translateX(100%);
transition: transform 200ms ease-in;
}
```
### Skeleton Loading
```css
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
0% { background-position: 200% 0; }
100% { background-position: -200% 0; }
}
```
---
## Loading States
### Types of Loading Indicators
| Type | Use When | Example |
|------|----------|---------|
| **Spinner** | Unknown duration, short wait expected | Button submission |
| **Progress bar** | Known progress, longer operations | File upload |
| **Skeleton** | Loading content layout | Feed items |
| **Pulse/Shimmer** | Refreshing existing content | Pull to refresh |
### Spinner Guidelines
- Don't show immediately (wait 300-500ms)
- If wait < 1 second, spinner may not be needed
- Position in context (where content will appear)
- Provide cancel option for long operations
```jsx
// Delay spinner to avoid flash for fast operations
const [showSpinner, setShowSpinner] = useState(false);
useEffect(() => {
if (isLoading) {
const timer = setTimeout(() => setShowSpinner(true), 400);
return () => clearTimeout(timer);
}
setShowSpinner(false);
}, [isLoading]);
```
### Progress Bar Guidelines
- Show percentage when meaningful
- Don't let it jump backwards
- Consider indeterminate state if progress unknown
- Complete to 100% before hiding
### Skeleton Screen Guidelines
- Match layout of actual content
- Use consistent bone shapes
- Animate subtly (shimmer, not bounce)
- Replace with content immediately when loaded
---
## Microinteractions
Small animations that provide feedback and delight.
### Effective Microinteractions
**Toggle switches:**
```css
.toggle-thumb {
transition: transform 150ms ease-out;
}
.toggle.on .toggle-thumb {
transform: translateX(20px);
}
```
**Checkbox check:**
```css
.checkmark {
stroke-dasharray: 20;
stroke-dashoffset: 20;
transition: stroke-dashoffset 200ms ease-out;
}
.checkbox.checked .checkmark {
stroke-dashoffset: 0;
}
```
**Like/Heart animation:**
```css
.heart {
transform: scale(1);
transition: transform 150ms ease-out;
}
.heart.liked {
animation: pop 300ms ease-out;
}
@keyframes pop {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
```
**Input focus:**
```css
.input {
border-color: #ccc;
transition: border-color 150ms ease-out, box-shadow 150ms ease-out;
}
.input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.2);
}
```
### When Microinteractions Help
- Confirming user action occurred
- Showing state change clearly
- Making interface feel responsive
- Guiding attention to changes
### When to Skip
- Repetitive actions (every keystroke)
- Performance-critical paths
- Accessibility mode (respect reduce-motion)
---
## Accessibility Considerations
### 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;
}
}
```
### Provide Alternatives
- Don't rely on animation alone to convey information
- Ensure state changes are visible without animation
- Allow users to disable animations in app settings
### Avoid Problematic Animations
| Avoid | Reason |
|-------|--------|
| Flashing/strobing | Can trigger seizures |
| Parallax scrolling | Causes motion sickness |
| Auto-playing video | Distracting, accessibility |
| Infinite loops | Drains attention, battery |
---
## Performance Guidelines
### GPU-Accelerated Properties
Animate these for smooth 60fps:
- `transform` (translate, scale, rotate)
- `opacity`
Avoid animating (causes reflow/repaint):
- `width`, `height`
- `top`, `left`, `right`, `bottom`
- `margin`, `padding`
- `border-width`
- `font-size`
### Use will-change Sparingly
```css
/* Only for elements about to animate */
.modal {
will-change: transform, opacity;
}
/* Remove after animation */
.modal.static {
will-change: auto;
}
```
### Batch Animations
Start animations together, not staggered excessively:
- 0-50ms stagger: feels cohesive
- 100ms+ stagger: feels slow, sequential
### Test on Low-end Devices
What's smooth on your MacBook may stutter on a budget Android phone. Test on real devices or throttle CPU in DevTools.
---
## Animation Checklist
Before shipping an animation:
- [ ] Does it serve a purpose (not just decoration)?
- [ ] Is duration appropriate for the action?
- [ ] Does easing feel natural?
- [ ] Does it work with `prefers-reduced-motion`?
- [ ] Is it GPU-accelerated (transform/opacity)?
- [ ] Does it perform well on low-end devices?
- [ ] Can the interface function without it?