UI Wellness Pattern
Transform your visual layer into a calming experience. This pattern focuses on colors, typography, spacing, and animations that reduce visual stress.
The Problem
Most interfaces are designed for attention capture, not human comfort:
- High-contrast colors that strain eyes
- Cramped layouts with no breathing room
- Jarring animations that spike anxiety
- Typography optimized for density, not readability
The Solution
UI Wellness applies principles from environmental psychology and accessibility research to create interfaces that feel calm.
Color System
Healing Palette
:root {
/* Primary: Healing Green */
--color-healing: #2dd284;
--color-healing-dark: #1fa865;
--color-healing-light: #86efac;
--color-healing-ghost: rgba(45, 210, 132, 0.1);
/* Secondary: Calm Blue */
--color-calm: #38bdf8;
--color-calm-dark: #0ea5e9;
--color-calm-light: #7dd3fc;
--color-calm-ghost: rgba(56, 189, 248, 0.1);
/* Accent: Sacred Gold */
--color-sacred: #fbbf24;
--color-sacred-dark: #f59e0b;
/* Backgrounds: Grounding Neutrals */
--bg-deepest: #0f172a;
--bg-deep: #1e293b;
--bg-medium: #334155;
--bg-light: #475569;
/* Text */
--text-primary: #f1f5f9;
--text-secondary: #94a3b8;
--text-muted: #64748b;
/* Feedback */
--color-success: #2dd284;
--color-warning: #fbbf24;
--color-error: #ff6b6b; /* Softer than pure red */
--color-info: #38bdf8;
}
Color Psychology
| Color | Emotion | Use For |
|---|---|---|
| Healing Green | Growth, calm, success | Primary actions, success states |
| Calm Blue | Trust, stability | Secondary actions, info |
| Sacred Gold | Wisdom, attention | Warnings, highlights |
| Soft Red | Alert without panic | Errors, destructive actions |
Dark Mode First
Dark interfaces reduce eye strain, especially during extended use:
/* Dark mode as default */
body {
background: var(--bg-deepest);
color: var(--text-primary);
}
/* Light mode for preference */
@media (prefers-color-scheme: light) {
body {
background: #ffffff;
color: #0f172a;
}
}
Typography
Comfortable Reading
:root {
/* Font Stack */
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI',
Roboto, 'Helvetica Neue', sans-serif;
--font-mono: 'SF Mono', 'Fira Code', Consolas, monospace;
/* Sizes - slightly larger for comfort */
--text-xs: 0.8125rem; /* 13px */
--text-sm: 0.9375rem; /* 15px */
--text-base: 1.0625rem; /* 17px - larger base */
--text-lg: 1.1875rem; /* 19px */
--text-xl: 1.375rem; /* 22px */
--text-2xl: 1.75rem; /* 28px */
--text-3xl: 2.25rem; /* 36px */
/* Line Heights - generous for readability */
--leading-tight: 1.3;
--leading-normal: 1.6; /* More generous than typical 1.5 */
--leading-relaxed: 1.8;
/* Letter Spacing */
--tracking-tight: -0.02em;
--tracking-normal: 0;
--tracking-wide: 0.02em;
}
body {
font-family: var(--font-sans);
font-size: var(--text-base);
line-height: var(--leading-normal);
-webkit-font-smoothing: antialiased;
}
/* Headings with breathing room */
h1, h2, h3, h4 {
line-height: var(--leading-tight);
margin-top: 2em;
margin-bottom: 0.75em;
}
Reading Width
Optimal line length reduces eye fatigue:
.content {
max-width: 65ch; /* 65 characters per line */
margin-inline: auto;
}
/* Wider for code, narrower for prose */
.prose { max-width: 60ch; }
.code-block { max-width: 80ch; }
Spacing System
Breathing Room
:root {
/* Spacing Scale - generous */
--space-1: 0.25rem; /* 4px */
--space-2: 0.5rem; /* 8px */
--space-3: 0.75rem; /* 12px */
--space-4: 1rem; /* 16px */
--space-5: 1.5rem; /* 24px */
--space-6: 2rem; /* 32px */
--space-8: 3rem; /* 48px */
--space-10: 4rem; /* 64px */
--space-12: 6rem; /* 96px */
--space-16: 8rem; /* 128px */
}
/* Section spacing */
section {
padding-block: var(--space-10);
}
/* Card padding */
.card {
padding: var(--space-6);
}
/* List item spacing */
li + li {
margin-top: var(--space-3);
}
Visual Hierarchy Through Space
/* Group related items tightly */
.form-group {
margin-bottom: var(--space-6);
}
.form-group label {
margin-bottom: var(--space-2);
}
/* Separate sections generously */
.section + .section {
margin-top: var(--space-12);
}
Animations
Gentle Motion
:root {
/* Durations */
--duration-fast: 150ms;
--duration-normal: 300ms;
--duration-slow: 500ms;
--duration-breathe: 4000ms;
/* Easings - organic, not mechanical */
--ease-out: cubic-bezier(0.33, 1, 0.68, 1);
--ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
--ease-breathe: cubic-bezier(0.4, 0, 0.6, 1);
}
/* Subtle hover states */
.button {
transition: transform var(--duration-fast) var(--ease-out),
box-shadow var(--duration-normal) var(--ease-out);
}
.button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(45, 210, 132, 0.2);
}
/* Breathing animation for focus states */
@keyframes breathe {
0%, 100% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.02); opacity: 0.9; }
}
.focus-breathing {
animation: breathe var(--duration-breathe) var(--ease-breathe) infinite;
}
Respect Motion Preferences
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
Component Examples
Calm Button
function CalmButton({ children, variant = 'primary', ...props }) {
return (
<button
className={`
px-6 py-3 rounded-lg font-medium
transition-all duration-300 ease-out
focus:outline-none focus:ring-2 focus:ring-offset-2
${variant === 'primary'
? 'bg-healing text-grounding-darkest hover:bg-healing-dark focus:ring-healing'
: 'bg-transparent border-2 border-grounding-medium text-text-primary hover:border-healing focus:ring-grounding-medium'
}
`}
{...props}
>
{children}
</button>
);
}
Breathing Card
function BreathingCard({ children, highlight = false }) {
return (
<div
className={`
p-6 rounded-xl border
transition-all duration-300
${highlight
? 'border-healing bg-healing-ghost shadow-healing animate-breathe'
: 'border-grounding-medium bg-grounding-dark hover:border-grounding-light'
}
`}
>
{children}
</div>
);
}
Gentle Input
function GentleInput({ label, error, ...props }) {
return (
<div className="space-y-2">
<label className="text-sm font-medium text-text-secondary">
{label}
</label>
<input
className={`
w-full px-4 py-3 rounded-lg
bg-grounding-dark border-2
text-text-primary placeholder-text-muted
transition-all duration-200
focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-grounding-darkest
${error
? 'border-error focus:ring-error'
: 'border-grounding-medium focus:border-healing focus:ring-healing'
}
`}
{...props}
/>
{error && (
<p className="text-sm text-error/90">{error}</p>
)}
</div>
);
}
Implementation Checklist
- Replace harsh colors with healing palette
- Increase base font size to 17px
- Set line-height to 1.6 minimum
- Limit content width to 65 characters
- Add generous padding to containers
- Soften all transitions to 200-300ms
- Implement dark mode as default
- Add
prefers-reduced-motionsupport - Test contrast ratios (WCAG AA minimum)
- Remove jarring hover effects
Tailwind Configuration
// tailwind.config.js
module.exports = {
theme: {
extend: {
colors: {
healing: {
DEFAULT: '#2dd284',
dark: '#1fa865',
light: '#86efac',
ghost: 'rgba(45, 210, 132, 0.1)',
},
calm: {
DEFAULT: '#38bdf8',
dark: '#0ea5e9',
light: '#7dd3fc',
},
grounding: {
darkest: '#0f172a',
dark: '#1e293b',
medium: '#334155',
light: '#475569',
},
},
fontSize: {
base: '1.0625rem',
},
lineHeight: {
normal: '1.6',
},
transitionDuration: {
DEFAULT: '300ms',
},
animation: {
breathe: 'breathe 4s ease-in-out infinite',
},
},
},
};
Related Patterns
- Gentle Errors - Apply UI wellness to error states
- Breathing Spaces - Strategic pauses in the UI
- Accessibility Healing - Inclusive design patterns