08 / 20
All 20 in 20 Tailwind CSS Claymorphism
- 01Tailwind CSS Claymorphism Box Shadow Recipe (3-Layer Formula)
- 02Tailwind Inner Shadow & Inset Highlight for Clay UI
- 03Tailwind Border Radius Scale & Squircle Corners for Puffy Shapes
- 04Claymorphism vs Neumorphism in Tailwind CSS (Side-by-Side)
- 05Tailwind Glassmorphism + Claymorphism Hybrid Card
- 06Tailwind Clay UI Light Source & Shadow Direction System
- 07Tailwind Elevation Shadow Scale: Clay Depth Tiers
- 08Tailwind CSS v4 Claymorphism: @theme Tokens, @utility & Arbitrary Values
- 09Tailwind 3D Clay Button with Press & Squish Animation
- 10Tailwind Toggle Switch, Checkbox & Segmented Control (Clay Style)
- 11Tailwind Search Bar & Floating Label Input (Clay Inset Style)
- 12Tailwind Badge & Pill Tag Components (Clay Style)
- 13Tailwind Claymorphism Card Component (Container Query Responsive)
- 14Tailwind Modal Dialog with Popover API (Clay Floating Depth)
- 15Tailwind Pricing Table with Monthly/Yearly Toggle (Clay Style)
- 16Tailwind Mobile Bottom Navigation Bar (Clay Tab Bar)
- 17Tailwind App Icon Grid with 3D Hover Tilt (Clay Tiles)
- 18Tailwind Toast Notification & Alert Components (Clay Style)
- 19Tailwind Dark Mode Claymorphism: Shadow Recalibration with light-dark()
- 20Tailwind Animation Performance: GPU Transform vs Box-Shadow (Live FPS Meter)
Tailwind CSS v4 Claymorphism: @theme Tokens, @utility & Arbitrary Values
The CSS collection's version of this demo could only mirror Tailwind's output. This one runs it. Three live buttons are built three ways — an @theme shadow token, a one-class @utility clay, and a raw arbitrary value — beside tabbed, copyable source for each. The tabs are native radios; the Copy button is the only script.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tcl-08 w-full min-h-svh grid place-items-center bg-tcl08-bg px-5 py-14 font-tcl08-sans text-tcl08-ink" aria-label="Tailwind CSS claymorphism implementation demo">
<div class="grid w-full justify-items-center gap-[clamp(24px,4vw,36px)]">
<header class="grid max-w-[62ch] gap-2.5 text-center">
<h2 class="font-tcl08-display text-[clamp(28px,4vw,40px)] font-extrabold leading-[1.05] tracking-[-0.03em] text-balance">Clay, the Tailwind way</h2>
<p class="text-[15px] leading-relaxed text-tcl08-mut text-pretty">Three live buttons, built three different ways, with identical output. Pick a tab to read the source, then copy it.</p>
</header>
<div class="group/lab grid w-full max-w-[1000px] items-center gap-[clamp(20px,3vw,30px)] min-[880px]:grid-cols-[minmax(0,.8fr)_minmax(0,1.2fr)]">
<div class="grid justify-items-center gap-5">
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-tcl08-clay bg-tcl08-clay px-7 font-tcl08-mono text-sm font-semibold shadow-tcl08-clay transition-[translate,box-shadow] duration-200 ease-[cubic-bezier(.34,1.4,.5,1)] hover:-translate-y-1 active:translate-y-0.5 active:shadow-tcl08-press focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer px-7 font-tcl08-mono text-sm font-semibold tcl08-clay focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-[40px] bg-[oklch(0.85_0.09_355)] px-7 font-tcl08-mono text-sm font-semibold shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),inset_0_-16px_26px_-10px_rgb(190_100_110/.55),inset_0_18px_26px_-8px_rgb(255_255_255/.9)] transition-[translate,box-shadow] duration-200 hover:-translate-y-1 active:translate-y-0.5 active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-[…arbitrary]</button>
</div>
<div class="grid gap-3 rounded-[36px] bg-tcl08-code p-[clamp(16px,2.6vw,24px)] text-tcl08-codeink shadow-tcl08-panel" data-tcl08-panel>
<div class="flex flex-wrap items-center justify-between gap-3">
<div role="radiogroup" aria-label="Implementation" class="flex gap-1 rounded-[22px] bg-tcl08-well p-1 shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)]">
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t1" checked>@theme token</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t2">@utility</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t3">Arbitrary</label>
</div>
<button type="button" data-copy class="group/btn inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-[18px] border border-white/15 px-4 text-xs font-bold text-tcl08-codeink transition-colors hover:bg-white/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white data-copied:border-tcl08-ok data-copied:text-tcl08-ok">
<svg class="size-3.5 group-data-copied/btn:hidden" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="5" y="5" width="8.5" height="8.5" rx="2"/><path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5"/></svg>
<svg class="hidden size-3.5 group-data-copied/btn:block" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5"/></svg>
<span class="group-data-copied/btn:hidden">Copy</span><span class="hidden group-data-copied/btn:inline">Copied</span>
</button>
</div>
<pre data-code="t1" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t1:checked]/lab:block"><code>@theme {
--radius-clay: 40px;
--shadow-clay:
0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
--shadow-clay-press: inset 0 14px 22px -8px var(--sh);
}
<button class="rounded-clay bg-rose-200 shadow-clay
hover:-translate-y-1 active:shadow-clay-press"></code></pre>
<pre data-code="t2" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t2:checked]/lab:block"><code>@utility clay {
border-radius: 40px;
background: var(--color-clay);
box-shadow: 0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
transition: translate .2s, box-shadow .2s;
&:hover { translate: 0 -4px; }
&:active { translate: 0 2px; box-shadow: inset 0 14px 22px -8px var(--sh); }
}
<button class="clay"></code></pre>
<pre data-code="t3" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t3:checked]/lab:block"><code><button class="rounded-[40px] bg-rose-200
shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),
inset_0_-16px_26px_-10px_rgb(190_100_110/.55),
inset_0_18px_26px_-8px_rgb(255_255_255/.9)]
hover:-translate-y-1
active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)]"></code></pre>
<span role="status" aria-live="polite" class="sr-only"></span>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/60 px-4 py-2 text-center font-tcl08-mono text-[11.5px] text-tcl08-mut">underscores for spaces · shadow utilities replace, never merge · tokens theme, arbitrary values don’t</p>
</div>
</section><section class="tcl-08 w-full min-h-svh grid place-items-center bg-tcl08-bg px-5 py-14 font-tcl08-sans text-tcl08-ink" aria-label="Tailwind CSS claymorphism implementation demo">
<div class="grid w-full justify-items-center gap-[clamp(24px,4vw,36px)]">
<header class="grid max-w-[62ch] gap-2.5 text-center">
<h2 class="font-tcl08-display text-[clamp(28px,4vw,40px)] font-extrabold leading-[1.05] tracking-[-0.03em] text-balance">Clay, the Tailwind way</h2>
<p class="text-[15px] leading-relaxed text-tcl08-mut text-pretty">Three live buttons, built three different ways, with identical output. Pick a tab to read the source, then copy it.</p>
</header>
<div class="group/lab grid w-full max-w-[1000px] items-center gap-[clamp(20px,3vw,30px)] min-[880px]:grid-cols-[minmax(0,.8fr)_minmax(0,1.2fr)]">
<div class="grid justify-items-center gap-5">
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-tcl08-clay bg-tcl08-clay px-7 font-tcl08-mono text-sm font-semibold shadow-tcl08-clay transition-[translate,box-shadow] duration-200 ease-[cubic-bezier(.34,1.4,.5,1)] hover:-translate-y-1 active:translate-y-0.5 active:shadow-tcl08-press focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer px-7 font-tcl08-mono text-sm font-semibold tcl08-clay focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-[40px] bg-[oklch(0.85_0.09_355)] px-7 font-tcl08-mono text-sm font-semibold shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),inset_0_-16px_26px_-10px_rgb(190_100_110/.55),inset_0_18px_26px_-8px_rgb(255_255_255/.9)] transition-[translate,box-shadow] duration-200 hover:-translate-y-1 active:translate-y-0.5 active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-[…arbitrary]</button>
</div>
<div class="grid gap-3 rounded-[36px] bg-tcl08-code p-[clamp(16px,2.6vw,24px)] text-tcl08-codeink shadow-tcl08-panel" data-tcl08-panel>
<div class="flex flex-wrap items-center justify-between gap-3">
<div role="radiogroup" aria-label="Implementation" class="flex gap-1 rounded-[22px] bg-tcl08-well p-1 shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)]">
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t1" checked>@theme token</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t2">@utility</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t3">Arbitrary</label>
</div>
<button type="button" data-copy class="group/btn inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-[18px] border border-white/15 px-4 text-xs font-bold text-tcl08-codeink transition-colors hover:bg-white/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white data-copied:border-tcl08-ok data-copied:text-tcl08-ok">
<svg class="size-3.5 group-data-copied/btn:hidden" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="5" y="5" width="8.5" height="8.5" rx="2"/><path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5"/></svg>
<svg class="hidden size-3.5 group-data-copied/btn:block" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5"/></svg>
<span class="group-data-copied/btn:hidden">Copy</span><span class="hidden group-data-copied/btn:inline">Copied</span>
</button>
</div>
<pre data-code="t1" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t1:checked]/lab:block"><code>@theme {
--radius-clay: 40px;
--shadow-clay:
0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
--shadow-clay-press: inset 0 14px 22px -8px var(--sh);
}
<button class="rounded-clay bg-rose-200 shadow-clay
hover:-translate-y-1 active:shadow-clay-press"></code></pre>
<pre data-code="t2" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t2:checked]/lab:block"><code>@utility clay {
border-radius: 40px;
background: var(--color-clay);
box-shadow: 0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
transition: translate .2s, box-shadow .2s;
&:hover { translate: 0 -4px; }
&:active { translate: 0 2px; box-shadow: inset 0 14px 22px -8px var(--sh); }
}
<button class="clay"></code></pre>
<pre data-code="t3" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t3:checked]/lab:block"><code><button class="rounded-[40px] bg-rose-200
shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),
inset_0_-16px_26px_-10px_rgb(190_100_110/.55),
inset_0_18px_26px_-8px_rgb(255_255_255/.9)]
hover:-translate-y-1
active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)]"></code></pre>
<span role="status" aria-live="polite" class="sr-only"></span>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/60 px-4 py-2 text-center font-tcl08-mono text-[11.5px] text-tcl08-mut">underscores for spaces · shadow utilities replace, never merge · tokens theme, arbitrary values don’t</p>
</div>
</section>@import "tailwindcss";
@theme {
--color-tcl08-bg: #e5f0fc;
--color-tcl08-bg: oklch(0.95 0.02 250);
--color-tcl08-clay: #feb6d1;
--color-tcl08-clay: oklch(0.85 0.09 355);
--color-tcl08-shade: #b463808c;
--color-tcl08-shade: oklch(0.6 0.11 358 / 0.55);
--color-tcl08-light: #fffdffeb;
--color-tcl08-light: oklch(1 0.01 340 / 0.92);
--color-tcl08-ink: #2c243a;
--color-tcl08-ink: oklch(0.28 0.04 300);
--color-tcl08-mut: #5b536b;
--color-tcl08-mut: oklch(0.46 0.04 300);
--color-tcl08-code: #222234;
--color-tcl08-code: oklch(0.26 0.035 285);
--color-tcl08-well: #171626;
--color-tcl08-well: oklch(0.21 0.03 285);
--color-tcl08-codeink: #d2eef0;
--color-tcl08-codeink: oklch(0.93 0.03 200);
--color-tcl08-codemut: #bdbad6;
--color-tcl08-codemut: oklch(0.8 0.04 290);
--color-tcl08-ok: #86eaab;
--color-tcl08-ok: oklch(0.86 0.13 155);
--font-tcl08-display: 'Bricolage Grotesque', system-ui, sans-serif;
--font-tcl08-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tcl08-mono: 'JetBrains Mono', ui-monospace, monospace;
--radius-tcl08-clay: 40px;
--shadow-tcl08-clay: 0 24px 44px -12px var(--color-tcl08-shade), inset 0 -16px 26px -10px var(--color-tcl08-shade), inset 0 18px 26px -8px var(--color-tcl08-light);
--shadow-tcl08-press: 0 0 0 0 transparent, inset 0 14px 22px -8px var(--color-tcl08-shade), inset 0 0 0 0 transparent;
--shadow-tcl08-panel: 0 30px 56px -22px oklch(0.3 0.05 290 / 0.6), inset 0 -14px 24px -14px oklch(0 0 0 / 0.6), inset 0 16px 24px -12px oklch(1 0 0 / 0.14);
}
@utility tcl08-clay {
border-radius: var(--radius-tcl08-clay);
background: var(--color-tcl08-clay);
box-shadow: var(--shadow-tcl08-clay);
transition: translate 0.2s cubic-bezier(0.34, 1.4, 0.5, 1), box-shadow 0.2s;
&:hover {
translate: 0 -4px;
}
&:active {
translate: 0 2px;
box-shadow: var(--shadow-tcl08-press);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcl-08 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tcl08-bg: #e5f0fc;
--color-tcl08-bg: oklch(0.95 0.02 250);
--color-tcl08-clay: #feb6d1;
--color-tcl08-clay: oklch(0.85 0.09 355);
--color-tcl08-shade: #b463808c;
--color-tcl08-shade: oklch(0.6 0.11 358 / 0.55);
--color-tcl08-light: #fffdffeb;
--color-tcl08-light: oklch(1 0.01 340 / 0.92);
--color-tcl08-ink: #2c243a;
--color-tcl08-ink: oklch(0.28 0.04 300);
--color-tcl08-mut: #5b536b;
--color-tcl08-mut: oklch(0.46 0.04 300);
--color-tcl08-code: #222234;
--color-tcl08-code: oklch(0.26 0.035 285);
--color-tcl08-well: #171626;
--color-tcl08-well: oklch(0.21 0.03 285);
--color-tcl08-codeink: #d2eef0;
--color-tcl08-codeink: oklch(0.93 0.03 200);
--color-tcl08-codemut: #bdbad6;
--color-tcl08-codemut: oklch(0.8 0.04 290);
--color-tcl08-ok: #86eaab;
--color-tcl08-ok: oklch(0.86 0.13 155);
--font-tcl08-display: 'Bricolage Grotesque', system-ui, sans-serif;
--font-tcl08-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tcl08-mono: 'JetBrains Mono', ui-monospace, monospace;
--radius-tcl08-clay: 40px;
--shadow-tcl08-clay: 0 24px 44px -12px var(--color-tcl08-shade), inset 0 -16px 26px -10px var(--color-tcl08-shade), inset 0 18px 26px -8px var(--color-tcl08-light);
--shadow-tcl08-press: 0 0 0 0 transparent, inset 0 14px 22px -8px var(--color-tcl08-shade), inset 0 0 0 0 transparent;
--shadow-tcl08-panel: 0 30px 56px -22px oklch(0.3 0.05 290 / 0.6), inset 0 -14px 24px -14px oklch(0 0 0 / 0.6), inset 0 16px 24px -12px oklch(1 0 0 / 0.14);
}
@utility tcl08-clay {
border-radius: var(--radius-tcl08-clay);
background: var(--color-tcl08-clay);
box-shadow: var(--shadow-tcl08-clay);
transition: translate 0.2s cubic-bezier(0.34, 1.4, 0.5, 1), box-shadow 0.2s;
&:hover {
translate: 0 -4px;
}
&:active {
translate: 0 2px;
box-shadow: var(--shadow-tcl08-press);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcl-08 {
width: 100%;
min-height: 100svh;
}(() => {
const copyText = async (text) => {
try { await navigator.clipboard.writeText(text); return true; }
catch {
const ta = Object.assign(document.createElement('textarea'), { value: text });
ta.setAttribute('readonly', '');
ta.style.cssText = 'position:fixed;opacity:0';
document.body.append(ta);
ta.select();
const ok = document.execCommand('copy');
ta.remove();
return ok;
}
};
document.querySelectorAll('[data-tcl08-panel]').forEach((panel) => {
if (panel.dataset.wired) return;
panel.dataset.wired = '1';
const btn = panel.querySelector('[data-copy]');
const status = panel.querySelector('[role="status"]');
let t = 0;
btn.addEventListener('click', async () => {
const tab = panel.querySelector('input[name="tcl-08-tab"]:checked').id.slice(-2);
const ok = await copyText(panel.querySelector('[data-code="' + tab + '"]').textContent);
clearTimeout(t);
if (ok) { btn.setAttribute('data-copied', ''); status.textContent = 'Code copied to clipboard'; }
else status.textContent = 'Copy failed — select the code instead';
t = setTimeout(() => { btn.removeAttribute('data-copied'); status.textContent = ''; }, 1800);
});
});
})();(() => {
const copyText = async (text) => {
try { await navigator.clipboard.writeText(text); return true; }
catch {
const ta = Object.assign(document.createElement('textarea'), { value: text });
ta.setAttribute('readonly', '');
ta.style.cssText = 'position:fixed;opacity:0';
document.body.append(ta);
ta.select();
const ok = document.execCommand('copy');
ta.remove();
return ok;
}
};
document.querySelectorAll('[data-tcl08-panel]').forEach((panel) => {
if (panel.dataset.wired) return;
panel.dataset.wired = '1';
const btn = panel.querySelector('[data-copy]');
const status = panel.querySelector('[role="status"]');
let t = 0;
btn.addEventListener('click', async () => {
const tab = panel.querySelector('input[name="tcl-08-tab"]:checked').id.slice(-2);
const ok = await copyText(panel.querySelector('[data-code="' + tab + '"]').textContent);
clearTimeout(t);
if (ok) { btn.setAttribute('data-copied', ''); status.textContent = 'Code copied to clipboard'; }
else status.textContent = 'Copy failed — select the code instead';
t = setTimeout(() => { btn.removeAttribute('data-copied'); status.textContent = ''; }, 1800);
});
});
})();Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tcl08-bg: #e5f0fc;
--color-tcl08-bg: oklch(0.95 0.02 250);
--color-tcl08-clay: #feb6d1;
--color-tcl08-clay: oklch(0.85 0.09 355);
--color-tcl08-shade: #b463808c;
--color-tcl08-shade: oklch(0.6 0.11 358 / 0.55);
--color-tcl08-light: #fffdffeb;
--color-tcl08-light: oklch(1 0.01 340 / 0.92);
--color-tcl08-ink: #2c243a;
--color-tcl08-ink: oklch(0.28 0.04 300);
--color-tcl08-mut: #5b536b;
--color-tcl08-mut: oklch(0.46 0.04 300);
--color-tcl08-code: #222234;
--color-tcl08-code: oklch(0.26 0.035 285);
--color-tcl08-well: #171626;
--color-tcl08-well: oklch(0.21 0.03 285);
--color-tcl08-codeink: #d2eef0;
--color-tcl08-codeink: oklch(0.93 0.03 200);
--color-tcl08-codemut: #bdbad6;
--color-tcl08-codemut: oklch(0.8 0.04 290);
--color-tcl08-ok: #86eaab;
--color-tcl08-ok: oklch(0.86 0.13 155);
--font-tcl08-display: 'Bricolage Grotesque', system-ui, sans-serif;
--font-tcl08-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tcl08-mono: 'JetBrains Mono', ui-monospace, monospace;
--radius-tcl08-clay: 40px;
--shadow-tcl08-clay: 0 24px 44px -12px var(--color-tcl08-shade), inset 0 -16px 26px -10px var(--color-tcl08-shade), inset 0 18px 26px -8px var(--color-tcl08-light);
--shadow-tcl08-press: 0 0 0 0 transparent, inset 0 14px 22px -8px var(--color-tcl08-shade), inset 0 0 0 0 transparent;
--shadow-tcl08-panel: 0 30px 56px -22px oklch(0.3 0.05 290 / 0.6), inset 0 -14px 24px -14px oklch(0 0 0 / 0.6), inset 0 16px 24px -12px oklch(1 0 0 / 0.14);
}
@utility tcl08-clay {
border-radius: var(--radius-tcl08-clay);
background: var(--color-tcl08-clay);
box-shadow: var(--shadow-tcl08-clay);
transition: translate 0.2s cubic-bezier(0.34, 1.4, 0.5, 1), box-shadow 0.2s;
&:hover {
translate: 0 -4px;
}
&:active {
translate: 0 2px;
box-shadow: var(--shadow-tcl08-press);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcl-08 {
width: 100%;
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tcl08-bg: #e5f0fc;
--color-tcl08-bg: oklch(0.95 0.02 250);
--color-tcl08-clay: #feb6d1;
--color-tcl08-clay: oklch(0.85 0.09 355);
--color-tcl08-shade: #b463808c;
--color-tcl08-shade: oklch(0.6 0.11 358 / 0.55);
--color-tcl08-light: #fffdffeb;
--color-tcl08-light: oklch(1 0.01 340 / 0.92);
--color-tcl08-ink: #2c243a;
--color-tcl08-ink: oklch(0.28 0.04 300);
--color-tcl08-mut: #5b536b;
--color-tcl08-mut: oklch(0.46 0.04 300);
--color-tcl08-code: #222234;
--color-tcl08-code: oklch(0.26 0.035 285);
--color-tcl08-well: #171626;
--color-tcl08-well: oklch(0.21 0.03 285);
--color-tcl08-codeink: #d2eef0;
--color-tcl08-codeink: oklch(0.93 0.03 200);
--color-tcl08-codemut: #bdbad6;
--color-tcl08-codemut: oklch(0.8 0.04 290);
--color-tcl08-ok: #86eaab;
--color-tcl08-ok: oklch(0.86 0.13 155);
--font-tcl08-display: 'Bricolage Grotesque', system-ui, sans-serif;
--font-tcl08-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tcl08-mono: 'JetBrains Mono', ui-monospace, monospace;
--radius-tcl08-clay: 40px;
--shadow-tcl08-clay: 0 24px 44px -12px var(--color-tcl08-shade), inset 0 -16px 26px -10px var(--color-tcl08-shade), inset 0 18px 26px -8px var(--color-tcl08-light);
--shadow-tcl08-press: 0 0 0 0 transparent, inset 0 14px 22px -8px var(--color-tcl08-shade), inset 0 0 0 0 transparent;
--shadow-tcl08-panel: 0 30px 56px -22px oklch(0.3 0.05 290 / 0.6), inset 0 -14px 24px -14px oklch(0 0 0 / 0.6), inset 0 16px 24px -12px oklch(1 0 0 / 0.14);
}
@utility tcl08-clay {
border-radius: var(--radius-tcl08-clay);
background: var(--color-tcl08-clay);
box-shadow: var(--shadow-tcl08-clay);
transition: translate 0.2s cubic-bezier(0.34, 1.4, 0.5, 1), box-shadow 0.2s;
&:hover {
translate: 0 -4px;
}
&:active {
translate: 0 2px;
box-shadow: var(--shadow-tcl08-press);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcl-08 {
width: 100%;
min-height: 100svh;
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500..800&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,500..800&family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- The CSS original had to hand-write Tailwind's compiled output; the port is the real thing — all three buttons are generated by Tailwind v4 from the source shown in the tabs.
--shadow-tcl08-presskeeps three layers (two transparent) soactive:shadow-tcl08-pressanimates from the rest shadow instead of snapping.- UI upgrade from the CSS original: the two static code blocks became three radio-driven tabs, one per approach, each paired with a live button — the comparison is now 1:1.
- UI upgrade: a Copy button with
data-copied:feedback, anaria-livestatus, and anexecCommandfallback for sandboxed iframes.
Here's a working CSS demo 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: Tailwind CSS v4 Claymorphism: @theme Tokens, @utility & Arbitrary Values
Source: https://codefronts.com/design-styles/tailwind-css-claymorphism/tailwind-css-v4-claymorphism-theme-utility-arbitrary/
The CSS collection's version of this demo could only mirror Tailwind's output. This one runs it. Three live buttons are built three ways — an @theme shadow token, a one-class @utility clay, and a raw arbitrary value — beside tabbed, copyable source for each. The tabs are native radios; the Copy button is the only script.
## HTML
```html
<section class="tcl-08 w-full min-h-svh grid place-items-center bg-tcl08-bg px-5 py-14 font-tcl08-sans text-tcl08-ink" aria-label="Tailwind CSS claymorphism implementation demo">
<div class="grid w-full justify-items-center gap-[clamp(24px,4vw,36px)]">
<header class="grid max-w-[62ch] gap-2.5 text-center">
<h2 class="font-tcl08-display text-[clamp(28px,4vw,40px)] font-extrabold leading-[1.05] tracking-[-0.03em] text-balance">Clay, the Tailwind way</h2>
<p class="text-[15px] leading-relaxed text-tcl08-mut text-pretty">Three live buttons, built three different ways, with identical output. Pick a tab to read the source, then copy it.</p>
</header>
<div class="group/lab grid w-full max-w-[1000px] items-center gap-[clamp(20px,3vw,30px)] min-[880px]:grid-cols-[minmax(0,.8fr)_minmax(0,1.2fr)]">
<div class="grid justify-items-center gap-5">
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-tcl08-clay bg-tcl08-clay px-7 font-tcl08-mono text-sm font-semibold shadow-tcl08-clay transition-[translate,box-shadow] duration-200 ease-[cubic-bezier(.34,1.4,.5,1)] hover:-translate-y-1 active:translate-y-0.5 active:shadow-tcl08-press focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer px-7 font-tcl08-mono text-sm font-semibold tcl08-clay focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-[40px] bg-[oklch(0.85_0.09_355)] px-7 font-tcl08-mono text-sm font-semibold shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),inset_0_-16px_26px_-10px_rgb(190_100_110/.55),inset_0_18px_26px_-8px_rgb(255_255_255/.9)] transition-[translate,box-shadow] duration-200 hover:-translate-y-1 active:translate-y-0.5 active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-[…arbitrary]</button>
</div>
<div class="grid gap-3 rounded-[36px] bg-tcl08-code p-[clamp(16px,2.6vw,24px)] text-tcl08-codeink shadow-tcl08-panel" data-tcl08-panel>
<div class="flex flex-wrap items-center justify-between gap-3">
<div role="radiogroup" aria-label="Implementation" class="flex gap-1 rounded-[22px] bg-tcl08-well p-1 shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)]">
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t1" checked>@theme token</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t2">@utility</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t3">Arbitrary</label>
</div>
<button type="button" data-copy class="group/btn inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-[18px] border border-white/15 px-4 text-xs font-bold text-tcl08-codeink transition-colors hover:bg-white/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white data-copied:border-tcl08-ok data-copied:text-tcl08-ok">
<svg class="size-3.5 group-data-copied/btn:hidden" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="5" y="5" width="8.5" height="8.5" rx="2"/><path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5"/></svg>
<svg class="hidden size-3.5 group-data-copied/btn:block" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5"/></svg>
<span class="group-data-copied/btn:hidden">Copy</span><span class="hidden group-data-copied/btn:inline">Copied</span>
</button>
</div>
<pre data-code="t1" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t1:checked]/lab:block"><code>@theme {
--radius-clay: 40px;
--shadow-clay:
0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
--shadow-clay-press: inset 0 14px 22px -8px var(--sh);
}
<button class="rounded-clay bg-rose-200 shadow-clay
hover:-translate-y-1 active:shadow-clay-press"></code></pre>
<pre data-code="t2" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t2:checked]/lab:block"><code>@utility clay {
border-radius: 40px;
background: var(--color-clay);
box-shadow: 0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
transition: translate .2s, box-shadow .2s;
&:hover { translate: 0 -4px; }
&:active { translate: 0 2px; box-shadow: inset 0 14px 22px -8px var(--sh); }
}
<button class="clay"></code></pre>
<pre data-code="t3" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t3:checked]/lab:block"><code><button class="rounded-[40px] bg-rose-200
shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),
inset_0_-16px_26px_-10px_rgb(190_100_110/.55),
inset_0_18px_26px_-8px_rgb(255_255_255/.9)]
hover:-translate-y-1
active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)]"></code></pre>
<span role="status" aria-live="polite" class="sr-only"></span>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/60 px-4 py-2 text-center font-tcl08-mono text-[11.5px] text-tcl08-mut">underscores for spaces · shadow utilities replace, never merge · tokens theme, arbitrary values don’t</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcl08-bg: #e5f0fc;
--color-tcl08-bg: oklch(0.95 0.02 250);
--color-tcl08-clay: #feb6d1;
--color-tcl08-clay: oklch(0.85 0.09 355);
--color-tcl08-shade: #b463808c;
--color-tcl08-shade: oklch(0.6 0.11 358 / 0.55);
--color-tcl08-light: #fffdffeb;
--color-tcl08-light: oklch(1 0.01 340 / 0.92);
--color-tcl08-ink: #2c243a;
--color-tcl08-ink: oklch(0.28 0.04 300);
--color-tcl08-mut: #5b536b;
--color-tcl08-mut: oklch(0.46 0.04 300);
--color-tcl08-code: #222234;
--color-tcl08-code: oklch(0.26 0.035 285);
--color-tcl08-well: #171626;
--color-tcl08-well: oklch(0.21 0.03 285);
--color-tcl08-codeink: #d2eef0;
--color-tcl08-codeink: oklch(0.93 0.03 200);
--color-tcl08-codemut: #bdbad6;
--color-tcl08-codemut: oklch(0.8 0.04 290);
--color-tcl08-ok: #86eaab;
--color-tcl08-ok: oklch(0.86 0.13 155);
--font-tcl08-display: 'Bricolage Grotesque', system-ui, sans-serif;
--font-tcl08-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tcl08-mono: 'JetBrains Mono', ui-monospace, monospace;
--radius-tcl08-clay: 40px;
--shadow-tcl08-clay: 0 24px 44px -12px var(--color-tcl08-shade), inset 0 -16px 26px -10px var(--color-tcl08-shade), inset 0 18px 26px -8px var(--color-tcl08-light);
--shadow-tcl08-press: 0 0 0 0 transparent, inset 0 14px 22px -8px var(--color-tcl08-shade), inset 0 0 0 0 transparent;
--shadow-tcl08-panel: 0 30px 56px -22px oklch(0.3 0.05 290 / 0.6), inset 0 -14px 24px -14px oklch(0 0 0 / 0.6), inset 0 16px 24px -12px oklch(1 0 0 / 0.14);
}
@utility tcl08-clay {
border-radius: var(--radius-tcl08-clay);
background: var(--color-tcl08-clay);
box-shadow: var(--shadow-tcl08-clay);
transition: translate 0.2s cubic-bezier(0.34, 1.4, 0.5, 1), box-shadow 0.2s;
&:hover {
translate: 0 -4px;
}
&:active {
translate: 0 2px;
box-shadow: var(--shadow-tcl08-press);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcl-08 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
const copyText = async (text) => {
try { await navigator.clipboard.writeText(text); return true; }
catch {
const ta = Object.assign(document.createElement('textarea'), { value: text });
ta.setAttribute('readonly', '');
ta.style.cssText = 'position:fixed;opacity:0';
document.body.append(ta);
ta.select();
const ok = document.execCommand('copy');
ta.remove();
return ok;
}
};
document.querySelectorAll('[data-tcl08-panel]').forEach((panel) => {
if (panel.dataset.wired) return;
panel.dataset.wired = '1';
const btn = panel.querySelector('[data-copy]');
const status = panel.querySelector('[role="status"]');
let t = 0;
btn.addEventListener('click', async () => {
const tab = panel.querySelector('input[name="tcl-08-tab"]:checked').id.slice(-2);
const ok = await copyText(panel.querySelector('[data-code="' + tab + '"]').textContent);
clearTimeout(t);
if (ok) { btn.setAttribute('data-copied', ''); status.textContent = 'Code copied to clipboard'; }
else status.textContent = 'Copy failed — select the code instead';
t = setTimeout(() => { btn.removeAttribute('data-copied'); status.textContent = ''; }, 1800);
});
});
})();
```Here's a working CSS demo 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: Tailwind CSS v4 Claymorphism: @theme Tokens, @utility & Arbitrary Values
Source: https://codefronts.com/design-styles/tailwind-css-claymorphism/tailwind-css-v4-claymorphism-theme-utility-arbitrary/
The CSS collection's version of this demo could only mirror Tailwind's output. This one runs it. Three live buttons are built three ways — an @theme shadow token, a one-class @utility clay, and a raw arbitrary value — beside tabbed, copyable source for each. The tabs are native radios; the Copy button is the only script.
## HTML
```html
<section class="tcl-08 w-full min-h-svh grid place-items-center bg-tcl08-bg px-5 py-14 font-tcl08-sans text-tcl08-ink" aria-label="Tailwind CSS claymorphism implementation demo">
<div class="grid w-full justify-items-center gap-[clamp(24px,4vw,36px)]">
<header class="grid max-w-[62ch] gap-2.5 text-center">
<h2 class="font-tcl08-display text-[clamp(28px,4vw,40px)] font-extrabold leading-[1.05] tracking-[-0.03em] text-balance">Clay, the Tailwind way</h2>
<p class="text-[15px] leading-relaxed text-tcl08-mut text-pretty">Three live buttons, built three different ways, with identical output. Pick a tab to read the source, then copy it.</p>
</header>
<div class="group/lab grid w-full max-w-[1000px] items-center gap-[clamp(20px,3vw,30px)] min-[880px]:grid-cols-[minmax(0,.8fr)_minmax(0,1.2fr)]">
<div class="grid justify-items-center gap-5">
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-tcl08-clay bg-tcl08-clay px-7 font-tcl08-mono text-sm font-semibold shadow-tcl08-clay transition-[translate,box-shadow] duration-200 ease-[cubic-bezier(.34,1.4,.5,1)] hover:-translate-y-1 active:translate-y-0.5 active:shadow-tcl08-press focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer px-7 font-tcl08-mono text-sm font-semibold tcl08-clay focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">tcl08-clay</button>
<button type="button" class="min-h-14 w-full max-w-[280px] cursor-pointer rounded-[40px] bg-[oklch(0.85_0.09_355)] px-7 font-tcl08-mono text-sm font-semibold shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),inset_0_-16px_26px_-10px_rgb(190_100_110/.55),inset_0_18px_26px_-8px_rgb(255_255_255/.9)] transition-[translate,box-shadow] duration-200 hover:-translate-y-1 active:translate-y-0.5 active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)] focus-visible:outline-3 focus-visible:outline-offset-4 focus-visible:outline-tcl08-ink">shadow-[…arbitrary]</button>
</div>
<div class="grid gap-3 rounded-[36px] bg-tcl08-code p-[clamp(16px,2.6vw,24px)] text-tcl08-codeink shadow-tcl08-panel" data-tcl08-panel>
<div class="flex flex-wrap items-center justify-between gap-3">
<div role="radiogroup" aria-label="Implementation" class="flex gap-1 rounded-[22px] bg-tcl08-well p-1 shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)]">
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t1" checked>@theme token</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t2">@utility</label>
<label class="grid min-h-11 cursor-pointer place-items-center rounded-[18px] px-3.5 text-xs font-bold tracking-[0.04em] text-tcl08-codemut transition-colors hover:text-white has-checked:bg-tcl08-clay has-checked:text-tcl08-ink has-focus-visible:outline-2 has-focus-visible:outline-white"><input class="sr-only" type="radio" name="tcl-08-tab" id="tcl-08-t3">Arbitrary</label>
</div>
<button type="button" data-copy class="group/btn inline-flex min-h-11 cursor-pointer items-center gap-2 rounded-[18px] border border-white/15 px-4 text-xs font-bold text-tcl08-codeink transition-colors hover:bg-white/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white data-copied:border-tcl08-ok data-copied:text-tcl08-ok">
<svg class="size-3.5 group-data-copied/btn:hidden" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><rect x="5" y="5" width="8.5" height="8.5" rx="2"/><path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5"/></svg>
<svg class="hidden size-3.5 group-data-copied/btn:block" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M3 8.5l3.2 3L13 4.5"/></svg>
<span class="group-data-copied/btn:hidden">Copy</span><span class="hidden group-data-copied/btn:inline">Copied</span>
</button>
</div>
<pre data-code="t1" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t1:checked]/lab:block"><code>@theme {
--radius-clay: 40px;
--shadow-clay:
0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
--shadow-clay-press: inset 0 14px 22px -8px var(--sh);
}
<button class="rounded-clay bg-rose-200 shadow-clay
hover:-translate-y-1 active:shadow-clay-press"></code></pre>
<pre data-code="t2" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t2:checked]/lab:block"><code>@utility clay {
border-radius: 40px;
background: var(--color-clay);
box-shadow: 0 24px 44px -12px var(--sh),
inset 0 -16px 26px -10px var(--sh),
inset 0 18px 26px -8px var(--hl);
transition: translate .2s, box-shadow .2s;
&:hover { translate: 0 -4px; }
&:active { translate: 0 2px; box-shadow: inset 0 14px 22px -8px var(--sh); }
}
<button class="clay"></code></pre>
<pre data-code="t3" class="m-0 hidden overflow-x-auto rounded-[22px] bg-tcl08-well px-4 py-3.5 font-tcl08-mono text-xs leading-[1.75] shadow-[inset_0_8px_14px_-10px_oklch(0_0_0/0.8)] group-has-[#tcl-08-t3:checked]/lab:block"><code><button class="rounded-[40px] bg-rose-200
shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),
inset_0_-16px_26px_-10px_rgb(190_100_110/.55),
inset_0_18px_26px_-8px_rgb(255_255_255/.9)]
hover:-translate-y-1
active:shadow-[inset_0_14px_22px_-8px_rgb(190_100_110/.6)]"></code></pre>
<span role="status" aria-live="polite" class="sr-only"></span>
</div>
</div>
<p aria-hidden="true" class="rounded-full bg-white/60 px-4 py-2 text-center font-tcl08-mono text-[11.5px] text-tcl08-mut">underscores for spaces · shadow utilities replace, never merge · tokens theme, arbitrary values don’t</p>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tcl08-bg: #e5f0fc;
--color-tcl08-bg: oklch(0.95 0.02 250);
--color-tcl08-clay: #feb6d1;
--color-tcl08-clay: oklch(0.85 0.09 355);
--color-tcl08-shade: #b463808c;
--color-tcl08-shade: oklch(0.6 0.11 358 / 0.55);
--color-tcl08-light: #fffdffeb;
--color-tcl08-light: oklch(1 0.01 340 / 0.92);
--color-tcl08-ink: #2c243a;
--color-tcl08-ink: oklch(0.28 0.04 300);
--color-tcl08-mut: #5b536b;
--color-tcl08-mut: oklch(0.46 0.04 300);
--color-tcl08-code: #222234;
--color-tcl08-code: oklch(0.26 0.035 285);
--color-tcl08-well: #171626;
--color-tcl08-well: oklch(0.21 0.03 285);
--color-tcl08-codeink: #d2eef0;
--color-tcl08-codeink: oklch(0.93 0.03 200);
--color-tcl08-codemut: #bdbad6;
--color-tcl08-codemut: oklch(0.8 0.04 290);
--color-tcl08-ok: #86eaab;
--color-tcl08-ok: oklch(0.86 0.13 155);
--font-tcl08-display: 'Bricolage Grotesque', system-ui, sans-serif;
--font-tcl08-sans: 'Plus Jakarta Sans', system-ui, sans-serif;
--font-tcl08-mono: 'JetBrains Mono', ui-monospace, monospace;
--radius-tcl08-clay: 40px;
--shadow-tcl08-clay: 0 24px 44px -12px var(--color-tcl08-shade), inset 0 -16px 26px -10px var(--color-tcl08-shade), inset 0 18px 26px -8px var(--color-tcl08-light);
--shadow-tcl08-press: 0 0 0 0 transparent, inset 0 14px 22px -8px var(--color-tcl08-shade), inset 0 0 0 0 transparent;
--shadow-tcl08-panel: 0 30px 56px -22px oklch(0.3 0.05 290 / 0.6), inset 0 -14px 24px -14px oklch(0 0 0 / 0.6), inset 0 16px 24px -12px oklch(1 0 0 / 0.14);
}
@utility tcl08-clay {
border-radius: var(--radius-tcl08-clay);
background: var(--color-tcl08-clay);
box-shadow: var(--shadow-tcl08-clay);
transition: translate 0.2s cubic-bezier(0.34, 1.4, 0.5, 1), box-shadow 0.2s;
&:hover {
translate: 0 -4px;
}
&:active {
translate: 0 2px;
box-shadow: var(--shadow-tcl08-press);
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's utilities, so the
stage is sized before the browser build compiles. */
.tcl-08 {
width: 100%;
min-height: 100svh;
}
```
## JavaScript
```js
(() => {
const copyText = async (text) => {
try { await navigator.clipboard.writeText(text); return true; }
catch {
const ta = Object.assign(document.createElement('textarea'), { value: text });
ta.setAttribute('readonly', '');
ta.style.cssText = 'position:fixed;opacity:0';
document.body.append(ta);
ta.select();
const ok = document.execCommand('copy');
ta.remove();
return ok;
}
};
document.querySelectorAll('[data-tcl08-panel]').forEach((panel) => {
if (panel.dataset.wired) return;
panel.dataset.wired = '1';
const btn = panel.querySelector('[data-copy]');
const status = panel.querySelector('[role="status"]');
let t = 0;
btn.addEventListener('click', async () => {
const tab = panel.querySelector('input[name="tcl-08-tab"]:checked').id.slice(-2);
const ok = await copyText(panel.querySelector('[data-code="' + tab + '"]').textContent);
clearTimeout(t);
if (ok) { btn.setAttribute('data-copied', ''); status.textContent = 'Code copied to clipboard'; }
else status.textContent = 'Copy failed — select the code instead';
t = setTimeout(() => { btn.removeAttribute('data-copied'); status.textContent = ''; }, 1800);
});
});
})();
```How this works
Three ways, one look.
/* 1. token — recommended */
@theme { --shadow-clay: 0 24px 44px -12px var(--sh), inset …, inset …; }
<button class="rounded-clay shadow-clay active:shadow-clay-press">
/* 2. utility — one class */
@utility clay { border-radius: 40px; box-shadow: …; }
<button class="clay">
/* 3. arbitrary — one-off */
<button class="shadow-[0_24px_44px_-12px_rgb(190_100_110/.55),inset_…]">Which to ship. Tokens compose with everything (hover:shadow-clay-lift, shadow-clay/50 is not needed because colours are tokens too). @utility is best when the material is more than a shadow. Arbitrary values are for prototypes: every underscore is a space, and they can't be themed.
Tabs without JS. Three radios live in the panel; each code block is hidden group-has-[#tcl-08-tN:checked]/lab:block.
Make it yours
- Rename
tcl08tokens toclayin your own@themeand the class names in the code panel become exactly what you'd type. - Add a lift state:
--shadow-tcl08-liftwith a longer drop, used ashover:shadow-tcl08-lift. - Dark theme: redefine the two colour tokens inside
@variant dark { … }; every approach except the arbitrary value follows.
Gotchas — read before shipping
- A
shadow-*utility replaces the whole shadow — you can't add a layer with a second class. Inset layers go inside the same token (or ininset-shadow-*). - Arbitrary values can't contain unescaped spaces;
rgb(190_100_110/.55)is right,rgb(190 100 110 / .55)breaks the class. navigator.clipboardis blocked in many sandboxed iframes; the script falls back toexecCommand('copy').
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
All three approaches compile to identical CSS; support is Tailwind v4's floor.