20 CSS Link Hover Effect Designs
A CSS link hover effect is a transition or animation that responds to :hover or :focus-visible on an anchor element. These 20 hand-coded designs go beyond the standard underline-grow-from-left genre — animated underlines, glitches, neon flickers, marker highlights, 3D flips, brutalist blocks and more. 100% pure CSS, semantic <a> elements, every continuous animation honours prefers-reduced-motion.
20 free CSS link hover effects — animated underlines, glitches, neon flickers, marker highlights, 3D flips and more. Every demo uses real <a> elements with proper :focus-visible states and respects prefers-reduced-motion.
.cle-squig-nav { display: flex; gap: 28px; }
.cle-squig {
position: relative;
padding-bottom: 14px;
color: #c5e8ff;
font: 600 18px/1.2 'Caveat', 'Comic Sans MS', cursive;
text-decoration: none;
transition: color 0.25s;
}
.cle-squig.is-active { color: #ddff8a; }
.cle-squig::after {
content: '';
position: absolute;
left: -2px; right: -2px; bottom: 0;
height: 10px;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 80 10' preserveAspectRatio='none'><path d='M2 5 Q 12 0 22 5 T 42 5 T 62 5 T 78 5' stroke='%23ddff8a' stroke-width='2' fill='none' stroke-linecap='round'/></svg>");
background-repeat: no-repeat;
background-size: 0% 100%;
background-position: left center;
transition: background-size 0.55s cubic-bezier(0.65,0,0.35,1);
}
.cle-squig:hover::after,
.cle-squig:focus-visible::after,
.cle-squig.is-active::after { background-size: 100% 100%; } <nav class="cle-squig-nav"> <a href="#" class="cle-squig">home</a> <a href="#" class="cle-squig is-active">blog</a> <a href="#" class="cle-squig">work</a> <a href="#" class="cle-squig">about</a> </nav>
.cle-tilde-row {
margin: 0;
display: flex; gap: 20px; flex-wrap: wrap;
}
.cle-tilde {
position: relative;
padding-bottom: 8px;
color: #ffd479;
font: 500 15px/1.2 Georgia, 'Times New Roman', serif;
text-decoration: none;
background-image: linear-gradient(#ffd479, #ffd479);
background-repeat: no-repeat;
background-size: 100% 1px;
background-position: 0 100%;
transition: background-image 0.4s, background-size 0.4s;
}
.cle-tilde:hover,
.cle-tilde:focus-visible {
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 6' preserveAspectRatio='none'><path d='M0 3 Q 15 0 30 3 T 60 3' stroke='%23ffd479' stroke-width='1.5' fill='none' stroke-linecap='round'/></svg>");
background-size: 100% 6px;
} <p class="cle-tilde-row"> <a href="#" class="cle-tilde">Root</a> <a href="#" class="cle-tilde">Perfect Fifth</a> <a href="#" class="cle-tilde">Perfect Fourth</a> <a href="#" class="cle-tilde">Major Third</a> </p>
.cle-dot {
display: inline-flex; align-items: center; gap: 6px;
padding: 4px 8px;
color: #ddff8a;
font: 700 16px/1.2 system-ui, sans-serif;
text-decoration: underline;
text-decoration-color: #ddff8a;
text-decoration-thickness: 2px;
text-underline-offset: 4px;
border: 1.5px dotted transparent;
border-radius: 3px;
transition: border-color 0.2s;
}
.cle-dot-bullet { color: #ddff8a; font-size: 14px; }
.cle-dot-caret { opacity: 0; color: #ddff8a; font-weight: 700; transition: opacity 0.2s; }
.cle-dot:hover,
.cle-dot:focus-visible { border-color: #ddff8a; }
.cle-dot:hover .cle-dot-caret,
.cle-dot:focus-visible .cle-dot-caret {
opacity: 1;
animation: cle-dot-blink 0.9s steps(1) infinite;
}
@keyframes cle-dot-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.cle-dot:hover .cle-dot-caret, .cle-dot:focus-visible .cle-dot-caret { animation: none; opacity: 1; }
} <a href="#" class="cle-dot"> <span class="cle-dot-bullet" aria-hidden="true">◉</span> Let's Goooo! <span class="cle-dot-caret" aria-hidden="true">_</span> </a>
Check out the link here
.cle-circle-bg {
padding: 24px;
background: #f4f5f9;
border-radius: 10px;
}
.cle-circle-wrap {
margin: 0;
font: 500 16px/1.5 system-ui, sans-serif;
color: #2a2a3e;
}
.cle-circle {
position: relative;
display: inline-block;
padding: 0 6px;
color: #6b8cff;
font-weight: 600;
text-decoration: none;
}
.cle-circle-svg {
position: absolute;
inset: -4px -2px;
width: calc(100% + 4px);
height: calc(100% + 8px);
color: #6b8cff;
pointer-events: none;
overflow: visible;
}
.cle-circle-svg ellipse {
stroke-dasharray: 200;
stroke-dashoffset: 200;
transform-origin: center;
transform: rotate(-3deg);
transition: stroke-dashoffset 0.7s cubic-bezier(0.65,0,0.35,1);
}
.cle-circle:hover .cle-circle-svg ellipse,
.cle-circle:focus-visible .cle-circle-svg ellipse { stroke-dashoffset: 0; } <div class="cle-circle-bg">
<p class="cle-circle-wrap">
Check out <a href="#" class="cle-circle">
the link
<svg class="cle-circle-svg" viewBox="0 0 100 36" aria-hidden="true">
<ellipse cx="50" cy="18" rx="46" ry="14" fill="none" stroke="currentColor" stroke-width="2"/>
</svg>
</a> here
</p>
</div> .cle-chev-bg {
padding: 28px 32px;
background: #f6f8ff;
border-radius: 10px;
}
.cle-chev-stack { display: flex; flex-direction: column; gap: 22px; align-items: flex-start; }
.cle-chev {
--line: #646b8c;
--color: #2b3044;
--background-size: 100%;
--background-delay: 0.15s;
--stroke-dashoffset: 46;
--stroke-duration: 0.15s;
--stroke-easing: linear;
--stroke-delay: 0s;
position: relative;
display: inline;
color: var(--color);
font: 500 16px/20px 'Inter', system-ui, sans-serif;
text-decoration: none;
}
.cle-chev span {
background-image: linear-gradient(0deg, var(--line) 0%, var(--line) 100%);
background-position: 100% 100%;
background-repeat: no-repeat;
background-size: var(--background-size) 1px;
transition: background-size 0.2s linear var(--background-delay);
transform: translateZ(0);
}
.cle-chev-arrow {
vertical-align: top;
display: inline;
line-height: 1;
width: 13px;
height: 20px;
position: relative;
left: -2px;
fill: none;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 1px;
stroke: var(--line);
stroke-dasharray: 7.95 30;
stroke-dashoffset: var(--stroke-dashoffset);
transition: stroke-dashoffset var(--stroke-duration) var(--stroke-easing) var(--stroke-delay);
}
.cle-chev:hover,
.cle-chev:focus-visible {
--background-size: 0%;
--background-delay: 0s;
--stroke-dashoffset: 26;
--stroke-duration: 0.3s;
--stroke-easing: cubic-bezier(0.3, 1.5, 0.5, 1);
--stroke-delay: 0.195s;
}
.cle-chev-multi { max-width: 180px; } <div class="cle-chev-bg">
<div class="cle-chev-stack">
<a href="#" class="cle-chev"><span>Link here</span><svg class="cle-chev-arrow" viewBox="0 0 13 20" aria-hidden="true"><polyline points="0.5 19.5 3 19.5 12.5 10 3 0.5"/></svg></a>
<a href="#" class="cle-chev cle-chev-multi"><span>Link here with multiple lines</span><svg class="cle-chev-arrow" viewBox="0 0 13 20" aria-hidden="true"><polyline points="0.5 19.5 3 19.5 12.5 10 3 0.5"/></svg></a>
</div>
</div> .cle-curblink {
position: relative;
display: inline-block;
padding-bottom: 6px;
color: #00e5ff;
font: 600 16px/1.2 ui-monospace, 'SF Mono', monospace;
text-decoration: none;
letter-spacing: 0.02em;
transition: color 0.2s, text-shadow 0.25s;
}
.cle-curblink::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 2px;
background: #00e5ff;
transform-origin: left;
animation: cle-curblink-pulse 1s steps(1, end) infinite;
}
.cle-curblink:hover,
.cle-curblink:focus-visible {
color: #fff;
text-shadow: 0 0 12px rgba(0,229,255,0.55);
}
.cle-curblink:hover::after,
.cle-curblink:focus-visible::after {
background: #fff;
box-shadow: 0 0 8px rgba(0,229,255,0.7);
}
@keyframes cle-curblink-pulse {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.cle-curblink::after { animation: none; opacity: 1; }
} <a href="#" class="cle-curblink">Open editor</a>
.cle-neonblink {
display: inline-block;
padding: 6px 18px;
color: #ff6c8a;
font: 700 17px/1.2 'Courier New', monospace;
letter-spacing: 0.18em;
text-decoration: none;
text-shadow: 0 0 8px rgba(255,108,138,0.4);
border: 1.5px solid rgba(255,108,138,0.4);
border-radius: 4px;
background: #15081a;
transition: color 0.2s, border-color 0.2s, background 0.3s;
}
.cle-neonblink:hover,
.cle-neonblink:focus-visible {
color: #ffe1ea;
border-color: #ff6c8a;
background: #1f0d24;
animation: cle-neonblink-flicker 1.1s steps(1, end);
}
@keyframes cle-neonblink-flicker {
0% { opacity: 1; text-shadow: none; }
3% { opacity: 0.2; text-shadow: none; }
6% { opacity: 1; text-shadow: 0 0 14px #ff6c8a, 0 0 4px #fff; }
10% { opacity: 0.4; text-shadow: none; }
13% { opacity: 1; text-shadow: 0 0 14px #ff6c8a, 0 0 4px #fff; }
18% { opacity: 0.1; text-shadow: none; }
22% { opacity: 1; text-shadow: 0 0 14px #ff6c8a, 0 0 4px #fff; }
35% { opacity: 0.5; text-shadow: 0 0 8px #ff6c8a; }
38% { opacity: 1; text-shadow: 0 0 18px #ff6c8a, 0 0 6px #fff; }
55% { opacity: 0.6; text-shadow: 0 0 6px #ff6c8a; }
58% { opacity: 1; text-shadow: 0 0 22px #ff6c8a, 0 0 8px #fff; }
100% { opacity: 1; text-shadow: 0 0 22px #ff6c8a, 0 0 8px #fff; }
}
@media (prefers-reduced-motion: reduce) {
.cle-neonblink:hover,
.cle-neonblink:focus-visible {
animation: none;
text-shadow: 0 0 22px #ff6c8a, 0 0 8px #fff;
}
} <a href="#" class="cle-neonblink">VACANCY</a>
.cle-heartbeat {
display: inline-flex; align-items: center; gap: 10px;
padding: 4px 6px;
color: #f0eeff;
font: 600 15px/1.2 system-ui, sans-serif;
text-decoration: none;
border-bottom: 1px solid transparent;
transition: border-color 0.25s, color 0.25s;
}
.cle-heartbeat-dot {
position: relative;
width: 9px; height: 9px;
border-radius: 50%;
background: #ff3d6e;
animation: cle-heartbeat-thump 1.6s ease-in-out infinite;
flex-shrink: 0;
}
.cle-heartbeat-dot::after {
content: '';
position: absolute;
inset: 0;
border-radius: inherit;
background: inherit;
animation: cle-heartbeat-ring 1.6s ease-out infinite;
}
.cle-heartbeat:hover,
.cle-heartbeat:focus-visible {
color: #fff;
border-bottom-color: rgba(255,61,110,0.5);
}
/* Real heartbeat curve: lub (small bump) → DUB (big bump) → long pause */
@keyframes cle-heartbeat-thump {
0% { transform: scale(1); }
10% { transform: scale(1.18); }
20% { transform: scale(1); }
30% { transform: scale(1.32); }
45% { transform: scale(1); }
100% { transform: scale(1); }
}
@keyframes cle-heartbeat-ring {
0% { transform: scale(1); opacity: 0; }
30% { opacity: 0.55; }
100% { transform: scale(3); opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.cle-heartbeat-dot, .cle-heartbeat-dot::after { animation: none; }
} <a href="#" class="cle-heartbeat"> <span class="cle-heartbeat-dot" aria-hidden="true"></span> <span>Live updates</span> </a>
.cle-sweep {
position: relative;
display: inline-block;
color: #f0eeff;
font: 600 16px/1.4 system-ui, sans-serif;
text-decoration: none;
padding-bottom: 6px;
overflow: hidden;
}
/* Permanent underline that draws in from the left */
.cle-sweep::before {
content: '';
position: absolute;
left: 0; bottom: 0;
width: 100%; height: 1.5px;
background: #7c6cff;
transform: scaleX(0);
transform-origin: left center;
transition: transform 0.5s cubic-bezier(0.65,0,0.35,1);
}
.cle-sweep:hover::before,
.cle-sweep:focus-visible::before { transform: scaleX(1); }
/* Bright gradient highlight that sweeps across once on hover */
.cle-sweep::after {
content: '';
position: absolute;
left: 0; bottom: 0;
width: 60%; height: 1.5px;
background: linear-gradient(90deg, transparent, #fff 50%, transparent);
transform: translateX(-100%);
opacity: 0;
transition: transform 0.7s cubic-bezier(0.65,0,0.35,1), opacity 0.2s;
pointer-events: none;
}
.cle-sweep:hover::after,
.cle-sweep:focus-visible::after {
transform: translateX(280%);
opacity: 1;
} <a href="#" class="cle-sweep">Read the article</a>
.cle-push {
display: inline-flex;
color: #c4b5fd;
font: 700 18px/1.2 system-ui, sans-serif;
text-decoration: none;
letter-spacing: 0.02em;
border-bottom: 1.5px solid rgba(124,108,255,0.4);
padding-bottom: 3px;
transition: border-color 0.25s;
}
.cle-push:hover { border-bottom-color: #7c6cff; }
.cle-push span {
display: inline-block;
transition: transform 0.35s cubic-bezier(0.65,0,0.35,1), color 0.25s;
}
.cle-push:hover span { color: #fff; transform: translateY(-3px); }
.cle-push:hover span:nth-child(1) { transition-delay: 0s; }
.cle-push:hover span:nth-child(2) { transition-delay: 0.04s; }
.cle-push:hover span:nth-child(3) { transition-delay: 0.08s; }
.cle-push:hover span:nth-child(4) { transition-delay: 0.12s; }
.cle-push:hover span:nth-child(5) { transition-delay: 0.16s; }
.cle-push:hover span:nth-child(6) { transition-delay: 0.20s; }
.cle-push:hover span:nth-child(7) { transition-delay: 0.24s; }
.cle-push:hover span:nth-child(8) { transition-delay: 0.28s; } <a href="#" class="cle-push"> <span>D</span><span>i</span><span>s</span><span>c</span><span>o</span><span>v</span><span>e</span><span>r</span> </a>
Try our brand new editor today.
.cle-marker-wrap {
margin: 0;
font: 500 16px/1.5 system-ui, sans-serif;
color: #d8d6f0;
}
.cle-marker {
position: relative;
display: inline-block;
padding: 1px 4px;
color: #fff;
font-weight: 600;
text-decoration: none;
z-index: 0;
}
.cle-marker::before {
content: '';
position: absolute;
inset: 0;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 28' preserveAspectRatio='none'><path d='M2 14 Q 12 6 25 11 T 50 12 T 75 14 T 98 13 L 98 22 Q 80 26 60 23 T 30 22 T 5 22 Z' fill='%23ffd479' opacity='0.55'/></svg>");
background-repeat: no-repeat;
background-size: 0% 100%;
background-position: left center;
transition: background-size 0.5s cubic-bezier(0.65,0,0.35,1);
z-index: -1;
}
.cle-marker:hover::before,
.cle-marker:focus-visible::before { background-size: 100% 100%; } <p class="cle-marker-wrap"> Try our <a href="#" class="cle-marker">brand new editor</a> today. </p>
.cle-slide {
position: relative;
display: inline-block;
padding: 4px 10px;
color: #f0eeff;
font: 700 13px/1.2 ui-monospace, monospace;
letter-spacing: 0.18em;
text-decoration: none;
overflow: hidden;
isolation: isolate;
}
.cle-slide span {
position: relative;
mix-blend-mode: difference;
color: #fff;
z-index: 1;
}
.cle-slide::before {
content: '';
position: absolute;
left: 0; right: 0;
bottom: 0;
height: 0;
background: #fff;
transition: height 0.4s cubic-bezier(0.65,0,0.35,1);
z-index: 0;
}
.cle-slide:hover::before,
.cle-slide:focus-visible::before { height: 100%; } <a href="#" class="cle-slide"><span>WATCH FILM</span></a>
.cle-bracket {
position: relative;
display: inline-block;
padding: 0 16px;
color: #00e5ff;
font: 700 16px/1.2 ui-monospace, monospace;
letter-spacing: 0.04em;
text-decoration: none;
transition: text-shadow 0.2s;
}
.cle-bracket::before, .cle-bracket::after {
position: absolute;
top: 50%;
font: inherit;
color: #00e5ff;
opacity: 0;
transform: translateY(-50%) scale(0.4);
transition: opacity 0.25s ease, transform 0.35s cubic-bezier(0.34,1.56,0.64,1);
}
.cle-bracket::before { content: '['; left: 0; }
.cle-bracket::after { content: ']'; right: 0; }
.cle-bracket:hover { text-shadow: 0 0 12px rgba(0,229,255,0.55); }
.cle-bracket:hover::before,
.cle-bracket:hover::after,
.cle-bracket:focus-visible::before,
.cle-bracket:focus-visible::after {
opacity: 1;
transform: translateY(-50%) scale(1);
} <a href="#" class="cle-bracket">execute()</a>
.cle-glitch {
position: relative;
display: inline-block;
color: #f0eeff;
font: 700 16px/1.2 ui-monospace, monospace;
text-decoration: none;
letter-spacing: 0.02em;
}
.cle-glitch:hover,
.cle-glitch:focus-visible {
animation: cle-glitch-shake 0.35s steps(2) infinite;
text-shadow:
2px 0 #ff3d6e,
-2px 0 #00e5ff,
0 0 12px rgba(255,255,255,0.2);
}
@keyframes cle-glitch-shake {
0% { transform: translate(0,0); }
25% { transform: translate(-1px, 1px); }
50% { transform: translate(1px, -1px); }
75% { transform: translate(-1px, -1px); }
100% { transform: translate(1px, 1px); }
}
@media (prefers-reduced-motion: reduce) {
.cle-glitch:hover, .cle-glitch:focus-visible { animation: none; }
} <a href="#" class="cle-glitch" data-text="System.exit(0)">System.exit(0)</a>
.cle-ink {
position: relative;
display: inline-block;
padding-bottom: 8px;
color: #ffd479;
font: 600 16px/1.2 Georgia, 'Times New Roman', serif;
text-decoration: none;
}
.cle-ink-line {
position: absolute;
left: 0; right: 0;
bottom: 0;
width: 100%;
height: 8px;
color: #ffd479;
pointer-events: none;
}
.cle-ink-line path {
stroke-dasharray: 200;
stroke-dashoffset: 200;
transition: stroke-dashoffset 0.7s cubic-bezier(0.65,0,0.35,1);
}
.cle-ink:hover .cle-ink-line path,
.cle-ink:focus-visible .cle-ink-line path { stroke-dashoffset: 0; } <a href="#" class="cle-ink">
<span>Subscribe</span>
<svg class="cle-ink-line" viewBox="0 0 120 8" preserveAspectRatio="none" aria-hidden="true">
<path d="M2 5 Q 30 1 60 4 T 118 5" stroke="currentColor" stroke-width="2.5" fill="none" stroke-linecap="round"/>
</svg>
</a> .cle-mag {
position: relative;
display: inline-block;
padding-bottom: 4px;
color: #c4b5fd;
font: 600 15px/1.2 system-ui, sans-serif;
text-decoration: none;
transition: transform 0.45s cubic-bezier(0.34,1.56,0.64,1), color 0.25s;
}
.cle-mag::after {
content: '';
position: absolute;
left: 0; bottom: 0;
width: 100%; height: 1.5px;
background: #a78bfa;
transform: scaleX(0);
transform-origin: left center;
transition: transform 0.4s cubic-bezier(0.65,0,0.35,1);
}
.cle-mag:hover,
.cle-mag:focus-visible {
transform: translateX(6px);
color: #fff;
}
.cle-mag:hover::after,
.cle-mag:focus-visible::after { transform: scaleX(1); } <a href="#" class="cle-mag">Open dashboard →</a>
.cle-caret {
position: relative;
display: inline-block;
padding: 2px 4px 4px;
color: #2eb88a;
font: 600 14px/1.4 ui-monospace, monospace;
text-decoration: none;
border-bottom: 1px solid transparent;
transition: border-color 0.25s ease 0.05s;
}
.cle-caret::after {
content: '';
display: inline-block;
width: 7px; height: 1em;
margin-left: 4px;
vertical-align: text-bottom;
background: #2eb88a;
opacity: 0;
transition: opacity 0.2s ease;
}
.cle-caret:hover,
.cle-caret:focus-visible {
border-bottom-color: rgba(46,184,138,0.5);
}
.cle-caret:hover::after,
.cle-caret:focus-visible::after {
opacity: 1;
animation: cle-caret-blink 1s steps(1) infinite;
}
@keyframes cle-caret-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
.cle-caret:hover::after, .cle-caret:focus-visible::after { animation: none; opacity: 1; }
} <a href="#" class="cle-caret">$ run --watch</a>
.cle-brut {
display: inline-block;
padding: 6px 12px;
background: transparent;
color: #fff;
font: 700 13px/1.2 'Courier New', monospace;
letter-spacing: 0.14em;
text-decoration: none;
border: 2px solid #fff;
transition: background 0.15s, color 0.15s, transform 0.12s, box-shadow 0.12s;
}
.cle-brut:hover,
.cle-brut:focus-visible {
background: #ff3d6e;
color: #0a0a0a;
border-color: #0a0a0a;
box-shadow: 5px 5px 0 #fff;
transform: translate(-2px, -2px);
}
.cle-brut:active {
transform: translate(3px, 3px);
box-shadow: 0 0 0 #fff;
} <a href="#" class="cle-brut">DOWNLOAD .ZIP</a>
.cle-flip {
position: relative;
display: inline-block;
height: 40px;
padding: 0 22px;
perspective: 800px;
text-decoration: none;
font: 700 13px/40px ui-monospace, monospace;
letter-spacing: 0.12em;
}
/* Invisible ghost claims the wider of the two faces so the chip width is stable */
.cle-flip-ghost {
visibility: hidden;
white-space: nowrap;
}
.cle-flip-inner {
position: absolute;
inset: 0;
transform-style: preserve-3d;
-webkit-transform-style: preserve-3d;
transition: transform 0.55s cubic-bezier(0.65,0,0.35,1);
}
.cle-flip-front, .cle-flip-back {
position: absolute;
inset: 0;
display: flex; align-items: center; justify-content: center;
border-radius: 999px;
backface-visibility: hidden;
-webkit-backface-visibility: hidden;
white-space: nowrap;
}
.cle-flip-front {
background: #7c6cff;
color: #fff;
}
.cle-flip-back {
background: #2eb88a;
color: #0a0f0c;
transform: rotateY(180deg);
}
.cle-flip:hover .cle-flip-inner,
.cle-flip:focus-visible .cle-flip-inner {
transform: rotateY(180deg);
} <a href="#" class="cle-flip">
<span class="cle-flip-inner">
<span class="cle-flip-front">Get Started</span>
<span class="cle-flip-back">Learn More →</span>
</span>
<span class="cle-flip-ghost" aria-hidden="true">Learn More →</span>
</a> .cle-type {
position: relative;
display: inline-block;
padding: 6px 14px;
background: #0a0a18;
border: 1px solid rgba(0,229,255,0.3);
border-radius: 4px;
text-decoration: none;
min-width: 160px;
}
.cle-type-text {
display: inline-block;
font: 600 13px/1.4 ui-monospace, monospace;
color: #00e5ff;
white-space: nowrap;
overflow: hidden;
width: 0;
border-right: 2px solid transparent;
vertical-align: bottom;
}
.cle-type:hover .cle-type-text,
.cle-type:focus-visible .cle-type-text {
width: 14ch;
border-right-color: #00e5ff;
animation:
cle-type-in 1.2s steps(14, end) forwards,
cle-type-blink 0.7s steps(1) 1.2s infinite;
}
@keyframes cle-type-in {
from { width: 0; }
to { width: 14ch; }
}
@keyframes cle-type-blink {
0%, 50% { border-right-color: #00e5ff; }
51%, 100% { border-right-color: transparent; }
}
@media (prefers-reduced-motion: reduce) {
.cle-type:hover .cle-type-text,
.cle-type:focus-visible .cle-type-text {
width: 14ch; animation: none; border-right-color: #00e5ff;
}
} <a href="#" class="cle-type"> <span class="cle-type-text">$ deploy --prod</span> </a>
Frequently asked questions
How do I create a CSS link hover effect?
Should I use :hover or :focus-visible for link effects?
Are these CSS link effects accessible?
Aren't blinking links bad for accessibility?
Do these link effects work on touch devices?
Can I use these effects in any framework?
Related collections
21 CSS Button Hover Effects
21 original CSS button hover effects — liquid fill, glitch, neon pulse, 3D flip, particle burst and more. Pure CSS with 3 vanilla JS enhancements. Copy-paste ready.
27 CSS Card Hover Effects
27 original CSS card hover effects — elastic lift, 3D tilt, holographic shimmer, spotlight, aurora and more. Pure CSS with 4 vanilla JS enhancements. Copy-paste ready.
15 Pure CSS Loading Animations
15 hand-coded CSS loading animations — DNA helix, liquid blob, orbit system, signal bars, clock sweep, bouncing chain, neon ring draw, pixel dissolve, hourglass flip, heartbeat line, House Unlock, Listing Card Skeleton, semantic Progress Path, Heat-Map Compass and a Floor-by-Floor building loader. Pure CSS, accessible, drop-in ready.