EmpireUI
Get Pro
← Blog8 min read#claymorphism#navbar#css

Claymorphism Navbar: Soft 3D Navigation With Clay Buttons

Build a claymorphism navbar with soft 3D clay buttons using pure CSS and React — complete with hover states, shadows, and a production-ready component.

Soft 3D clay-style navigation bar with rounded colorful buttons

What Makes a Navbar Feel Like Clay?

Claymorphism is about tactile weight. Not the sharp drop shadows of flat design, not the translucent shimmer of glassmorphism — clay UI elements look like they're made of soft, slightly squeezable material that catches light from above. Applied to a navbar, that means buttons that pop off the background with a thick, multi-layered shadow and enough border-radius to make a rectangle look almost blob-like.

The visual trick is stacking two shadows. One is a tight, dark under-shadow (usually 3–5px offset) that grounds the element. The second is a softer, larger blur spread in a lighter or complementary tone that sells the illusion of volume. That combination — plus a bright, saturated fill color and a 14–20px border-radius — is all CSS, zero JS, and it works in every modern browser as of 2026.

Honestly, the hardest part isn't the CSS. It's restraint. Clay navbars look incredible with 4–5 nav items and breathing room between them. Pack in 10 links and it reads as noise. The fat buttons demand space. Give it to them. If you want to see how claymorphism handles the full component spectrum, Empire UI's claymorphism hub has everything from cards to modals done right.

Worth noting: claymorphism shares DNA with neumorphism but is fundamentally friendlier. Neumorphism relies on the background being a near-identical hue to the element — which kills contrast. Clay elements are *colored*, which means accessibility is way less of a headache as long as you pick text colors correctly.

The Core CSS — Clay Buttons From Scratch

Let's start with the single most important property: box-shadow. A clay button in 2026 typically uses a compound shadow value that combines an inset highlight at the top (simulating light coming from above) with an outer drop shadow at the bottom. Here's the base formula:

.clay-btn {
  background: #6C63FF;
  color: #fff;
  border: none;
  border-radius: 16px;
  padding: 10px 24px;
  font-weight: 600;
  font-size: 0.95rem;
  cursor: pointer;

  /* The clay stack */
  box-shadow:
    0 6px 0 #4B44CC,          /* hard bottom edge — the 'thickness' */
    0 8px 16px rgba(0,0,0,0.25); /* soft ambient shadow             */

  /* Subtle top highlight */
  background: linear-gradient(
    to bottom,
    #7B74FF 0%,
    #6C63FF 100%
  );

  transition: transform 80ms ease, box-shadow 80ms ease;
}

.clay-btn:hover {
  transform: translateY(-2px);
  box-shadow:
    0 8px 0 #4B44CC,
    0 12px 20px rgba(0,0,0,0.28);
}

.clay-btn:active {
  transform: translateY(4px);
  box-shadow:
    0 2px 0 #4B44CC,
    0 4px 8px rgba(0,0,0,0.2);
}

The translateY on :active is what sells it as a physical press. Without it the button looks like a flat rectangle with a shadow. With it, the element *pushes down* into the surface — that 4px translate paired with the collapsed shadow is all the JS-free press feedback you need.

