Pure CSSMIT licensed

Clay Depth Tiers for Visual Hierarchy

Four elevation tiers — inset well, resting, raised, floating — driven by a single --elev number per element. Every shadow length, blur and highlight strength is computed from it with calc(), so a component changes tier by changing one integer. Hover promotes a card one tier, which is how clay communicates interactivity without borders or color changes.

Live Demo
Plain CSSTailwind v4
Try it

Untitled UI Pro: a 10,000+ component Figma UI kit · from $129 one-time

Untitled UI · Affiliate link

Get Untitled UI Pro

The code

<section class="cl-07" aria-label="Clay depth tiers for visual hierarchy demo">
  <div class="cl-07__stage">
    <header class="cl-07__head">
      <h2 class="cl-07__h">Four tiers, one number</h2>
      <p class="cl-07__lede">Hover any tile: it is promoted exactly one tier. Every length below is calc()-derived from <code>--elev</code>.</p>
    </header>
    <div class="cl-07__well">
      <p class="cl-07__wellLabel">Tier 0 — inset well · holds content</p>
      <div class="cl-07__row">
        <article class="cl-07__tile" style="--cl-07-elev:1">
          <b class="cl-07__t">Tier 1</b>
          <span class="cl-07__d">Resting surface</span>
          <span class="cl-07__use">lists, groups, rows</span>
        </article>
        <article class="cl-07__tile" style="--cl-07-elev:2">
          <b class="cl-07__t">Tier 2</b>
          <span class="cl-07__d">Raised surface</span>
          <span class="cl-07__use">cards, buttons, tabs</span>
        </article>
        <article class="cl-07__tile" style="--cl-07-elev:3">
          <b class="cl-07__t">Tier 3</b>
          <span class="cl-07__d">Floating surface</span>
          <span class="cl-07__use">menus, popovers, FABs</span>
        </article>
      </div>
    </div>
    <p class="cl-07__chip" aria-hidden="true">--elev drives shadow length + blur + highlight + translate</p>
  </div>
</section>
.cl-07,
.cl-07 *,
.cl-07 *::before,
.cl-07 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cl-07 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--cl-07-bg);
  --cl-07-bg: oklch(0.93 0.03 15);
  --cl-07-clay: oklch(0.88 0.06 20);
  --cl-07-shade: oklch(0.58 0.1 18/.55);
  --cl-07-light: oklch(1 0.015 40/.95);
  --cl-07-ink: oklch(0.3 0.05 20);
  --cl-07-mut: oklch(0.51 0.04 20);
  font-family: system-ui,-apple-system,'Segoe UI',sans-serif;
  color: var(--cl-07-ink);
}

.cl-07__stage {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: clamp(22px,4vw,36px);
  padding: clamp(20px,5vw,58px);
  background: radial-gradient(80% 55% at 50% 0%,oklch(0.97 0.03 30),transparent 66%);
}

.cl-07__head {
  text-align: center;
  max-width: 58ch;
  display: grid;
  gap: 9px;
}

.cl-07__h {
  font-size: clamp(24px,3.8vw,34px);
  font-weight: 780;
  letter-spacing: -.02em;
  text-wrap: balance;
}

.cl-07__lede {
  font-size: 15px;
  line-height: 1.6;
  color: var(--cl-07-mut);
  text-wrap: pretty;
}

.cl-07__lede code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 13.5px;
}

.cl-07__well {
  width: min(100%,900px);
  display: grid;
  gap: 16px;
  padding: clamp(20px,3vw,32px);
  border-radius: 44px;
  background: color-mix(in oklch,var(--cl-07-bg) 94%,oklch(0.7 0.06 20));
  box-shadow: inset 0 14px 26px -10px var(--cl-07-shade),inset 0 -12px 22px -12px var(--cl-07-light);
}

.cl-07__wellLabel {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--cl-07-mut);
  text-align: center;
}

.cl-07__row {
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(196px,1fr));
  gap: clamp(16px,2.6vw,26px);
  align-items: center;
}

