42 / 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 Aged Brass Subway Token Button
A 1953 transit token: milled rim, tarnished brass, and a knocked-out letter you can see the background through — a mask-composite cut, not a coloured glyph. Press it and the token drops into the slot and springs back.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tb-42 w-full min-h-svh grid place-items-center font-tb42-sans p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" aria-label="Use one transit token" class="group relative cursor-pointer border-0 bg-transparent p-0 w-30 h-[9.5rem] grid place-items-start justify-center [-webkit-tap-highlight-color:transparent] focus-visible:outline-none disabled:cursor-not-allowed disabled:grayscale-[0.7] disabled:brightness-[0.85]">
<span aria-hidden="true" class="absolute inset-x-[12%] bottom-1.5 h-2 rounded-full bg-[linear-gradient(180deg,#0a0c0b,#1d221f)] shadow-[inset_0_2px_4px_#000000e6,0_1px_0_#ffffff14]"></span>
<span aria-hidden="true" class="tb-42__disc relative grid place-items-center size-30 rounded-full isolate text-tb42-brass-lo shadow-[inset_0_2px_3px_#ffffff66,inset_0_-3px_6px_#00000073,0_14px_22px_-10px_#000000cc] transition-[translate,filter] duration-300 ease-tb42-out group-hover:-translate-y-[3px] group-hover:brightness-105 group-active:animate-tb42-drop group-focus-visible:outline-2 group-focus-visible:outline-offset-[5px] group-focus-visible:outline-tb42-brass-hi motion-reduce:transition-none motion-reduce:group-active:animate-none">
<span class="absolute inset-0 z-20 rounded-full opacity-85 bg-[repeating-conic-gradient(from_0deg,var(--color-tb42-reed-hi)_0deg_1.6deg,var(--color-tb42-reed-lo)_1.6deg_3.2deg)] [mask-image:radial-gradient(farthest-side,transparent_calc(100%-6px),#000_calc(100%-6px))]"></span>
<span class="tb-42__patina absolute inset-0 z-10 rounded-full opacity-45"></span>
<span class="tb-42__glyph relative z-30 text-[3.1rem] font-bold leading-none text-tb42-brass-lo [text-shadow:0_1px_0_#ffffff59,0_-1px_0_#00000073]">T</span>
</span>
</button>
</div><div class="tb-42 w-full min-h-svh grid place-items-center font-tb42-sans p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" aria-label="Use one transit token" class="group relative cursor-pointer border-0 bg-transparent p-0 w-30 h-[9.5rem] grid place-items-start justify-center [-webkit-tap-highlight-color:transparent] focus-visible:outline-none disabled:cursor-not-allowed disabled:grayscale-[0.7] disabled:brightness-[0.85]">
<span aria-hidden="true" class="absolute inset-x-[12%] bottom-1.5 h-2 rounded-full bg-[linear-gradient(180deg,#0a0c0b,#1d221f)] shadow-[inset_0_2px_4px_#000000e6,0_1px_0_#ffffff14]"></span>
<span aria-hidden="true" class="tb-42__disc relative grid place-items-center size-30 rounded-full isolate text-tb42-brass-lo shadow-[inset_0_2px_3px_#ffffff66,inset_0_-3px_6px_#00000073,0_14px_22px_-10px_#000000cc] transition-[translate,filter] duration-300 ease-tb42-out group-hover:-translate-y-[3px] group-hover:brightness-105 group-active:animate-tb42-drop group-focus-visible:outline-2 group-focus-visible:outline-offset-[5px] group-focus-visible:outline-tb42-brass-hi motion-reduce:transition-none motion-reduce:group-active:animate-none">
<span class="absolute inset-0 z-20 rounded-full opacity-85 bg-[repeating-conic-gradient(from_0deg,var(--color-tb42-reed-hi)_0deg_1.6deg,var(--color-tb42-reed-lo)_1.6deg_3.2deg)] [mask-image:radial-gradient(farthest-side,transparent_calc(100%-6px),#000_calc(100%-6px))]"></span>
<span class="tb-42__patina absolute inset-0 z-10 rounded-full opacity-45"></span>
<span class="tb-42__glyph relative z-30 text-[3.1rem] font-bold leading-none text-tb42-brass-lo [text-shadow:0_1px_0_#ffffff59,0_-1px_0_#00000073]">T</span>
</span>
</button>
</div>@import "tailwindcss";
@theme {
--color-tb42-bg: #191c1a;
--color-tb42-bg: oklch(0.21 0.008 150);
--color-tb42-brass: #a98a4a;
--color-tb42-brass: oklch(0.64 0.08 88);
--color-tb42-brass-hi: #dcbe7c;
--color-tb42-brass-hi: oklch(0.82 0.09 90);
--color-tb42-brass-lo: #6d5527;
--color-tb42-brass-lo: oklch(0.44 0.07 85);
--color-tb42-patina: #4f7a5f;
--color-tb42-patina: oklch(0.55 0.07 160);
--color-tb42-reed-hi: #cfae68;
--color-tb42-reed-hi: oklch(0.76 0.09 88);
--color-tb42-reed-lo: #7a5f2c;
--color-tb42-reed-lo: oklch(0.48 0.08 86);
--font-tb42-sans: "Oswald", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb42-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb42-drop: tb-42-drop 0.62s cubic-bezier(0.3, 0.05, 0.4, 1);
}
@keyframes tb-42-drop {
0% {
translate: 0 0;
}
38% {
translate: 0 1.15rem;
scale: 1 0.94;
}
62% {
translate: 0 -0.3rem;
scale: 1 1.02;
}
100% {
translate: 0 0;
scale: 1 1;
}
}
.tb-42 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(30rem 20rem at 50% 44%, #26302a, transparent 74%), var(--color-tb42-bg);
}
.tb-42__disc {
background: radial-gradient(circle at 34% 28%, var(--color-tb42-brass-hi), transparent 54%), radial-gradient(circle at 72% 78%, var(--color-tb42-brass-lo), transparent 56%), linear-gradient(152deg, var(--color-tb42-brass), color-mix(in oklab, var(--color-tb42-brass) 80%, black));
}
.tb-42__patina {
mix-blend-mode: color-burn;
background: radial-gradient(2rem 1.6rem at 26% 74%, var(--color-tb42-patina), transparent 72%), radial-gradient(1.6rem 2rem at 76% 32%, var(--color-tb42-patina), transparent 74%);
}
/* Safari ≤15.3 rim mask shim. */
.tb-42 [class*="mask-image"] {
-webkit-mask-image: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
}
/* THE KNOCKOUT: the glyph is subtracted from the metal, so the hole shows the page.
A data-URI SVG text mask keeps the cut crisp at any size. Where mask-composite is
unavailable the fallback above stands: a normal engraved letter. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: destination-out)) {
.tb-42__glyph {
color: transparent;
text-shadow: none;
}
.tb-42__disc {
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
-webkit-mask-composite: destination-out;
mask-composite: exclude;
}
}
@media (prefers-color-scheme: light) {
.tb-42:not([data-theme="dark"]) {
--color-tb42-bg: oklch(0.26 0.01 150);
}
}
.tb-42[data-theme="light"] {
--color-tb42-bg: oklch(0.26 0.01 150);
}
@media (forced-colors: active) {
.tb-42__disc {
background: ButtonFace;
border: 1px solid ButtonText;
box-shadow: none;
mask: none;
-webkit-mask: none;
}
.tb-42__glyph {
color: ButtonText;
text-shadow: none;
}
.tb-42__disc > span:not(.tb-42__glyph) {
display: none;
}
}@import "tailwindcss";
@theme {
--color-tb42-bg: #191c1a;
--color-tb42-bg: oklch(0.21 0.008 150);
--color-tb42-brass: #a98a4a;
--color-tb42-brass: oklch(0.64 0.08 88);
--color-tb42-brass-hi: #dcbe7c;
--color-tb42-brass-hi: oklch(0.82 0.09 90);
--color-tb42-brass-lo: #6d5527;
--color-tb42-brass-lo: oklch(0.44 0.07 85);
--color-tb42-patina: #4f7a5f;
--color-tb42-patina: oklch(0.55 0.07 160);
--color-tb42-reed-hi: #cfae68;
--color-tb42-reed-hi: oklch(0.76 0.09 88);
--color-tb42-reed-lo: #7a5f2c;
--color-tb42-reed-lo: oklch(0.48 0.08 86);
--font-tb42-sans: "Oswald", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb42-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb42-drop: tb-42-drop 0.62s cubic-bezier(0.3, 0.05, 0.4, 1);
}
@keyframes tb-42-drop {
0% {
translate: 0 0;
}
38% {
translate: 0 1.15rem;
scale: 1 0.94;
}
62% {
translate: 0 -0.3rem;
scale: 1 1.02;
}
100% {
translate: 0 0;
scale: 1 1;
}
}
.tb-42 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(30rem 20rem at 50% 44%, #26302a, transparent 74%), var(--color-tb42-bg);
}
.tb-42__disc {
background: radial-gradient(circle at 34% 28%, var(--color-tb42-brass-hi), transparent 54%), radial-gradient(circle at 72% 78%, var(--color-tb42-brass-lo), transparent 56%), linear-gradient(152deg, var(--color-tb42-brass), color-mix(in oklab, var(--color-tb42-brass) 80%, black));
}
.tb-42__patina {
mix-blend-mode: color-burn;
background: radial-gradient(2rem 1.6rem at 26% 74%, var(--color-tb42-patina), transparent 72%), radial-gradient(1.6rem 2rem at 76% 32%, var(--color-tb42-patina), transparent 74%);
}
/* Safari ≤15.3 rim mask shim. */
.tb-42 [class*="mask-image"] {
-webkit-mask-image: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
}
/* THE KNOCKOUT: the glyph is subtracted from the metal, so the hole shows the page.
A data-URI SVG text mask keeps the cut crisp at any size. Where mask-composite is
unavailable the fallback above stands: a normal engraved letter. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: destination-out)) {
.tb-42__glyph {
color: transparent;
text-shadow: none;
}
.tb-42__disc {
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
-webkit-mask-composite: destination-out;
mask-composite: exclude;
}
}
@media (prefers-color-scheme: light) {
.tb-42:not([data-theme="dark"]) {
--color-tb42-bg: oklch(0.26 0.01 150);
}
}
.tb-42[data-theme="light"] {
--color-tb42-bg: oklch(0.26 0.01 150);
}
@media (forced-colors: active) {
.tb-42__disc {
background: ButtonFace;
border: 1px solid ButtonText;
box-shadow: none;
mask: none;
-webkit-mask: none;
}
.tb-42__glyph {
color: ButtonText;
text-shadow: none;
}
.tb-42__disc > span:not(.tb-42__glyph) {
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-tb42-bg: #191c1a;
--color-tb42-bg: oklch(0.21 0.008 150);
--color-tb42-brass: #a98a4a;
--color-tb42-brass: oklch(0.64 0.08 88);
--color-tb42-brass-hi: #dcbe7c;
--color-tb42-brass-hi: oklch(0.82 0.09 90);
--color-tb42-brass-lo: #6d5527;
--color-tb42-brass-lo: oklch(0.44 0.07 85);
--color-tb42-patina: #4f7a5f;
--color-tb42-patina: oklch(0.55 0.07 160);
--color-tb42-reed-hi: #cfae68;
--color-tb42-reed-hi: oklch(0.76 0.09 88);
--color-tb42-reed-lo: #7a5f2c;
--color-tb42-reed-lo: oklch(0.48 0.08 86);
--font-tb42-sans: "Oswald", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb42-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb42-drop: tb-42-drop 0.62s cubic-bezier(0.3, 0.05, 0.4, 1);
}
@keyframes tb-42-drop {
0% {
translate: 0 0;
}
38% {
translate: 0 1.15rem;
scale: 1 0.94;
}
62% {
translate: 0 -0.3rem;
scale: 1 1.02;
}
100% {
translate: 0 0;
scale: 1 1;
}
}
.tb-42 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(30rem 20rem at 50% 44%, #26302a, transparent 74%), var(--color-tb42-bg);
}
.tb-42__disc {
background: radial-gradient(circle at 34% 28%, var(--color-tb42-brass-hi), transparent 54%), radial-gradient(circle at 72% 78%, var(--color-tb42-brass-lo), transparent 56%), linear-gradient(152deg, var(--color-tb42-brass), color-mix(in oklab, var(--color-tb42-brass) 80%, black));
}
.tb-42__patina {
mix-blend-mode: color-burn;
background: radial-gradient(2rem 1.6rem at 26% 74%, var(--color-tb42-patina), transparent 72%), radial-gradient(1.6rem 2rem at 76% 32%, var(--color-tb42-patina), transparent 74%);
}
/* Safari ≤15.3 rim mask shim. */
.tb-42 [class*="mask-image"] {
-webkit-mask-image: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
}
/* THE KNOCKOUT: the glyph is subtracted from the metal, so the hole shows the page.
A data-URI SVG text mask keeps the cut crisp at any size. Where mask-composite is
unavailable the fallback above stands: a normal engraved letter. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: destination-out)) {
.tb-42__glyph {
color: transparent;
text-shadow: none;
}
.tb-42__disc {
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
-webkit-mask-composite: destination-out;
mask-composite: exclude;
}
}
@media (prefers-color-scheme: light) {
.tb-42:not([data-theme="dark"]) {
--color-tb42-bg: oklch(0.26 0.01 150);
}
}
.tb-42[data-theme="light"] {
--color-tb42-bg: oklch(0.26 0.01 150);
}
@media (forced-colors: active) {
.tb-42__disc {
background: ButtonFace;
border: 1px solid ButtonText;
box-shadow: none;
mask: none;
-webkit-mask: none;
}
.tb-42__glyph {
color: ButtonText;
text-shadow: none;
}
.tb-42__disc > span:not(.tb-42__glyph) {
display: none;
}
}@import "tailwindcss";
@theme {
--color-tb42-bg: #191c1a;
--color-tb42-bg: oklch(0.21 0.008 150);
--color-tb42-brass: #a98a4a;
--color-tb42-brass: oklch(0.64 0.08 88);
--color-tb42-brass-hi: #dcbe7c;
--color-tb42-brass-hi: oklch(0.82 0.09 90);
--color-tb42-brass-lo: #6d5527;
--color-tb42-brass-lo: oklch(0.44 0.07 85);
--color-tb42-patina: #4f7a5f;
--color-tb42-patina: oklch(0.55 0.07 160);
--color-tb42-reed-hi: #cfae68;
--color-tb42-reed-hi: oklch(0.76 0.09 88);
--color-tb42-reed-lo: #7a5f2c;
--color-tb42-reed-lo: oklch(0.48 0.08 86);
--font-tb42-sans: "Oswald", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb42-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb42-drop: tb-42-drop 0.62s cubic-bezier(0.3, 0.05, 0.4, 1);
}
@keyframes tb-42-drop {
0% {
translate: 0 0;
}
38% {
translate: 0 1.15rem;
scale: 1 0.94;
}
62% {
translate: 0 -0.3rem;
scale: 1 1.02;
}
100% {
translate: 0 0;
scale: 1 1;
}
}
.tb-42 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(30rem 20rem at 50% 44%, #26302a, transparent 74%), var(--color-tb42-bg);
}
.tb-42__disc {
background: radial-gradient(circle at 34% 28%, var(--color-tb42-brass-hi), transparent 54%), radial-gradient(circle at 72% 78%, var(--color-tb42-brass-lo), transparent 56%), linear-gradient(152deg, var(--color-tb42-brass), color-mix(in oklab, var(--color-tb42-brass) 80%, black));
}
.tb-42__patina {
mix-blend-mode: color-burn;
background: radial-gradient(2rem 1.6rem at 26% 74%, var(--color-tb42-patina), transparent 72%), radial-gradient(1.6rem 2rem at 76% 32%, var(--color-tb42-patina), transparent 74%);
}
/* Safari ≤15.3 rim mask shim. */
.tb-42 [class*="mask-image"] {
-webkit-mask-image: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
}
/* THE KNOCKOUT: the glyph is subtracted from the metal, so the hole shows the page.
A data-URI SVG text mask keeps the cut crisp at any size. Where mask-composite is
unavailable the fallback above stands: a normal engraved letter. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: destination-out)) {
.tb-42__glyph {
color: transparent;
text-shadow: none;
}
.tb-42__disc {
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
-webkit-mask-composite: destination-out;
mask-composite: exclude;
}
}
@media (prefers-color-scheme: light) {
.tb-42:not([data-theme="dark"]) {
--color-tb42-bg: oklch(0.26 0.01 150);
}
}
.tb-42[data-theme="light"] {
--color-tb42-bg: oklch(0.26 0.01 150);
}
@media (forced-colors: active) {
.tb-42__disc {
background: ButtonFace;
border: 1px solid ButtonText;
box-shadow: none;
mask: none;
-webkit-mask: none;
}
.tb-42__glyph {
color: ButtonText;
text-shadow: none;
}
.tb-42__disc > span:not(.tb-42__glyph) {
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=Oswald:wght@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=Oswald:wght@500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Utilities: the disc, the reeded rim and its mask, the drop animation, the hover lift, the slot, the focus ring routed to the disc. CSS: the struck-metal gradients, the blended patina, and the entire knockout
@supportsblock. - The data-URI mask is the collection's clearest example of a value that cannot be a utility: it is a URL-encoded SVG document, and Tailwind's arbitrary-value syntax would need every space, quote and angle bracket escaped twice.
- The
@supportsblock does two things at once — transparent the glyph and mask the disc — which is why it is a block rather than two variants. Either half alone leaves either an invisible letter or a solid one. - A11y fix from the CSS original: the CSS version's focus style was
outline:noneon the button with a ring on.disc, correct, but the drop only fired on:activewith no keyboard equivalent and no reduced-motion guard on the animation itself (only on the transition). The port addsmotion-reduce:group-active:animate-none, so a vestibular-sensitive user pressing Space gets no fall.
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 Aged Brass Subway Token Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-aged-brass-subway-token-button/
A 1953 transit token: milled rim, tarnished brass, and a knocked-out letter you can see the background through — a mask-composite cut, not a coloured glyph. Press it and the token drops into the slot and springs back.
## HTML
```html
<div class="tb-42 w-full min-h-svh grid place-items-center font-tb42-sans p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" aria-label="Use one transit token" class="group relative cursor-pointer border-0 bg-transparent p-0 w-30 h-[9.5rem] grid place-items-start justify-center [-webkit-tap-highlight-color:transparent] focus-visible:outline-none disabled:cursor-not-allowed disabled:grayscale-[0.7] disabled:brightness-[0.85]">
<span aria-hidden="true" class="absolute inset-x-[12%] bottom-1.5 h-2 rounded-full bg-[linear-gradient(180deg,#0a0c0b,#1d221f)] shadow-[inset_0_2px_4px_#000000e6,0_1px_0_#ffffff14]"></span>
<span aria-hidden="true" class="tb-42__disc relative grid place-items-center size-30 rounded-full isolate text-tb42-brass-lo shadow-[inset_0_2px_3px_#ffffff66,inset_0_-3px_6px_#00000073,0_14px_22px_-10px_#000000cc] transition-[translate,filter] duration-300 ease-tb42-out group-hover:-translate-y-[3px] group-hover:brightness-105 group-active:animate-tb42-drop group-focus-visible:outline-2 group-focus-visible:outline-offset-[5px] group-focus-visible:outline-tb42-brass-hi motion-reduce:transition-none motion-reduce:group-active:animate-none">
<span class="absolute inset-0 z-20 rounded-full opacity-85 bg-[repeating-conic-gradient(from_0deg,var(--color-tb42-reed-hi)_0deg_1.6deg,var(--color-tb42-reed-lo)_1.6deg_3.2deg)] [mask-image:radial-gradient(farthest-side,transparent_calc(100%-6px),#000_calc(100%-6px))]"></span>
<span class="tb-42__patina absolute inset-0 z-10 rounded-full opacity-45"></span>
<span class="tb-42__glyph relative z-30 text-[3.1rem] font-bold leading-none text-tb42-brass-lo [text-shadow:0_1px_0_#ffffff59,0_-1px_0_#00000073]">T</span>
</span>
</button>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb42-bg: #191c1a;
--color-tb42-bg: oklch(0.21 0.008 150);
--color-tb42-brass: #a98a4a;
--color-tb42-brass: oklch(0.64 0.08 88);
--color-tb42-brass-hi: #dcbe7c;
--color-tb42-brass-hi: oklch(0.82 0.09 90);
--color-tb42-brass-lo: #6d5527;
--color-tb42-brass-lo: oklch(0.44 0.07 85);
--color-tb42-patina: #4f7a5f;
--color-tb42-patina: oklch(0.55 0.07 160);
--color-tb42-reed-hi: #cfae68;
--color-tb42-reed-hi: oklch(0.76 0.09 88);
--color-tb42-reed-lo: #7a5f2c;
--color-tb42-reed-lo: oklch(0.48 0.08 86);
--font-tb42-sans: "Oswald", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb42-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb42-drop: tb-42-drop 0.62s cubic-bezier(0.3, 0.05, 0.4, 1);
}
@keyframes tb-42-drop {
0% {
translate: 0 0;
}
38% {
translate: 0 1.15rem;
scale: 1 0.94;
}
62% {
translate: 0 -0.3rem;
scale: 1 1.02;
}
100% {
translate: 0 0;
scale: 1 1;
}
}
.tb-42 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(30rem 20rem at 50% 44%, #26302a, transparent 74%), var(--color-tb42-bg);
}
.tb-42__disc {
background: radial-gradient(circle at 34% 28%, var(--color-tb42-brass-hi), transparent 54%), radial-gradient(circle at 72% 78%, var(--color-tb42-brass-lo), transparent 56%), linear-gradient(152deg, var(--color-tb42-brass), color-mix(in oklab, var(--color-tb42-brass) 80%, black));
}
.tb-42__patina {
mix-blend-mode: color-burn;
background: radial-gradient(2rem 1.6rem at 26% 74%, var(--color-tb42-patina), transparent 72%), radial-gradient(1.6rem 2rem at 76% 32%, var(--color-tb42-patina), transparent 74%);
}
/* Safari ≤15.3 rim mask shim. */
.tb-42 [class*="mask-image"] {
-webkit-mask-image: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
}
/* THE KNOCKOUT: the glyph is subtracted from the metal, so the hole shows the page.
A data-URI SVG text mask keeps the cut crisp at any size. Where mask-composite is
unavailable the fallback above stands: a normal engraved letter. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: destination-out)) {
.tb-42__glyph {
color: transparent;
text-shadow: none;
}
.tb-42__disc {
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
-webkit-mask-composite: destination-out;
mask-composite: exclude;
}
}
@media (prefers-color-scheme: light) {
.tb-42:not([data-theme="dark"]) {
--color-tb42-bg: oklch(0.26 0.01 150);
}
}
.tb-42[data-theme="light"] {
--color-tb42-bg: oklch(0.26 0.01 150);
}
@media (forced-colors: active) {
.tb-42__disc {
background: ButtonFace;
border: 1px solid ButtonText;
box-shadow: none;
mask: none;
-webkit-mask: none;
}
.tb-42__glyph {
color: ButtonText;
text-shadow: none;
}
.tb-42__disc > span:not(.tb-42__glyph) {
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 Aged Brass Subway Token Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-aged-brass-subway-token-button/
A 1953 transit token: milled rim, tarnished brass, and a knocked-out letter you can see the background through — a mask-composite cut, not a coloured glyph. Press it and the token drops into the slot and springs back.
## HTML
```html
<div class="tb-42 w-full min-h-svh grid place-items-center font-tb42-sans p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" aria-label="Use one transit token" class="group relative cursor-pointer border-0 bg-transparent p-0 w-30 h-[9.5rem] grid place-items-start justify-center [-webkit-tap-highlight-color:transparent] focus-visible:outline-none disabled:cursor-not-allowed disabled:grayscale-[0.7] disabled:brightness-[0.85]">
<span aria-hidden="true" class="absolute inset-x-[12%] bottom-1.5 h-2 rounded-full bg-[linear-gradient(180deg,#0a0c0b,#1d221f)] shadow-[inset_0_2px_4px_#000000e6,0_1px_0_#ffffff14]"></span>
<span aria-hidden="true" class="tb-42__disc relative grid place-items-center size-30 rounded-full isolate text-tb42-brass-lo shadow-[inset_0_2px_3px_#ffffff66,inset_0_-3px_6px_#00000073,0_14px_22px_-10px_#000000cc] transition-[translate,filter] duration-300 ease-tb42-out group-hover:-translate-y-[3px] group-hover:brightness-105 group-active:animate-tb42-drop group-focus-visible:outline-2 group-focus-visible:outline-offset-[5px] group-focus-visible:outline-tb42-brass-hi motion-reduce:transition-none motion-reduce:group-active:animate-none">
<span class="absolute inset-0 z-20 rounded-full opacity-85 bg-[repeating-conic-gradient(from_0deg,var(--color-tb42-reed-hi)_0deg_1.6deg,var(--color-tb42-reed-lo)_1.6deg_3.2deg)] [mask-image:radial-gradient(farthest-side,transparent_calc(100%-6px),#000_calc(100%-6px))]"></span>
<span class="tb-42__patina absolute inset-0 z-10 rounded-full opacity-45"></span>
<span class="tb-42__glyph relative z-30 text-[3.1rem] font-bold leading-none text-tb42-brass-lo [text-shadow:0_1px_0_#ffffff59,0_-1px_0_#00000073]">T</span>
</span>
</button>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb42-bg: #191c1a;
--color-tb42-bg: oklch(0.21 0.008 150);
--color-tb42-brass: #a98a4a;
--color-tb42-brass: oklch(0.64 0.08 88);
--color-tb42-brass-hi: #dcbe7c;
--color-tb42-brass-hi: oklch(0.82 0.09 90);
--color-tb42-brass-lo: #6d5527;
--color-tb42-brass-lo: oklch(0.44 0.07 85);
--color-tb42-patina: #4f7a5f;
--color-tb42-patina: oklch(0.55 0.07 160);
--color-tb42-reed-hi: #cfae68;
--color-tb42-reed-hi: oklch(0.76 0.09 88);
--color-tb42-reed-lo: #7a5f2c;
--color-tb42-reed-lo: oklch(0.48 0.08 86);
--font-tb42-sans: "Oswald", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
--ease-tb42-out: cubic-bezier(0.16, 1, 0.3, 1);
--animate-tb42-drop: tb-42-drop 0.62s cubic-bezier(0.3, 0.05, 0.4, 1);
}
@keyframes tb-42-drop {
0% {
translate: 0 0;
}
38% {
translate: 0 1.15rem;
scale: 1 0.94;
}
62% {
translate: 0 -0.3rem;
scale: 1 1.02;
}
100% {
translate: 0 0;
scale: 1 1;
}
}
.tb-42 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(30rem 20rem at 50% 44%, #26302a, transparent 74%), var(--color-tb42-bg);
}
.tb-42__disc {
background: radial-gradient(circle at 34% 28%, var(--color-tb42-brass-hi), transparent 54%), radial-gradient(circle at 72% 78%, var(--color-tb42-brass-lo), transparent 56%), linear-gradient(152deg, var(--color-tb42-brass), color-mix(in oklab, var(--color-tb42-brass) 80%, black));
}
.tb-42__patina {
mix-blend-mode: color-burn;
background: radial-gradient(2rem 1.6rem at 26% 74%, var(--color-tb42-patina), transparent 72%), radial-gradient(1.6rem 2rem at 76% 32%, var(--color-tb42-patina), transparent 74%);
}
/* Safari ≤15.3 rim mask shim. */
.tb-42 [class*="mask-image"] {
-webkit-mask-image: radial-gradient(farthest-side, transparent calc(100% - 6px), #000 calc(100% - 6px));
}
/* THE KNOCKOUT: the glyph is subtracted from the metal, so the hole shows the page.
A data-URI SVG text mask keeps the cut crisp at any size. Where mask-composite is
unavailable the fallback above stands: a normal engraved letter. */
@supports ((mask-composite: exclude) or (-webkit-mask-composite: destination-out)) {
.tb-42__glyph {
color: transparent;
text-shadow: none;
}
.tb-42__disc {
-webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Ctext x='60' y='86' font-family='Oswald,sans-serif' font-size='78' font-weight='700' text-anchor='middle' fill='%23000'%3ET%3C/text%3E%3C/svg%3E") 50% 50%/60% 60% no-repeat, linear-gradient(#000, #000);
-webkit-mask-composite: destination-out;
mask-composite: exclude;
}
}
@media (prefers-color-scheme: light) {
.tb-42:not([data-theme="dark"]) {
--color-tb42-bg: oklch(0.26 0.01 150);
}
}
.tb-42[data-theme="light"] {
--color-tb42-bg: oklch(0.26 0.01 150);
}
@media (forced-colors: active) {
.tb-42__disc {
background: ButtonFace;
border: 1px solid ButtonText;
box-shadow: none;
mask: none;
-webkit-mask: none;
}
.tb-42__glyph {
color: ButtonText;
text-shadow: none;
}
.tb-42__disc > span:not(.tb-42__glyph) {
display: none;
}
}
```How this works
The letter is genuinely removed from the metal. A text layer masks the disc, and mask-composite subtracts it, so the hole shows whatever is behind the button:
mask: url("data:image/svg+xml,…%3Ctext…%3ET%3C/text%3E…") 50% 50%/60% 60% no-repeat,
linear-gradient(#000,#000);
mask-composite: exclude;An inline SVG data URI keeps the cut crisp at any size and needs no font-loading gymnastics. It also cannot be a utility: the value is a URL-encoded document.
The drop is what sells it. Fall with a 0.94 vertical squash, overshoot 0.3rem up, settle — an ease-in descent and a spring return, rather than a single symmetrical bounce.
The fallback is an engraved glyph. Where mask-composite is unavailable the T is a normal embossed letter, which is why it is real text in the markup rather than a pseudo-element.
Make it yours
- Change the cut letter by editing the data URI's text content — any single glyph works.
- Nickel instead of brass: desaturate the three metal tokens.
- Real tokens are unevenly worn — raise the patina layer's opacity for a rougher one.
- Drop the fall animation for a simple press in a dense toolbar.
- Rim reed count is the two conic angles, as on the copper coin.
Gotchas — read before shipping
- A knocked-out glyph is invisible to assistive tech. The
aria-labelis the accessible name; the hole is decoration. mask-compositespelling differs per engine (destination-outvsexclude); declare both, base layer last for the prefixed form.- Data-URI SVG masks must be URL-encoded (
%3C,%22) — raw angle brackets break some parsers. - Because the letter is a hole, the token cannot sit on a busy background.
- Circular buttons need ≥44px hit area. This one is 7.5rem.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 120+ | 16.4+ | 128+ | 120+ |
mask-composite needs Chrome 120 / Safari 15.4 / Firefox 53. The @supports block keeps a solid engraved glyph as the fallback, so the token still reads correctly.