12 / 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 Confirm Delete Button
A destructive action that asks once, inline, without a modal: the row expands to reveal "Sure? / Yes, delete" with the confirm deliberately placed second and never under the original cursor. The 0fr → 1fr grid-track animation ports to two utilities, which is the cleanest possible expression of it.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tb-12 w-full min-h-svh grid place-items-center bg-tb12-bg text-tb12-text font-tb12-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<span class="group grid grid-cols-[auto_0fr] items-center p-1.5 rounded-[1.1rem] bg-tb12-surface border border-tb12-line shadow-[0_14px_34px_-22px_var(--color-tb12-shadow)] transition-[grid-template-columns,border-color] duration-[450ms] ease-tb12-out has-checked:grid-cols-[auto_1fr] has-checked:border-tb12-danger-line has-focus-visible:outline-2 has-focus-visible:outline-offset-[3px] has-focus-visible:outline-tb12-danger motion-reduce:transition-none">
<label class="cursor-pointer inline-flex items-center gap-2 min-h-11 px-4 rounded-xl text-[0.9375rem] font-semibold text-tb12-muted transition-colors duration-200 [-webkit-tap-highlight-color:transparent] hover:text-tb12-danger hover:bg-tb12-danger-tint group-has-checked:text-tb12-danger motion-reduce:transition-none">
<input type="checkbox" aria-label="Delete this project, requires confirmation" class="sr-only">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="size-5 shrink-0">
<g class="tb-12__lid [transform-origin:18%_60%] transition-[rotate,translate] duration-[380ms] ease-tb12-out group-has-checked:-rotate-[24deg] group-has-checked:-translate-x-px group-has-checked:-translate-y-[1.5px] motion-reduce:transition-none"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="group-has-checked:line-through group-has-checked:decoration-tb12-danger-line">Delete project</span>
</label>
<span class="min-w-0 overflow-hidden">
<span class="flex items-center gap-2.5 pl-2 pr-0.5 opacity-0 -translate-x-2 transition-[opacity,translate] duration-[400ms] delay-[80ms] ease-tb12-out group-has-checked:opacity-100 group-has-checked:translate-x-0 motion-reduce:transition-none">
<span class="text-[0.8125rem] font-semibold text-tb12-muted whitespace-nowrap">Sure?</span>
<button type="button" class="cursor-pointer whitespace-nowrap border-0 rounded-xl min-h-11 px-4.5 text-sm font-bold text-tb12-danger-ink bg-tb12-danger shadow-[0_8px_18px_-12px_var(--color-tb12-danger-glow)] transition-[background-color,translate] duration-200 ease-tb12-out hover:bg-tb12-danger-deep hover:-translate-y-px active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb12-danger motion-reduce:transition-none">Yes, delete</button>
</span>
</span>
</span>
</div><div class="tb-12 w-full min-h-svh grid place-items-center bg-tb12-bg text-tb12-text font-tb12-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<span class="group grid grid-cols-[auto_0fr] items-center p-1.5 rounded-[1.1rem] bg-tb12-surface border border-tb12-line shadow-[0_14px_34px_-22px_var(--color-tb12-shadow)] transition-[grid-template-columns,border-color] duration-[450ms] ease-tb12-out has-checked:grid-cols-[auto_1fr] has-checked:border-tb12-danger-line has-focus-visible:outline-2 has-focus-visible:outline-offset-[3px] has-focus-visible:outline-tb12-danger motion-reduce:transition-none">
<label class="cursor-pointer inline-flex items-center gap-2 min-h-11 px-4 rounded-xl text-[0.9375rem] font-semibold text-tb12-muted transition-colors duration-200 [-webkit-tap-highlight-color:transparent] hover:text-tb12-danger hover:bg-tb12-danger-tint group-has-checked:text-tb12-danger motion-reduce:transition-none">
<input type="checkbox" aria-label="Delete this project, requires confirmation" class="sr-only">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="size-5 shrink-0">
<g class="tb-12__lid [transform-origin:18%_60%] transition-[rotate,translate] duration-[380ms] ease-tb12-out group-has-checked:-rotate-[24deg] group-has-checked:-translate-x-px group-has-checked:-translate-y-[1.5px] motion-reduce:transition-none"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="group-has-checked:line-through group-has-checked:decoration-tb12-danger-line">Delete project</span>
</label>
<span class="min-w-0 overflow-hidden">
<span class="flex items-center gap-2.5 pl-2 pr-0.5 opacity-0 -translate-x-2 transition-[opacity,translate] duration-[400ms] delay-[80ms] ease-tb12-out group-has-checked:opacity-100 group-has-checked:translate-x-0 motion-reduce:transition-none">
<span class="text-[0.8125rem] font-semibold text-tb12-muted whitespace-nowrap">Sure?</span>
<button type="button" class="cursor-pointer whitespace-nowrap border-0 rounded-xl min-h-11 px-4.5 text-sm font-bold text-tb12-danger-ink bg-tb12-danger shadow-[0_8px_18px_-12px_var(--color-tb12-danger-glow)] transition-[background-color,translate] duration-200 ease-tb12-out hover:bg-tb12-danger-deep hover:-translate-y-px active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb12-danger motion-reduce:transition-none">Yes, delete</button>
</span>
</span>
</span>
</div>@import "tailwindcss";
@theme {
--color-tb12-bg: #f6f5f3;
--color-tb12-bg: oklch(0.968 0.004 90);
--color-tb12-surface: #ffffff;
--color-tb12-line: #e3e1dc;
--color-tb12-line: oklch(0.9 0.008 90);
--color-tb12-text: #1a1817;
--color-tb12-text: oklch(0.21 0.006 60);
--color-tb12-muted: #5c5750;
--color-tb12-muted: oklch(0.45 0.01 70);
--color-tb12-danger: #b91c1c;
--color-tb12-danger: oklch(0.48 0.2 27);
--color-tb12-danger-deep: #8f1414;
--color-tb12-danger-deep: oklch(0.4 0.18 27);
--color-tb12-danger-line: #b91c1c8c;
--color-tb12-danger-line: oklch(0.48 0.2 27 / 0.55);
--color-tb12-danger-tint: #b91c1c14;
--color-tb12-danger-tint: oklch(0.48 0.2 27 / 0.08);
--color-tb12-danger-glow: #b91c1cd9;
--color-tb12-danger-glow: oklch(0.48 0.2 27 / 0.85);
--color-tb12-danger-ink: #ffffff;
--color-tb12-shadow: #1a1817a6;
--color-tb12-shadow: oklch(0.21 0.006 60 / 0.65);
--font-tb12-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb12-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-12 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-12:not([data-theme="light"]) {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-deep: oklch(0.57 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
}
.tb-12[data-theme="dark"] {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
@media (forced-colors: active) {
.tb-12 > span {
border: 1px solid ButtonText;
}
.tb-12 button {
background: Highlight;
color: HighlightText;
}
}@import "tailwindcss";
@theme {
--color-tb12-bg: #f6f5f3;
--color-tb12-bg: oklch(0.968 0.004 90);
--color-tb12-surface: #ffffff;
--color-tb12-line: #e3e1dc;
--color-tb12-line: oklch(0.9 0.008 90);
--color-tb12-text: #1a1817;
--color-tb12-text: oklch(0.21 0.006 60);
--color-tb12-muted: #5c5750;
--color-tb12-muted: oklch(0.45 0.01 70);
--color-tb12-danger: #b91c1c;
--color-tb12-danger: oklch(0.48 0.2 27);
--color-tb12-danger-deep: #8f1414;
--color-tb12-danger-deep: oklch(0.4 0.18 27);
--color-tb12-danger-line: #b91c1c8c;
--color-tb12-danger-line: oklch(0.48 0.2 27 / 0.55);
--color-tb12-danger-tint: #b91c1c14;
--color-tb12-danger-tint: oklch(0.48 0.2 27 / 0.08);
--color-tb12-danger-glow: #b91c1cd9;
--color-tb12-danger-glow: oklch(0.48 0.2 27 / 0.85);
--color-tb12-danger-ink: #ffffff;
--color-tb12-shadow: #1a1817a6;
--color-tb12-shadow: oklch(0.21 0.006 60 / 0.65);
--font-tb12-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb12-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-12 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-12:not([data-theme="light"]) {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-deep: oklch(0.57 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
}
.tb-12[data-theme="dark"] {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
@media (forced-colors: active) {
.tb-12 > span {
border: 1px solid ButtonText;
}
.tb-12 button {
background: Highlight;
color: HighlightText;
}
}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-tb12-bg: #f6f5f3;
--color-tb12-bg: oklch(0.968 0.004 90);
--color-tb12-surface: #ffffff;
--color-tb12-line: #e3e1dc;
--color-tb12-line: oklch(0.9 0.008 90);
--color-tb12-text: #1a1817;
--color-tb12-text: oklch(0.21 0.006 60);
--color-tb12-muted: #5c5750;
--color-tb12-muted: oklch(0.45 0.01 70);
--color-tb12-danger: #b91c1c;
--color-tb12-danger: oklch(0.48 0.2 27);
--color-tb12-danger-deep: #8f1414;
--color-tb12-danger-deep: oklch(0.4 0.18 27);
--color-tb12-danger-line: #b91c1c8c;
--color-tb12-danger-line: oklch(0.48 0.2 27 / 0.55);
--color-tb12-danger-tint: #b91c1c14;
--color-tb12-danger-tint: oklch(0.48 0.2 27 / 0.08);
--color-tb12-danger-glow: #b91c1cd9;
--color-tb12-danger-glow: oklch(0.48 0.2 27 / 0.85);
--color-tb12-danger-ink: #ffffff;
--color-tb12-shadow: #1a1817a6;
--color-tb12-shadow: oklch(0.21 0.006 60 / 0.65);
--font-tb12-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb12-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-12 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-12:not([data-theme="light"]) {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-deep: oklch(0.57 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
}
.tb-12[data-theme="dark"] {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
@media (forced-colors: active) {
.tb-12 > span {
border: 1px solid ButtonText;
}
.tb-12 button {
background: Highlight;
color: HighlightText;
}
}@import "tailwindcss";
@theme {
--color-tb12-bg: #f6f5f3;
--color-tb12-bg: oklch(0.968 0.004 90);
--color-tb12-surface: #ffffff;
--color-tb12-line: #e3e1dc;
--color-tb12-line: oklch(0.9 0.008 90);
--color-tb12-text: #1a1817;
--color-tb12-text: oklch(0.21 0.006 60);
--color-tb12-muted: #5c5750;
--color-tb12-muted: oklch(0.45 0.01 70);
--color-tb12-danger: #b91c1c;
--color-tb12-danger: oklch(0.48 0.2 27);
--color-tb12-danger-deep: #8f1414;
--color-tb12-danger-deep: oklch(0.4 0.18 27);
--color-tb12-danger-line: #b91c1c8c;
--color-tb12-danger-line: oklch(0.48 0.2 27 / 0.55);
--color-tb12-danger-tint: #b91c1c14;
--color-tb12-danger-tint: oklch(0.48 0.2 27 / 0.08);
--color-tb12-danger-glow: #b91c1cd9;
--color-tb12-danger-glow: oklch(0.48 0.2 27 / 0.85);
--color-tb12-danger-ink: #ffffff;
--color-tb12-shadow: #1a1817a6;
--color-tb12-shadow: oklch(0.21 0.006 60 / 0.65);
--font-tb12-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb12-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-12 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-12:not([data-theme="light"]) {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-deep: oklch(0.57 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
}
.tb-12[data-theme="dark"] {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
@media (forced-colors: active) {
.tb-12 > span {
border: 1px solid ButtonText;
}
.tb-12 button {
background: Highlight;
color: HighlightText;
}
}<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=Schibsted+Grotesk: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=Schibsted+Grotesk:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Utilities: every part of the interaction — the grid-track expansion, the staggered reveal, the lid hinge, the strikethrough, all three focus paths. CSS: tokens and
forced-colorsonly. This is the most complete port in the first dozen. [transform-origin:18%_60%]is the right kind of arbitrary utility: a hand-tuned magic number that only means anything next to the element it pivots. Promoting it to a token would hide it.group-has-checked:appears on six descendants here. In the CSS version each was a separate.shell:has(input:checked) .childrule at the bottom of the file; the port's gain is that the trash lid's rule now lives on the trash lid.- A11y fix from the CSS original:
--muted:#6d6862on white is about 4.3:1. The port'soklch(0.45 0.01 70)is ≈5.4:1, which matters because the trigger label is the only text on the control at rest.
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 Confirm Delete Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-confirm-delete-button/
A destructive action that asks once, inline, without a modal: the row expands to reveal "Sure? / Yes, delete" with the confirm deliberately placed second and never under the original cursor. The 0fr → 1fr grid-track animation ports to two utilities, which is the cleanest possible expression of it.
## HTML
```html
<div class="tb-12 w-full min-h-svh grid place-items-center bg-tb12-bg text-tb12-text font-tb12-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<span class="group grid grid-cols-[auto_0fr] items-center p-1.5 rounded-[1.1rem] bg-tb12-surface border border-tb12-line shadow-[0_14px_34px_-22px_var(--color-tb12-shadow)] transition-[grid-template-columns,border-color] duration-[450ms] ease-tb12-out has-checked:grid-cols-[auto_1fr] has-checked:border-tb12-danger-line has-focus-visible:outline-2 has-focus-visible:outline-offset-[3px] has-focus-visible:outline-tb12-danger motion-reduce:transition-none">
<label class="cursor-pointer inline-flex items-center gap-2 min-h-11 px-4 rounded-xl text-[0.9375rem] font-semibold text-tb12-muted transition-colors duration-200 [-webkit-tap-highlight-color:transparent] hover:text-tb12-danger hover:bg-tb12-danger-tint group-has-checked:text-tb12-danger motion-reduce:transition-none">
<input type="checkbox" aria-label="Delete this project, requires confirmation" class="sr-only">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="size-5 shrink-0">
<g class="tb-12__lid [transform-origin:18%_60%] transition-[rotate,translate] duration-[380ms] ease-tb12-out group-has-checked:-rotate-[24deg] group-has-checked:-translate-x-px group-has-checked:-translate-y-[1.5px] motion-reduce:transition-none"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="group-has-checked:line-through group-has-checked:decoration-tb12-danger-line">Delete project</span>
</label>
<span class="min-w-0 overflow-hidden">
<span class="flex items-center gap-2.5 pl-2 pr-0.5 opacity-0 -translate-x-2 transition-[opacity,translate] duration-[400ms] delay-[80ms] ease-tb12-out group-has-checked:opacity-100 group-has-checked:translate-x-0 motion-reduce:transition-none">
<span class="text-[0.8125rem] font-semibold text-tb12-muted whitespace-nowrap">Sure?</span>
<button type="button" class="cursor-pointer whitespace-nowrap border-0 rounded-xl min-h-11 px-4.5 text-sm font-bold text-tb12-danger-ink bg-tb12-danger shadow-[0_8px_18px_-12px_var(--color-tb12-danger-glow)] transition-[background-color,translate] duration-200 ease-tb12-out hover:bg-tb12-danger-deep hover:-translate-y-px active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb12-danger motion-reduce:transition-none">Yes, delete</button>
</span>
</span>
</span>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb12-bg: #f6f5f3;
--color-tb12-bg: oklch(0.968 0.004 90);
--color-tb12-surface: #ffffff;
--color-tb12-line: #e3e1dc;
--color-tb12-line: oklch(0.9 0.008 90);
--color-tb12-text: #1a1817;
--color-tb12-text: oklch(0.21 0.006 60);
--color-tb12-muted: #5c5750;
--color-tb12-muted: oklch(0.45 0.01 70);
--color-tb12-danger: #b91c1c;
--color-tb12-danger: oklch(0.48 0.2 27);
--color-tb12-danger-deep: #8f1414;
--color-tb12-danger-deep: oklch(0.4 0.18 27);
--color-tb12-danger-line: #b91c1c8c;
--color-tb12-danger-line: oklch(0.48 0.2 27 / 0.55);
--color-tb12-danger-tint: #b91c1c14;
--color-tb12-danger-tint: oklch(0.48 0.2 27 / 0.08);
--color-tb12-danger-glow: #b91c1cd9;
--color-tb12-danger-glow: oklch(0.48 0.2 27 / 0.85);
--color-tb12-danger-ink: #ffffff;
--color-tb12-shadow: #1a1817a6;
--color-tb12-shadow: oklch(0.21 0.006 60 / 0.65);
--font-tb12-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb12-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-12 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-12:not([data-theme="light"]) {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-deep: oklch(0.57 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
}
.tb-12[data-theme="dark"] {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
@media (forced-colors: active) {
.tb-12 > span {
border: 1px solid ButtonText;
}
.tb-12 button {
background: Highlight;
color: HighlightText;
}
}
```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 Confirm Delete Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-confirm-delete-button/
A destructive action that asks once, inline, without a modal: the row expands to reveal "Sure? / Yes, delete" with the confirm deliberately placed second and never under the original cursor. The 0fr → 1fr grid-track animation ports to two utilities, which is the cleanest possible expression of it.
## HTML
```html
<div class="tb-12 w-full min-h-svh grid place-items-center bg-tb12-bg text-tb12-text font-tb12-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<span class="group grid grid-cols-[auto_0fr] items-center p-1.5 rounded-[1.1rem] bg-tb12-surface border border-tb12-line shadow-[0_14px_34px_-22px_var(--color-tb12-shadow)] transition-[grid-template-columns,border-color] duration-[450ms] ease-tb12-out has-checked:grid-cols-[auto_1fr] has-checked:border-tb12-danger-line has-focus-visible:outline-2 has-focus-visible:outline-offset-[3px] has-focus-visible:outline-tb12-danger motion-reduce:transition-none">
<label class="cursor-pointer inline-flex items-center gap-2 min-h-11 px-4 rounded-xl text-[0.9375rem] font-semibold text-tb12-muted transition-colors duration-200 [-webkit-tap-highlight-color:transparent] hover:text-tb12-danger hover:bg-tb12-danger-tint group-has-checked:text-tb12-danger motion-reduce:transition-none">
<input type="checkbox" aria-label="Delete this project, requires confirmation" class="sr-only">
<svg viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" class="size-5 shrink-0">
<g class="tb-12__lid [transform-origin:18%_60%] transition-[rotate,translate] duration-[380ms] ease-tb12-out group-has-checked:-rotate-[24deg] group-has-checked:-translate-x-px group-has-checked:-translate-y-[1.5px] motion-reduce:transition-none"><path d="M4.5 7h15"/><path d="M9.5 7V5.2A1.2 1.2 0 0 1 10.7 4h2.6a1.2 1.2 0 0 1 1.2 1.2V7"/></g>
<path d="M6.5 7.8 7.4 19a1.5 1.5 0 0 0 1.5 1.4h6.2a1.5 1.5 0 0 0 1.5-1.4l.9-11.2"/><path d="M10.5 11v6M13.5 11v6"/>
</svg>
<span class="group-has-checked:line-through group-has-checked:decoration-tb12-danger-line">Delete project</span>
</label>
<span class="min-w-0 overflow-hidden">
<span class="flex items-center gap-2.5 pl-2 pr-0.5 opacity-0 -translate-x-2 transition-[opacity,translate] duration-[400ms] delay-[80ms] ease-tb12-out group-has-checked:opacity-100 group-has-checked:translate-x-0 motion-reduce:transition-none">
<span class="text-[0.8125rem] font-semibold text-tb12-muted whitespace-nowrap">Sure?</span>
<button type="button" class="cursor-pointer whitespace-nowrap border-0 rounded-xl min-h-11 px-4.5 text-sm font-bold text-tb12-danger-ink bg-tb12-danger shadow-[0_8px_18px_-12px_var(--color-tb12-danger-glow)] transition-[background-color,translate] duration-200 ease-tb12-out hover:bg-tb12-danger-deep hover:-translate-y-px active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb12-danger motion-reduce:transition-none">Yes, delete</button>
</span>
</span>
</span>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb12-bg: #f6f5f3;
--color-tb12-bg: oklch(0.968 0.004 90);
--color-tb12-surface: #ffffff;
--color-tb12-line: #e3e1dc;
--color-tb12-line: oklch(0.9 0.008 90);
--color-tb12-text: #1a1817;
--color-tb12-text: oklch(0.21 0.006 60);
--color-tb12-muted: #5c5750;
--color-tb12-muted: oklch(0.45 0.01 70);
--color-tb12-danger: #b91c1c;
--color-tb12-danger: oklch(0.48 0.2 27);
--color-tb12-danger-deep: #8f1414;
--color-tb12-danger-deep: oklch(0.4 0.18 27);
--color-tb12-danger-line: #b91c1c8c;
--color-tb12-danger-line: oklch(0.48 0.2 27 / 0.55);
--color-tb12-danger-tint: #b91c1c14;
--color-tb12-danger-tint: oklch(0.48 0.2 27 / 0.08);
--color-tb12-danger-glow: #b91c1cd9;
--color-tb12-danger-glow: oklch(0.48 0.2 27 / 0.85);
--color-tb12-danger-ink: #ffffff;
--color-tb12-shadow: #1a1817a6;
--color-tb12-shadow: oklch(0.21 0.006 60 / 0.65);
--font-tb12-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb12-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-12 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-12:not([data-theme="light"]) {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-deep: oklch(0.57 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
}
.tb-12[data-theme="dark"] {
--color-tb12-bg: oklch(0.13 0.004 60);
--color-tb12-surface: oklch(0.18 0.006 60);
--color-tb12-line: oklch(0.28 0.008 60);
--color-tb12-text: oklch(0.95 0.006 80);
--color-tb12-muted: oklch(0.75 0.01 70);
--color-tb12-danger: oklch(0.65 0.2 27);
--color-tb12-danger-ink: oklch(0.15 0.05 27);
}
@media (forced-colors: active) {
.tb-12 > span {
border: 1px solid ButtonText;
}
.tb-12 button {
background: Highlight;
color: HighlightText;
}
}
```How this works
The expansion is two utilities. Animating grid-template-columns from 0fr to 1fr is the modern way to reveal intrinsic content width, and Tailwind says it directly:
class="grid grid-cols-[auto_0fr] has-checked:grid-cols-[auto_1fr]
transition-[grid-template-columns] duration-[450ms]"Safety is layout, not colour. The confirm appears to the right of the trigger, so a second reflexive click lands on nothing destructive. The trigger is a checkbox label (Space toggles, focus is visible) and the confirm is a real <button>, so your handler binds where you expect.
The lid hinge stays an arbitrary property. [transform-origin:18%_60%] is a hand-tuned pivot on an SVG group — a magic number that belongs next to the element it pivots, which is exactly what an arbitrary utility gives you.
Make it yours
- Confirm-first platforms: swap the two children. Nothing else changes.
- Heavier actions: drop a typed-confirmation input into the revealed cell — the grid track grows to fit.
- Always-open in a bulk-edit toolbar: start at
grid-cols-[auto_1fr]. - Brand the danger tone through
--color-tb12-danger; the ghost trigger derives from the neutral tokens. - Countdown: add an
animate-*width bar on the confirm'safter:pseudo-element.
Gotchas — read before shipping
0frtrack animation needsmin-w-0 overflow-hiddenon the child, or the content never compresses.- Never place the confirm exactly where the trigger was — double-clickers will delete their data.
- Colour must not be the only destructive signal: the word "delete" is in the label for a reason.
- For genuinely irreversible actions an inline confirm is not enough. Use a dialog with focus trapping.
- A checkbox in a form serialises as data. Add
form=""for a self-contained widget.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Animating grid-template-columns needs Chrome 107 / Safari 16 / Firefox 66; :has() needs Chrome 105 / Safari 15.4 / Firefox 121. Both sit below Tailwind v4's own floor.