EmpireUI
Get Pro
← Blog7 min read#neumorphism#soft-ui#react-components

Neumorphism Cards: 8 Soft-UI Variants with Accessibility Fixes

8 neumorphism card variants with real code, WCAG contrast fixes, and Tailwind v4 utility classes — no fluffy theory, just components you can actually ship.

Soft neumorphic card UI with light gray background and subtle shadow depth

Neumorphism Cards Are Still Worth Using — With Conditions

Honestly, neumorphism got a bad reputation because most early implementations were inaccessible by design. The entire aesthetic depends on low-contrast shadows that make interactive elements nearly invisible to users with low vision. That's the real problem — not the style itself.

When you apply the right contrast ratios and pair neumorphic shadows with visible focus rings, the style is actually one of the most tactile and satisfying UIs you can ship in 2026. It reads as physical and familiar. Users instinctively understand 'this is a button that depresses.'

This article walks through 8 concrete card variants — flat, pressed, floating, inset, convex, glass-neuro hybrid, dark-mode inversion, and a high-contrast accessible version — with working TSX and CSS. If you want the conceptual background first, what is neumorphism covers the origin and design principles well. We're skipping straight to code.

The Base Shadow System Behind All 8 Variants

Every neumorphic card derives from two shadows — a light source shadow (white or near-white) and a dark shadow (a darkened version of the background color). The shadows are offset in opposite directions. That's it. The whole illusion lives in that pair.

For a base background of #e0e5ec, the canonical shadow pair is 6px 6px 12px rgba(163,177,198,0.6), -6px -6px 12px rgba(255,255,255,0.8). Change the blur to 20px and you get a floaty card. Drop it to 4px and the element looks pressed into the surface. The spread value controls how 'puffy' the element reads.

In Tailwind v4.0.2, you can't express multi-shadow neumorphism with a single utility class out of the box. You'll either extend your tailwind.config.ts with named shadows or use inline style props on the component. For production components, the config extension approach is cleaner and keeps your JSX readable.

// tailwind.config.ts — Tailwind v4.0.2
export default {
  theme: {
    extend: {
      boxShadow: {
        'neu-flat':    '6px 6px 12px rgba(163,177,198,0.6), -6px -6px 12px rgba(255,255,255,0.8)',
        'neu-pressed': 'inset 4px 4px 8px rgba(163,177,198,0.6), inset -4px -4px 8px rgba(255,255,255,0.8)',
        'neu-float':   '10px 10px 20px rgba(163,177,198,0.7), -10px -10px 20px rgba(255,255,255,0.9)',
        'neu-convex':  '6px 6px 12px rgba(163,177,198,0.5), -6px -6px 12px rgba(255,255,255,0.7), inset 1px 1px 2px rgba(255,255,255,0.9)',
        'neu-inset':   'inset 6px 6px 12px rgba(163,177,198,0.5), inset -6px -6px 12px rgba(255,255,255,0.8)',
      },
    },
  },
};

Variants 1–3: Flat, Pressed, and Floating Cards

The flat card is the default raised state — element appears to sit 6–8px above the background. It's the most common usage and what people picture when they hear 'neumorphism.' The pressed variant inverts the shadows to inset, simulating a button being physically pushed down. You'd typically toggle between these two on mousedown/mouseup for interactive elements.

The floating card pushes the blur and offset further: 10–14px offset, 20–28px blur. It creates a dramatic lifted look, good for modal dialogs or feature highlight cards. One thing to watch: floating cards look wrong if the background isn't a uniform mid-gray. On busy backgrounds or gradients, the shadow edges become muddy.

// NeuCard.tsx — variants 1-3
import { cn } from '@/lib/utils';

type Variant = 'flat' | 'pressed' | 'float';

interface NeuCardProps {
  variant?: Variant;
  children: React.ReactNode;
  className?: string;
}

const variantStyles: Record<Variant, string> = {
  flat:    'shadow-neu-flat',
  pressed: 'shadow-neu-pressed',
  float:   'shadow-neu-float',
};

export function NeuCard({ variant = 'flat', children, className }: NeuCardProps) {
  return (
    <div
      className={cn(
        'rounded-2xl bg-[#e0e5ec] p-6 transition-shadow duration-200',
        variantStyles[variant],
        className
      )}
    >
      {children}
    </div>
  );
}