.cl-07__tile {
  --cl-07-elev: 2;
  display: grid;
  gap: 5px;
  align-content: center;
  min-height: 150px;
  padding: 24px;
  border-radius: 34px;
  background: var(--cl-07-clay);
  translate: 0 calc(var(--cl-07-elev) * -1px);
  box-shadow: 0 calc(var(--cl-07-elev) * 9px) calc(var(--cl-07-elev) * 18px) calc(var(--cl-07-elev) * -5px) var(--cl-07-shade),inset 0 calc(var(--cl-07-elev) * -5px) calc(var(--cl-07-elev) * 9px) -8px var(--cl-07-shade),inset 0 calc(var(--cl-07-elev) * 6px) calc(var(--cl-07-elev) * 9px) -6px var(--cl-07-light);
  transition: box-shadow .4s cubic-bezier(.4,0,.2,1),translate .4s cubic-bezier(.34,1.3,.5,1),scale .4s cubic-bezier(.34,1.3,.5,1);
  cursor: pointer;
}

.cl-07__tile:nth-child(1):hover,
.cl-07__tile:nth-child(1):focus-visible {
  --cl-07-elev: 2;
}

.cl-07__tile:nth-child(2):hover,
.cl-07__tile:nth-child(2):focus-visible {
  --cl-07-elev: 3;
}

.cl-07__tile:nth-child(3):hover,
.cl-07__tile:nth-child(3):focus-visible {
  --cl-07-elev: 4;
  scale: 1.02;
}

.cl-07__tile:active {
  --cl-07-elev: 0;
  translate: 0;
  box-shadow: inset 0 12px 22px -8px var(--cl-07-shade),inset 0 -10px 18px -10px var(--cl-07-light);
}

.cl-07__t {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: color-mix(in oklch,var(--cl-07-ink) 60%,transparent);
}

.cl-07__d {
  font-size: 19px;
  font-weight: 740;
  letter-spacing: -.015em;
}

.cl-07__use {
  font-size: 13px;
  color: color-mix(in oklch,var(--cl-07-ink) 72%,transparent);
}

.cl-07__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--cl-07-mut);
  padding: 9px 16px;
  border-radius: 99px;
  text-align: center;
  background: oklch(1 0 0/.55);
  box-shadow: inset 0 -4px 10px -8px var(--cl-07-shade),inset 0 6px 10px -6px oklch(1 0 0);
}

@media (prefers-reduced-motion: reduce) {
  .cl-07 * {
    transition-duration: .01ms !important;
  }
}
Paste this into ChatGPT, Claude, Cursor, or any coding assistant. The block below is pre-framed with everything the AI needs to integrate this demo into your project — markup, styles, scoping notes, and the source URL. Hit Copy and paste straight into your chat.
Here's a working CSS Claymorphism from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Clay Depth Tiers for Visual Hierarchy
Source: https://codefronts.com/design-styles/css-claymorphism/clay-depth-tiers-for-visual-hierarchy/

