45 / 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 Regal Postage Stamp Button
A commemorative stamp with true perforations on all four edges, an engraved portrait in sepia, a denomination cartouche, and a postmark that lands across the corner on hover — concentric rings plus a wavy cancellation bar, rotated and off-centre.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tb-45 w-full min-h-svh grid place-items-center font-tb45-body p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-45__stamp group relative cursor-pointer border-0 p-2 w-54 text-center text-tb45-ink bg-[linear-gradient(165deg,var(--color-tb45-stock),var(--color-tb45-stock-lo))] drop-shadow-[0_12px_18px_#0000008c] transition-[translate,rotate,filter] duration-[320ms] ease-tb45-out [-webkit-tap-highlight-color:transparent] hover:-translate-y-[3px] hover:-rotate-[1.2deg] hover:drop-shadow-[0_20px_26px_#00000099] focus-visible:-translate-y-[3px] focus-visible:-rotate-[1.2deg] focus-visible:outline-2 focus-visible:outline-offset-[5px] focus-visible:outline-tb45-accent active:translate-y-px active:rotate-0 disabled:cursor-not-allowed disabled:grayscale-[0.6] disabled:opacity-75 motion-reduce:transition-none">
<span class="grid gap-2 px-2.5 pt-3 pb-2.5 border border-tb45-frame">
<span class="tb-45__win block aspect-[7/5] overflow-hidden">
<img src="https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=700&q=70&auto=format&fit=crop" alt="Engraved view of a European cathedral square" width="700" height="500" loading="lazy" decoding="async" class="block size-full object-cover sepia-[0.72] contrast-125 saturate-[0.7] brightness-95">
</span>
<span class="grid gap-0.5">
<span class="font-tb45-serif text-[0.6875rem] tracking-[0.18em] uppercase text-tb45-accent">Post & Telegraph</span>
<span class="font-tb45-serif text-[0.9375rem] font-semibold tracking-[0.03em]">Send the letter</span>
</span>
</span>
<span class="absolute top-4 right-4 px-1.5 py-0.5 rounded-[0.1rem] font-tb45-serif text-[0.9375rem] font-bold leading-none tabular-nums text-tb45-stock bg-tb45-accent">7½<small class="text-[0.6875rem]">d</small></span>
<span aria-hidden="true" class="absolute left-3.5 top-5.5 size-26 pointer-events-none -rotate-16 scale-150 opacity-0 transition-[scale,opacity] duration-[380ms] ease-[cubic-bezier(0.2,1.5,0.4,1)] group-hover:scale-100 group-hover:opacity-[0.72] group-focus-visible:scale-100 group-focus-visible:opacity-[0.72] motion-reduce:transition-opacity motion-reduce:duration-150 motion-reduce:scale-100">
<span class="absolute inset-0 rounded-full border-2 border-tb45-mark"></span>
<span class="absolute inset-[12%] rounded-full border border-tb45-mark"></span>
<span class="tb-45__bar absolute -inset-x-[25%] top-[46%] h-3.5 opacity-85"></span>
</span>
</button>
</div><div class="tb-45 w-full min-h-svh grid place-items-center font-tb45-body p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-45__stamp group relative cursor-pointer border-0 p-2 w-54 text-center text-tb45-ink bg-[linear-gradient(165deg,var(--color-tb45-stock),var(--color-tb45-stock-lo))] drop-shadow-[0_12px_18px_#0000008c] transition-[translate,rotate,filter] duration-[320ms] ease-tb45-out [-webkit-tap-highlight-color:transparent] hover:-translate-y-[3px] hover:-rotate-[1.2deg] hover:drop-shadow-[0_20px_26px_#00000099] focus-visible:-translate-y-[3px] focus-visible:-rotate-[1.2deg] focus-visible:outline-2 focus-visible:outline-offset-[5px] focus-visible:outline-tb45-accent active:translate-y-px active:rotate-0 disabled:cursor-not-allowed disabled:grayscale-[0.6] disabled:opacity-75 motion-reduce:transition-none">
<span class="grid gap-2 px-2.5 pt-3 pb-2.5 border border-tb45-frame">
<span class="tb-45__win block aspect-[7/5] overflow-hidden">
<img src="https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=700&q=70&auto=format&fit=crop" alt="Engraved view of a European cathedral square" width="700" height="500" loading="lazy" decoding="async" class="block size-full object-cover sepia-[0.72] contrast-125 saturate-[0.7] brightness-95">
</span>
<span class="grid gap-0.5">
<span class="font-tb45-serif text-[0.6875rem] tracking-[0.18em] uppercase text-tb45-accent">Post & Telegraph</span>
<span class="font-tb45-serif text-[0.9375rem] font-semibold tracking-[0.03em]">Send the letter</span>
</span>
</span>
<span class="absolute top-4 right-4 px-1.5 py-0.5 rounded-[0.1rem] font-tb45-serif text-[0.9375rem] font-bold leading-none tabular-nums text-tb45-stock bg-tb45-accent">7½<small class="text-[0.6875rem]">d</small></span>
<span aria-hidden="true" class="absolute left-3.5 top-5.5 size-26 pointer-events-none -rotate-16 scale-150 opacity-0 transition-[scale,opacity] duration-[380ms] ease-[cubic-bezier(0.2,1.5,0.4,1)] group-hover:scale-100 group-hover:opacity-[0.72] group-focus-visible:scale-100 group-focus-visible:opacity-[0.72] motion-reduce:transition-opacity motion-reduce:duration-150 motion-reduce:scale-100">
<span class="absolute inset-0 rounded-full border-2 border-tb45-mark"></span>
<span class="absolute inset-[12%] rounded-full border border-tb45-mark"></span>
<span class="tb-45__bar absolute -inset-x-[25%] top-[46%] h-3.5 opacity-85"></span>
</span>
</button>
</div>@import "tailwindcss";
@theme {
--color-tb45-bg: #20211c;
--color-tb45-bg: oklch(0.25 0.012 110);
--color-tb45-stock: #f0e6cf;
--color-tb45-stock: oklch(0.93 0.03 88);
--color-tb45-stock-lo: #ddd0b3;
--color-tb45-stock-lo: oklch(0.86 0.04 86);
--color-tb45-ink: #2d2a1f;
--color-tb45-ink: oklch(0.27 0.02 95);
--color-tb45-frame: #2d2a1f59;
--color-tb45-frame: oklch(0.27 0.02 95 / 0.35);
--color-tb45-accent: #6d3526;
--color-tb45-accent: oklch(0.38 0.09 38);
--color-tb45-mark: #2c3a4a;
--color-tb45-mark: oklch(0.33 0.04 245);
--font-tb45-serif: "Cinzel", ui-serif, Georgia, "Times New Roman", serif;
--font-tb45-body: "EB Garamond", ui-serif, Georgia, serif;
--ease-tb45-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-45 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(32rem 20rem at 50% 40%, rgba(255,255,255,.06), transparent 72%), var(--color-tb45-bg);
}
/* PERFORATION on four edges: opaque dot columns/rows XOR-ed against a solid base.
Exclude subtracts each dot; where two edges overlap at a corner, parity keeps
the metal — real stamps have that quirk too. */
.tb-45__stamp {
-webkit-mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
/* The flat fallback stays a legible engraved-looking plate if the photo 404s. */
.tb-45__win {
background: repeating-linear-gradient(28deg, color-mix(in oklab, var(--color-tb45-ink) 30%, transparent) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--color-tb45-ink) 18%, var(--color-tb45-stock));
}
/* CANCELLATION BAR: a striped bar with a scalloped top edge. */
.tb-45__bar {
background: repeating-linear-gradient(0deg, var(--color-tb45-mark) 0 2px, transparent 2px 5px);
-webkit-mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
}
@media (prefers-color-scheme: light) {
.tb-45:not([data-theme="dark"]) {
--color-tb45-bg: oklch(0.31 0.015 110);
}
}
.tb-45[data-theme="light"] {
--color-tb45-bg: oklch(0.31 0.015 110);
}
@media (forced-colors: active) {
.tb-45__stamp {
mask: none;
-webkit-mask: none;
filter: none;
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
}
.tb-45__stamp span[aria-hidden] {
display: none;
}
}@import "tailwindcss";
@theme {
--color-tb45-bg: #20211c;
--color-tb45-bg: oklch(0.25 0.012 110);
--color-tb45-stock: #f0e6cf;
--color-tb45-stock: oklch(0.93 0.03 88);
--color-tb45-stock-lo: #ddd0b3;
--color-tb45-stock-lo: oklch(0.86 0.04 86);
--color-tb45-ink: #2d2a1f;
--color-tb45-ink: oklch(0.27 0.02 95);
--color-tb45-frame: #2d2a1f59;
--color-tb45-frame: oklch(0.27 0.02 95 / 0.35);
--color-tb45-accent: #6d3526;
--color-tb45-accent: oklch(0.38 0.09 38);
--color-tb45-mark: #2c3a4a;
--color-tb45-mark: oklch(0.33 0.04 245);
--font-tb45-serif: "Cinzel", ui-serif, Georgia, "Times New Roman", serif;
--font-tb45-body: "EB Garamond", ui-serif, Georgia, serif;
--ease-tb45-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-45 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(32rem 20rem at 50% 40%, rgba(255,255,255,.06), transparent 72%), var(--color-tb45-bg);
}
/* PERFORATION on four edges: opaque dot columns/rows XOR-ed against a solid base.
Exclude subtracts each dot; where two edges overlap at a corner, parity keeps
the metal — real stamps have that quirk too. */
.tb-45__stamp {
-webkit-mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
/* The flat fallback stays a legible engraved-looking plate if the photo 404s. */
.tb-45__win {
background: repeating-linear-gradient(28deg, color-mix(in oklab, var(--color-tb45-ink) 30%, transparent) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--color-tb45-ink) 18%, var(--color-tb45-stock));
}
/* CANCELLATION BAR: a striped bar with a scalloped top edge. */
.tb-45__bar {
background: repeating-linear-gradient(0deg, var(--color-tb45-mark) 0 2px, transparent 2px 5px);
-webkit-mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
}
@media (prefers-color-scheme: light) {
.tb-45:not([data-theme="dark"]) {
--color-tb45-bg: oklch(0.31 0.015 110);
}
}
.tb-45[data-theme="light"] {
--color-tb45-bg: oklch(0.31 0.015 110);
}
@media (forced-colors: active) {
.tb-45__stamp {
mask: none;
-webkit-mask: none;
filter: none;
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
}
.tb-45__stamp span[aria-hidden] {
display: none;
}
}Setup — do this once per project
Skip this if your project already runs Tailwind v4.
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>Compiles in the browser — perfect for a CodePen or a quick test, not for production.
npm install tailwindcss @tailwindcss/vite
npm install tailwindcss @tailwindcss/vite// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})// vite.config.js ("type": "module" in package.json)
import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [tailwindcss()],
})Any v4 setup works — Vite, PostCSS or the CLI. Nothing in this demo is bundler-specific.
@import "tailwindcss";
@theme {
--color-tb45-bg: #20211c;
--color-tb45-bg: oklch(0.25 0.012 110);
--color-tb45-stock: #f0e6cf;
--color-tb45-stock: oklch(0.93 0.03 88);
--color-tb45-stock-lo: #ddd0b3;
--color-tb45-stock-lo: oklch(0.86 0.04 86);
--color-tb45-ink: #2d2a1f;
--color-tb45-ink: oklch(0.27 0.02 95);
--color-tb45-frame: #2d2a1f59;
--color-tb45-frame: oklch(0.27 0.02 95 / 0.35);
--color-tb45-accent: #6d3526;
--color-tb45-accent: oklch(0.38 0.09 38);
--color-tb45-mark: #2c3a4a;
--color-tb45-mark: oklch(0.33 0.04 245);
--font-tb45-serif: "Cinzel", ui-serif, Georgia, "Times New Roman", serif;
--font-tb45-body: "EB Garamond", ui-serif, Georgia, serif;
--ease-tb45-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-45 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(32rem 20rem at 50% 40%, rgba(255,255,255,.06), transparent 72%), var(--color-tb45-bg);
}
/* PERFORATION on four edges: opaque dot columns/rows XOR-ed against a solid base.
Exclude subtracts each dot; where two edges overlap at a corner, parity keeps
the metal — real stamps have that quirk too. */
.tb-45__stamp {
-webkit-mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
/* The flat fallback stays a legible engraved-looking plate if the photo 404s. */
.tb-45__win {
background: repeating-linear-gradient(28deg, color-mix(in oklab, var(--color-tb45-ink) 30%, transparent) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--color-tb45-ink) 18%, var(--color-tb45-stock));
}
/* CANCELLATION BAR: a striped bar with a scalloped top edge. */
.tb-45__bar {
background: repeating-linear-gradient(0deg, var(--color-tb45-mark) 0 2px, transparent 2px 5px);
-webkit-mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
}
@media (prefers-color-scheme: light) {
.tb-45:not([data-theme="dark"]) {
--color-tb45-bg: oklch(0.31 0.015 110);
}
}
.tb-45[data-theme="light"] {
--color-tb45-bg: oklch(0.31 0.015 110);
}
@media (forced-colors: active) {
.tb-45__stamp {
mask: none;
-webkit-mask: none;
filter: none;
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
}
.tb-45__stamp span[aria-hidden] {
display: none;
}
}@import "tailwindcss";
@theme {
--color-tb45-bg: #20211c;
--color-tb45-bg: oklch(0.25 0.012 110);
--color-tb45-stock: #f0e6cf;
--color-tb45-stock: oklch(0.93 0.03 88);
--color-tb45-stock-lo: #ddd0b3;
--color-tb45-stock-lo: oklch(0.86 0.04 86);
--color-tb45-ink: #2d2a1f;
--color-tb45-ink: oklch(0.27 0.02 95);
--color-tb45-frame: #2d2a1f59;
--color-tb45-frame: oklch(0.27 0.02 95 / 0.35);
--color-tb45-accent: #6d3526;
--color-tb45-accent: oklch(0.38 0.09 38);
--color-tb45-mark: #2c3a4a;
--color-tb45-mark: oklch(0.33 0.04 245);
--font-tb45-serif: "Cinzel", ui-serif, Georgia, "Times New Roman", serif;
--font-tb45-body: "EB Garamond", ui-serif, Georgia, serif;
--ease-tb45-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-45 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(32rem 20rem at 50% 40%, rgba(255,255,255,.06), transparent 72%), var(--color-tb45-bg);
}
/* PERFORATION on four edges: opaque dot columns/rows XOR-ed against a solid base.
Exclude subtracts each dot; where two edges overlap at a corner, parity keeps
the metal — real stamps have that quirk too. */
.tb-45__stamp {
-webkit-mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
/* The flat fallback stays a legible engraved-looking plate if the photo 404s. */
.tb-45__win {
background: repeating-linear-gradient(28deg, color-mix(in oklab, var(--color-tb45-ink) 30%, transparent) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--color-tb45-ink) 18%, var(--color-tb45-stock));
}
/* CANCELLATION BAR: a striped bar with a scalloped top edge. */
.tb-45__bar {
background: repeating-linear-gradient(0deg, var(--color-tb45-mark) 0 2px, transparent 2px 5px);
-webkit-mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
}
@media (prefers-color-scheme: light) {
.tb-45:not([data-theme="dark"]) {
--color-tb45-bg: oklch(0.31 0.015 110);
}
}
.tb-45[data-theme="light"] {
--color-tb45-bg: oklch(0.31 0.015 110);
}
@media (forced-colors: active) {
.tb-45__stamp {
mask: none;
-webkit-mask: none;
filter: none;
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
}
.tb-45__stamp span[aria-hidden] {
display: none;
}
}<link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;600;700&family=EB+Garamond:wght@500;600&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=Cinzel:wght@500;600;700&family=EB+Garamond:wght@500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Utilities: the layout, the engraving filter chain, the denomination cartouche, the postmark's rings, rotation and overshoot arrival, the lift-and-tilt. CSS: the five-layer perforation, the hatched image fallback and the cancellation bar's own mask.
- The filter chain is the second demo (after the Polaroid) where splitting
filter:into utilities is strictly better:sepia-[0.72] contrast-125 saturate-[0.7] brightness-95can each be tuned alone. - A11y fix from the CSS original: the accent was
oklch(0.42 0.09 38), used both for the "Post & Telegraph" line (11px, tracked out) and as the denomination badge's fill under near-white text. As a badge fill that measured ≈4.2:1 against--stock. The port darkens it to0.38(≈5.1:1 for the badge text, ≈6.4:1 for the caption). - The hatched
.tb-45__winfallback is deliberately kept from the original and called out in the customization notes: every demo in this collection that hotlinks a photo has one, because a withdrawn Unsplash ID should degrade to a plate, not a hole.
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 Regal Postage Stamp Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-regal-postage-stamp-button/
A commemorative stamp with true perforations on all four edges, an engraved portrait in sepia, a denomination cartouche, and a postmark that lands across the corner on hover — concentric rings plus a wavy cancellation bar, rotated and off-centre.
## HTML
```html
<div class="tb-45 w-full min-h-svh grid place-items-center font-tb45-body p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-45__stamp group relative cursor-pointer border-0 p-2 w-54 text-center text-tb45-ink bg-[linear-gradient(165deg,var(--color-tb45-stock),var(--color-tb45-stock-lo))] drop-shadow-[0_12px_18px_#0000008c] transition-[translate,rotate,filter] duration-[320ms] ease-tb45-out [-webkit-tap-highlight-color:transparent] hover:-translate-y-[3px] hover:-rotate-[1.2deg] hover:drop-shadow-[0_20px_26px_#00000099] focus-visible:-translate-y-[3px] focus-visible:-rotate-[1.2deg] focus-visible:outline-2 focus-visible:outline-offset-[5px] focus-visible:outline-tb45-accent active:translate-y-px active:rotate-0 disabled:cursor-not-allowed disabled:grayscale-[0.6] disabled:opacity-75 motion-reduce:transition-none">
<span class="grid gap-2 px-2.5 pt-3 pb-2.5 border border-tb45-frame">
<span class="tb-45__win block aspect-[7/5] overflow-hidden">
<img src="https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=700&q=70&auto=format&fit=crop" alt="Engraved view of a European cathedral square" width="700" height="500" loading="lazy" decoding="async" class="block size-full object-cover sepia-[0.72] contrast-125 saturate-[0.7] brightness-95">
</span>
<span class="grid gap-0.5">
<span class="font-tb45-serif text-[0.6875rem] tracking-[0.18em] uppercase text-tb45-accent">Post & Telegraph</span>
<span class="font-tb45-serif text-[0.9375rem] font-semibold tracking-[0.03em]">Send the letter</span>
</span>
</span>
<span class="absolute top-4 right-4 px-1.5 py-0.5 rounded-[0.1rem] font-tb45-serif text-[0.9375rem] font-bold leading-none tabular-nums text-tb45-stock bg-tb45-accent">7½<small class="text-[0.6875rem]">d</small></span>
<span aria-hidden="true" class="absolute left-3.5 top-5.5 size-26 pointer-events-none -rotate-16 scale-150 opacity-0 transition-[scale,opacity] duration-[380ms] ease-[cubic-bezier(0.2,1.5,0.4,1)] group-hover:scale-100 group-hover:opacity-[0.72] group-focus-visible:scale-100 group-focus-visible:opacity-[0.72] motion-reduce:transition-opacity motion-reduce:duration-150 motion-reduce:scale-100">
<span class="absolute inset-0 rounded-full border-2 border-tb45-mark"></span>
<span class="absolute inset-[12%] rounded-full border border-tb45-mark"></span>
<span class="tb-45__bar absolute -inset-x-[25%] top-[46%] h-3.5 opacity-85"></span>
</span>
</button>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb45-bg: #20211c;
--color-tb45-bg: oklch(0.25 0.012 110);
--color-tb45-stock: #f0e6cf;
--color-tb45-stock: oklch(0.93 0.03 88);
--color-tb45-stock-lo: #ddd0b3;
--color-tb45-stock-lo: oklch(0.86 0.04 86);
--color-tb45-ink: #2d2a1f;
--color-tb45-ink: oklch(0.27 0.02 95);
--color-tb45-frame: #2d2a1f59;
--color-tb45-frame: oklch(0.27 0.02 95 / 0.35);
--color-tb45-accent: #6d3526;
--color-tb45-accent: oklch(0.38 0.09 38);
--color-tb45-mark: #2c3a4a;
--color-tb45-mark: oklch(0.33 0.04 245);
--font-tb45-serif: "Cinzel", ui-serif, Georgia, "Times New Roman", serif;
--font-tb45-body: "EB Garamond", ui-serif, Georgia, serif;
--ease-tb45-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-45 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(32rem 20rem at 50% 40%, rgba(255,255,255,.06), transparent 72%), var(--color-tb45-bg);
}
/* PERFORATION on four edges: opaque dot columns/rows XOR-ed against a solid base.
Exclude subtracts each dot; where two edges overlap at a corner, parity keeps
the metal — real stamps have that quirk too. */
.tb-45__stamp {
-webkit-mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
/* The flat fallback stays a legible engraved-looking plate if the photo 404s. */
.tb-45__win {
background: repeating-linear-gradient(28deg, color-mix(in oklab, var(--color-tb45-ink) 30%, transparent) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--color-tb45-ink) 18%, var(--color-tb45-stock));
}
/* CANCELLATION BAR: a striped bar with a scalloped top edge. */
.tb-45__bar {
background: repeating-linear-gradient(0deg, var(--color-tb45-mark) 0 2px, transparent 2px 5px);
-webkit-mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
}
@media (prefers-color-scheme: light) {
.tb-45:not([data-theme="dark"]) {
--color-tb45-bg: oklch(0.31 0.015 110);
}
}
.tb-45[data-theme="light"] {
--color-tb45-bg: oklch(0.31 0.015 110);
}
@media (forced-colors: active) {
.tb-45__stamp {
mask: none;
-webkit-mask: none;
filter: none;
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
}
.tb-45__stamp span[aria-hidden] {
display: none;
}
}
```Here's a working CSS demo from CodeFronts. Use it as-is or adapt to your framework. All classes are scoped under a unique prefix so the code won't collide with your existing styles. MIT licensed.
Demo: Tailwind Regal Postage Stamp Button
Source: https://codefronts.com/components/tailwind-css-buttons/tailwind-regal-postage-stamp-button/
A commemorative stamp with true perforations on all four edges, an engraved portrait in sepia, a denomination cartouche, and a postmark that lands across the corner on hover — concentric rings plus a wavy cancellation bar, rotated and off-centre.
## HTML
```html
<div class="tb-45 w-full min-h-svh grid place-items-center font-tb45-body p-[clamp(1.5rem,6vw,4rem)]">
<button type="button" class="tb-45__stamp group relative cursor-pointer border-0 p-2 w-54 text-center text-tb45-ink bg-[linear-gradient(165deg,var(--color-tb45-stock),var(--color-tb45-stock-lo))] drop-shadow-[0_12px_18px_#0000008c] transition-[translate,rotate,filter] duration-[320ms] ease-tb45-out [-webkit-tap-highlight-color:transparent] hover:-translate-y-[3px] hover:-rotate-[1.2deg] hover:drop-shadow-[0_20px_26px_#00000099] focus-visible:-translate-y-[3px] focus-visible:-rotate-[1.2deg] focus-visible:outline-2 focus-visible:outline-offset-[5px] focus-visible:outline-tb45-accent active:translate-y-px active:rotate-0 disabled:cursor-not-allowed disabled:grayscale-[0.6] disabled:opacity-75 motion-reduce:transition-none">
<span class="grid gap-2 px-2.5 pt-3 pb-2.5 border border-tb45-frame">
<span class="tb-45__win block aspect-[7/5] overflow-hidden">
<img src="https://images.unsplash.com/photo-1502602898657-3e91760cbb34?w=700&q=70&auto=format&fit=crop" alt="Engraved view of a European cathedral square" width="700" height="500" loading="lazy" decoding="async" class="block size-full object-cover sepia-[0.72] contrast-125 saturate-[0.7] brightness-95">
</span>
<span class="grid gap-0.5">
<span class="font-tb45-serif text-[0.6875rem] tracking-[0.18em] uppercase text-tb45-accent">Post & Telegraph</span>
<span class="font-tb45-serif text-[0.9375rem] font-semibold tracking-[0.03em]">Send the letter</span>
</span>
</span>
<span class="absolute top-4 right-4 px-1.5 py-0.5 rounded-[0.1rem] font-tb45-serif text-[0.9375rem] font-bold leading-none tabular-nums text-tb45-stock bg-tb45-accent">7½<small class="text-[0.6875rem]">d</small></span>
<span aria-hidden="true" class="absolute left-3.5 top-5.5 size-26 pointer-events-none -rotate-16 scale-150 opacity-0 transition-[scale,opacity] duration-[380ms] ease-[cubic-bezier(0.2,1.5,0.4,1)] group-hover:scale-100 group-hover:opacity-[0.72] group-focus-visible:scale-100 group-focus-visible:opacity-[0.72] motion-reduce:transition-opacity motion-reduce:duration-150 motion-reduce:scale-100">
<span class="absolute inset-0 rounded-full border-2 border-tb45-mark"></span>
<span class="absolute inset-[12%] rounded-full border border-tb45-mark"></span>
<span class="tb-45__bar absolute -inset-x-[25%] top-[46%] h-3.5 opacity-85"></span>
</span>
</button>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tb45-bg: #20211c;
--color-tb45-bg: oklch(0.25 0.012 110);
--color-tb45-stock: #f0e6cf;
--color-tb45-stock: oklch(0.93 0.03 88);
--color-tb45-stock-lo: #ddd0b3;
--color-tb45-stock-lo: oklch(0.86 0.04 86);
--color-tb45-ink: #2d2a1f;
--color-tb45-ink: oklch(0.27 0.02 95);
--color-tb45-frame: #2d2a1f59;
--color-tb45-frame: oklch(0.27 0.02 95 / 0.35);
--color-tb45-accent: #6d3526;
--color-tb45-accent: oklch(0.38 0.09 38);
--color-tb45-mark: #2c3a4a;
--color-tb45-mark: oklch(0.33 0.04 245);
--font-tb45-serif: "Cinzel", ui-serif, Georgia, "Times New Roman", serif;
--font-tb45-body: "EB Garamond", ui-serif, Georgia, serif;
--ease-tb45-out: cubic-bezier(0.16, 1, 0.3, 1);
}
.tb-45 {
width: 100%;
min-height: 100vh;
min-height: 100svh;
background: radial-gradient(32rem 20rem at 50% 40%, rgba(255,255,255,.06), transparent 72%), var(--color-tb45-bg);
}
/* PERFORATION on four edges: opaque dot columns/rows XOR-ed against a solid base.
Exclude subtracts each dot; where two edges overlap at a corner, parity keeps
the metal — real stamps have that quirk too. */
.tb-45__stamp {
-webkit-mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
mask: radial-gradient(.3rem .3rem at 50% 0, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 50% 100%, #000 96%, transparent 0) 0 100%/.62rem .62rem repeat-x, radial-gradient(.3rem .3rem at 0 50%, #000 96%, transparent 0) 0 0/.62rem .62rem repeat-y, radial-gradient(.3rem .3rem at 100% 50%, #000 96%, transparent 0) 100% 0/.62rem .62rem repeat-y, linear-gradient(#000, #000);
-webkit-mask-composite: xor;
mask-composite: exclude;
}
/* The flat fallback stays a legible engraved-looking plate if the photo 404s. */
.tb-45__win {
background: repeating-linear-gradient(28deg, color-mix(in oklab, var(--color-tb45-ink) 30%, transparent) 0 1px, transparent 1px 5px), color-mix(in oklab, var(--color-tb45-ink) 18%, var(--color-tb45-stock));
}
/* CANCELLATION BAR: a striped bar with a scalloped top edge. */
.tb-45__bar {
background: repeating-linear-gradient(0deg, var(--color-tb45-mark) 0 2px, transparent 2px 5px);
-webkit-mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
mask: radial-gradient(.4rem .35rem at 50% 0, transparent 96%, #000 0) 0 0/.8rem .9rem repeat-x;
}
@media (prefers-color-scheme: light) {
.tb-45:not([data-theme="dark"]) {
--color-tb45-bg: oklch(0.31 0.015 110);
}
}
.tb-45[data-theme="light"] {
--color-tb45-bg: oklch(0.31 0.015 110);
}
@media (forced-colors: active) {
.tb-45__stamp {
mask: none;
-webkit-mask: none;
filter: none;
background: ButtonFace;
color: ButtonText;
border: 1px solid ButtonText;
}
.tb-45__stamp span[aria-hidden] {
display: none;
}
}
```How this works
Four edges of perforation is five mask layers, and the composite is parity-based: where two dot layers overlap at a corner the result stays opaque. Real stamps have that quirk too, which is why the port keeps it rather than fighting it.
The engraving is a filter chain on a real photograph, and here the port is a straight win — four independently editable utilities instead of one filter: declaration you must retype to change the sepia.
The postmark stays under 80% opacity and sits over the image, never the type. A cancellation that obscures the label is a decoration that broke the button.
Make it yours
- Perforation size and pitch are the mask's
.3remradius and.62remtile — keep the radius under half the tile. --color-tb45-stockand--color-tb45-ink; classic issues were single-colour.- Move the postmark to the opposite corner by editing one rotation and its position.
- Architecture and botanical plates suit the engraving filter best.
- Check the image URL resolves — the hatched fallback is there because hotlinked photo IDs get withdrawn.
Gotchas — read before shipping
- Five mask layers is the practical maximum before the composite gets expensive. Don't animate any of them.
- Perforated edges shrink the visual hit area at the corners. Keep padding generous.
- The postmark must not obscure the label.
- Sepia lowers contrast — check the denomination and label at 4.5:1 after filtering.
mask-compositekeywords differ per engine. Both are declared, and XOR is parity-based by design.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 120+ | 16.4+ | 128+ | 120+ |
mask + mask-composite need Chrome 120 / Safari 15.4 / Firefox 53. Without them the stamp has straight edges and remains fully usable.