Neumorphism (or soft UI) is the design style where elements appear extruded from the background itself, as if the interface were milled from a single piece of soft plastic. It achieves that effect with something as simple as two well-calculated simultaneous shadows. It is a visually appealing style with a serious accessibility trap you should know about before adopting it.
The key: two shadows in opposite directions
A neumorphic button has no border and no outline. The sense of relief is built exclusively with box-shadow:
.neu {
background: #e0e5ec;
border-radius: 32px;
box-shadow:
10px -7px 24px #ffffff,
-10px 7px 24px #c1c5cb;
}
The logic mimics an object lit from the top-right:
- Light shadow (white or nearly) cast in the direction opposite to the light: simulates the highlight on the edge receiving direct light.
- Dark shadow cast in the same direction as the light: simulates the part of the element falling into shade against the background.
The element's background must be exactly the same color as the page background. If it differs even slightly, the trick falls apart: it stops looking like extruded material and starts looking like a rectangle floating on top.
Light and dark colors are not picked by hand
In a coherent neumorphic system, shadow tones are mathematically derived from the base color by multiplying each RGB channel:
function shift(hex, factor) {
const clamp = v => Math.max(0, Math.min(255, Math.round(v)));
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
const toHex = v => clamp(v).toString(16).padStart(2, "0");
return "#" + toHex(r * factor) + toHex(g * factor) + toHex(b * factor);
}
const base = "#e0e5ec";
const light = shift(base, 1 + intensity); // factor > 1 lightens
const dark = shift(base, 1 - intensity); // factor < 1 darkens
With a 14% intensity over #e0e5ec, the light red channel would be 224 × 1.14 = 255 (saturated), and the dark one 224 × 0.86 ≈ 193. This derivation guarantees that any base color you choose automatically produces harmonious shadows.
The geometry: light angle
Shadow positions come from basic trigonometry. Given a light angle θ (in degrees) and a distance d:
const rad = (angle * Math.PI) / 180;
const dx = Math.round(Math.cos(rad) * distance);
const dy = Math.round(Math.sin(rad) * distance);
const lightShadow = `${-dx} ${-dy} ${blur} ${lightColor}`;
const darkShadow = `${dx} ${dy} ${blur} ${darkColor}`;
The light shadow sits at exactly the opposite point of the dark one relative to the element's center. Changing the light angle moves both at once, like rotating a lamp around the object.
The pressed state with inset
A neumorphic button needs click feedback, and the canonical response is "sinking in": the same two shadows but pointing inward:
.neu:active {
box-shadow:
inset 10px -7px 24px #ffffff,
inset -10px 7px 24px #c1c5cb;
}
The inset keyword flips each shadow's direction. The perceived effect shifts from "sticks out" to "is pressed into the surface", just like a physical button. Combined with a micro scale reduction (transform: scale(0.98)), the tactile feel is very convincing.
The serious problem: contrast
Here lies the reason neumorphism never conquered the world despite its Dribbble popularity: contrast between element and background is near zero by definition. A low-vision user cannot tell where the button is; someone with motor tremors loses visual reference for the control.
If you use it, mitigate like this:
- Reserve neumorphism for decorative elements or interfaces where users already know where everything is (a music player, a personal dashboard).
- Add subtle reinforcements: a 1px border with very low alpha or a slight text color variation.
- Never use it for critical navigation elements or destructive actions.
- Verify hover/focus states with additional indicators (outline, font weight change).
Neumorphism works best as an aesthetic accent on large surfaces (cards, sliders, toggles) rather than as the interface's complete language.
Distance, blur and intensity: how they interact
The three numeric parameters of the style govern each other:
- Distance separates shadows from the element: more distance = more pronounced relief. Below 4px the effect almost disappears.
- Blur defines how soft the shadow gradient is. A value between 1.5× and 2× the distance usually gives the most natural result.
- Intensity (how much the base color is lightened or darkened) determines how visible the effect is. Above 20% it starts looking dirty; below 8% it becomes invisible on low-contrast screens.
Generate your values without doing math
Calculating angles, color factors and testing combinations by hand is tedious. Our Neumorphism Generator does the conversions for you: pick base color, light angle, distance, blur and intensity, and get both the normal and pressed states ready to copy.
FAQ
Neumorphism or glassmorphism? They are different, compatible trends: neumorphism plays with monochrome shadows on flat backgrounds; glassmorphism with transparency and blur over colorful backgrounds. You can use neumorphism on a panel and glassmorphism on a modal within the same product.
Does it work in dark mode? Yes, with nuances: on dark backgrounds the light shadows gain prominence and intensity must be reduced. The typical neumorphic dark mode base color hovers around #2d3239 with proportional derivatives.
Does it affect performance? Two box-shadow declarations per element is negligible cost. As with any shadow, avoid animating shadow parameters directly; animate transform if you need fluid transitions.
Create your neumorphic style with live preview of both states using the Neumorphism Generator, free and no sign-up.