05 / 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 Dual-Line Intersection Border Draw Button
A crisp architectural outline CTA for editorial and agency sites: on hover two lines draw from opposite corners and meet to close a rectangular border around the label. The port is the cleanest kind — two pseudo-elements, four utilities each, and the staggered corner-meet is one delay-300.
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<div class="tbh-05-group w-full min-h-svh flex flex-wrap">
<section class="tbh-05a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-sans bg-tbh05a-bg" aria-label="Dual-line border draw button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-10 py-[18px] text-[15px] font-bold uppercase tracking-[0.08em] text-tbh05a-line before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:border-t-2 before:border-r-2 before:border-tbh05a-line before:scale-0 before:origin-top-left before:transition-[scale] before:duration-300 before:ease-out after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:border-b-2 after:border-l-2 after:border-tbh05a-line after:scale-0 after:origin-bottom-right after:transition-[scale] after:duration-300 after:ease-out hover:before:scale-100 hover:after:scale-100 hover:after:delay-300 focus-visible:before:scale-100 focus-visible:after:scale-100 focus-visible:after:delay-300 focus-visible:outline-2 focus-visible:outline-dashed focus-visible:outline-tbh05a-line focus-visible:outline-offset-[6px] motion-reduce:before:transition-none motion-reduce:after:transition-none motion-reduce:hover:after:delay-0">
<span>Explore work</span>
</button>
</section>
<section class="tbh-05b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-mono bg-tbh05b-bg" aria-label="Corner bracket button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-[46px] py-5 text-sm font-bold uppercase tracking-[0.14em] text-tbh05b-line before:content-[''] before:absolute before:top-0 before:left-0 before:w-[18px] before:h-[18px] before:pointer-events-none before:border-2 before:border-r-0 before:border-b-0 before:border-tbh05b-line before:transition-[width,height] before:duration-300 before:ease-out after:content-[''] after:absolute after:bottom-0 after:right-0 after:w-[18px] after:h-[18px] after:pointer-events-none after:border-2 after:border-l-0 after:border-t-0 after:border-tbh05b-line after:transition-[width,height] after:duration-300 after:ease-out hover:before:w-full hover:before:h-full hover:after:w-full hover:after:h-full focus-visible:before:w-full focus-visible:before:h-full focus-visible:after:w-full focus-visible:after:h-full focus-visible:outline-none focus-visible:underline focus-visible:underline-offset-4 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span>View case study</span>
</button>
</section>
</div><div class="tbh-05-group w-full min-h-svh flex flex-wrap">
<section class="tbh-05a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-sans bg-tbh05a-bg" aria-label="Dual-line border draw button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-10 py-[18px] text-[15px] font-bold uppercase tracking-[0.08em] text-tbh05a-line before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:border-t-2 before:border-r-2 before:border-tbh05a-line before:scale-0 before:origin-top-left before:transition-[scale] before:duration-300 before:ease-out after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:border-b-2 after:border-l-2 after:border-tbh05a-line after:scale-0 after:origin-bottom-right after:transition-[scale] after:duration-300 after:ease-out hover:before:scale-100 hover:after:scale-100 hover:after:delay-300 focus-visible:before:scale-100 focus-visible:after:scale-100 focus-visible:after:delay-300 focus-visible:outline-2 focus-visible:outline-dashed focus-visible:outline-tbh05a-line focus-visible:outline-offset-[6px] motion-reduce:before:transition-none motion-reduce:after:transition-none motion-reduce:hover:after:delay-0">
<span>Explore work</span>
</button>
</section>
<section class="tbh-05b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-mono bg-tbh05b-bg" aria-label="Corner bracket button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-[46px] py-5 text-sm font-bold uppercase tracking-[0.14em] text-tbh05b-line before:content-[''] before:absolute before:top-0 before:left-0 before:w-[18px] before:h-[18px] before:pointer-events-none before:border-2 before:border-r-0 before:border-b-0 before:border-tbh05b-line before:transition-[width,height] before:duration-300 before:ease-out after:content-[''] after:absolute after:bottom-0 after:right-0 after:w-[18px] after:h-[18px] after:pointer-events-none after:border-2 after:border-l-0 after:border-t-0 after:border-tbh05b-line after:transition-[width,height] after:duration-300 after:ease-out hover:before:w-full hover:before:h-full hover:after:w-full hover:after:h-full focus-visible:before:w-full focus-visible:before:h-full focus-visible:after:w-full focus-visible:after:h-full focus-visible:outline-none focus-visible:underline focus-visible:underline-offset-4 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span>View case study</span>
</button>
</section>
</div>@import "tailwindcss";
@theme {
--color-tbh05a-line: #6b7280;
--color-tbh05a-line: oklch(0.55 0.02 260);
--color-tbh05a-bg: #f4f3ef;
--color-tbh05a-bg: oklch(0.96 0.005 90);
--color-tbh05b-line: #3ad8cf;
--color-tbh05b-line: oklch(0.72 0.16 190);
--color-tbh05b-bg: #0a1416;
--color-tbh05b-bg: oklch(0.16 0.02 200);
--font-tbh05-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tbh05-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Nothing needs to stay CSS in this demo — the whole effect is utilities, which is why it is
the batch's clearest argument for the port. */
.tbh-05-group {
width: 100%;
min-height: 100svh;
}
.tbh-05a,
.tbh-05b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh05a-line: #6b7280;
--color-tbh05a-line: oklch(0.55 0.02 260);
--color-tbh05a-bg: #f4f3ef;
--color-tbh05a-bg: oklch(0.96 0.005 90);
--color-tbh05b-line: #3ad8cf;
--color-tbh05b-line: oklch(0.72 0.16 190);
--color-tbh05b-bg: #0a1416;
--color-tbh05b-bg: oklch(0.16 0.02 200);
--font-tbh05-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tbh05-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Nothing needs to stay CSS in this demo — the whole effect is utilities, which is why it is
the batch's clearest argument for the port. */
.tbh-05-group {
width: 100%;
min-height: 100svh;
}
.tbh-05a,
.tbh-05b {
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-tbh05a-line: #6b7280;
--color-tbh05a-line: oklch(0.55 0.02 260);
--color-tbh05a-bg: #f4f3ef;
--color-tbh05a-bg: oklch(0.96 0.005 90);
--color-tbh05b-line: #3ad8cf;
--color-tbh05b-line: oklch(0.72 0.16 190);
--color-tbh05b-bg: #0a1416;
--color-tbh05b-bg: oklch(0.16 0.02 200);
--font-tbh05-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tbh05-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Nothing needs to stay CSS in this demo — the whole effect is utilities, which is why it is
the batch's clearest argument for the port. */
.tbh-05-group {
width: 100%;
min-height: 100svh;
}
.tbh-05a,
.tbh-05b {
min-height: 100svh;
}@import "tailwindcss";
@theme {
--color-tbh05a-line: #6b7280;
--color-tbh05a-line: oklch(0.55 0.02 260);
--color-tbh05a-bg: #f4f3ef;
--color-tbh05a-bg: oklch(0.96 0.005 90);
--color-tbh05b-line: #3ad8cf;
--color-tbh05b-line: oklch(0.72 0.16 190);
--color-tbh05b-bg: #0a1416;
--color-tbh05b-bg: oklch(0.16 0.02 200);
--font-tbh05-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tbh05-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Nothing needs to stay CSS in this demo — the whole effect is utilities, which is why it is
the batch's clearest argument for the port. */
.tbh-05-group {
width: 100%;
min-height: 100svh;
}
.tbh-05a,
.tbh-05b {
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&family=JetBrains+Mono:wght@400;500;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&family=JetBrains+Mono:wght@400;500;700&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
- Six selectors collapse into two pseudo runs on one element, and this is the demo where the port is unambiguously better: the resting stroke, the hover target, the stagger and the reduced-motion fallback are all readable in one place, and the stylesheet holds nothing but tokens.
before:transition-[scale]rather thanbefore:transition-transformis deliberate — v4'stransition-transformcovers transform, translate, scale and rotate, and naming onlyscalekeeps the transition from ever picking up a translate you add later.- UI change from the CSS original: design A's focus ring is now a dashed outline at 6px offset that reads as intentional next to the drawn border, and reduced motion drops the 300ms stagger to 0 so the frame appears complete rather than in two visible halves.
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 Dual-Line Intersection Border Draw Button
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-dual-line-intersection-border-draw-button/
A crisp architectural outline CTA for editorial and agency sites: on hover two lines draw from opposite corners and meet to close a rectangular border around the label. The port is the cleanest kind — two pseudo-elements, four utilities each, and the staggered corner-meet is one delay-300.
## HTML
```html
<div class="tbh-05-group w-full min-h-svh flex flex-wrap">
<section class="tbh-05a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-sans bg-tbh05a-bg" aria-label="Dual-line border draw button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-10 py-[18px] text-[15px] font-bold uppercase tracking-[0.08em] text-tbh05a-line before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:border-t-2 before:border-r-2 before:border-tbh05a-line before:scale-0 before:origin-top-left before:transition-[scale] before:duration-300 before:ease-out after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:border-b-2 after:border-l-2 after:border-tbh05a-line after:scale-0 after:origin-bottom-right after:transition-[scale] after:duration-300 after:ease-out hover:before:scale-100 hover:after:scale-100 hover:after:delay-300 focus-visible:before:scale-100 focus-visible:after:scale-100 focus-visible:after:delay-300 focus-visible:outline-2 focus-visible:outline-dashed focus-visible:outline-tbh05a-line focus-visible:outline-offset-[6px] motion-reduce:before:transition-none motion-reduce:after:transition-none motion-reduce:hover:after:delay-0">
<span>Explore work</span>
</button>
</section>
<section class="tbh-05b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-mono bg-tbh05b-bg" aria-label="Corner bracket button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-[46px] py-5 text-sm font-bold uppercase tracking-[0.14em] text-tbh05b-line before:content-[''] before:absolute before:top-0 before:left-0 before:w-[18px] before:h-[18px] before:pointer-events-none before:border-2 before:border-r-0 before:border-b-0 before:border-tbh05b-line before:transition-[width,height] before:duration-300 before:ease-out after:content-[''] after:absolute after:bottom-0 after:right-0 after:w-[18px] after:h-[18px] after:pointer-events-none after:border-2 after:border-l-0 after:border-t-0 after:border-tbh05b-line after:transition-[width,height] after:duration-300 after:ease-out hover:before:w-full hover:before:h-full hover:after:w-full hover:after:h-full focus-visible:before:w-full focus-visible:before:h-full focus-visible:after:w-full focus-visible:after:h-full focus-visible:outline-none focus-visible:underline focus-visible:underline-offset-4 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span>View case study</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh05a-line: #6b7280;
--color-tbh05a-line: oklch(0.55 0.02 260);
--color-tbh05a-bg: #f4f3ef;
--color-tbh05a-bg: oklch(0.96 0.005 90);
--color-tbh05b-line: #3ad8cf;
--color-tbh05b-line: oklch(0.72 0.16 190);
--color-tbh05b-bg: #0a1416;
--color-tbh05b-bg: oklch(0.16 0.02 200);
--font-tbh05-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tbh05-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Nothing needs to stay CSS in this demo — the whole effect is utilities, which is why it is
the batch's clearest argument for the port. */
.tbh-05-group {
width: 100%;
min-height: 100svh;
}
.tbh-05a,
.tbh-05b {
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 Dual-Line Intersection Border Draw Button
Source: https://codefronts.com/motion/tailwind-css-button-hover-effects/tailwind-dual-line-intersection-border-draw-button/
A crisp architectural outline CTA for editorial and agency sites: on hover two lines draw from opposite corners and meet to close a rectangular border around the label. The port is the cleanest kind — two pseudo-elements, four utilities each, and the staggered corner-meet is one delay-300.
## HTML
```html
<div class="tbh-05-group w-full min-h-svh flex flex-wrap">
<section class="tbh-05a flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-sans bg-tbh05a-bg" aria-label="Dual-line border draw button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-10 py-[18px] text-[15px] font-bold uppercase tracking-[0.08em] text-tbh05a-line before:content-[''] before:absolute before:inset-0 before:pointer-events-none before:border-t-2 before:border-r-2 before:border-tbh05a-line before:scale-0 before:origin-top-left before:transition-[scale] before:duration-300 before:ease-out after:content-[''] after:absolute after:inset-0 after:pointer-events-none after:border-b-2 after:border-l-2 after:border-tbh05a-line after:scale-0 after:origin-bottom-right after:transition-[scale] after:duration-300 after:ease-out hover:before:scale-100 hover:after:scale-100 hover:after:delay-300 focus-visible:before:scale-100 focus-visible:after:scale-100 focus-visible:after:delay-300 focus-visible:outline-2 focus-visible:outline-dashed focus-visible:outline-tbh05a-line focus-visible:outline-offset-[6px] motion-reduce:before:transition-none motion-reduce:after:transition-none motion-reduce:hover:after:delay-0">
<span>Explore work</span>
</button>
</section>
<section class="tbh-05b flex-[1_1_340px] min-w-[300px] grid place-items-center px-6 py-14 font-tbh05-mono bg-tbh05b-bg" aria-label="Corner bracket button">
<button type="button" class="relative cursor-pointer border-0 bg-transparent px-[46px] py-5 text-sm font-bold uppercase tracking-[0.14em] text-tbh05b-line before:content-[''] before:absolute before:top-0 before:left-0 before:w-[18px] before:h-[18px] before:pointer-events-none before:border-2 before:border-r-0 before:border-b-0 before:border-tbh05b-line before:transition-[width,height] before:duration-300 before:ease-out after:content-[''] after:absolute after:bottom-0 after:right-0 after:w-[18px] after:h-[18px] after:pointer-events-none after:border-2 after:border-l-0 after:border-t-0 after:border-tbh05b-line after:transition-[width,height] after:duration-300 after:ease-out hover:before:w-full hover:before:h-full hover:after:w-full hover:after:h-full focus-visible:before:w-full focus-visible:before:h-full focus-visible:after:w-full focus-visible:after:h-full focus-visible:outline-none focus-visible:underline focus-visible:underline-offset-4 motion-reduce:before:transition-none motion-reduce:after:transition-none">
<span>View case study</span>
</button>
</section>
</div>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tbh05a-line: #6b7280;
--color-tbh05a-line: oklch(0.55 0.02 260);
--color-tbh05a-bg: #f4f3ef;
--color-tbh05a-bg: oklch(0.96 0.005 90);
--color-tbh05b-line: #3ad8cf;
--color-tbh05b-line: oklch(0.72 0.16 190);
--color-tbh05b-bg: #0a1416;
--color-tbh05b-bg: oklch(0.16 0.02 200);
--font-tbh05-sans: 'Space Grotesk', system-ui, sans-serif;
--font-tbh05-mono: 'JetBrains Mono', ui-monospace, monospace;
}
/* Nothing needs to stay CSS in this demo — the whole effect is utilities, which is why it is
the batch's clearest argument for the port. */
.tbh-05-group {
width: 100%;
min-height: 100svh;
}
.tbh-05a,
.tbh-05b {
min-height: 100svh;
}
```How this works
Two selectors become two class runs. The CSS version wrote .btn::before, .btn::after, then .btn:hover::before, .btn:focus-visible::before, .btn:hover::after, .btn:focus-visible::after — six selectors for one effect. Tailwind states it as two pseudo runs with their own hover twins, all inside the button's class list.
The stagger is the technique. Both half-frames scale from 0 to 1 over 300ms, but the second waits 300ms. That is what turns two independent strokes into one continuous line travelling around the perimeter, and it is one utility: hover:after:delay-300.
Zero layout cost. Only scale animates, so the drawn border never reflows the stage no matter how long the label is. Design B's width/height growth does touch layout, but on absolutely positioned pseudo-elements, where it costs nothing outside their own box.
Make it yours
- Draw speed and corner-meet:
before:duration-300plushover:after:delay-300— the delay should equal the duration, or the lines visibly overlap. - Direction: flip
before:origin-top-left/after:origin-bottom-rightto reverse which corners the strokes leave from. - Weight:
border-t-2→border-tfor a hairline,border-t-4for a poster feel. - Recolour through
--color-tbh05a-line; the label, the strokes and the focus ring all read it.
Gotchas — read before shipping
- Each pseudo-element needs its own
origin-*utility, or both lines grow from the centre and the effect collapses into a fade. - The button must stay a positioning context —
relativeon the button,absolute inset-0on the pseudos. - Pad the label generously (
px-10 py-[18px]): the drawn box lands exactly on the padding edge, and a tight button reads as cramped the moment the border appears. - Design B removes the focus outline in favour of an underline, which is only acceptable because the underline is a visible, non-motion indicator. Never drop the outline without replacing it.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
Transforms, pseudo-elements and transition-delay are universal — this demo needs nothing beyond Tailwind v4's own floor, and no fallback.