07 / 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 Disabled Button State (Full State Reference)
Every button state side by side — rest, hover, focus, pressed, truly disabled, and aria-disabled (focusable, explains itself, refuses activation). The frozen cards use data-force attributes rather than .is-* classes, which is the tidiest thing the port does: demo-only styling is visibly demo-only.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tb-07 w-full min-h-svh grid place-items-center bg-tb07-bg font-tb07-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<div class="flex flex-wrap justify-center gap-3 max-w-[44rem]">
<button type="button" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px active:scale-[0.985] active:shadow-none focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none disabled:translate-y-0 aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] aria-disabled:active:animate-tb07-nope motion-reduce:transition-none motion-reduce:aria-disabled:active:animate-none">Rest</button>
<button type="button" data-force-hover class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px motion-reduce:transition-none">Hover</button>
<button type="button" data-force-focus class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent motion-reduce:transition-none">Focus</button>
<button type="button" data-force-active class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h active:bg-tb07-fill-a focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] data-force-active:shadow-none motion-reduce:transition-none">Pressed</button>
<button type="button" disabled class="grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none">Disabled</button>
<button type="button" aria-disabled="true" aria-describedby="tb-07-why" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none aria-disabled:active:animate-tb07-nope focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent motion-reduce:aria-disabled:active:animate-none motion-reduce:aria-disabled:active:outline-2 motion-reduce:aria-disabled:active:outline-dashed motion-reduce:aria-disabled:active:outline-tb07-muted">Unavailable</button>
<span id="tb-07-why" class="sr-only">Complete the form above to enable this action</span>
</div>
</div><div class="tb-07 w-full min-h-svh grid place-items-center bg-tb07-bg font-tb07-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<div class="flex flex-wrap justify-center gap-3 max-w-[44rem]">
<button type="button" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px active:scale-[0.985] active:shadow-none focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none disabled:translate-y-0 aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] aria-disabled:active:animate-tb07-nope motion-reduce:transition-none motion-reduce:aria-disabled:active:animate-none">Rest</button>
<button type="button" data-force-hover class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px motion-reduce:transition-none">Hover</button>
<button type="button" data-force-focus class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent motion-reduce:transition-none">Focus</button>
<button type="button" data-force-active class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h active:bg-tb07-fill-a focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] data-force-active:shadow-none motion-reduce:transition-none">Pressed</button>
<button type="button" disabled class="grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none">Disabled</button>
<button type="button" aria-disabled="true" aria-describedby="tb-07-why" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none aria-disabled:active:animate-tb07-nope focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent motion-reduce:aria-disabled:active:animate-none motion-reduce:aria-disabled:active:outline-2 motion-reduce:aria-disabled:active:outline-dashed motion-reduce:aria-disabled:active:outline-tb07-muted">Unavailable</button>
<span id="tb-07-why" class="sr-only">Complete the form above to enable this action</span>
</div>
</div>@import "tailwindcss";
@theme {
--color-tb07-bg: #f5f5f3;
--color-tb07-bg: oklch(0.968 0.004 110);
--color-tb07-accent: #0f766e;
--color-tb07-accent: oklch(0.48 0.1 180);
--color-tb07-fill: #0f766e;
--color-tb07-fill: oklch(0.48 0.1 180);
--color-tb07-fill-h: #0d6a62;
--color-tb07-fill-h: oklch(0.44 0.1 180);
--color-tb07-fill-a: #0b5a53;
--color-tb07-fill-a: oklch(0.39 0.09 180);
--color-tb07-off: #dcdcd8;
--color-tb07-off: oklch(0.89 0.006 110);
--color-tb07-muted: #4b5157;
--color-tb07-muted: oklch(0.42 0.012 260);
--color-tb07-ink: #ffffff;
--color-tb07-glow: #0f766ed9;
--color-tb07-glow: oklch(0.48 0.1 180 / 0.85);
--font-tb07-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb07-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb07-nope: tb-07-nope 0.32s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes tb-07-nope {
0%,
100% {
translate: 0 0;
}
25% {
translate: -3px 0;
}
75% {
translate: 3px 0;
}
}
/* Demo-only: the data-force-* variants need a custom variant declaration. */
@custom-variant data-force-hover (&[data-force-hover]);
@custom-variant data-force-focus (&[data-force-focus]);
@custom-variant data-force-active (&[data-force-active]);
.tb-07 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-07:not([data-theme="light"]) {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-fill-h: oklch(0.66 0.12 180);
--color-tb07-fill-a: oklch(0.6 0.11 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
--color-tb07-accent: oklch(0.72 0.12 180);
}
}
.tb-07[data-theme="dark"] {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
}
@media (forced-colors: active) {
.tb-07 button {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
box-shadow: none;
}
.tb-07 button:disabled,
.tb-07 button[aria-disabled="true"] {
color: GrayText;
border-color: GrayText;
}
}@import "tailwindcss";
@theme {
--color-tb07-bg: #f5f5f3;
--color-tb07-bg: oklch(0.968 0.004 110);
--color-tb07-accent: #0f766e;
--color-tb07-accent: oklch(0.48 0.1 180);
--color-tb07-fill: #0f766e;
--color-tb07-fill: oklch(0.48 0.1 180);
--color-tb07-fill-h: #0d6a62;
--color-tb07-fill-h: oklch(0.44 0.1 180);
--color-tb07-fill-a: #0b5a53;
--color-tb07-fill-a: oklch(0.39 0.09 180);
--color-tb07-off: #dcdcd8;
--color-tb07-off: oklch(0.89 0.006 110);
--color-tb07-muted: #4b5157;
--color-tb07-muted: oklch(0.42 0.012 260);
--color-tb07-ink: #ffffff;
--color-tb07-glow: #0f766ed9;
--color-tb07-glow: oklch(0.48 0.1 180 / 0.85);
--font-tb07-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb07-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb07-nope: tb-07-nope 0.32s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes tb-07-nope {
0%,
100% {
translate: 0 0;
}
25% {
translate: -3px 0;
}
75% {
translate: 3px 0;
}
}
/* Demo-only: the data-force-* variants need a custom variant declaration. */
@custom-variant data-force-hover (&[data-force-hover]);
@custom-variant data-force-focus (&[data-force-focus]);
@custom-variant data-force-active (&[data-force-active]);
.tb-07 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-07:not([data-theme="light"]) {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-fill-h: oklch(0.66 0.12 180);
--color-tb07-fill-a: oklch(0.6 0.11 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
--color-tb07-accent: oklch(0.72 0.12 180);
}
}
.tb-07[data-theme="dark"] {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
}
@media (forced-colors: active) {
.tb-07 button {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
box-shadow: none;
}
.tb-07 button:disabled,
.tb-07 button[aria-disabled="true"] {
color: GrayText;
border-color: GrayText;
}
}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-tb07-bg: #f5f5f3;
--color-tb07-bg: oklch(0.968 0.004 110);
--color-tb07-accent: #0f766e;
--color-tb07-accent: oklch(0.48 0.1 180);
--color-tb07-fill: #0f766e;
--color-tb07-fill: oklch(0.48 0.1 180);
--color-tb07-fill-h: #0d6a62;
--color-tb07-fill-h: oklch(0.44 0.1 180);
--color-tb07-fill-a: #0b5a53;
--color-tb07-fill-a: oklch(0.39 0.09 180);
--color-tb07-off: #dcdcd8;
--color-tb07-off: oklch(0.89 0.006 110);
--color-tb07-muted: #4b5157;
--color-tb07-muted: oklch(0.42 0.012 260);
--color-tb07-ink: #ffffff;
--color-tb07-glow: #0f766ed9;
--color-tb07-glow: oklch(0.48 0.1 180 / 0.85);
--font-tb07-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb07-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb07-nope: tb-07-nope 0.32s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes tb-07-nope {
0%,
100% {
translate: 0 0;
}
25% {
translate: -3px 0;
}
75% {
translate: 3px 0;
}
}
/* Demo-only: the data-force-* variants need a custom variant declaration. */
@custom-variant data-force-hover (&[data-force-hover]);
@custom-variant data-force-focus (&[data-force-focus]);
@custom-variant data-force-active (&[data-force-active]);
.tb-07 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-07:not([data-theme="light"]) {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-fill-h: oklch(0.66 0.12 180);
--color-tb07-fill-a: oklch(0.6 0.11 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
--color-tb07-accent: oklch(0.72 0.12 180);
}
}
.tb-07[data-theme="dark"] {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
}
@media (forced-colors: active) {
.tb-07 button {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
box-shadow: none;
}
.tb-07 button:disabled,
.tb-07 button[aria-disabled="true"] {
color: GrayText;
border-color: GrayText;
}
}@import "tailwindcss";
@theme {
--color-tb07-bg: #f5f5f3;
--color-tb07-bg: oklch(0.968 0.004 110);
--color-tb07-accent: #0f766e;
--color-tb07-accent: oklch(0.48 0.1 180);
--color-tb07-fill: #0f766e;
--color-tb07-fill: oklch(0.48 0.1 180);
--color-tb07-fill-h: #0d6a62;
--color-tb07-fill-h: oklch(0.44 0.1 180);
--color-tb07-fill-a: #0b5a53;
--color-tb07-fill-a: oklch(0.39 0.09 180);
--color-tb07-off: #dcdcd8;
--color-tb07-off: oklch(0.89 0.006 110);
--color-tb07-muted: #4b5157;
--color-tb07-muted: oklch(0.42 0.012 260);
--color-tb07-ink: #ffffff;
--color-tb07-glow: #0f766ed9;
--color-tb07-glow: oklch(0.48 0.1 180 / 0.85);
--font-tb07-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb07-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb07-nope: tb-07-nope 0.32s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes tb-07-nope {
0%,
100% {
translate: 0 0;
}
25% {
translate: -3px 0;
}
75% {
translate: 3px 0;
}
}
/* Demo-only: the data-force-* variants need a custom variant declaration. */
@custom-variant data-force-hover (&[data-force-hover]);
@custom-variant data-force-focus (&[data-force-focus]);
@custom-variant data-force-active (&[data-force-active]);
.tb-07 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-07:not([data-theme="light"]) {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-fill-h: oklch(0.66 0.12 180);
--color-tb07-fill-a: oklch(0.6 0.11 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
--color-tb07-accent: oklch(0.72 0.12 180);
}
}
.tb-07[data-theme="dark"] {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
}
@media (forced-colors: active) {
.tb-07 button {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
box-shadow: none;
}
.tb-07 button:disabled,
.tb-07 button[aria-disabled="true"] {
color: GrayText;
border-color: GrayText;
}
}<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 state, both off-states, the shake and its reduced-motion replacement. CSS: the keyframes, the three
@custom-variantdeclarations for the demo-only forced states, andforced-colors, whereGrayTextmust win over every token. @custom-variant data-force-hover (&[data-force-hover])is the v4 mechanism that made the demo scaffolding honest: the frozen cards carry an attribute, not a class that looks like production styling.- A11y fix from the CSS original: the disabled label was
--muted:#6b7280on#dcdcd8— about 3.9:1, under the 4.5:1 minimum. The port darkens it tooklch(0.42 0.012 260), roughly 5.6:1. The point of the demo is that disabled text still has to be readable, so the original failing its own advice was worth fixing. - The base recipe is repeated on all six buttons rather than extracted. In a real codebase this is where you reach for a component, not an
@applyclass — and the demo shows the full list precisely so the component's contents are visible.
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 Disabled Button State (Full State Reference)
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-disabled-button-state-full-reference/
Every button state side by side — rest, hover, focus, pressed, truly disabled, and aria-disabled (focusable, explains itself, refuses activation). The frozen cards use data-force attributes rather than .is-* classes, which is the tidiest thing the port does: demo-only styling is visibly demo-only.
## HTML
```html
<div class="tb-07 w-full min-h-svh grid place-items-center bg-tb07-bg font-tb07-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<div class="flex flex-wrap justify-center gap-3 max-w-[44rem]">
<button type="button" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px active:scale-[0.985] active:shadow-none focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none disabled:translate-y-0 aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] aria-disabled:active:animate-tb07-nope motion-reduce:transition-none motion-reduce:aria-disabled:active:animate-none">Rest</button>
<button type="button" data-force-hover class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px motion-reduce:transition-none">Hover</button>
<button type="button" data-force-focus class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent motion-reduce:transition-none">Focus</button>
<button type="button" data-force-active class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h active:bg-tb07-fill-a focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] data-force-active:shadow-none motion-reduce:transition-none">Pressed</button>
<button type="button" disabled class="grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none">Disabled</button>
<button type="button" aria-disabled="true" aria-describedby="tb-07-why" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none aria-disabled:active:animate-tb07-nope focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent motion-reduce:aria-disabled:active:animate-none motion-reduce:aria-disabled:active:outline-2 motion-reduce:aria-disabled:active:outline-dashed motion-reduce:aria-disabled:active:outline-tb07-muted">Unavailable</button>
<span id="tb-07-why" class="sr-only">Complete the form above to enable this action</span>
</div>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb07-bg: #f5f5f3;
--color-tb07-bg: oklch(0.968 0.004 110);
--color-tb07-accent: #0f766e;
--color-tb07-accent: oklch(0.48 0.1 180);
--color-tb07-fill: #0f766e;
--color-tb07-fill: oklch(0.48 0.1 180);
--color-tb07-fill-h: #0d6a62;
--color-tb07-fill-h: oklch(0.44 0.1 180);
--color-tb07-fill-a: #0b5a53;
--color-tb07-fill-a: oklch(0.39 0.09 180);
--color-tb07-off: #dcdcd8;
--color-tb07-off: oklch(0.89 0.006 110);
--color-tb07-muted: #4b5157;
--color-tb07-muted: oklch(0.42 0.012 260);
--color-tb07-ink: #ffffff;
--color-tb07-glow: #0f766ed9;
--color-tb07-glow: oklch(0.48 0.1 180 / 0.85);
--font-tb07-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb07-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb07-nope: tb-07-nope 0.32s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes tb-07-nope {
0%,
100% {
translate: 0 0;
}
25% {
translate: -3px 0;
}
75% {
translate: 3px 0;
}
}
/* Demo-only: the data-force-* variants need a custom variant declaration. */
@custom-variant data-force-hover (&[data-force-hover]);
@custom-variant data-force-focus (&[data-force-focus]);
@custom-variant data-force-active (&[data-force-active]);
.tb-07 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-07:not([data-theme="light"]) {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-fill-h: oklch(0.66 0.12 180);
--color-tb07-fill-a: oklch(0.6 0.11 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
--color-tb07-accent: oklch(0.72 0.12 180);
}
}
.tb-07[data-theme="dark"] {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
}
@media (forced-colors: active) {
.tb-07 button {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
box-shadow: none;
}
.tb-07 button:disabled,
.tb-07 button[aria-disabled="true"] {
color: GrayText;
border-color: GrayText;
}
}
```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 Disabled Button State (Full State Reference)
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-disabled-button-state-full-reference/
Every button state side by side — rest, hover, focus, pressed, truly disabled, and aria-disabled (focusable, explains itself, refuses activation). The frozen cards use data-force attributes rather than .is-* classes, which is the tidiest thing the port does: demo-only styling is visibly demo-only.
## HTML
```html
<div class="tb-07 w-full min-h-svh grid place-items-center bg-tb07-bg font-tb07-sans antialiased p-[clamp(1.5rem,6vw,4rem)]">
<div class="flex flex-wrap justify-center gap-3 max-w-[44rem]">
<button type="button" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px active:scale-[0.985] active:shadow-none focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none disabled:translate-y-0 aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] aria-disabled:active:animate-tb07-nope motion-reduce:transition-none motion-reduce:aria-disabled:active:animate-none">Rest</button>
<button type="button" data-force-hover class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-hover:bg-tb07-fill-h data-force-hover:-translate-y-px motion-reduce:transition-none">Hover</button>
<button type="button" data-force-focus class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill shadow-[0_8px_18px_-12px_var(--color-tb07-glow)] transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h hover:-translate-y-px active:bg-tb07-fill-a active:translate-y-px focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-focus:outline-2 data-force-focus:outline-offset-[3px] data-force-focus:outline-tb07-accent motion-reduce:transition-none">Focus</button>
<button type="button" data-force-active class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill transition-[background-color,translate,scale] duration-200 ease-tb07-out hover:bg-tb07-fill-h active:bg-tb07-fill-a focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent data-force-active:bg-tb07-fill-a data-force-active:translate-y-px data-force-active:scale-[0.985] data-force-active:shadow-none motion-reduce:transition-none">Pressed</button>
<button type="button" disabled class="grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed disabled:shadow-none">Disabled</button>
<button type="button" aria-disabled="true" aria-describedby="tb-07-why" class="cursor-pointer grid place-items-center min-h-12 min-w-30 px-5 border-0 rounded-[0.7rem] text-[0.9375rem] font-semibold text-tb07-ink bg-tb07-fill aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed aria-disabled:shadow-none aria-disabled:active:animate-tb07-nope focus-visible:outline-2 focus-visible:outline-offset-[3px] focus-visible:outline-tb07-accent motion-reduce:aria-disabled:active:animate-none motion-reduce:aria-disabled:active:outline-2 motion-reduce:aria-disabled:active:outline-dashed motion-reduce:aria-disabled:active:outline-tb07-muted">Unavailable</button>
<span id="tb-07-why" class="sr-only">Complete the form above to enable this action</span>
</div>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb07-bg: #f5f5f3;
--color-tb07-bg: oklch(0.968 0.004 110);
--color-tb07-accent: #0f766e;
--color-tb07-accent: oklch(0.48 0.1 180);
--color-tb07-fill: #0f766e;
--color-tb07-fill: oklch(0.48 0.1 180);
--color-tb07-fill-h: #0d6a62;
--color-tb07-fill-h: oklch(0.44 0.1 180);
--color-tb07-fill-a: #0b5a53;
--color-tb07-fill-a: oklch(0.39 0.09 180);
--color-tb07-off: #dcdcd8;
--color-tb07-off: oklch(0.89 0.006 110);
--color-tb07-muted: #4b5157;
--color-tb07-muted: oklch(0.42 0.012 260);
--color-tb07-ink: #ffffff;
--color-tb07-glow: #0f766ed9;
--color-tb07-glow: oklch(0.48 0.1 180 / 0.85);
--font-tb07-sans: "Schibsted Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb07-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb07-nope: tb-07-nope 0.32s cubic-bezier(0.36, 0.07, 0.19, 0.97);
}
@keyframes tb-07-nope {
0%,
100% {
translate: 0 0;
}
25% {
translate: -3px 0;
}
75% {
translate: 3px 0;
}
}
/* Demo-only: the data-force-* variants need a custom variant declaration. */
@custom-variant data-force-hover (&[data-force-hover]);
@custom-variant data-force-focus (&[data-force-focus]);
@custom-variant data-force-active (&[data-force-active]);
.tb-07 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
}
@media (prefers-color-scheme: dark) {
.tb-07:not([data-theme="light"]) {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-fill-h: oklch(0.66 0.12 180);
--color-tb07-fill-a: oklch(0.6 0.11 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
--color-tb07-accent: oklch(0.72 0.12 180);
}
}
.tb-07[data-theme="dark"] {
--color-tb07-bg: oklch(0.14 0.008 190);
--color-tb07-fill: oklch(0.72 0.12 180);
--color-tb07-off: oklch(0.28 0.008 190);
--color-tb07-muted: oklch(0.8 0.01 190);
--color-tb07-ink: oklch(0.18 0.04 180);
}
@media (forced-colors: active) {
.tb-07 button {
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
box-shadow: none;
}
.tb-07 button:disabled,
.tb-07 button[aria-disabled="true"] {
color: GrayText;
border-color: GrayText;
}
}
```How this works
Two off-states, one class list. disabled removes the button from the tab order and the interactive accessibility tree — correct for a control that is irrelevant right now. aria-disabled="true" keeps it focusable and announceable — correct for "you can't do this yet". Tailwind styles both by writing both variants:
class="… disabled:bg-tb07-off disabled:text-tb07-muted disabled:cursor-not-allowed
aria-disabled:bg-tb07-off aria-disabled:text-tb07-muted aria-disabled:cursor-not-allowed"Verbose, and correct: there is no combinator that says "either of these", and inventing one would hide the semantic difference the demo exists to teach.
The frozen states are attributes, not classes. data-force-hover and friends compile through data-force-hover: variants, so the production class list is exactly the real one and the demo scaffolding is a single attribute you delete.
Contrast is the real subject. The disabled label sits on a computed token at 4.5:1, not at opacity-50 — WCAG has no exemption for disabled text, and a user still has to read what they cannot press.
Make it yours
- Tune the off-state through
--color-tb07-offand--color-tb07-muted; never reach fordisabled:opacity-50, which also fades the focus ring and icons. - For
aria-disabledbuttons, block the action in JS — ARIA changes semantics, not behaviour. - Add a pending state by pairing the same visuals with
aria-busy:instead. - Delete every
data-force-*attribute in production; they exist only to display frozen states. - If your brand's disabled fill fails contrast, keep the fill and darken the label only.
Gotchas — read before shipping
disabledbuttons fire no pointer or focus events, so a tooltip explaining why can never appear on them. That is the main argument foraria-disabledon form submits.disabled:opacity-50is not a disabled style, it is a contrast bug.cursor-not-allowedis a hint, not a state — without a fill change, colour-blind and zoomed users get nothing.- Tailwind's
aria-disabled:shorthand compiles to[aria-disabled="true"], which is the right value here — unlikearia-current:, whose"true"is almost never what you want. - In forced-colors mode
GrayTextis the only reliable disabled colour; system colours override your tokens by design, and the CSS block says so explicitly.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Everything here is baseline: :disabled, attribute selectors, :focus-visible and forced-colors system colours. The floor is Tailwind v4's generated sheet.