Note the transition-shadow duration-200 — that's what makes hover and press states feel physical rather than janky. 150–200ms is the right range for shadow transitions. Faster feels snappy but loses the tactile read; slower feels sluggish.

Variants 4–5: Convex and Inset (and When Each Makes Sense)

The convex variant adds a subtle inner highlight on top of the outer shadows, making the card look like a slightly curved physical surface — like a pill or puck. It's best for icon containers, avatar wrappers, or small stat cards. Don't use it on large panels; the curvature illusion breaks down at big sizes.

The inset variant is the container equivalent of pressed. Where pressed is an interactive state, inset is a structural state — it communicates 'this is a well or trough holding content,' not a button. Think input fields, code blocks, or metric displays inside a dashboard. If you're building a theme-toggle-react component, an inset container for the toggle track reads extremely naturally in neumorphic UIs.

Both variants are in the Tailwind config we defined above. Wire them the same way as variants 1–3 by extending the variantStyles map. One gotcha: the convex shadow uses three shadow layers, and some older mobile Safari versions render triple box-shadows with visible seams. Test on iOS 16 if your audience is mobile-heavy.

Variants 6–7: Glass-Neuro Hybrid and Dark-Mode Inversion

The glass-neuro hybrid combines neumorphic shadows with a frosted-glass backdrop: backdrop-blur-md and rgba(255,255,255,0.15) background. It's the crossover point between neumorphism and glassmorphism. The glassmorphism vs neumorphism comparison explains why these two aesthetics are actually compatible — both rely on depth perception, just through different mechanisms.

For dark-mode, you can't just invert the shadow colors — you need to rethink the base. On a dark background like #1e2329, the light shadow becomes a subtle rgba(255,255,255,0.05) and the dark shadow becomes rgba(0,0,0,0.5). The contrast ratio between shadow colors has to stay tight, or the effect disappears entirely on OLED displays.

/* globals.css — dark-mode neumorphism */
.neu-dark {
  background: #1e2329;
  box-shadow:
    6px 6px 14px rgba(0, 0, 0, 0.5),
    -6px -6px 14px rgba(255, 255, 255, 0.04);
  border-radius: 16px;
}

.neu-dark.pressed {
  box-shadow:
    inset 4px 4px 8px rgba(0, 0, 0, 0.6),
    inset -4px -4px 8px rgba(255, 255, 255, 0.03);
}

/* Glass-Neuro hybrid */
.neu-glass {
  background: rgba(255, 255, 255, 0.15);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow:
    6px 6px 16px rgba(163, 177, 198, 0.4),
    -6px -6px 16px rgba(255, 255, 255, 0.7),
    inset 0 1px 0 rgba(255, 255, 255, 0.6);
  border-radius: 20px;
  border: 1px solid rgba(255, 255, 255, 0.3);
}

The inset 0 1px 0 rgba(255,255,255,0.6) line on the glass variant is doing a lot of work — it simulates light catching the top edge of the glass surface. Remove it and the glass-neuro effect looks flat despite the blur. It's a small detail that matters.

Variant 8: The Accessible Neumorphism Card

Here's the thing: most neumorphic implementations fail WCAG 2.1 AA on non-text contrast (3:1 minimum for UI components). The solution isn't to abandon the aesthetic — it's to add a visible border that carries the contrast independently of the shadow. Think of the shadow as decoration and the border as the accessibility signal.

For the accessible variant, add a 1px solid rgba(163,177,198,0.6) border on the card. That border against the #e0e5ec background clears 3:1 on most calibrated displays. Then add a focus ring that's 2px solid with at least 3:1 contrast against both the card and the background — outline: 2px solid #4a6fa5 works well and doesn't fight the neumorphic aesthetic.

Do focus rings feel out of place in neumorphic UI? A little, yes. But keyboard users aren't experiencing the aesthetic the same way mouse users are — they need that visible indicator. What you can do is use a neumorphic-style focus that mimics the pressed state while also being clearly visible: combine the inset shadow with a colored outline. It's not perfect but it's honest.

Contrast test all text colors independently. On #e0e5ec, body text needs to be at least #767676 to pass AA — but #888 is too light. Use #555 for body and #333 for headings. Run everything through the WebAIM Contrast Checker before shipping.