One more thing — the hard bottom edge (0 6px 0 #4B44CC) should always be a darker shade of your fill color, not black. Using black here immediately looks like a cartoon drop shadow from 2008. Sample the fill with a CSS color tool, drop lightness by 25–30%, and you get a shadow that reads as the underside of a three-dimensional object.

Quick aside: if you're iterating on shadow values manually, the box shadow generator on Empire UI will save you real time — you can dial in both the outer and inner shadow layers with live preview before copying the CSS.

Building the Full Claymorphism Navbar Component in React

A production navbar needs more than a styled button. You need an active state that visually pins the current page, a mobile-responsive collapse, and a wrapper that handles the clay background surface. Here's a complete component:

// ClayNavbar.tsx
'use client';
import { useState } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';

const NAV_LINKS = [
  { href: '/', label: 'Home' },
  { href: '/components', label: 'Components' },
  { href: '/templates', label: 'Templates' },
  { href: '/pricing', label: 'Pricing' },
];

export function ClayNavbar() {
  const pathname = usePathname();
  const [open, setOpen] = useState(false);

  return (
    <nav
      style={{
        background: '#F0EDFF',
        borderRadius: '20px',
        padding: '12px 20px',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'space-between',
        boxShadow: '0 8px 24px rgba(108,99,255,0.15)',
        margin: '16px',
      }}
    >
      <span style={{ fontWeight: 800, fontSize: '1.2rem', color: '#3D3780' }}>
        YourBrand
      </span>

      {/* Desktop links */}
      <ul
        style={{
          display: 'flex',
          gap: '8px',
          listStyle: 'none',
          margin: 0,
          padding: 0,
        }}
        className="clay-nav-list"
      >
        {NAV_LINKS.map(({ href, label }) => {
          const active = pathname === href;
          return (
            <li key={href}>
              <Link
                href={href}
                style={{
                  display: 'inline-block',
                  padding: '8px 20px',
                  borderRadius: '14px',
                  fontWeight: 600,
                  textDecoration: 'none',
                  fontSize: '0.9rem',
                  background: active
                    ? 'linear-gradient(to bottom, #7B74FF, #6C63FF)'
                    : 'linear-gradient(to bottom, #E8E4FF, #DDD8FF)',
                  color: active ? '#fff' : '#5048C0',
                  boxShadow: active
                    ? '0 5px 0 #4B44CC, 0 7px 14px rgba(0,0,0,0.2)'
                    : '0 4px 0 #C5BFEF, 0 6px 12px rgba(0,0,0,0.08)',
                  transform: active ? 'translateY(1px)' : 'translateY(0)',
                  transition: 'all 80ms ease',
                }}
              >
                {label}
              </Link>
            </li>
          );
        })}
      </ul>

      {/* CTA */}
      <button
        style={{
          background: 'linear-gradient(to bottom, #FF7E67, #FF5C3F)',
          color: '#fff',
          border: 'none',
          borderRadius: '14px',
          padding: '10px 22px',
          fontWeight: 700,
          cursor: 'pointer',
          boxShadow: '0 5px 0 #CC3A22, 0 8px 16px rgba(255,92,63,0.3)',
          transition: 'transform 80ms ease, box-shadow 80ms ease',
        }}
        onMouseDown={(e) => {
          (e.currentTarget as HTMLButtonElement).style.transform = 'translateY(4px)';
          (e.currentTarget as HTMLButtonElement).style.boxShadow =
            '0 1px 0 #CC3A22, 0 3px 8px rgba(255,92,63,0.2)';
        }}
        onMouseUp={(e) => {
          (e.currentTarget as HTMLButtonElement).style.transform = 'translateY(0)';
          (e.currentTarget as HTMLButtonElement).style.boxShadow =
            '0 5px 0 #CC3A22, 0 8px 16px rgba(255,92,63,0.3)';
        }}
      >
        Get Started
      </button>
    </nav>
  );
}

The active link is translated down by 1px and uses the filled color — it looks *pressed*, like the current page has weight. That small detail communicates state without needing a colored underline or a badge. It's one of those micro-interactions that users feel before they consciously notice it.

In practice, you'd pull the inline styles into a CSS module or Tailwind classes. I've kept them inline here so the mapping between property and visual effect stays obvious. Once you've got it working, a CSS module refactor takes about 10 minutes and makes the hover/active states cleaner via proper pseudo-class selectors rather than JS event handlers.

That said, for production usage the claymorphism components on Empire UI are already fully extracted into reusable, typed React components — navbar, buttons, cards, badges — so you're not hand-rolling any of this from scratch unless you want to.

Tailwind Version: Clay Navbar Without Writing Any CSS

If you're a Tailwind-first developer, you can get very close to the clay look without dropping into arbitrary values — Tailwind 3.4+ shadow utilities plus the rounded-2xl class get you 80% of the way there. The remaining 20% — the hard bottom edge and the inset top highlight — require shadow-[...] arbitrary syntax.

// TailwindClayButton.tsx
export function ClayButton({
  children,
  active = false,
}: {
  children: React.ReactNode;
  active?: boolean;
}) {
  return (
    <button
      className={[
        'px-5 py-2 rounded-[14px] font-semibold text-sm',
        'transition-all duration-75',
        'active:translate-y-1',
        active
          ? [
              'bg-gradient-to-b from-[#7B74FF] to-[#6C63FF] text-white',
              'shadow-[0_5px_0_#4B44CC,0_7px_14px_rgba(0,0,0,0.20)]',
              'translate-y-px',
            ].join(' ')
          : [
              'bg-gradient-to-b from-[#E8E4FF] to-[#DDD8FF] text-[#5048C0]',
              'shadow-[0_4px_0_#C5BFEF,0_6px_12px_rgba(0,0,0,0.08)]',
              'hover:-translate-y-0.5',
              'hover:shadow-[0_6px_0_#C5BFEF,0_10px_16px_rgba(0,0,0,0.12)]',
            ].join(' '),
      ].join(' ')}
    >
      {children}
    </button>
  );
}

Look, arbitrary Tailwind shadow values get messy fast. The shadow-[0_5px_0_#4B44CC,...] syntax works, but if you're using the same shadow on 15 components you'll want to define it as a custom boxShadow token in tailwind.config.ts under theme.extend.boxShadow. That keeps the class names readable (shadow-clay-active) and the values in one place.

// tailwind.config.ts
import type { Config } from 'tailwindcss';

const config: Config = {
  theme: {
    extend: {
      boxShadow: {
        'clay':        '0 4px 0 #C5BFEF, 0 6px 12px rgba(0,0,0,0.08)',
        'clay-hover':  '0 6px 0 #C5BFEF, 0 10px 16px rgba(0,0,0,0.12)',
        'clay-active': '0 5px 0 #4B44CC, 0 7px 14px rgba(0,0,0,0.20)',
        'clay-press':  '0 1px 0 #4B44CC, 0 3px 6px rgba(0,0,0,0.15)',
      },
    },
  },
};

export default config;

Once those tokens are registered you get shadow-clay, shadow-clay-hover etc. as first-class Tailwind utilities — and your JSX reads like intent, not implementation.

Color Palettes That Work for Clay Navigation

Clay UI lives and dies by its palette. The style was popularized heavily in 2022 during the Dribbble clay-3D explosion — lots of purple, coral, mint, and yellow. Those still work, but by 2026 the more interesting applications use muted, dusty versions: dusty lavender (#DDD8FF), warm sand (#FFE8D0), sage green (#D4EDDA), and soft sky (#D0E8FF). They're less loud than the original palette and hold up better in real products.

Whatever palette you pick, the rule is: the clay button fill should be a 40–60% saturation color, the hard shadow should be that color at 25–30% lower lightness, and the navbar background surface should be an even lighter (85–90% lightness) tint of the same hue. That single-hue-family approach is what makes the whole thing feel cohesive rather than a box of crayons.

If you're having trouble picking values, the gradient generator on Empire UI is useful for finding complementary hues — you can see how colors play against each other before committing. And if you want to explore claymorphism applied across a full component set with consistent tokens, the claymorphism page shows exactly how the palette maps across buttons, cards, and form elements.

One palette pattern worth stealing: use a neutral clay background (off-white with a warm or cool tint) for the navbar wrapper, then give each nav item its own unique accent — a coral 'Home', a lavender 'Components', a mint 'Templates'. It reads as playful without being chaotic and gives the navigation a memorable visual fingerprint.

Responsive Behavior and Mobile Clay Nav

The fat padding on clay buttons — usually 10–12px top/bottom, 20–24px left/right — means a 5-item desktop navbar is roughly 520px wide before you even add the logo and CTA. On mobile (375px viewport) that collapses into garbage without a proper responsive strategy. You've got two options: a hamburger-triggered drawer, or a horizontal scroll container.

For most marketing sites, the drawer is the right call. Here's the pattern: below 768px, hide the full nav list with display: none and show a clay-styled hamburger button. Clicking it absolutely positions a panel from the top of the screen with the nav items stacked vertically, each spanning full width. The clay treatment on vertical stacked items actually looks *better* on mobile — the buttons feel more touchable with their physical depth.

// Mobile clay nav drawer skeleton
<div className="fixed inset-x-4 top-4 z-50 rounded-[20px] bg-[#F0EDFF] p-4
                shadow-[0_8px_32px_rgba(108,99,255,0.2)] md:hidden">
  <div className="flex items-center justify-between mb-4">
    <span className="font-black text-[#3D3780]">YourBrand</span>
    <button onClick={() => setOpen(false)} className="clay-btn-sm">✕</button>
  </div>
  <ul className="flex flex-col gap-3">
    {NAV_LINKS.map(({ href, label }) => (
      <li key={href}>
        <Link href={href}
          className="block w-full text-center py-3 rounded-[14px] font-semibold
                     bg-gradient-to-b from-[#E8E4FF] to-[#DDD8FF] text-[#5048C0]
                     shadow-[0_4px_0_#C5BFEF]">
          {label}
        </Link>
      </li>
    ))}
  </ul>
</div>

The horizontal-scroll approach works well for apps (tabs-style navigation at the bottom) but poorly for top navbars — users rarely think to scroll a navbar. Stick with the drawer for top-positioned clay navs. If you need full page-level starter templates with responsive clay navigation already wired up, check out the templates section — several industry starters ship with clay-compatible nav patterns.

That said, always test on a real device. The box-shadow performance on an entry-level Android phone (think 2023-era Moto G) can stutter if you have too many clay surfaces animating simultaneously during the drawer open/close transition. Keep the drawer animation to transform and opacity only — both are compositor-thread properties and won't trigger layout recalculation.

Accessibility and When to Avoid This Style

The good news: clay buttons have inherent contrast advantages over glassmorphism and neumorphism because they use solid, saturated fills. A white label on a #6C63FF purple background clears the WCAG AA 4.5:1 contrast ratio comfortably. The bad news: the playful aesthetic is a mismatch for certain audiences and contexts.

Does your product serve healthcare, legal, or financial users? Probably skip claymorphism for navigation — the softness reads as toy-like and can undermine trust. Does your brand palette center on dark, muted, or monochromatic colors? Clay elements need saturation to work; a desaturated dark-mode clay navbar tends to look like a mistake rather than a style choice. Save clay for light-mode interfaces with vibrant palettes, or as an accent component within an otherwise neutral design system.

Focus states are the one accessibility debt clay navbars tend to accumulate. The default browser focus ring (:focus-visible) is often invisible against the clay button fill at certain color values. Always add an explicit focus style — a 2px offset outline in a high-contrast color works perfectly: outline: 2px solid #3D3780; outline-offset: 3px;. That keeps keyboard navigation usable without breaking the visual design.

In practice, claymorphism navbars are a great fit for: SaaS apps targeting creative or consumer audiences, portfolio sites, educational products, gaming UI, and landing pages for apps with a fun or playful positioning. If any of those describe your product, browse the full claymorphism component set and you'll have a production-ready navbar in under 30 minutes.

FAQ

What CSS properties create the clay effect on a navbar?

The clay look comes from two things: a compound box-shadow with a hard-offset dark layer (simulating the 3D bottom edge) plus a soft ambient blur, and a subtle linear-gradient fill from a slightly lighter to a slightly darker shade of your base color. Add border-radius: 14–16px and you're there.

How do I show the active link in a claymorphism navbar?

Apply the filled, saturated version of your clay button style to the active link, and combine it with a slight translateY(1–2px) and a collapsed shadow — this makes it look pressed down, which visually anchors it as the current page. Inactive links should use a lighter, lower-saturation version of the same hue.

Does claymorphism work in dark mode?

It can, but it's harder. Dark clay backgrounds need to stay at mid-range lightness (not near-black) so the shadow layers stay visible, and saturation needs to stay high. Most designers keep claymorphism light-mode-only and switch to a different style token — like a deeper neobrutalism or glassmorphism variant — for dark mode.

Can I build a clay navbar with Tailwind only, without writing custom CSS?

Yes, using Tailwind's arbitrary shadow-[...] syntax for the compound box-shadow and bg-gradient-to-b for the fill. For maintainability, define custom boxShadow tokens in tailwind.config.ts (shadow-clay, shadow-clay-active) so your class names stay readable rather than repeating long arbitrary values everywhere.

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

Read next

Claymorphism Card Components: 3D Puffy Cards With Soft ShadowsClaymorphism Button: 3D Clay Press Animation in CSSCSS Flip Card: 3D Rotate Animation With and Without JavaScriptNavbar Design Patterns in React: 6 Architectures That Scale