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',
},
},
},
};