Skip to main content

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

ColorEmotionUse For
Healing GreenGrowth, calm, successPrimary actions, success states
Calm BlueTrust, stabilitySecondary actions, info
Sacred GoldWisdom, attentionWarnings, highlights
Soft RedAlert without panicErrors, 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-motion support
  • 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',
},
},
},
};