| name | Neumorphism / Soft UI Design Style |
| description | Design with Neumorphism—a soft, sculptural design style using dual shadows (light and dark) on monochromatic backgrounds to create subtle depth and tactile surfaces. Trigger this skill when designing soft, approachable interfaces, minimalist dashboards, health/wellness apps, or design-forward products emphasizing gentle aesthetics. |
What Is Neumorphism?
Neumorphism (new skeuomorphism) combines flat design with subtle 3D depth. It uses dual shadows—a light shadow (top-left) and a dark shadow (bottom-right) on a monochromatic background to create the illusion that elements are either raised or inset. It feels organic, tactile, and premium.
Why it works:
- Soft, approachable: Dual shadows feel less harsh than solid shadows
- Depth without color: Entire design lives in one color family; depth comes from shadows
- Minimalist aesthetic: Focuses on form and shadow, not decoration
- Tactile feel: Suggests elements you could touch and press
Core Design Principles
1. Dual Shadow Technique (The Core)
Every element uses two shadows:
- Light shadow (top-left): Simulates ambient light
- Dark shadow (bottom-right): Simulates depth
Example: box-shadow: -8px -8px 16px #ffffff, 8px 8px 16px #bebebe
The two shadows should be equidistant from the element's edges, creating symmetrical depth.
2. Monochromatic Palette
All colors derive from ONE base hue. Neumorphism strength lies in subtle tonal shifts, not color variety. Common bases: light gray (#e0e0e0), soft blue, soft beige.
3. Inset Shadows for Pressed States
When an element is "pressed" or "active," reverse the shadow direction (inset). This makes it appear recessed, like you've touched it.
Pressed: box-shadow: inset -8px -8px 16px #ffffff, inset 8px 8px 16px #bebebe
4. Smooth Transitions
Changes between states (hover, active, focus) should be gradual. Use transition: box-shadow 200ms ease.
Visual Language
Color Palette (Gray Base):
Background: #e0e0e0 (light gray, neutral)
Light shadow: #ffffff (white, 80% opacity)
Dark shadow: #bebebe (darker gray, 80% opacity)
Text: #4a4a4a (dark gray, high contrast)
Accent: Subtle tint within gray range, e.g., #d1d1d1
Alternative base: Soft blue (#e3f2fd) → adjust shadows to match blue family.
Typography:
- Font: system fonts (-apple-system, sans-serif) for clarity
- Body: 14px / 1.6 line-height
- Headings: 16px–24px, weight 500–600
- Text color: #4a4a4a (body), #6a6a6a (secondary)
Spacing (8px base):
- Padding: 16px, 24px, 32px
- Margin: 8px, 16px, 24px
- Border-radius: 8px–16px (soft, rounded corners enhance soft feel)
Shadows (The Critical Part):
box-shadow:
-8px -8px 16px #ffffff,
8px 8px 16px #bebebe;
box-shadow:
inset -8px -8px 16px #ffffff,
inset 8px 8px 16px #bebebe;
box-shadow:
-12px -12px 20px #ffffff,
12px 12px 20px #bebebe;
Shadow Offset & Blur:
- Small elements (16px–32px): offset 4px, blur 8px
- Medium elements (40px–80px): offset 8px, blur 16px
- Large elements (120px+): offset 12px, blur 24px
Component Patterns
Neumorphic Button
Raised button that depresses on click.
.neomorphic-button {
background: #e0e0e0;
border: none;
border-radius: 12px;
padding: 14px 28px;
font-weight: 600;
font-size: 14px;
color: #4a4a4a;
cursor: pointer;
box-shadow:
-8px -8px 16px #ffffff,
8px 8px 16px #bebebe;
transition: box-shadow 200ms ease, transform 100ms ease;
}
.neomorphic-button:hover {
box-shadow:
-12px -12px 20px #ffffff,
12px 12px 20px #bebebe;
transform: translateY(-2px);
}
.neomorphic-button:active {
box-shadow:
inset -8px -8px 16px #ffffff,
inset 8px 8px 16px #bebebe;
transform: translateY(0);
}
.neomorphic-button:focus {
outline: 2px solid #bebebe;
outline-offset: 4px;
}
Neumorphic Card
Floating container with soft depth.
.neomorphic-card {
background: #e0e0e0;
border-radius: 16px;
padding: 24px;
box-shadow:
-12px -12px 24px #ffffff,
12px 12px 24px #bebebe;
transition: box-shadow 300ms ease;
}
.neomorphic-card:hover {
box-shadow:
-16px -16px 32px #ffffff,
16px 16px 32px #9a9a9a;
}
.neomorphic-card h2 {
color: #4a4a4a;
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
}
.neomorphic-card p {
color: #6a6a6a;
line-height: 1.6;
}
Neumorphic Input Field
Text input with soft styling.
.neomorphic-input {
background: #e0e0e0;
border: none;
border-radius: 12px;
padding: 12px 18px;
font-size: 14px;
color: #4a4a4a;
box-shadow: inset -4px -4px 8px #ffffff, inset 4px 4px 8px #bebebe;
transition: box-shadow 150ms ease;
}
.neomorphic-input::placeholder {
color: #9a9a9a;
}
.neomorphic-input:focus {
outline: none;
box-shadow:
inset -4px -4px 8px #ffffff,
inset 4px 4px 8px #bebebe,
0 0 0 3px rgba(190, 190, 190, 0.3);
}
Neumorphic Toggle Switch
Binary input with inset/raised states.
.neomorphic-toggle {
position: relative;
width: 60px;
height: 32px;
background: #e0e0e0;
border-radius: 16px;
cursor: pointer;
box-shadow: inset -4px -4px 8px #ffffff, inset 4px 4px 8px #bebebe;
transition: all 200ms ease;
}
.neomorphic-toggle::after {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 26px;
height: 26px;
background: #e0e0e0;
border-radius: 50%;
box-shadow: -4px -4px 8px #ffffff, 4px 4px 8px #bebebe;
transition: left 200ms ease;
}
.neomorphic-toggle.active {
box-shadow: inset -4px -4px 8px #ffffff, inset 4px 4px 8px #bebebe;
}
.neomorphic-toggle.active::after {
left: 31px;
box-shadow: -4px -4px 8px #ffffff, 4px 4px 8px #bebebe;
}
Code Generation Guidance
Use Neumorphism when:
- Designing soft, approachable interfaces (wellness, health apps)
- Creating minimalist dashboards (focus on data, not decoration)
- Building design-forward products emphasizing gentleness
- Designing for audiences preferring calm, subtle aesthetics
- Creating internal tools with a premium feel
React patterns:
function NeomorphicButton({ children, onClick }) {
return (
<button
onClick={onClick}
style={{
background: '#e0e0e0',
border: 'none',
borderRadius: '12px',
padding: '14px 28px',
fontWeight: '600',
cursor: 'pointer',
boxShadow: '-8px -8px 16px #ffffff, 8px 8px 16px #bebebe',
transition: 'all 200ms ease'
}}
>
{children}
</button>
);
}
function NeomorphicCard({ title, children }) {
return (
<div style={{
background: '#e0e0e0',
borderRadius: '16px',
padding: '24px',
boxShadow: '-12px -12px 24px #ffffff, 12px 12px 24px #bebebe'
}}>
<h2 style={{ color: '#4a4a4a', marginBottom: '12px' }}>{title}</h2>
{children}
</div>
);
}
CSS Recipe for Any Element:
- Base:
background: #e0e0e0 (or your monochromatic base)
- Raised:
box-shadow: -8px -8px 16px #ffffff, 8px 8px 16px #bebebe
- Pressed:
box-shadow: inset -8px -8px 16px #ffffff, inset 8px 8px 16px #bebebe
- Rounded:
border-radius: 12px (12px–16px is ideal)
- Smooth:
transition: box-shadow 200ms ease (transitions feel premium)
Accessibility (Critical)
Challenge: Neumorphism has accessibility risks
1. Low Contrast
Dual shadows don't provide color contrast. Text on #e0e0e0 background must have sufficient color value.
SOLUTION: Darken text to #4a4a4a (7:1 contrast ratio, exceeds WCAG AAA).
2. Unclear Interactivity
Subtle shadows may not signal "clickable" to users.
SOLUTION:
- Always show clear focus indicators:
outline: 2px solid #bebebe; outline-offset: 4px
- Use hover effects (slight shadow increase or lift)
- Test with keyboard navigation
3. Color Alone
Some users with color blindness may not perceive depth from shadows.
SOLUTION:
- Include text labels ("Active", "Disabled")
- Use aria-pressed, aria-selected for state
- Provide focus states independent of shadow
Best Practices:
- Always test with WCAG color contrast checker
- Provide focus outlines for keyboard users
- Use ARIA attributes to communicate state
- Test with screen readers (shadows are invisible to assistive tech)
Examples
Example 1: Neumorphic Health Dashboard
Input: "Create a wellness dashboard with neumorphic cards showing metrics (steps, water, sleep) and interactive buttons"
export function HealthDashboard() {
const [metrics, setMetrics] = useState({
steps: 7200,
water: 6,
sleep: 7.5
});
const cardStyle = {
background: '#e0e0e0',
borderRadius: '16px',
padding: '24px',
boxShadow: '-12px -12px 24px #ffffff, 12px 12px 24px #bebebe',
textAlign: 'center',
transition: 'box-shadow 300ms ease'
};
const buttonStyle = {
background: '#e0e0e0',
border: 'none',
borderRadius: '12px',
padding: '12px 20px',
fontWeight: '600',
cursor: 'pointer',
boxShadow: '-8px -8px 16px #ffffff, 8px 8px 16px #bebebe',
transition: 'all 200ms ease',
marginTop: '16px'
};
return (
<div style={{ background: '#e0e0e0', minHeight: '100vh', padding: '32px' }}>
<h1 style={{ color: '#4a4a4a', marginBottom: '32px' }}>My Wellness</h1>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(200px, 1fr))', gap: '24px' }}>
<div style={cardStyle}>
<p style={{ color: '#6a6a6a', marginBottom: '8px' }}>Steps</p>
<h2 style={{ color: '#4a4a4a', fontSize: '32px', margin: '0' }}>7,200</h2>
<p style={{ color: '#9a9a9a', margin: '8px 0 0 0' }}>/ 10,000</p>
<button style={buttonStyle}>Log Activity</button>
</div>
<div style={cardStyle}>
<p style={{ color: '#6a6a6a', marginBottom: '8px' }}>Water</p>
<h2 style={{ color: '#4a4a4a', fontSize: '32px', margin: '0' }}>6</h2>
<p style={{ color: '#9a9a9a', margin: '8px 0 0 0' }}>/ 8 cups</p>
<button style={buttonStyle}>Add Water</button>
</div>
<div style={cardStyle}>
<p style={{ color: '#6a6a6a', marginBottom: '8px' }}>Sleep</p>
<h2 style={{ color: '#4a4a4a', fontSize: '32px', margin: '0' }}>7.5</h2>
<p style={{ color: '#9a9a9a', margin: '8px 0 0 0' }}>hours</p>
<button style={buttonStyle}>Log Sleep</button>
</div>
</div>
</div>
);
}
Example 2: Neumorphic Settings Panel
Input: "Build a settings panel with neumorphic toggle switches, text inputs, and a save button"
export function SettingsPanel() {
const [settings, setSettings] = useState({
notifications: true,
darkMode: false,
email: 'user@example.com'
});
const toggleStyle = {
position: 'relative',
width: '60px',
height: '32px',
background: '#e0e0e0',
borderRadius: '16px',
cursor: 'pointer',
boxShadow: 'inset -4px -4px 8px #ffffff, inset 4px 4px 8px #bebebe',
border: 'none',
transition: 'all 200ms ease'
};
const inputStyle = {
width: '100%',
background: '#e0e0e0',
border: 'none',
borderRadius: '12px',
padding: '12px 18px',
fontSize: '14px',
color: '#4a4a4a',
boxShadow: 'inset -4px -4px 8px #ffffff, inset 4px 4px 8px #bebebe',
marginBottom: '24px'
};
const containerStyle = {
background: '#e0e0e0',
borderRadius: '16px',
padding: '32px',
boxShadow: '-16px -16px 32px #ffffff, 16px 16px 32px #bebebe',
maxWidth: '400px',
margin: '0 auto'
};
const buttonStyle = {
width: '100%',
background: '#e0e0e0',
border: 'none',
borderRadius: '12px',
padding: '14px',
fontWeight: '600',
cursor: 'pointer',
boxShadow: '-8px -8px 16px #ffffff, 8px 8px 16px #bebebe',
transition: 'all 200ms ease'
};
return (
<div style={containerStyle}>
<h2 style={{ color: '#4a4a4a', marginBottom: '24px' }}>Settings</h2>
<div style={{ marginBottom: '24px' }}>
<label style={{ color: '#4a4a4a', fontWeight: '600', display: 'block', marginBottom: '12px' }}>
Email Address
</label>
<input
type="email"
value={settings.email}
onChange={(e) => setSettings({ ...settings, email: e.target.value })}
style={inputStyle}
/>
</div>
<div style={{ marginBottom: '24px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<label style={{ color: '#4a4a4a', fontWeight: '600' }}>Enable Notifications</label>
<button
onClick={() => setSettings({ ...settings, notifications: !settings.notifications })}
style={{
...toggleStyle,
boxShadow: settings.notifications
? 'inset -4px -4px 8px #ffffff, inset 4px 4px 8px #bebebe'
: '-8px -8px 16px #ffffff, 8px 8px 16px #bebebe'
}}
aria-label="Toggle notifications"
aria-pressed={settings.notifications}
/>
</div>
<div style={{ marginBottom: '24px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<label style={{ color: '#4a4a4a', fontWeight: '600' }}>Dark Mode</label>
<button
onClick={() => setSettings({ ...settings, darkMode: !settings.darkMode })}
style={{
...toggleStyle,
boxShadow: settings.darkMode
? 'inset -4px -4px 8px #ffffff, inset 4px 4px 8px #bebebe'
: '-8px -8px 16px #ffffff, 8px 8px 16px #bebebe'
}}
aria-label="Toggle dark mode"
aria-pressed={settings.darkMode}
/>
</div>
<button style={buttonStyle}>Save Settings</button>
</div>
);
}