20 / 36
All 36 in 36 Tailwind CSS Button Hover Effects
- 01Tailwind Magnetic Ripple Attraction Button Hover Effect
- 02Tailwind Cyberpunk Holographic Glitch Grid Button
- 03Tailwind Frosted Glassmorphism Shimmer Glow Button
- 04Tailwind Organic Blob Morph Button Hover Effect
- 05Tailwind Dual-Line Intersection Border Draw Button
- 06Tailwind Isometric Tactile 3D Depress Button
- 07Tailwind Infinite Sliding Kinetic Marquee Button
- 08Tailwind Chroma Conic Gradient Border Spin Button
- 09Tailwind Minimalist Border & Outline Drawing Hover Effects
- 10Tailwind Modern 3D Skeuomorphic Button Press Effects
- 11Tailwind Neo-Brutalist & Cyberpunk Button Glow Effects
- 12Tailwind Sliding Background Color Wipe Button Effects
- 13Tailwind Liquid Fill & Organic Morphing Button Effects
- 14Tailwind Kinetic Text Flip & Icon Reveal Button Effects
- 15Tailwind Liquid Wave Fill Button Hover Effect
- 16Tailwind Magnetic Border Drift Button
- 17Tailwind Glitch Slice Button Hover Effect
- 18Tailwind Neon Pulse Glow Button Hover Effect
- 19Tailwind Split Reveal Curtain Button Effect
- 20Tailwind Diagonal Shutter Sweep Button Effect
- 21Tailwind Elastic Lift Spring Button Hover Effect
- 22Tailwind Center Underline Draw Link Button Effects
- 23Tailwind 3D Flip Card Button Hover Effect
- 24Tailwind Particle Burst Confetti Button Effect
- 25Tailwind Slide Doors Closing Panel Button Effect
- 26Tailwind Typewriter Retype Terminal Button Effect
- 27Tailwind Hard Shadow Shift Flat Button Effect
- 28Tailwind Material Ripple Wave Button Effect
- 29Tailwind Rainbow Gradient Border Spin Button
- 30Tailwind Morph Shape Silhouette Button Effects
- 31Tailwind Icon Slide In Directional Button Effect
- 32Tailwind Cursor Spotlight Glow Button
- 33Tailwind Stagger Fill Equaliser Button Effect
- 34Tailwind Sunburst Expand Radiant Button Effect
- 35Tailwind 3D Press Plunger Key Button Effect
- 36Tailwind Add-to-Cart Liquid Glass Ripple Button
Tailwind Diagonal Shutter Sweep Button Effect
A dynamic CTA where a skewed colour panel sweeps across on hover like a diagonal shutter closing over the surface, with the label swapping colour as it passes. In v4 the skew and the sweep are separate properties, so the blade's angle is stated once instead of in every state.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tbh-20-group w-full min-h-svh flex flex-wrap">
<section class="tbh-20a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20a-stage" aria-label="Diagonal shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20a-blade px-10 py-[17px] text-base font-extrabold tracking-[0.03em] text-tbh20a-blade bg-tbh20a-panel transition-colors duration-300 delay-[120ms] before:content-[''] before:absolute before:-top-[20%] before:-left-[20%] before:w-[140%] before:h-[140%] before:z-0 before:bg-tbh20a-blade before:skew-x-[-20deg] before:scale-x-0 before:origin-center before:transition-[scale] before:duration-[450ms] before:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20a-onblade hover:before:scale-x-100 focus-visible:text-tbh20a-onblade focus-visible:before:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20a-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none">
<span class="relative z-[1]">View gallery</span>
</button>
</section>
<section class="tbh-20b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20b-stage" aria-label="Twin shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20b-blade px-[42px] py-[17px] text-base font-extrabold text-tbh20b-blade bg-transparent transition-colors duration-300 delay-[140ms] before:content-[''] before:absolute before:-top-[30%] before:-left-[20%] before:h-[160%] before:w-[70%] before:z-0 before:bg-tbh20b-blade before:skew-x-[-18deg] before:scale-x-0 before:origin-left before:transition-[scale] before:duration-400 before:ease-[cubic-bezier(.5,0,.2,1)] after:content-[''] after:absolute after:-top-[30%] after:-right-[20%] after:h-[160%] after:w-[70%] after:z-0 after:bg-tbh20b-blade after:skew-x-[-18deg] after:scale-x-0 after:origin-right after:transition-[scale] after:duration-400 after:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20b-onblade hover:before:scale-x-100 hover:after:scale-x-100 focus-visible:text-tbh20b-onblade focus-visible:before:scale-x-100 focus-visible:after:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20b-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Book demo</span>
</button>
</section>
</div><div class="tbh-20-group w-full min-h-svh flex flex-wrap">
<section class="tbh-20a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20a-stage" aria-label="Diagonal shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20a-blade px-10 py-[17px] text-base font-extrabold tracking-[0.03em] text-tbh20a-blade bg-tbh20a-panel transition-colors duration-300 delay-[120ms] before:content-[''] before:absolute before:-top-[20%] before:-left-[20%] before:w-[140%] before:h-[140%] before:z-0 before:bg-tbh20a-blade before:skew-x-[-20deg] before:scale-x-0 before:origin-center before:transition-[scale] before:duration-[450ms] before:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20a-onblade hover:before:scale-x-100 focus-visible:text-tbh20a-onblade focus-visible:before:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20a-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none">
<span class="relative z-[1]">View gallery</span>
</button>
</section>
<section class="tbh-20b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20b-stage" aria-label="Twin shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20b-blade px-[42px] py-[17px] text-base font-extrabold text-tbh20b-blade bg-transparent transition-colors duration-300 delay-[140ms] before:content-[''] before:absolute before:-top-[30%] before:-left-[20%] before:h-[160%] before:w-[70%] before:z-0 before:bg-tbh20b-blade before:skew-x-[-18deg] before:scale-x-0 before:origin-left before:transition-[scale] before:duration-400 before:ease-[cubic-bezier(.5,0,.2,1)] after:content-[''] after:absolute after:-top-[30%] after:-right-[20%] after:h-[160%] after:w-[70%] after:z-0 after:bg-tbh20b-blade after:skew-x-[-18deg] after:scale-x-0 after:origin-right after:transition-[scale] after:duration-400 after:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20b-onblade hover:before:scale-x-100 hover:after:scale-x-100 focus-visible:text-tbh20b-onblade focus-visible:before:scale-x-100 focus-visible:after:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20b-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Book demo</span>
</button>
</section>
</div>@import "tailwindcss";
@theme {
--color-tbh20a-blade: #171717;
--color-tbh20a-blade: oklch(0.18 0 0);
--color-tbh20a-onblade: #ffffff;
--color-tbh20a-onblade: oklch(1 0 0);
--color-tbh20a-panel: #f7f5f0;
--color-tbh20a-panel: oklch(0.97 0.006 90);
--color-tbh20a-stage: #e9e6dd;
--color-tbh20a-stage: oklch(0.92 0.01 90);
--color-tbh20b-blade: #3f6ad0;
--color-tbh20b-blade: oklch(0.55 0.17 255);
--color-tbh20b-onblade: #ffffff;
--color-tbh20b-onblade: oklch(1 0 0);
--color-tbh20b-stage: #0b1220;
--color-tbh20b-stage: oklch(0.14 0.02 255);
--font-tbh20-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the skew is a persistent utility and only the scale animates. */
.tbh-20-group {
width: 100%;
min-height: 100svh;
}
.tbh-20a,
.tbh-20b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh20a-blade: #171717;
--color-tbh20a-blade: oklch(0.18 0 0);
--color-tbh20a-onblade: #ffffff;
--color-tbh20a-onblade: oklch(1 0 0);
--color-tbh20a-panel: #f7f5f0;
--color-tbh20a-panel: oklch(0.97 0.006 90);
--color-tbh20a-stage: #e9e6dd;
--color-tbh20a-stage: oklch(0.92 0.01 90);
--color-tbh20b-blade: #3f6ad0;
--color-tbh20b-blade: oklch(0.55 0.17 255);
--color-tbh20b-onblade: #ffffff;
--color-tbh20b-onblade: oklch(1 0 0);
--color-tbh20b-stage: #0b1220;
--color-tbh20b-stage: oklch(0.14 0.02 255);
--font-tbh20-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the skew is a persistent utility and only the scale animates. */
.tbh-20-group {
width: 100%;
min-height: 100svh;
}
.tbh-20a,
.tbh-20b {
min-height: 100svh;
}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-tbh20a-blade: #171717;
--color-tbh20a-blade: oklch(0.18 0 0);
--color-tbh20a-onblade: #ffffff;
--color-tbh20a-onblade: oklch(1 0 0);
--color-tbh20a-panel: #f7f5f0;
--color-tbh20a-panel: oklch(0.97 0.006 90);
--color-tbh20a-stage: #e9e6dd;
--color-tbh20a-stage: oklch(0.92 0.01 90);
--color-tbh20b-blade: #3f6ad0;
--color-tbh20b-blade: oklch(0.55 0.17 255);
--color-tbh20b-onblade: #ffffff;
--color-tbh20b-onblade: oklch(1 0 0);
--color-tbh20b-stage: #0b1220;
--color-tbh20b-stage: oklch(0.14 0.02 255);
--font-tbh20-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the skew is a persistent utility and only the scale animates. */
.tbh-20-group {
width: 100%;
min-height: 100svh;
}
.tbh-20a,
.tbh-20b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh20a-blade: #171717;
--color-tbh20a-blade: oklch(0.18 0 0);
--color-tbh20a-onblade: #ffffff;
--color-tbh20a-onblade: oklch(1 0 0);
--color-tbh20a-panel: #f7f5f0;
--color-tbh20a-panel: oklch(0.97 0.006 90);
--color-tbh20a-stage: #e9e6dd;
--color-tbh20a-stage: oklch(0.92 0.01 90);
--color-tbh20b-blade: #3f6ad0;
--color-tbh20b-blade: oklch(0.55 0.17 255);
--color-tbh20b-onblade: #ffffff;
--color-tbh20b-onblade: oklch(1 0 0);
--color-tbh20b-stage: #0b1220;
--color-tbh20b-stage: oklch(0.14 0.02 255);
--font-tbh20-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the skew is a persistent utility and only the scale animates. */
.tbh-20-group {
width: 100%;
min-height: 100svh;
}
.tbh-20a,
.tbh-20b {
min-height: 100svh;
}<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=Space+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=Space+Grotesk:wght@400;500;600;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Design B is where the transform split really pays: the CSS original repeated
skewX(-18deg)six times across two blades and three states. Here it appears twice — once per blade — and every state names onlyscale-x. before:transition-[scale]rather thantransition-transformis intentional; naming the property keeps the skew out of the transition so the blade never appears to un-skew as it sweeps.- UI change from the CSS original: the sweep and label flip now fire on
focus-visible:, and the reduced-motion path drops the delay to 0 so the colour change is immediate rather than lagging behind a blade that no longer animates.
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 Diagonal Shutter Sweep Button Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-diagonal-shutter-sweep-button-effect/
A dynamic CTA where a skewed colour panel sweeps across on hover like a diagonal shutter closing over the surface, with the label swapping colour as it passes. In v4 the skew and the sweep are separate properties, so the blade's angle is stated once instead of in every state.
## HTML
```html
<div class="tbh-20-group w-full min-h-svh flex flex-wrap">
<section class="tbh-20a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20a-stage" aria-label="Diagonal shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20a-blade px-10 py-[17px] text-base font-extrabold tracking-[0.03em] text-tbh20a-blade bg-tbh20a-panel transition-colors duration-300 delay-[120ms] before:content-[''] before:absolute before:-top-[20%] before:-left-[20%] before:w-[140%] before:h-[140%] before:z-0 before:bg-tbh20a-blade before:skew-x-[-20deg] before:scale-x-0 before:origin-center before:transition-[scale] before:duration-[450ms] before:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20a-onblade hover:before:scale-x-100 focus-visible:text-tbh20a-onblade focus-visible:before:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20a-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none">
<span class="relative z-[1]">View gallery</span>
</button>
</section>
<section class="tbh-20b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20b-stage" aria-label="Twin shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20b-blade px-[42px] py-[17px] text-base font-extrabold text-tbh20b-blade bg-transparent transition-colors duration-300 delay-[140ms] before:content-[''] before:absolute before:-top-[30%] before:-left-[20%] before:h-[160%] before:w-[70%] before:z-0 before:bg-tbh20b-blade before:skew-x-[-18deg] before:scale-x-0 before:origin-left before:transition-[scale] before:duration-400 before:ease-[cubic-bezier(.5,0,.2,1)] after:content-[''] after:absolute after:-top-[30%] after:-right-[20%] after:h-[160%] after:w-[70%] after:z-0 after:bg-tbh20b-blade after:skew-x-[-18deg] after:scale-x-0 after:origin-right after:transition-[scale] after:duration-400 after:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20b-onblade hover:before:scale-x-100 hover:after:scale-x-100 focus-visible:text-tbh20b-onblade focus-visible:before:scale-x-100 focus-visible:after:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20b-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Book demo</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh20a-blade: #171717;
--color-tbh20a-blade: oklch(0.18 0 0);
--color-tbh20a-onblade: #ffffff;
--color-tbh20a-onblade: oklch(1 0 0);
--color-tbh20a-panel: #f7f5f0;
--color-tbh20a-panel: oklch(0.97 0.006 90);
--color-tbh20a-stage: #e9e6dd;
--color-tbh20a-stage: oklch(0.92 0.01 90);
--color-tbh20b-blade: #3f6ad0;
--color-tbh20b-blade: oklch(0.55 0.17 255);
--color-tbh20b-onblade: #ffffff;
--color-tbh20b-onblade: oklch(1 0 0);
--color-tbh20b-stage: #0b1220;
--color-tbh20b-stage: oklch(0.14 0.02 255);
--font-tbh20-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the skew is a persistent utility and only the scale animates. */
.tbh-20-group {
width: 100%;
min-height: 100svh;
}
.tbh-20a,
.tbh-20b {
min-height: 100svh;
}
```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 Diagonal Shutter Sweep Button Effect
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-diagonal-shutter-sweep-button-effect/
A dynamic CTA where a skewed colour panel sweeps across on hover like a diagonal shutter closing over the surface, with the label swapping colour as it passes. In v4 the skew and the sweep are separate properties, so the blade's angle is stated once instead of in every state.
## HTML
```html
<div class="tbh-20-group w-full min-h-svh flex flex-wrap">
<section class="tbh-20a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20a-stage" aria-label="Diagonal shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20a-blade px-10 py-[17px] text-base font-extrabold tracking-[0.03em] text-tbh20a-blade bg-tbh20a-panel transition-colors duration-300 delay-[120ms] before:content-[''] before:absolute before:-top-[20%] before:-left-[20%] before:w-[140%] before:h-[140%] before:z-0 before:bg-tbh20a-blade before:skew-x-[-20deg] before:scale-x-0 before:origin-center before:transition-[scale] before:duration-[450ms] before:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20a-onblade hover:before:scale-x-100 focus-visible:text-tbh20a-onblade focus-visible:before:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20a-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none">
<span class="relative z-[1]">View gallery</span>
</button>
</section>
<section class="tbh-20b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh20-sans bg-tbh20b-stage" aria-label="Twin shutter button">
<button type="button" class="relative isolate overflow-hidden cursor-pointer rounded-lg border-2 border-tbh20b-blade px-[42px] py-[17px] text-base font-extrabold text-tbh20b-blade bg-transparent transition-colors duration-300 delay-[140ms] before:content-[''] before:absolute before:-top-[30%] before:-left-[20%] before:h-[160%] before:w-[70%] before:z-0 before:bg-tbh20b-blade before:skew-x-[-18deg] before:scale-x-0 before:origin-left before:transition-[scale] before:duration-400 before:ease-[cubic-bezier(.5,0,.2,1)] after:content-[''] after:absolute after:-top-[30%] after:-right-[20%] after:h-[160%] after:w-[70%] after:z-0 after:bg-tbh20b-blade after:skew-x-[-18deg] after:scale-x-0 after:origin-right after:transition-[scale] after:duration-400 after:ease-[cubic-bezier(.5,0,.2,1)] hover:text-tbh20b-onblade hover:before:scale-x-100 hover:after:scale-x-100 focus-visible:text-tbh20b-onblade focus-visible:before:scale-x-100 focus-visible:after:scale-x-100 focus-visible:outline-3 focus-visible:outline-tbh20b-blade focus-visible:outline-offset-4 motion-reduce:transition-colors motion-reduce:delay-0 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span class="relative z-[1]">Book demo</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh20a-blade: #171717;
--color-tbh20a-blade: oklch(0.18 0 0);
--color-tbh20a-onblade: #ffffff;
--color-tbh20a-onblade: oklch(1 0 0);
--color-tbh20a-panel: #f7f5f0;
--color-tbh20a-panel: oklch(0.97 0.006 90);
--color-tbh20a-stage: #e9e6dd;
--color-tbh20a-stage: oklch(0.92 0.01 90);
--color-tbh20b-blade: #3f6ad0;
--color-tbh20b-blade: oklch(0.55 0.17 255);
--color-tbh20b-onblade: #ffffff;
--color-tbh20b-onblade: oklch(1 0 0);
--color-tbh20b-stage: #0b1220;
--color-tbh20b-stage: oklch(0.14 0.02 255);
--font-tbh20-sans: 'Space Grotesk', system-ui, sans-serif;
}
/* Nothing stays CSS: the skew is a persistent utility and only the scale animates. */
.tbh-20-group {
width: 100%;
min-height: 100svh;
}
.tbh-20a,
.tbh-20b {
min-height: 100svh;
}
```How this works
The angle stopped being part of the animation. The CSS version wrote transform: skewX(-20deg) scaleX(0) and then had to repeat skewX(-20deg) in the hover rule, the reduced-motion rule, and again for the second blade. In v4, skew-x-[-20deg] compiles to the skew/transform side and scale-x-* to scale, so the hover state is one utility and the blade's identity is stated once.
Oversize the blade, always. A skewed rectangle scaled to exactly 100% leaves triangular gaps at two corners. 140% with a -20% offset covers them at every angle you are likely to pick, and costs nothing because the button clips it.
Design B is two blades meeting. Each is 70% wide with an outward origin, so they close on the centre like shutter leaves rather than sweeping past. Same utilities, doubled — and the reason the entry keeps both.
Make it yours
- Blade angle:
before:skew-x-[-20deg](and B's-18deg). Past about 30deg the oversize needs raising too. - Sweep origin:
before:origin-centerfor a centre-out blade,origin-leftfor an edge sweep. - Palette:
--color-tbh20a-blade/-onblade; design A is the monochrome editorial take, B the accent-colour product take. - Pace:
before:duration-[450ms]withease-[cubic-bezier(.5,0,.2,1)]— a fast start and a soft landing, which is what makes it feel like a physical blade.
Gotchas — read before shipping
- Scale the blade well past the button's bounds or the skewed corners gap — the single most common failure in this pattern.
- Delay the label's colour swap (
delay-[120ms]), or the text flips before the blade covers it and is briefly unreadable. - Both label colours need a contrast check: resting ink on the stage, and
-onbladeink on the blade. overflow-hiddenplusisolateon the button: the first clips the blade, the second keeps itsz-0from escaping the button's stacking context.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Skew and scale transforms are universal; with CSS unavailable each button is a plain bordered CTA.