Modern CSS Transform Effects
2D and 3D transforms - tilts, cubes, perspective and chained motion - driven only by hover and keyframes.
13D Card Tilt
Tilts a card in 3D with a moving glare on hover - a pure CSS take on the mouse-follow tilt. Class: fx-3d-card-tilt
Our chocolate tart, step by step
$75.75
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 p-3 h-100" style="min-height:200px">
<div class="card border-0 shadow-sm fx-3d-card-tilt" style="width:12rem">
<img src="https://images.unsplash.com/photo-1565895405140-6b9830a88c19?auto=format&fit=crop&w=1200&h=800&q=70" alt="Card cover" class="card-img-top object-fit-cover" style="height:100px">
<div class="card-body py-2">
<h3 class="h6 mb-0">Our chocolate tart, step by step</h3>
<p class="small text-body-secondary mb-0">$75.75</p>
</div>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-3d-card-tilt {
position: relative; overflow: hidden; transform-style: preserve-3d;
transform: perspective(900px) rotateX(0deg) rotateY(0deg);
transition: transform .55s cubic-bezier(.2, .8, .2, 1), box-shadow .55s;
}
.fx-3d-card-tilt::after {
content: ""; position: absolute; inset: 0; pointer-events: none; opacity: 0;
background: linear-gradient(115deg, transparent 30%, rgba(255, 255, 255, .45) 48%, transparent 62%);
transform: translateX(-60%); transition: transform .7s ease, opacity .4s;
}
.fx-3d-card-tilt:hover, .fx-3d-card-tilt:focus-within {
transform: perspective(900px) rotateX(8deg) rotateY(-12deg) translateY(-4px);
box-shadow: 18px 22px 40px -18px rgba(var(--wt-primary-rgb), .55);
}
.fx-3d-card-tilt:hover::after, .fx-3d-card-tilt:focus-within::after { opacity: 1; transform: translateX(60%); }
@media (prefers-reduced-motion: reduce) {
.fx-3d-card-tilt, .fx-3d-card-tilt::before, .fx-3d-card-tilt::after, .fx-3d-card-tilt *, .fx-3d-card-tilt *::before, .fx-3d-card-tilt *::after { animation: none !important; transition: none !important; }
}23D Rotating Cube
A spinning six-sided cube; give the element six children for the faces and set --fx-size if needed. Class: fx-3d-rotating-cube
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 p-3 h-100" style="min-height:200px">
<div class="fx-3d-rotating-cube" aria-hidden="true">
<div>
<i class="bi bi-box" aria-hidden="true"></i>
</div>
<div>
<i class="bi bi-gem" aria-hidden="true"></i>
</div>
<div>
<i class="bi bi-star-fill" aria-hidden="true"></i>
</div>
<div>
<i class="bi bi-heart-fill" aria-hidden="true"></i>
</div>
<div>
<i class="bi bi-trophy-fill" aria-hidden="true"></i>
</div>
<div>
<i class="bi bi-bell-fill" aria-hidden="true"></i>
</div>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-3d-rotating-cube {
--fx-half: calc(var(--fx-size, 6rem) / 2);
position: relative; width: var(--fx-size, 6rem); height: var(--fx-size, 6rem);
transform-style: preserve-3d;
animation: fx-3d-rotating-cube-spin 12s linear infinite;
}
.fx-3d-rotating-cube:hover { animation-play-state: paused; }
.fx-3d-rotating-cube > * {
position: absolute; inset: 0; display: grid; place-items: center; font-size: 1.6rem;
color: var(--wt-on-primary, #fff); border: 1px solid rgba(255, 255, 255, .35);
background: rgba(var(--wt-primary-rgb), .88);
}
.fx-3d-rotating-cube > :nth-child(1) { transform: translateZ(var(--fx-half)); }
.fx-3d-rotating-cube > :nth-child(2) { transform: rotateY(180deg) translateZ(var(--fx-half)); background: rgba(var(--wt-secondary-rgb), .88); }
.fx-3d-rotating-cube > :nth-child(3) { transform: rotateY(90deg) translateZ(var(--fx-half)); background: rgba(var(--wt-accent-rgb), .88); }
.fx-3d-rotating-cube > :nth-child(4) { transform: rotateY(-90deg) translateZ(var(--fx-half)); background: rgba(var(--wt-secondary-rgb), .88); }
.fx-3d-rotating-cube > :nth-child(5) { transform: rotateX(90deg) translateZ(var(--fx-half)); background: rgba(var(--wt-accent-rgb), .88); }
.fx-3d-rotating-cube > :nth-child(6) { transform: rotateX(-90deg) translateZ(var(--fx-half)); }
@keyframes fx-3d-rotating-cube-spin {
from { transform: perspective(700px) rotateX(-22deg) rotateY(0deg); }
to { transform: perspective(700px) rotateX(-22deg) rotateY(360deg); }
}
@media (prefers-reduced-motion: reduce) {
.fx-3d-rotating-cube, .fx-3d-rotating-cube::before, .fx-3d-rotating-cube::after, .fx-3d-rotating-cube *, .fx-3d-rotating-cube *::before, .fx-3d-rotating-cube *::after { animation: none !important; transition: none !important; }
}3Rotate 3D
A badge or tile that rocks around a diagonal axis with rotate3d() - eye-catching for feature icons. Class: fx-rotate-3d
<div class="d-flex flex-wrap align-items-center justify-content-center gap-3 p-3 h-100" style="min-height:200px">
<div class="fx-rotate-3d rounded-4 text-bg-primary d-grid fs-1 shadow" style="width:96px;height:96px;place-items:center">
<i class="bi bi-layers-fill" aria-hidden="true"></i>
</div>
<div class="fx-rotate-3d rounded-4 border d-grid fs-1 text-primary" style="width:96px;height:96px;place-items:center;animation-delay:-2s">
<i class="bi bi-stars" aria-hidden="true"></i>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-rotate-3d { animation: fx-rotate-3d-rock 4s ease-in-out infinite; }
.fx-rotate-3d:hover { animation-play-state: paused; }
@keyframes fx-rotate-3d-rock {
0%, 100% { transform: perspective(600px) rotate3d(1, 1, 0, 0deg); }
50% { transform: perspective(600px) rotate3d(1, 1, 0, 32deg); }
}
@media (prefers-reduced-motion: reduce) {
.fx-rotate-3d, .fx-rotate-3d::before, .fx-rotate-3d::after, .fx-rotate-3d *, .fx-rotate-3d *::before, .fx-rotate-3d *::after { animation: none !important; transition: none !important; }
}