16 / 20
All 20 in 20 Tailwind CSS Image Hover Effects
- 01Tailwind Image Hover Zoom In Card
- 02Tailwind Hover Reveal Text Over Image
- 03Tailwind E-Commerce Product Image Swap on Hover
- 04Tailwind Grid Image Blur Overlay Text Hover
- 05Tailwind Card Slide Up Caption on Hover
- 06Tailwind Grayscale to Color Image Hover
- 07Tailwind Image Color Overlay Tint on Hover
- 08Tailwind Neumorphic Image Card Hover Effect
- 09Tailwind Glassmorphism Overlay on Image Hover
- 10Tailwind Glowing Neon Border Image Hover
- 11Tailwind Image Clip-Path Transition Hover
- 12Tailwind Image Tilt 3D Perspective Hover
- 13Tailwind Shining Light Glare Image Hover
- 14Tailwind Image Split Transition Hover
- 15Tailwind Image Ripple Animation Hover
- 16Tailwind Circular Team Profile Avatar Hover
- 17Tailwind Fullscreen Background Image Hover
- 18Tailwind Masonry Gallery Zoom Hover
- 19Tailwind Image Icon Reveal Center Hover
- 20Tailwind Image Card Flip 3D Animation Hover
Tailwind Circular Team Profile Avatar Hover
A comet-tail arc that spins only while hovered — not by adding the animation, but by flipping animation-play-state from paused to running, so the arc resumes from wherever it stopped. In utilities that is two arbitrary properties on a before: chain, and the rest of the ring is bg-conic-[…].
Untitled UI Pro: 5,000+ React + Tailwind CSS v4 components · from $349 one-time
Untitled UI React · Affiliate link
The code
<section class="tih-16 w-full min-h-svh grid place-items-center bg-tih16-bg font-tih16-sans px-6 py-10" aria-label="Circular avatar hover with spinning ring demo">
<div class="grid w-full max-w-195 gap-5 grid-cols-2 sm:grid-cols-4">
<article style="--ring:oklch(0.75 0.16 300)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=5'),radial-gradient(circle_at_30%_30%,oklch(0.35_0.18_295),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Yuki Tanaka</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Frontend Eng.</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#yuki-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on X">𝕏</a>
<a href="#yuki-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on GitHub">gh</a>
</div>
</article>
<article style="--ring:oklch(0.8 0.14 163)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=14'),radial-gradient(circle_at_30%_30%,oklch(0.42_0.09_165),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Amara Diallo</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">UX Designer</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#amara-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on X">𝕏</a>
<a href="#amara-be" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on Behance">be</a>
</div>
</article>
<article style="--ring:oklch(0.76 0.16 55)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=60'),radial-gradient(circle_at_30%_30%,oklch(0.45_0.11_60),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Ravi Patel</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Data Scientist</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#ravi-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on X">𝕏</a>
<a href="#ravi-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on LinkedIn">in</a>
</div>
</article>
<article style="--ring:oklch(0.74 0.14 250)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=9'),radial-gradient(circle_at_30%_30%,oklch(0.3_0.05_250),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Zoe Mercer</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">DevOps Lead</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#zoe-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on GitHub">gh</a>
<a href="#zoe-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on LinkedIn">in</a>
</div>
</article>
</div>
</section><section class="tih-16 w-full min-h-svh grid place-items-center bg-tih16-bg font-tih16-sans px-6 py-10" aria-label="Circular avatar hover with spinning ring demo">
<div class="grid w-full max-w-195 gap-5 grid-cols-2 sm:grid-cols-4">
<article style="--ring:oklch(0.75 0.16 300)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=5'),radial-gradient(circle_at_30%_30%,oklch(0.35_0.18_295),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Yuki Tanaka</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Frontend Eng.</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#yuki-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on X">𝕏</a>
<a href="#yuki-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on GitHub">gh</a>
</div>
</article>
<article style="--ring:oklch(0.8 0.14 163)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=14'),radial-gradient(circle_at_30%_30%,oklch(0.42_0.09_165),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Amara Diallo</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">UX Designer</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#amara-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on X">𝕏</a>
<a href="#amara-be" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on Behance">be</a>
</div>
</article>
<article style="--ring:oklch(0.76 0.16 55)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=60'),radial-gradient(circle_at_30%_30%,oklch(0.45_0.11_60),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Ravi Patel</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Data Scientist</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#ravi-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on X">𝕏</a>
<a href="#ravi-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on LinkedIn">in</a>
</div>
</article>
<article style="--ring:oklch(0.74 0.14 250)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=9'),radial-gradient(circle_at_30%_30%,oklch(0.3_0.05_250),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Zoe Mercer</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">DevOps Lead</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#zoe-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on GitHub">gh</a>
<a href="#zoe-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on LinkedIn">in</a>
</div>
</article>
</div>
</section>@import "tailwindcss";
@theme {
--color-tih16-bg: #080812;
--color-tih16-bg: oklch(0.12 0.015 285);
--color-tih16-text: #f1f5f9;
--color-tih16-text: oklch(0.96 0.005 250);
--color-tih16-muted: #64748b;
--color-tih16-muted: oklch(0.58 0.035 255);
--color-tih16-acc: #a78bfa;
--color-tih16-acc: oklch(0.75 0.16 300);
--font-tih16-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih16: cubic-bezier(0.34, 1.56, 0.64, 1);
--animate-tih16-spin: tih16-spin 1.2s linear infinite;
}
@keyframes tih16-spin {
to {
rotate: 360deg;
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-16 {
width: 100%;
min-height: 100svh;
}
.tih-16 ::selection {
background: oklch(0.75 0.16 300);
color: #000;
}
/* @supports has no variant form: engines without conic-gradient get a solid ring. */
@supports not (background: conic-gradient(red, blue)) {
.tih-16 .group > div::before {
background: var(--ring);
}
}@import "tailwindcss";
@theme {
--color-tih16-bg: #080812;
--color-tih16-bg: oklch(0.12 0.015 285);
--color-tih16-text: #f1f5f9;
--color-tih16-text: oklch(0.96 0.005 250);
--color-tih16-muted: #64748b;
--color-tih16-muted: oklch(0.58 0.035 255);
--color-tih16-acc: #a78bfa;
--color-tih16-acc: oklch(0.75 0.16 300);
--font-tih16-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih16: cubic-bezier(0.34, 1.56, 0.64, 1);
--animate-tih16-spin: tih16-spin 1.2s linear infinite;
}
@keyframes tih16-spin {
to {
rotate: 360deg;
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-16 {
width: 100%;
min-height: 100svh;
}
.tih-16 ::selection {
background: oklch(0.75 0.16 300);
color: #000;
}
/* @supports has no variant form: engines without conic-gradient get a solid ring. */
@supports not (background: conic-gradient(red, blue)) {
.tih-16 .group > div::before {
background: var(--ring);
}
}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-tih16-bg: #080812;
--color-tih16-bg: oklch(0.12 0.015 285);
--color-tih16-text: #f1f5f9;
--color-tih16-text: oklch(0.96 0.005 250);
--color-tih16-muted: #64748b;
--color-tih16-muted: oklch(0.58 0.035 255);
--color-tih16-acc: #a78bfa;
--color-tih16-acc: oklch(0.75 0.16 300);
--font-tih16-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih16: cubic-bezier(0.34, 1.56, 0.64, 1);
--animate-tih16-spin: tih16-spin 1.2s linear infinite;
}
@keyframes tih16-spin {
to {
rotate: 360deg;
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-16 {
width: 100%;
min-height: 100svh;
}
.tih-16 ::selection {
background: oklch(0.75 0.16 300);
color: #000;
}
/* @supports has no variant form: engines without conic-gradient get a solid ring. */
@supports not (background: conic-gradient(red, blue)) {
.tih-16 .group > div::before {
background: var(--ring);
}
}@import "tailwindcss";
@theme {
--color-tih16-bg: #080812;
--color-tih16-bg: oklch(0.12 0.015 285);
--color-tih16-text: #f1f5f9;
--color-tih16-text: oklch(0.96 0.005 250);
--color-tih16-muted: #64748b;
--color-tih16-muted: oklch(0.58 0.035 255);
--color-tih16-acc: #a78bfa;
--color-tih16-acc: oklch(0.75 0.16 300);
--font-tih16-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih16: cubic-bezier(0.34, 1.56, 0.64, 1);
--animate-tih16-spin: tih16-spin 1.2s linear infinite;
}
@keyframes tih16-spin {
to {
rotate: 360deg;
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-16 {
width: 100%;
min-height: 100svh;
}
.tih-16 ::selection {
background: oklch(0.75 0.16 300);
color: #000;
}
/* @supports has no variant form: engines without conic-gradient get a solid ring. */
@supports not (background: conic-gradient(red, blue)) {
.tih-16 .group > div::before {
background: var(--ring);
}
}<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;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=Space+Grotesk:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap">What changed in the port
Which parts became utilities, which stayed CSS, and why.
animation-play-statehas no utility and should not — it is a state machine value, not a design token. Both halves stay arbitrary properties, and the important part is that they sit adjacent on the element.- The conic arc becomes
before:bg-conic-[…], reading a per-card--ring, so four:nth-childcolour rules disappear. - The keyframe animates
rotaterather thantransform: rotate(), matching what v4's own utilities set. focus-within:is added to the social row: it was hover-only in the CSS original, which made the links focusable but invisible.motion-reduce:shows the ring and pauses it — the arc still communicates hover, with no rotation.- The
@supportsfallback stays in CSS. Feature queries are the other category with no variant form, alongside keyframes.
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 Circular Team Profile Avatar Hover
Source: https://codefronts.com/motion/tailwind-css-image-hover-effects/tailwind-circular-team-profile-avatar-hover/
A comet-tail arc that spins only while hovered — not by adding the animation, but by flipping animation-play-state from paused to running, so the arc resumes from wherever it stopped. In utilities that is two arbitrary properties on a before: chain, and the rest of the ring is bg-conic-[…].
## HTML
```html
<section class="tih-16 w-full min-h-svh grid place-items-center bg-tih16-bg font-tih16-sans px-6 py-10" aria-label="Circular avatar hover with spinning ring demo">
<div class="grid w-full max-w-195 gap-5 grid-cols-2 sm:grid-cols-4">
<article style="--ring:oklch(0.75 0.16 300)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=5'),radial-gradient(circle_at_30%_30%,oklch(0.35_0.18_295),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Yuki Tanaka</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Frontend Eng.</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#yuki-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on X">𝕏</a>
<a href="#yuki-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on GitHub">gh</a>
</div>
</article>
<article style="--ring:oklch(0.8 0.14 163)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=14'),radial-gradient(circle_at_30%_30%,oklch(0.42_0.09_165),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Amara Diallo</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">UX Designer</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#amara-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on X">𝕏</a>
<a href="#amara-be" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on Behance">be</a>
</div>
</article>
<article style="--ring:oklch(0.76 0.16 55)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=60'),radial-gradient(circle_at_30%_30%,oklch(0.45_0.11_60),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Ravi Patel</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Data Scientist</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#ravi-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on X">𝕏</a>
<a href="#ravi-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on LinkedIn">in</a>
</div>
</article>
<article style="--ring:oklch(0.74 0.14 250)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=9'),radial-gradient(circle_at_30%_30%,oklch(0.3_0.05_250),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Zoe Mercer</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">DevOps Lead</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#zoe-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on GitHub">gh</a>
<a href="#zoe-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on LinkedIn">in</a>
</div>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tih16-bg: #080812;
--color-tih16-bg: oklch(0.12 0.015 285);
--color-tih16-text: #f1f5f9;
--color-tih16-text: oklch(0.96 0.005 250);
--color-tih16-muted: #64748b;
--color-tih16-muted: oklch(0.58 0.035 255);
--color-tih16-acc: #a78bfa;
--color-tih16-acc: oklch(0.75 0.16 300);
--font-tih16-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih16: cubic-bezier(0.34, 1.56, 0.64, 1);
--animate-tih16-spin: tih16-spin 1.2s linear infinite;
}
@keyframes tih16-spin {
to {
rotate: 360deg;
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-16 {
width: 100%;
min-height: 100svh;
}
.tih-16 ::selection {
background: oklch(0.75 0.16 300);
color: #000;
}
/* @supports has no variant form: engines without conic-gradient get a solid ring. */
@supports not (background: conic-gradient(red, blue)) {
.tih-16 .group > div::before {
background: var(--ring);
}
}
```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 Circular Team Profile Avatar Hover
Source: https://codefronts.com/motion/tailwind-css-image-hover-effects/tailwind-circular-team-profile-avatar-hover/
A comet-tail arc that spins only while hovered — not by adding the animation, but by flipping animation-play-state from paused to running, so the arc resumes from wherever it stopped. In utilities that is two arbitrary properties on a before: chain, and the rest of the ring is bg-conic-[…].
## HTML
```html
<section class="tih-16 w-full min-h-svh grid place-items-center bg-tih16-bg font-tih16-sans px-6 py-10" aria-label="Circular avatar hover with spinning ring demo">
<div class="grid w-full max-w-195 gap-5 grid-cols-2 sm:grid-cols-4">
<article style="--ring:oklch(0.75 0.16 300)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=5'),radial-gradient(circle_at_30%_30%,oklch(0.35_0.18_295),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Yuki Tanaka</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Frontend Eng.</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#yuki-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on X">𝕏</a>
<a href="#yuki-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Yuki on GitHub">gh</a>
</div>
</article>
<article style="--ring:oklch(0.8 0.14 163)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=14'),radial-gradient(circle_at_30%_30%,oklch(0.42_0.09_165),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Amara Diallo</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">UX Designer</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#amara-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on X">𝕏</a>
<a href="#amara-be" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Amara on Behance">be</a>
</div>
</article>
<article style="--ring:oklch(0.76 0.16 55)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=60'),radial-gradient(circle_at_30%_30%,oklch(0.45_0.11_60),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Ravi Patel</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">Data Scientist</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#ravi-x" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on X">𝕏</a>
<a href="#ravi-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Ravi on LinkedIn">in</a>
</div>
</article>
<article style="--ring:oklch(0.74 0.14 250)" class="group flex flex-col items-center gap-2.5">
<div class="relative w-20 h-20 overflow-hidden rounded-full before:content-[''] before:absolute before:-inset-[3px] before:rounded-full before:bg-conic-[from_0deg,transparent_0deg_270deg,var(--ring)_270deg_360deg] before:opacity-0 before:transition-opacity before:duration-300 before:animate-tih16-spin before:[animation-play-state:paused] group-hover:before:opacity-100 group-hover:before:[animation-play-state:running] motion-reduce:group-hover:before:[animation-play-state:paused]">
<div class="relative z-2 grid place-items-center w-[74px] h-[74px] m-[3px] rounded-full text-[34px] bg-[image:url('https://i.pravatar.cc/400?img=9'),radial-gradient(circle_at_30%_30%,oklch(0.3_0.05_250),oklch(0.2_0.03_265))] bg-cover bg-center bg-no-repeat transition-transform duration-350 ease-tih16 group-hover:scale-106 motion-reduce:transition-none motion-reduce:group-hover:scale-100" aria-hidden="true"></div>
</div>
<p class="text-xs font-bold text-center text-tih16-text">Zoe Mercer</p>
<p class="-mt-2 text-[10px] text-center text-tih16-muted">DevOps Lead</p>
<div class="flex gap-1.5 opacity-0 translate-y-1.5 transition-[opacity,translate] duration-300 ease-tih16 group-hover:opacity-100 group-hover:translate-y-0 focus-within:opacity-100 focus-within:translate-y-0 motion-reduce:transition-none motion-reduce:opacity-100 motion-reduce:translate-y-0">
<a href="#zoe-gh" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on GitHub">gh</a>
<a href="#zoe-in" class="px-1.25 py-0.5 rounded text-xs text-tih16-muted no-underline bg-[oklch(1_0_0/0.07)] focus-visible:outline-2 focus-visible:outline-tih16-acc focus-visible:outline-offset-2" aria-label="Zoe on LinkedIn">in</a>
</div>
</article>
</div>
</section>
```
## CSS
```css
@import "tailwindcss";
@theme {
--color-tih16-bg: #080812;
--color-tih16-bg: oklch(0.12 0.015 285);
--color-tih16-text: #f1f5f9;
--color-tih16-text: oklch(0.96 0.005 250);
--color-tih16-muted: #64748b;
--color-tih16-muted: oklch(0.58 0.035 255);
--color-tih16-acc: #a78bfa;
--color-tih16-acc: oklch(0.75 0.16 300);
--font-tih16-sans: 'Space Grotesk', system-ui, sans-serif;
--ease-tih16: cubic-bezier(0.34, 1.56, 0.64, 1);
--animate-tih16-spin: tih16-spin 1.2s linear infinite;
}
@keyframes tih16-spin {
to {
rotate: 360deg;
}
}
/* Root rule: the full-bleed contract, stated in CSS as well as in the markup's
w-full min-h-svh, so the stage is correctly sized before the browser build compiles. */
.tih-16 {
width: 100%;
min-height: 100svh;
}
.tih-16 ::selection {
background: oklch(0.75 0.16 300);
color: #000;
}
/* @supports has no variant form: engines without conic-gradient get a solid ring. */
@supports not (background: conic-gradient(red, blue)) {
.tih-16 .group > div::before {
background: var(--ring);
}
}
```How this works
Play-state, not presence. The animation is always declared and always paused:
class="before:animate-tih16-spin
before:[animation-play-state:paused]
before:opacity-0
group-hover:before:opacity-100
group-hover:before:[animation-play-state:running]"This is the opposite trade-off from demo 15, and deliberately so: pausing keeps the arc's rotation continuous across hovers, where re-adding an animation restarts it from 0deg. Both are correct — a ripple should restart, a spinner should not.
The arc is a gradient, not a border. conic-gradient with a transparent 0–270deg span and colour from 270–360deg gives a quarter arc; the avatar sits on top at z-2 with a 3px margin, so only the ring shows around it.
Per-card colour without per-card rules. Each item sets --ring inline; the conic utility reads it. Four :nth-child rules disappear.
Make it yours
- Arc length: the
270degstop —180degis a half ring,330dega short comet. - Spin speed: the duration inside
--animate-tih16-spin. - Solid ring: set both stops to
var(--ring). - Counter-rotation: add
before:[animation-direction:reverse]to alternate cards. - Resting placeholder:
before:opacity-30instead of0shows a dim ring before hover.
Gotchas — read before shipping
- The animation must be declared at rest for
animation-play-stateto have anything to pause. Declaring it only undergroup-hover:makes the pause meaningless. conic-gradientneeds Firefox 83+; the@supportsfallback that swaps in a solid border stays in the stylesheet, because@supportshas no variant form.- Safari can bleed 1–2px of the gradient past a
rounded-fullclip; the wrapper'soverflow-hiddenis the fix and must not be removed. before:content-['']is required even for a purely decorative ring.- Stacked variants read outside-in:
group-hover:before:is “the before of this element, while the group is hovered” — not the reverse. - The browser CDN build compiles in the page and is for prototyping only. Ship the CLI or a plugin build.
Browser support
| Chrome | Safari | Firefox | Edge |
|---|---|---|---|
| 111+ | 16.4+ | 128+ | 111+ |
conic-gradient is Chrome 69+, Safari 12.1+, Firefox 83+; the stylesheet carries an @supports solid-border fallback for older engines.