Animating Neumorphism Cards Without Breaking the Illusion

The flat-to-pressed transition on click is the canonical neumorphic animation. Keep it between 150–200ms with an ease-out curve. Any easing that overshoots (spring, bounce) destroys the physical metaphor — real surfaces don't bounce when you press them.

Hover states are where most implementations go wrong. Don't float the card on hover — that's a common mistake that makes the interaction model confusing. Instead, use a very subtle shadow intensification: increase the blur from 12px to 14px and the opacity from 0.6 to 0.65. The user should barely notice the hover state; they're just priming for a click.

For entrance animations, opacity + transform: translateY(4px) works cleanly without interfering with the box-shadow. If you're generating multiple cards in a grid — say from a particles-background-react layout or a dashboard grid — stagger the entrance with animation-delay: calc(var(--index) * 60ms). It's a nice touch that doesn't require a motion library.

Using These Cards in a Real Empire UI Project

Empire UI ships neumorphic card components under the soft-ui style preset. You can toggle between all 8 variants via the variant prop and switch the color theme via CSS custom properties — no Tailwind config changes needed if you use the component package directly.

If you're assembling a dashboard or landing page, pair neumorphic stat cards (convex or flat) with neumorphic input fields (inset) and neumorphic buttons (pressed on active). Mixing variants creates visual hierarchy. All-flat is boring. All-pressed is confusing. The contrast between surface types is what makes the depth system readable.

For projects where you're comparing style systems — say evaluating whether soft-ui or brutalism fits your brand — the what is neobrutalism article is worth reading alongside this one. They're interesting counterpoints: neobrutalism maximizes contrast and sharpness; neumorphism minimizes both. Knowing which one fits your product context saves you two weeks of second-guessing design decisions.

FAQ

Why does neumorphism look flat on my dark background?

Dark-mode neumorphism needs tightly tuned shadow colors. Your light shadow should be rgba(255,255,255,0.04)–0.07 and your dark shadow rgba(0,0,0,0.4)–0.6. If the background is too dark (below #1a1a1a) or too light (above #2e2e2e), the shadow delta isn't enough to read. Stick to a mid-dark background like #1e2329 or #23282e.

Can I use neumorphism cards in Tailwind v4 without a config extension?

You can use inline style props for one-offs — style={{ boxShadow: '6px 6px 12px rgba(163,177,198,0.6), -6px -6px 12px rgba(255,255,255,0.8)' }} — but for anything you'll reuse more than twice, extending the theme in tailwind.config.ts is much cleaner. Tailwind v4.0.2 supports arbitrary values in square brackets (e.g. shadow-[...]) but multi-shadow syntax with commas breaks the bracket syntax.

How do I make neumorphic cards pass WCAG 2.1 AA?

Add a visible border (1px solid) that achieves 3:1 contrast against the background independently of shadows. Shadows don't count toward WCAG non-text contrast — they're treated as decorative. Also ensure all text meets 4.5:1 for normal text and 3:1 for large text (18px+ bold or 24px+ regular). On a #e0e5ec base, use #555 or darker for body copy.

What's the correct border-radius for neumorphism cards?

There's no single correct value, but 12–20px is the practical range. Below 8px the soft-UI effect reads as inconsistent — the shadow radius and corner radius fight each other visually. Above 24px you're into pill territory, which only works for small elements like buttons or badges, not full cards.

Does neumorphism work with CSS Modules or only Tailwind?

It works with anything that can express box-shadow. CSS Modules, vanilla CSS, styled-components, Emotion — all fine. Tailwind just makes the variant-switching pattern cleaner via class composition. The shadow values themselves are pure CSS.

Can I animate between flat and pressed states with Framer Motion?

Yes. Use the boxShadow property in your variants object — Framer Motion interpolates box-shadow values correctly as long as both states have the same number of shadow layers. Mismatched layer counts (e.g. flat has 2 shadows, pressed has 2 inset shadows) interpolate fine in modern browsers but can stutter in Firefox 120 and below.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Soft UI Buttons: 12 Neumorphic Variants with Hover StatesNeumorphism in 2027: Is the Soft UI Trend Making a Comeback?Mobile App UI with Tailwind: Bottom Nav, Cards, GesturesReact Aria + Tailwind: Accessible Components with Utility Classes