Physics-based animations and wabi-sabi aesthetics for mindful technology
This document describes the Zenborg design system - a foundation layer implementing physics-based animations, responsive utilities, and material design patterns aligned with wabi-sabi philosophy and convivial technology principles.
- Restraint over maximalism: Monochromatic stone palette with area color accents
- Natural, organic motion: Physics-based easing (elastic settle, smooth exit)
- Layered transparency: Subtle depth through opacity and blur (when appropriate)
- Contextual, not rigid: Fluid sizing adapts to viewport, not fixed breakpoints
- Transparent: CSS variables visible, no abstraction magic
- Modifiable: Developers can adjust values without breaking system
- Appropriate: Animation curves match interaction weight
- Human-scale: Design tokens in readable units (rem, ms, named curves)
- Minimal attention: Smooth transitions don't startle
- Inform, don't demand: Hover states reveal, not interrupt
- Peripheral awareness: Subtle opacity shifts, gentle lifts
:root {
/* Spacing */
--spacing: 0.25rem; /* 4px base unit */
/* Animation Easing (Physics-based) */
--ease-elastic: cubic-bezier(.25, 1, .5, 1); /* Settle with overshoot */
--ease-smooth: cubic-bezier(.4, 0, .2, 1); /* Standard ease */
--ease-bounce: cubic-bezier(.68, -.55, .265, 1.55); /* Strong bounce */
/* Animation Durations */
--duration-fast: 150ms; /* Micro-interactions */
--duration-medium: 400ms; /* Component transitions */
--duration-slow: 600ms; /* Page transitions */
/* Glassmorphism */
--glass-blur: clamp(1px, 0.125em, 4px);
--glass-opacity-light: 0.1;
--glass-opacity-heavy: 0.25;
}Animation Easing:
.transition-elastic /* cubic-bezier(.25, 1, .5, 1) */
.transition-smooth /* cubic-bezier(.4, 0, .2, 1) */
.transition-bounce /* cubic-bezier(.68, -.55, .265, 1.55) */Animation Durations:
.duration-fast /* 150ms */
.duration-medium /* 400ms */
.duration-slow /* 600ms */Combined utilities:
.transition-elastic-medium /* elastic + 400ms */
.transition-smooth-fast /* smooth + 150ms */Glassmorphism:
.glass-base /* blur(var(--glass-blur)) */
.glass-moment /* blur + shadow (for moment cards) */
.glass-overlay /* blur*2 + opacity (for modals) */Animation:
import { animation } from "@/lib/design-tokens";
// Durations
animation.fast // "150ms"
animation.medium // "400ms"
animation.slow // "600ms"
// Easing curves
animation.elastic // "cubic-bezier(.25, 1, .5, 1)"
animation.smooth // "cubic-bezier(.4, 0, .2, 1)"
animation.bounce // "cubic-bezier(.68, -.55, .265, 1.55)"
// CSS variable references (for inline styles)
animation.cssVars.durationMedium // "var(--duration-medium)"
animation.cssVars.easeElastic // "var(--ease-elastic)"Glassmorphism:
import { glassmorphism, shadows } from "@/lib/design-tokens";
// Blur values
glassmorphism.blur // "clamp(1px, 0.125em, 4px)"
glassmorphism.blurHeavy // "calc(...* 2)"
// Shadows
shadows.glass // "0 1px 2px oklch(...)"
shadows.glassHover // "0 4px 8px oklch(...)"
// Helper for area-colored glass
glassmorphism.momentCard(areaColor) // Returns style objectTypography (Future - Phase 4):
import { typography } from "@/lib/design-tokens";
// Fluid sizing with clamp()
typography.fluid.moment // "clamp(1.125rem, 2.5vw, 1.5rem)"
typography.fluid.dayLabel // "clamp(1.5rem, 4vw, 2rem)"
typography.fluid.hero // "clamp(2rem, 8svw, 5rem)"Before:
<button className="transition-all cursor-pointer">After:
<button className="transition-all duration-medium transition-elastic hover:-translate-y-0.5">What changed:
duration-medium→ 400ms transitiontransition-elastic→ Elastic easing curve (settles with overshoot)hover:-translate-y-0.5→ Subtle lift on hover for depth
Before:
<div className="transition-opacity duration-300">After:
<div className="transition-opacity duration-slow transition-smooth">What changed:
duration-slow→ 600ms for page-level animationtransition-smooth→ Standard easing for fade transitions
Before:
<button className="hover:bg-stone-100 transition-colors">After:
<button className="hover:bg-stone-100 transition-all duration-fast transition-smooth">What changed:
duration-fast→ 150ms for micro-interactionstransition-smooth→ Quick, responsive feel
Elastic (cubic-bezier(.25, 1, .5, 1))
- ✅ Hover states (moment cards, buttons)
- ✅ Expanding elements (drawing board)
- ✅ Modal open (dialog zoom-in)
- ✅ Elements entering the viewport
- ❌ Exits (too bouncy)
- ❌ Color transitions (no physical meaning)
Smooth (cubic-bezier(.4, 0, .2, 1))
- ✅ Fade-ins/fade-outs
- ✅ Exits (modal close, drawer slide)
- ✅ Drag hover states (timeline cells)
- ✅ Color transitions
- ✅ Default for most transitions
Bounce (cubic-bezier(.68, -.55, .265, 1.55))
⚠️ Use sparingly - very exaggerated- ✅ Playful interactions (easter eggs)
- ✅ Success states (moment created)
- ❌ Standard UI interactions (too distracting)
| Duration | Value | Use Case | Examples |
|---|---|---|---|
| Fast | 150ms | Micro-interactions | Hover, focus, button press |
| Medium | 400ms | Component transitions | Card expand, drawer open |
| Slow | 600ms | Page transitions | Modal open, view change |
✅ Appropriate:
- Moment cards (subtle area-colored glass)
- Modal overlays (frosted background)
- Floating UI elements (settings drawer)
- Depth layers (when meaningful)
❌ Avoid:
- Timeline cell backgrounds (too busy)
- Text containers (readability issues)
- Over complex patterns/images (blur becomes mud)
- More than ~5 simultaneous blurred elements (performance)
Tested on:
- iPad Air (2019) - Mid-range reference device
- iOS Safari 17+ - Primary target platform
- Chrome Android - Secondary target
Safeguards:
/* Disable blur if motion is reduced */
@media (prefers-reduced-motion: reduce) {
.glass-base { backdrop-filter: none; }
}
/* Fallback for unsupported browsers */
@supports not (backdrop-filter: blur(1px)) {
.glass-base { background: var(--surface-alt); }
}All animations respect prefers-reduced-motion:
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
transition-duration: 0.01ms !important;
}
}- Maintain visible focus rings (using area colors)
- Keyboard navigation fully supported
- ARIA labels on all interactive elements
Dynamic Viewport Units (dvh):
- ✅ Dialogs:
max-h-[90dvh] - ✅ Drawers:
h-dvhon mobile landscape - ✅ Adapts as browser chrome appears/hides
Future Enhancement (Phase 4):
- Small viewport units (svh) for stable content
- Fluid typography with clamp() + viewport units
- Example:
clamp(1.5rem, 4svw, 2rem)for day labels
<button
className={cn(
"rounded-lg cursor-pointer w-full",
// Design system utilities
"transition-all duration-medium transition-elastic",
"hover:-translate-y-0.5",
// Ring states
isSelected ? "ring-2 ring-offset-2" : "ring-0 hover:ring-2"
)}
style={{
backgroundColor: area.color,
minHeight: momentCard.minHeight,
// Ring color from area
"--tw-ring-color": `${area.color}99`,
}}
><div
className={cn(
"w-full h-full flex overflow-x-scroll",
// Smooth fade-in on load
"transition-opacity duration-slow transition-smooth",
isReady ? "opacity-100" : "opacity-0"
)}
><div
className={cn(
"h-full flex flex-col min-h-[240px]",
// Fast transitions for drag states
"transition-all duration-fast transition-smooth",
isOver && "ring-2 ring-slate-400"
)}
><button
className={cn(
"w-full px-6 py-3",
// Quick hover response
"hover:bg-stone-100 transition-all duration-fast transition-smooth"
)}
onClick={() => drawingBoardExpanded$.set(true)}
>Pattern 1: Generic transitions
- className="transition-colors"
+ className="transition-all duration-fast transition-smooth"Pattern 2: Hover states
- className="hover:bg-stone-100 transition-colors"
+ className="hover:bg-stone-100 transition-all duration-fast transition-smooth"Pattern 3: Modal animations
- className="transition-all duration-200"
+ className="transition-all duration-medium transition-elastic"- Test on actual iPad (iOS Safari)
- Test on Chrome Android
- Verify animations feel elastic, not robotic
- Check 60fps performance (DevTools → Performance)
- Validate blur doesn't cause jank
-
prefers-reduced-motiondisables animations - Focus states visible on all components
- Keyboard navigation works (Tab, Arrow keys)
- Screen reader announces state changes
- Safari 17+ (primary)
- Chrome 120+ (secondary)
- Firefox 120+ (tertiary)
- Fallbacks work when backdrop-filter unsupported
// Timeline day labels
<h2 style={{ fontSize: typography.fluid.dayLabel }}>
Today
</h2>
// Moment cards (optional)
<p style={{ fontSize: typography.fluid.moment }}>
{moment.name}
</p>// Helper for moment cards
import { glassmorphism } from "@/lib/design-tokens";
<button style={glassmorphism.momentCard(area.color)}>
{moment.name}
</button>// Replace hardcoded spacing
- className="px-6 py-3"
+ style={{ padding: `calc(var(--spacing) * 3) calc(var(--spacing) * 6)` }}Before (Robotic):
- Instant transition
- Linear easing
- No depth cues
After (Organic):
- 400ms elastic settle
- Slight overshoot (bounces into place)
- Subtle -2px lift on hover
- Feels alive, responds to touch
User Experience:
- "Did I cause that?" → Yes, clear causality
- "Was that pleasant?" → Yes, not jarring
- "Did I notice it thinking?" → No, felt instant
Before (Abrupt):
- 300ms linear fade
- Content pops in
After (Calm):
- 600ms smooth ease
- Gentle reveal
- Gives user time to orient
User Experience:
- "Did content appear smoothly?" → Yes, no flash
- "Can I start interacting?" → Yes, clearly ready
- "Do I feel rushed?" → No, calm pacing
- ✅ Animations feel natural, not robotic
- ✅ Depth is subtle, not overwhelming
- ✅ Changes are understandable without docs
- ✅ Developers can modify without breaking
- ✅ All animations 60fps on iPad Air
- ✅ No jank on scroll/drag
- ✅ Blur doesn't cause performance regression
- ✅ Text readable on all backgrounds
- ✅ Animation curves standardized across 26+ files
- ✅ CSS variables used for 90%+ of colors/spacing
- ✅ Zero viewport unit warnings in mobile Safari
- ✅ Lighthouse performance score maintained (>90)
Q: Why elastic easing for hovers? A: Creates "settling into place" feel - element responds to attention, then stabilizes. More human than instant snap.
Q: Why 400ms for medium transitions? A: Perceptual threshold - fast enough to feel responsive, slow enough to perceive motion. Matches Jakob Nielsen's 0.1s/1s/10s thresholds.
Q: Why not use Framer Motion or similar? A: Wabi-sabi principle of restraint. CSS transitions are simple, transparent, performant. Animation library adds complexity for marginal gain.
Q: Should we use glassmorphism everywhere? A: No. Restraint over maximalism. Use only where it adds meaningful depth (moment cards, overlays). Skip for timeline cells (too busy).
Q: Why OKLCH color space? A: Perceptually uniform - 50% lightness looks equally bright across all hues. Better for programmatic adjustments than RGB/HSL.
- Wabi-Sabi: Leonard Koren - "Wabi-Sabi for Artists, Designers, Poets & Philosophers"
- Calm Technology: Mark Weiser & John Seely Brown - "Designing Calm Technology"
- Convivial Tools: Ivan Illich - "Tools for Conviviality"
- Animation Easing: Robert Penner's easing functions
- OKLCH: CSS Color Module Level 4 specification
- Viewport Units: CSS Values and Units Module Level 4
- Glassmorphism: Apple Human Interface Guidelines (Material Design)
- Response Times: Jakob Nielsen - "Response Times: The 3 Important Limits"
- Motion Design: Material Design Motion guidelines
- Accessibility: WCAG 2.1 AA - Animation from Interactions
Phase 1: Foundation Layer
- Added CSS custom properties (spacing, animations, glassmorphism)
- Created utility classes for easing curves and durations
- Extended design-tokens.ts with animation and glassmorphism utilities
Phase 2: Animation Harmonization
- Updated MomentCard.tsx with elastic transitions and hover lift
- Updated Timeline.tsx with smooth fade-in
- Updated TimelineCell.tsx with fast drag-hover transitions
- Updated DrawingBoard.tsx with smooth expand/collapse
- Updated Dialog.tsx with elastic zoom animation
Phase 3: Documentation
- Created DESIGN_SYSTEM.md with usage guide and philosophy
- Documented all patterns and migration strategies
Phase 4: Future
- Fluid typography with clamp() (ready for implementation)
- Advanced glassmorphism helpers (ready for opt-in)
- Spacing system standardization (proposed)
"Where will I place my consciousness today?" - A design system for mindful technology.