Four elevation tiers — inset well, resting, raised, floating — driven by a single --elev number per element. Every shadow length, blur and highlight strength is computed from it with calc(), so a component changes tier by changing one integer. Hover promotes a card one tier, which is how clay communicates interactivity without borders or color changes.
## HTML
```html
<section class="cl-07" aria-label="Clay depth tiers for visual hierarchy demo">
  <div class="cl-07__stage">
    <header class="cl-07__head">
      <h2 class="cl-07__h">Four tiers, one number</h2>
      <p class="cl-07__lede">Hover any tile: it is promoted exactly one tier. Every length below is calc()-derived from <code>--elev</code>.</p>
    </header>
    <div class="cl-07__well">
      <p class="cl-07__wellLabel">Tier 0 — inset well · holds content</p>
      <div class="cl-07__row">
        <article class="cl-07__tile" style="--cl-07-elev:1">
          <b class="cl-07__t">Tier 1</b>
          <span class="cl-07__d">Resting surface</span>
          <span class="cl-07__use">lists, groups, rows</span>
        </article>
        <article class="cl-07__tile" style="--cl-07-elev:2">
          <b class="cl-07__t">Tier 2</b>
          <span class="cl-07__d">Raised surface</span>
          <span class="cl-07__use">cards, buttons, tabs</span>
        </article>
        <article class="cl-07__tile" style="--cl-07-elev:3">
          <b class="cl-07__t">Tier 3</b>
          <span class="cl-07__d">Floating surface</span>
          <span class="cl-07__use">menus, popovers, FABs</span>
        </article>
      </div>
    </div>
    <p class="cl-07__chip" aria-hidden="true">--elev drives shadow length + blur + highlight + translate</p>
  </div>
</section>
```
## CSS
```css
.cl-07,
.cl-07 *,
.cl-07 *::before,
.cl-07 *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

.cl-07 {
  width: 100%;
  min-height: 100vh;
  min-height: 100svh;
  display: block;
  background: var(--cl-07-bg);
  --cl-07-bg: oklch(0.93 0.03 15);
  --cl-07-clay: oklch(0.88 0.06 20);
  --cl-07-shade: oklch(0.58 0.1 18/.55);
  --cl-07-light: oklch(1 0.015 40/.95);
  --cl-07-ink: oklch(0.3 0.05 20);
  --cl-07-mut: oklch(0.51 0.04 20);
  font-family: system-ui,-apple-system,'Segoe UI',sans-serif;
  color: var(--cl-07-ink);
}

.cl-07__stage {
  min-height: 100vh;
  min-height: 100svh;
  display: grid;
  place-items: center;
  align-content: center;
  gap: clamp(22px,4vw,36px);
  padding: clamp(20px,5vw,58px);
  background: radial-gradient(80% 55% at 50% 0%,oklch(0.97 0.03 30),transparent 66%);
}

.cl-07__head {
  text-align: center;
  max-width: 58ch;
  display: grid;
  gap: 9px;
}

.cl-07__h {
  font-size: clamp(24px,3.8vw,34px);
  font-weight: 780;
  letter-spacing: -.02em;
  text-wrap: balance;
}

.cl-07__lede {
  font-size: 15px;
  line-height: 1.6;
  color: var(--cl-07-mut);
  text-wrap: pretty;
}

.cl-07__lede code {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 13.5px;
}

.cl-07__well {
  width: min(100%,900px);
  display: grid;
  gap: 16px;
  padding: clamp(20px,3vw,32px);
  border-radius: 44px;
  background: color-mix(in oklch,var(--cl-07-bg) 94%,oklch(0.7 0.06 20));
  box-shadow: inset 0 14px 26px -10px var(--cl-07-shade),inset 0 -12px 22px -12px var(--cl-07-light);
}

.cl-07__wellLabel {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: .13em;
  text-transform: uppercase;
  color: var(--cl-07-mut);
  text-align: center;
}

.cl-07__row {
  display: grid;
  grid-template-columns: repeat(auto-fit,minmax(196px,1fr));
  gap: clamp(16px,2.6vw,26px);
  align-items: center;
}

.cl-07__tile {
  --cl-07-elev: 2;
  display: grid;
  gap: 5px;
  align-content: center;
  min-height: 150px;
  padding: 24px;
  border-radius: 34px;
  background: var(--cl-07-clay);
  translate: 0 calc(var(--cl-07-elev) * -1px);
  box-shadow: 0 calc(var(--cl-07-elev) * 9px) calc(var(--cl-07-elev) * 18px) calc(var(--cl-07-elev) * -5px) var(--cl-07-shade),inset 0 calc(var(--cl-07-elev) * -5px) calc(var(--cl-07-elev) * 9px) -8px var(--cl-07-shade),inset 0 calc(var(--cl-07-elev) * 6px) calc(var(--cl-07-elev) * 9px) -6px var(--cl-07-light);
  transition: box-shadow .4s cubic-bezier(.4,0,.2,1),translate .4s cubic-bezier(.34,1.3,.5,1),scale .4s cubic-bezier(.34,1.3,.5,1);
  cursor: pointer;
}

.cl-07__tile:nth-child(1):hover,
.cl-07__tile:nth-child(1):focus-visible {
  --cl-07-elev: 2;
}

.cl-07__tile:nth-child(2):hover,
.cl-07__tile:nth-child(2):focus-visible {
  --cl-07-elev: 3;
}

.cl-07__tile:nth-child(3):hover,
.cl-07__tile:nth-child(3):focus-visible {
  --cl-07-elev: 4;
  scale: 1.02;
}

.cl-07__tile:active {
  --cl-07-elev: 0;
  translate: 0;
  box-shadow: inset 0 12px 22px -8px var(--cl-07-shade),inset 0 -10px 18px -10px var(--cl-07-light);
}

.cl-07__t {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: color-mix(in oklch,var(--cl-07-ink) 60%,transparent);
}

.cl-07__d {
  font-size: 19px;
  font-weight: 740;
  letter-spacing: -.015em;
}

.cl-07__use {
  font-size: 13px;
  color: color-mix(in oklch,var(--cl-07-ink) 72%,transparent);
}

.cl-07__chip {
  font-family: ui-monospace,Menlo,Consolas,monospace;
  font-size: 11.5px;
  color: var(--cl-07-mut);
  padding: 9px 16px;
  border-radius: 99px;
  text-align: center;
  background: oklch(1 0 0/.55);
  box-shadow: inset 0 -4px 10px -8px var(--cl-07-shade),inset 0 6px 10px -6px oklch(1 0 0);
}

@media (prefers-reduced-motion: reduce) {
  .cl-07 * {
    transition-duration: .01ms !important;
  }
}
```

