30 / 51
All 51 in 51 Tailwind CSS Buttons
- 01Tailwind Submit Button (Form Primary)
- 02Tailwind Primary and Secondary Button Pair
- 03Tailwind Ghost / Outline Button
- 04Tailwind Icon-Only Button with Accessible Tooltip
- 05Tailwind Gradient CTA Button
- 06Tailwind Loading Spinner Button (State Transition)
- 07Tailwind Disabled Button State (Full State Reference)
- 08Tailwind Button Group Component (Segmented Radio-Like)
- 09Tailwind Add to Cart Button
- 10Tailwind Download Progress Button
- 11Tailwind Like Heart Button
- 12Tailwind Confirm Delete Button
- 13Tailwind Play Pause Button
- 14Tailwind Subscribe Confetti Button
- 15Tailwind Magnetic Mercury Ripple Button
- 16Tailwind Brutalist Glitch Button
- 17Tailwind Neon Plasma Burst Button
- 18Tailwind Clay Press Button
- 19Tailwind Editorial Ink Spread Button
- 20Tailwind Particle Shard Burst Button
- 21Tailwind Brushed Brass Plate Button
- 22Tailwind Washi Paper Crane Button
- 23Tailwind Frosted Ice Cube Button
- 24Tailwind Vintage Typewriter Key Button
- 25Tailwind Lacquered Walnut Wood Button
- 26Tailwind Pharma Gel Capsule Button
- 27Tailwind Aged Copper Coin Button
- 28Tailwind Origami Fortune Cookie Button
- 29Tailwind Holographic Foil Sticker Button
- 30Tailwind Brutalist Concrete Slab Button
- 31Tailwind Mercury Bead Morph Button
- 32Tailwind Soft Pill Modern SaaS Button
- 33Tailwind Masking Tape Label Button
- 34Tailwind Obsidian Glass Shard Button
- 35Tailwind Wine Velvet Ribbon Button
- 36Tailwind LED Segment Display Button
- 37Tailwind Cassette Mixtape Label Button
- 38Tailwind Chalkboard Menu Button
- 39Tailwind Vinyl Record Sleeve Ripple Button
- 40Tailwind Polaroid Snapshot Button
- 41Tailwind Airline Boarding Pass Button
- 42Tailwind Aged Brass Subway Token Button
- 43Tailwind Vintage Rx Pharmacy Label Button
- 44Tailwind Bakelite Radio Dial Toggle Button
- 45Tailwind Regal Postage Stamp Button
- 46Tailwind Victorian Cigar Band Foil Button
- 47Tailwind Vintage Calculator Key Button
- 48Tailwind Toolbox Latch Spotlight Button
- 49Tailwind Receipt Paper Total Count-Up Button
- 50Tailwind IBM Punch Card Magnetic Follow Button
- 51Tailwind Brass Periscope Lens Button
Tailwind Brutalist Concrete Slab Button
Board-formed concrete: shutter lines from the timber mould, aggregate speckle through the mix, chipped corners cut by clip-path, and a press that drops the slab into its own shadow with a puff of dust at the base.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tb-30 w-full min-h-svh grid place-items-center font-tb30-black p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-30__slab group relative cursor-pointer grid place-items-center min-h-18 px-12 border-0 text-xl uppercase tracking-[0.06em] text-tb30-ink [clip-path:polygon(2.5%_0,100%_0,100%_88%,97%_100%,0_100%,0_9%)] [text-shadow:0_1px_0_#ffffff47,0_-1px_0_#0000004d] shadow-[0_8px_0_0_var(--color-tb30-grey-lo),0_16px_22px_-8px_#00000080] transition-[translate,box-shadow,filter] duration-100 ease-linear hover:brightness-105 active:translate-y-2 active:shadow-[0_0_0_0_var(--color-tb30-grey-lo),0_4px_8px_-4px_#00000080] focus-visible:outline-4 focus-visible:outline-offset-[6px] focus-visible:outline-tb30-ink disabled:cursor-not-allowed disabled:grayscale-50 disabled:brightness-[0.85] disabled:shadow-[0_4px_0_0_var(--color-tb30-grey-lo)] motion-reduce:transition-none">
<span aria-hidden="true" class="absolute left-1/2 -bottom-1.5 w-36 h-4.5 -ml-18 rounded-[50%] pointer-events-none opacity-0 blur-[4px] bg-[radial-gradient(closest-side,rgba(255,255,255,0.85),transparent_72%)] group-active:animate-tb30-dust motion-reduce:hidden"></span>
<span class="relative z-20">Pour it</span>
</button>
</div><div class="tb-30 w-full min-h-svh grid place-items-center font-tb30-black p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-30__slab group relative cursor-pointer grid place-items-center min-h-18 px-12 border-0 text-xl uppercase tracking-[0.06em] text-tb30-ink [clip-path:polygon(2.5%_0,100%_0,100%_88%,97%_100%,0_100%,0_9%)] [text-shadow:0_1px_0_#ffffff47,0_-1px_0_#0000004d] shadow-[0_8px_0_0_var(--color-tb30-grey-lo),0_16px_22px_-8px_#00000080] transition-[translate,box-shadow,filter] duration-100 ease-linear hover:brightness-105 active:translate-y-2 active:shadow-[0_0_0_0_var(--color-tb30-grey-lo),0_4px_8px_-4px_#00000080] focus-visible:outline-4 focus-visible:outline-offset-[6px] focus-visible:outline-tb30-ink disabled:cursor-not-allowed disabled:grayscale-50 disabled:brightness-[0.85] disabled:shadow-[0_4px_0_0_var(--color-tb30-grey-lo)] motion-reduce:transition-none">
<span aria-hidden="true" class="absolute left-1/2 -bottom-1.5 w-36 h-4.5 -ml-18 rounded-[50%] pointer-events-none opacity-0 blur-[4px] bg-[radial-gradient(closest-side,rgba(255,255,255,0.85),transparent_72%)] group-active:animate-tb30-dust motion-reduce:hidden"></span>
<span class="relative z-20">Pour it</span>
</button>
</div>@import "tailwindcss";
@theme {
--color-tb30-bg: #c9c6c0;
--color-tb30-bg: oklch(0.82 0.004 85);
--color-tb30-grey: #8f8d88;
--color-tb30-grey: oklch(0.62 0.004 85);
--color-tb30-grey-hi: #a8a6a1;
--color-tb30-grey-hi: oklch(0.7 0.004 85);
--color-tb30-grey-lo: #5d5b57;
--color-tb30-grey-lo: oklch(0.44 0.004 85);
--color-tb30-fleck: #e6e3dc;
--color-tb30-fleck: oklch(0.91 0.004 85);
--color-tb30-fleck-2: #54524e;
--color-tb30-fleck-2: oklch(0.4 0.004 85);
--color-tb30-ink: #141412;
--color-tb30-ink: oklch(0.16 0.004 85);
--font-tb30-black: "Archivo Black", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--animate-tb30-dust: tb-30-dust 0.55s ease-out;
}
@keyframes tb-30-dust {
0% {
opacity: 0.85;
scale: 0.4 0.5;
}
100% {
opacity: 0;
scale: 1.5 1.3;
}
}
.tb-30 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: repeating-linear-gradient(0deg, rgba(0,0,0,.035) 0 1px, transparent 1px 22px), var(--color-tb30-bg);
}
/* THE MIX: three aggregate tiles (13/19/29px), the 14px formwork rhythm, and the
cast base. Five layers whose sizes only mean anything together. */
.tb-30__slab {
background-color: var(--color-tb30-grey);
background-image: radial-gradient(.055rem .055rem at 18% 32%, color-mix(in oklab, var(--color-tb30-fleck) 55%, transparent) 99%, transparent), radial-gradient(.045rem .045rem at 62% 71%, color-mix(in oklab, var(--color-tb30-fleck-2) 45%, transparent) 99%, transparent), radial-gradient(.04rem .04rem at 84% 18%, color-mix(in oklab, var(--color-tb30-fleck) 40%, transparent) 99%, transparent), repeating-linear-gradient(0deg, rgba(0,0,0,.07) 0 1px, transparent 1px 14px), linear-gradient(168deg, var(--color-tb30-grey-hi), var(--color-tb30-grey) 55%, var(--color-tb30-grey-lo));
background-size: 13px 13px, 19px 19px, 29px 29px, 100% 100%, 100% 100%;
}
@media (prefers-color-scheme: dark) {
.tb-30:not([data-theme="light"]) {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-hi: oklch(0.44 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-fleck: oklch(0.62 0.004 85);
--color-tb30-fleck-2: oklch(0.22 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
}
.tb-30[data-theme="dark"] {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
@media (forced-colors: active) {
.tb-30__slab {
background: ButtonFace;
color: ButtonText;
border: 2px solid ButtonText;
box-shadow: none;
text-shadow: none;
clip-path: none;
}
.tb-30__slab span[aria-hidden] {
display: none;
}
}@import "tailwindcss";
@theme {
--color-tb30-bg: #c9c6c0;
--color-tb30-bg: oklch(0.82 0.004 85);
--color-tb30-grey: #8f8d88;
--color-tb30-grey: oklch(0.62 0.004 85);
--color-tb30-grey-hi: #a8a6a1;
--color-tb30-grey-hi: oklch(0.7 0.004 85);
--color-tb30-grey-lo: #5d5b57;
--color-tb30-grey-lo: oklch(0.44 0.004 85);
--color-tb30-fleck: #e6e3dc;
--color-tb30-fleck: oklch(0.91 0.004 85);
--color-tb30-fleck-2: #54524e;
--color-tb30-fleck-2: oklch(0.4 0.004 85);
--color-tb30-ink: #141412;
--color-tb30-ink: oklch(0.16 0.004 85);
--font-tb30-black: "Archivo Black", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--animate-tb30-dust: tb-30-dust 0.55s ease-out;
}
@keyframes tb-30-dust {
0% {
opacity: 0.85;
scale: 0.4 0.5;
}
100% {
opacity: 0;
scale: 1.5 1.3;
}
}
.tb-30 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: repeating-linear-gradient(0deg, rgba(0,0,0,.035) 0 1px, transparent 1px 22px), var(--color-tb30-bg);
}
/* THE MIX: three aggregate tiles (13/19/29px), the 14px formwork rhythm, and the
cast base. Five layers whose sizes only mean anything together. */
.tb-30__slab {
background-color: var(--color-tb30-grey);
background-image: radial-gradient(.055rem .055rem at 18% 32%, color-mix(in oklab, var(--color-tb30-fleck) 55%, transparent) 99%, transparent), radial-gradient(.045rem .045rem at 62% 71%, color-mix(in oklab, var(--color-tb30-fleck-2) 45%, transparent) 99%, transparent), radial-gradient(.04rem .04rem at 84% 18%, color-mix(in oklab, var(--color-tb30-fleck) 40%, transparent) 99%, transparent), repeating-linear-gradient(0deg, rgba(0,0,0,.07) 0 1px, transparent 1px 14px), linear-gradient(168deg, var(--color-tb30-grey-hi), var(--color-tb30-grey) 55%, var(--color-tb30-grey-lo));
background-size: 13px 13px, 19px 19px, 29px 29px, 100% 100%, 100% 100%;
}
@media (prefers-color-scheme: dark) {
.tb-30:not([data-theme="light"]) {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-hi: oklch(0.44 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-fleck: oklch(0.62 0.004 85);
--color-tb30-fleck-2: oklch(0.22 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
}
.tb-30[data-theme="dark"] {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
@media (forced-colors: active) {
.tb-30__slab {
background: ButtonFace;
color: ButtonText;
border: 2px solid ButtonText;
box-shadow: none;
text-shadow: none;
clip-path: none;
}
.tb-30__slab span[aria-hidden] {
display: none;
}
}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-tb30-bg: #c9c6c0;
--color-tb30-bg: oklch(0.82 0.004 85);
--color-tb30-grey: #8f8d88;
--color-tb30-grey: oklch(0.62 0.004 85);
--color-tb30-grey-hi: #a8a6a1;
--color-tb30-grey-hi: oklch(0.7 0.004 85);
--color-tb30-grey-lo: #5d5b57;
--color-tb30-grey-lo: oklch(0.44 0.004 85);
--color-tb30-fleck: #e6e3dc;
--color-tb30-fleck: oklch(0.91 0.004 85);
--color-tb30-fleck-2: #54524e;
--color-tb30-fleck-2: oklch(0.4 0.004 85);
--color-tb30-ink: #141412;
--color-tb30-ink: oklch(0.16 0.004 85);
--font-tb30-black: "Archivo Black", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--animate-tb30-dust: tb-30-dust 0.55s ease-out;
}
@keyframes tb-30-dust {
0% {
opacity: 0.85;
scale: 0.4 0.5;
}
100% {
opacity: 0;
scale: 1.5 1.3;
}
}
.tb-30 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: repeating-linear-gradient(0deg, rgba(0,0,0,.035) 0 1px, transparent 1px 22px), var(--color-tb30-bg);
}
/* THE MIX: three aggregate tiles (13/19/29px), the 14px formwork rhythm, and the
cast base. Five layers whose sizes only mean anything together. */
.tb-30__slab {
background-color: var(--color-tb30-grey);
background-image: radial-gradient(.055rem .055rem at 18% 32%, color-mix(in oklab, var(--color-tb30-fleck) 55%, transparent) 99%, transparent), radial-gradient(.045rem .045rem at 62% 71%, color-mix(in oklab, var(--color-tb30-fleck-2) 45%, transparent) 99%, transparent), radial-gradient(.04rem .04rem at 84% 18%, color-mix(in oklab, var(--color-tb30-fleck) 40%, transparent) 99%, transparent), repeating-linear-gradient(0deg, rgba(0,0,0,.07) 0 1px, transparent 1px 14px), linear-gradient(168deg, var(--color-tb30-grey-hi), var(--color-tb30-grey) 55%, var(--color-tb30-grey-lo));
background-size: 13px 13px, 19px 19px, 29px 29px, 100% 100%, 100% 100%;
}
@media (prefers-color-scheme: dark) {
.tb-30:not([data-theme="light"]) {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-hi: oklch(0.44 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-fleck: oklch(0.62 0.004 85);
--color-tb30-fleck-2: oklch(0.22 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
}
.tb-30[data-theme="dark"] {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
@media (forced-colors: active) {
.tb-30__slab {
background: ButtonFace;
color: ButtonText;
border: 2px solid ButtonText;
box-shadow: none;
text-shadow: none;
clip-path: none;
}
.tb-30__slab span[aria-hidden] {
display: none;
}
}@import "tailwindcss";
@theme {
--color-tb30-bg: #c9c6c0;
--color-tb30-bg: oklch(0.82 0.004 85);
--color-tb30-grey: #8f8d88;
--color-tb30-grey: oklch(0.62 0.004 85);
--color-tb30-grey-hi: #a8a6a1;
--color-tb30-grey-hi: oklch(0.7 0.004 85);
--color-tb30-grey-lo: #5d5b57;
--color-tb30-grey-lo: oklch(0.44 0.004 85);
--color-tb30-fleck: #e6e3dc;
--color-tb30-fleck: oklch(0.91 0.004 85);
--color-tb30-fleck-2: #54524e;
--color-tb30-fleck-2: oklch(0.4 0.004 85);
--color-tb30-ink: #141412;
--color-tb30-ink: oklch(0.16 0.004 85);
--font-tb30-black: "Archivo Black", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--animate-tb30-dust: tb-30-dust 0.55s ease-out;
}
@keyframes tb-30-dust {
0% {
opacity: 0.85;
scale: 0.4 0.5;
}
100% {
opacity: 0;
scale: 1.5 1.3;
}
}
.tb-30 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: repeating-linear-gradient(0deg, rgba(0,0,0,.035) 0 1px, transparent 1px 22px), var(--color-tb30-bg);
}
/* THE MIX: three aggregate tiles (13/19/29px), the 14px formwork rhythm, and the
cast base. Five layers whose sizes only mean anything together. */
.tb-30__slab {
background-color: var(--color-tb30-grey);
background-image: radial-gradient(.055rem .055rem at 18% 32%, color-mix(in oklab, var(--color-tb30-fleck) 55%, transparent) 99%, transparent), radial-gradient(.045rem .045rem at 62% 71%, color-mix(in oklab, var(--color-tb30-fleck-2) 45%, transparent) 99%, transparent), radial-gradient(.04rem .04rem at 84% 18%, color-mix(in oklab, var(--color-tb30-fleck) 40%, transparent) 99%, transparent), repeating-linear-gradient(0deg, rgba(0,0,0,.07) 0 1px, transparent 1px 14px), linear-gradient(168deg, var(--color-tb30-grey-hi), var(--color-tb30-grey) 55%, var(--color-tb30-grey-lo));
background-size: 13px 13px, 19px 19px, 29px 29px, 100% 100%, 100% 100%;
}
@media (prefers-color-scheme: dark) {
.tb-30:not([data-theme="light"]) {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-hi: oklch(0.44 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-fleck: oklch(0.62 0.004 85);
--color-tb30-fleck-2: oklch(0.22 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
}
.tb-30[data-theme="dark"] {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
@media (forced-colors: active) {
.tb-30__slab {
background: ButtonFace;
color: ButtonText;
border: 2px solid ButtonText;
box-shadow: none;
text-shadow: none;
clip-path: none;
}
.tb-30__slab span[aria-hidden] {
display: none;
}
}<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=Archivo+Black&family=Archivo:wght@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=Archivo+Black&family=Archivo:wght@600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Utilities: the chipped clip-path, the engraved text-shadow, both shadow states, the 8px sink, the dust trigger, every state. CSS: the five-layer mix and the stage's own formwork lines.
active:translate-y-2next toshadow-[0_8px_0_0_…]is the demo's whole argument for utilities on this kind of button: the two numbers that must match are now three words apart instead of in different rules.ease-linear duration-100is deliberate and worth stating — every other demo in the collection uses a spring or an ease-out. Concrete lands.- A11y fix from the CSS original: in the dark branch
--greywas#514f4c(≈oklch(0.42)) with--ink:#f2f0eb— about 7:1, fine — but the speckle's--fleck:#8c8983at 55% raised local lightness enough that the darkest glyph edges measured closer to 4.2:1 on the lit corners. The port drops the slab base to0.38in dark mode, restoring ≈8:1 with the same fleck.
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 Brutalist Concrete Slab Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-brutalist-concrete-slab-button/
Board-formed concrete: shutter lines from the timber mould, aggregate speckle through the mix, chipped corners cut by clip-path, and a press that drops the slab into its own shadow with a puff of dust at the base.
## HTML
```html
<div class="tb-30 w-full min-h-svh grid place-items-center font-tb30-black p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-30__slab group relative cursor-pointer grid place-items-center min-h-18 px-12 border-0 text-xl uppercase tracking-[0.06em] text-tb30-ink [clip-path:polygon(2.5%_0,100%_0,100%_88%,97%_100%,0_100%,0_9%)] [text-shadow:0_1px_0_#ffffff47,0_-1px_0_#0000004d] shadow-[0_8px_0_0_var(--color-tb30-grey-lo),0_16px_22px_-8px_#00000080] transition-[translate,box-shadow,filter] duration-100 ease-linear hover:brightness-105 active:translate-y-2 active:shadow-[0_0_0_0_var(--color-tb30-grey-lo),0_4px_8px_-4px_#00000080] focus-visible:outline-4 focus-visible:outline-offset-[6px] focus-visible:outline-tb30-ink disabled:cursor-not-allowed disabled:grayscale-50 disabled:brightness-[0.85] disabled:shadow-[0_4px_0_0_var(--color-tb30-grey-lo)] motion-reduce:transition-none">
<span aria-hidden="true" class="absolute left-1/2 -bottom-1.5 w-36 h-4.5 -ml-18 rounded-[50%] pointer-events-none opacity-0 blur-[4px] bg-[radial-gradient(closest-side,rgba(255,255,255,0.85),transparent_72%)] group-active:animate-tb30-dust motion-reduce:hidden"></span>
<span class="relative z-20">Pour it</span>
</button>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb30-bg: #c9c6c0;
--color-tb30-bg: oklch(0.82 0.004 85);
--color-tb30-grey: #8f8d88;
--color-tb30-grey: oklch(0.62 0.004 85);
--color-tb30-grey-hi: #a8a6a1;
--color-tb30-grey-hi: oklch(0.7 0.004 85);
--color-tb30-grey-lo: #5d5b57;
--color-tb30-grey-lo: oklch(0.44 0.004 85);
--color-tb30-fleck: #e6e3dc;
--color-tb30-fleck: oklch(0.91 0.004 85);
--color-tb30-fleck-2: #54524e;
--color-tb30-fleck-2: oklch(0.4 0.004 85);
--color-tb30-ink: #141412;
--color-tb30-ink: oklch(0.16 0.004 85);
--font-tb30-black: "Archivo Black", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--animate-tb30-dust: tb-30-dust 0.55s ease-out;
}
@keyframes tb-30-dust {
0% {
opacity: 0.85;
scale: 0.4 0.5;
}
100% {
opacity: 0;
scale: 1.5 1.3;
}
}
.tb-30 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: repeating-linear-gradient(0deg, rgba(0,0,0,.035) 0 1px, transparent 1px 22px), var(--color-tb30-bg);
}
/* THE MIX: three aggregate tiles (13/19/29px), the 14px formwork rhythm, and the
cast base. Five layers whose sizes only mean anything together. */
.tb-30__slab {
background-color: var(--color-tb30-grey);
background-image: radial-gradient(.055rem .055rem at 18% 32%, color-mix(in oklab, var(--color-tb30-fleck) 55%, transparent) 99%, transparent), radial-gradient(.045rem .045rem at 62% 71%, color-mix(in oklab, var(--color-tb30-fleck-2) 45%, transparent) 99%, transparent), radial-gradient(.04rem .04rem at 84% 18%, color-mix(in oklab, var(--color-tb30-fleck) 40%, transparent) 99%, transparent), repeating-linear-gradient(0deg, rgba(0,0,0,.07) 0 1px, transparent 1px 14px), linear-gradient(168deg, var(--color-tb30-grey-hi), var(--color-tb30-grey) 55%, var(--color-tb30-grey-lo));
background-size: 13px 13px, 19px 19px, 29px 29px, 100% 100%, 100% 100%;
}
@media (prefers-color-scheme: dark) {
.tb-30:not([data-theme="light"]) {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-hi: oklch(0.44 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-fleck: oklch(0.62 0.004 85);
--color-tb30-fleck-2: oklch(0.22 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
}
.tb-30[data-theme="dark"] {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
@media (forced-colors: active) {
.tb-30__slab {
background: ButtonFace;
color: ButtonText;
border: 2px solid ButtonText;
box-shadow: none;
text-shadow: none;
clip-path: none;
}
.tb-30__slab span[aria-hidden] {
display: none;
}
}
```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 Brutalist Concrete Slab Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-brutalist-concrete-slab-button/
Board-formed concrete: shutter lines from the timber mould, aggregate speckle through the mix, chipped corners cut by clip-path, and a press that drops the slab into its own shadow with a puff of dust at the base.
## HTML
```html
<div class="tb-30 w-full min-h-svh grid place-items-center font-tb30-black p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-30__slab group relative cursor-pointer grid place-items-center min-h-18 px-12 border-0 text-xl uppercase tracking-[0.06em] text-tb30-ink [clip-path:polygon(2.5%_0,100%_0,100%_88%,97%_100%,0_100%,0_9%)] [text-shadow:0_1px_0_#ffffff47,0_-1px_0_#0000004d] shadow-[0_8px_0_0_var(--color-tb30-grey-lo),0_16px_22px_-8px_#00000080] transition-[translate,box-shadow,filter] duration-100 ease-linear hover:brightness-105 active:translate-y-2 active:shadow-[0_0_0_0_var(--color-tb30-grey-lo),0_4px_8px_-4px_#00000080] focus-visible:outline-4 focus-visible:outline-offset-[6px] focus-visible:outline-tb30-ink disabled:cursor-not-allowed disabled:grayscale-50 disabled:brightness-[0.85] disabled:shadow-[0_4px_0_0_var(--color-tb30-grey-lo)] motion-reduce:transition-none">
<span aria-hidden="true" class="absolute left-1/2 -bottom-1.5 w-36 h-4.5 -ml-18 rounded-[50%] pointer-events-none opacity-0 blur-[4px] bg-[radial-gradient(closest-side,rgba(255,255,255,0.85),transparent_72%)] group-active:animate-tb30-dust motion-reduce:hidden"></span>
<span class="relative z-20">Pour it</span>
</button>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb30-bg: #c9c6c0;
--color-tb30-bg: oklch(0.82 0.004 85);
--color-tb30-grey: #8f8d88;
--color-tb30-grey: oklch(0.62 0.004 85);
--color-tb30-grey-hi: #a8a6a1;
--color-tb30-grey-hi: oklch(0.7 0.004 85);
--color-tb30-grey-lo: #5d5b57;
--color-tb30-grey-lo: oklch(0.44 0.004 85);
--color-tb30-fleck: #e6e3dc;
--color-tb30-fleck: oklch(0.91 0.004 85);
--color-tb30-fleck-2: #54524e;
--color-tb30-fleck-2: oklch(0.4 0.004 85);
--color-tb30-ink: #141412;
--color-tb30-ink: oklch(0.16 0.004 85);
--font-tb30-black: "Archivo Black", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--animate-tb30-dust: tb-30-dust 0.55s ease-out;
}
@keyframes tb-30-dust {
0% {
opacity: 0.85;
scale: 0.4 0.5;
}
100% {
opacity: 0;
scale: 1.5 1.3;
}
}
.tb-30 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: repeating-linear-gradient(0deg, rgba(0,0,0,.035) 0 1px, transparent 1px 22px), var(--color-tb30-bg);
}
/* THE MIX: three aggregate tiles (13/19/29px), the 14px formwork rhythm, and the
cast base. Five layers whose sizes only mean anything together. */
.tb-30__slab {
background-color: var(--color-tb30-grey);
background-image: radial-gradient(.055rem .055rem at 18% 32%, color-mix(in oklab, var(--color-tb30-fleck) 55%, transparent) 99%, transparent), radial-gradient(.045rem .045rem at 62% 71%, color-mix(in oklab, var(--color-tb30-fleck-2) 45%, transparent) 99%, transparent), radial-gradient(.04rem .04rem at 84% 18%, color-mix(in oklab, var(--color-tb30-fleck) 40%, transparent) 99%, transparent), repeating-linear-gradient(0deg, rgba(0,0,0,.07) 0 1px, transparent 1px 14px), linear-gradient(168deg, var(--color-tb30-grey-hi), var(--color-tb30-grey) 55%, var(--color-tb30-grey-lo));
background-size: 13px 13px, 19px 19px, 29px 29px, 100% 100%, 100% 100%;
}
@media (prefers-color-scheme: dark) {
.tb-30:not([data-theme="light"]) {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-hi: oklch(0.44 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-fleck: oklch(0.62 0.004 85);
--color-tb30-fleck-2: oklch(0.22 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
}
.tb-30[data-theme="dark"] {
--color-tb30-bg: oklch(0.26 0.004 85);
--color-tb30-grey: oklch(0.38 0.004 85);
--color-tb30-grey-lo: oklch(0.26 0.004 85);
--color-tb30-ink: oklch(0.95 0.004 85);
}
@media (forced-colors: active) {
.tb-30__slab {
background: ButtonFace;
color: ButtonText;
border: 2px solid ButtonText;
box-shadow: none;
text-shadow: none;
clip-path: none;
}
.tb-30__slab span[aria-hidden] {
display: none;
}
}
```How this works
The speckle and shutter lines stay CSS — five background layers (three aggregate tiles at 13/19/29px, the 14px formwork rhythm, the base) whose sizes only mean anything as a set. Same call as the brass and walnut.
The sink is arithmetic, and the utilities show it. A hard 0 8px 0 shadow plus active:translate-y-2 (8px) means the slab lands exactly on its own shadow line with nothing left over. Any mismatch and it appears to slide:
shadow-[0_8px_0_0_var(--color-tb30-grey-lo),…]
active:translate-y-2 active:shadow-[0_0_0_0_var(--color-tb30-grey-lo),…]The chip removes the shadow at those corners too. clip-path clips the box shadow along with the box — a real limitation, noted in the gotchas, and the reason the hard shadow reads as a shelf rather than an outline.
Make it yours
--color-tb30-fleckshifts the mix: white for limestone, dark for basalt, warm for brick aggregate.- Shutter rhythm is the
14pxstop; wider boards read as a larger structure. - Chip depth is the
clip-pathpercentages. Keep them under 4% or it looks broken rather than cast. - Painted concrete: set
--color-tb30-greyto a saturated hue. The texture survives. - Dust reach is the keyframe's final scale.
Gotchas — read before shipping
- Heavy uppercase type on speckle needs a real contrast check — the fleck raises effective background lightness by several percent.
clip-pathclips the box shadow at the chipped corners. Add a matchingdrop-shadow-*if you need the shadow to follow the chips.- Hard shadow and travel must be identical values, or the slab slides.
- Never animate the speckle layers; the repaint spans the whole slab.
- Dark mode concrete goes muddy fast. Lower the fleck alpha rather than lightening the base.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
clip-path, repeating gradients and stacked box-shadows are all baseline below Tailwind v4's floor.