Soft-UI got abandoned for good reasons. Low-contrast text on a low-contrast surface, buttons distinguished from backgrounds only by a shadow, and nothing at all for anyone using a high-contrast mode. Those were implementation failures, not properties of the aesthetic.
The rule: soft chrome, hard ink
Depth belongs to the material — surfaces, containers, controls. Contrast belongs to the content — text, icons, focus indicators.
In practice: a card can be a soft extrusion of the background, and the text inside it is still near-black on light or near-white on dark. The moment you tint text toward the surface to "match the aesthetic", you have broken it. The aesthetic lives in the shadow, not the type.
Concretely, on a #e8ebf0 surface, body text at #12151a clears 14:1. Nothing about the softness required a compromise.
Never let depth be the only affordance
A shadow is invisible in forced-colours mode, hard to perceive with low vision, and meaningless to a screen reader. So every control needs a second signal:
- A text label or an accessible name.
- A focus ring that does not rely on the shadow — a solid 2px accent outline with an offset.
- An accent LED or icon carrying state, so "pressed" is not communicated by inversion alone.
- Real `aria-pressed` / `aria-current` / `role="slider"` semantics.
The nice thing about this constraint is it improves the design. A knob that also has tap-target chips beneath it is both more delightful and more usable than a knob alone.
Inversion as the press state
The single detail that makes soft-UI feel physical: on :active, a raised control becomes a pressed one. The same shadow definition, inverted to inset, plus a 1px translate.
It costs almost nothing and it is the difference between a card that looks like a button and something that behaves like one.
Two themes, one depth engine
Define depth once as custom properties and re-point them per theme:
- Light: shadow is a cool grey, highlight is pure white.
- Dark: shadow goes near-black, highlight becomes a lifted grey — never white, which reads as plastic glare.
Because every component references var(--d-2) rather than a literal shadow, the whole system re-materialises on theme switch with no component changes. Dark neumorphism needs a slightly stronger shadow and a weaker highlight than light, which is a nuance most implementations miss.
Respect reduced motion, and mean it
Soft-UI invites pointer-tracked highlights, tilt and magnetic hover. All of it must collapse cleanly under prefers-reduced-motion: reduce — and "cleanly" means the static state is complete on its own, not a broken frame of an animation that never ran.
Test it by forcing the setting on and reading the whole page. If anything is missing or mid-transform, the fallback was an afterthought.
Where it genuinely does not belong
Dense data tables, long-form reading, and anything where the user's job is to scan hundreds of rows. Depth on every cell is noise. Use it for the frame, then let the table be flat and quiet inside it.
Aman Boora
Frappe/ERPNext developer and founder of Google IT Solution. Builds production ERP, pharmacy and logistics systems, and runs the Maps and SEO side that gets those clients found in the first place. Certified Ethical Hacker.