How this works

Material's elevation scale works because it is arithmetic, not taste. Same here — one number feeds every layer:

.clay{
  --elev: 2;                                   /* 0 = inset well, 1..3 = lifted */
  box-shadow:
    0 calc(var(--elev) * 9px) calc(var(--elev) * 18px) calc(var(--elev) * -5px) var(--shade),
    inset 0 calc(var(--elev) * -5px) calc(var(--elev) * 9px) -8px var(--shade),
    inset 0 calc(var(--elev) * 6px)  calc(var(--elev) * 9px) -6px var(--light);
  translate: 0 calc(var(--elev) * -1px);
}
.clay:hover{ --elev: 3; }                       /* promote one tier */

Tier 0 is the special case: instead of a drop shadow it flips the insets, producing a well — the container things sit in. That gives you a complete vocabulary: wells hold, resting surfaces group, raised surfaces act, floating surfaces interrupt.

The tiny translate tied to the same number is what sells it. Real objects rise as their shadow grows; moving the element up 1-3px while the shadow lengthens reads as lift rather than as a shadow that mysteriously got bigger. Because translate is compositor-only, the movement is free even while the shadow repaints.

Discipline rule: never skip a tier. A jump from resting to floating on hover looks like a bug; one tier per interaction step is the entire trick.

Make it yours

  • Add a tier 4 for modals and toasts (--elev:4 plus a page scrim) — demo 14 and 18 use exactly that.
  • Tone the shade alpha with the tier as well: color-mix(in oklch, var(--shade) calc(var(--elev) * 22%), transparent) makes high tiers read heavier, not just longer.
  • Pressed state = tier 0: set --elev:0 on :active and any raised control becomes a well while held. One line, physically correct.
  • Container queries: drop every tier by 1 inside narrow containers so mobile layouts stay flat and fast.

Gotchas — read before shipping

  • calc() inside box-shadow cannot use a unitless var directly for lengths — multiply by a unit (calc(var(--elev) * 9px)), never calc(var(--elev)px).
  • Tier 0 must not also carry an outer drop shadow; a well that casts a shadow is visually incoherent.
  • More than 4 tiers stops being perceivable. If you need more separation, change hue or size instead of depth.
  • Shadows do not stack cleanly across overflow:hidden boundaries — a floating child inside a clipped well gets its shadow cut. Promote it out of the well in the DOM.
  • Hover-only tier promotion is invisible on touch. Pair it with :active and :focus-visible.

Browser support

ChromeSafariFirefoxEdge
114+17.5+121+114+

Floor set by oklch(), color-mix(), text-wrap as this demo is written. The core technique itself goes back further — Chrome 88+.

calc() in box-shadow lengths and the translate property are broadly supported (translate as a standalone property: Chrome 104 / Safari 14.1 / Firefox 72). oklch()/color-mix() theming is the only modern floor.

Techniques used in this demo

Search CodeFronts

Loading…