Modern CSS Transform Effects
2D and 3D transforms - tilts, cubes, perspective and chained motion - driven only by hover and keyframes.
13D 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="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<div class="fx-3d-rotating-cube" aria-hidden="true">
<div>
<i class="bi bi-box-fill" 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; }
}2Translate 3D
Stacked layers separate along the Z axis on hover, like an exploded view of your interface. Class: fx-translate-3d
Hover me
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<div class="fx-translate-3d" style="width:150px;height:110px">
<div class="absolute top-0 start-0 w-full h-full rounded-lg border bg-surface-3">
</div>
<div class="absolute top-0 start-0 w-full h-full rounded-lg" style="background:rgba(var(--wt-secondary-rgb), .45)">
</div>
<div class="absolute top-0 start-0 w-full h-full rounded-lg" style="background:rgba(var(--wt-primary-rgb), .55)">
</div>
<div class="absolute top-0 start-0 w-full h-full rounded-lg grid text-white font-semibold" style="place-items:center">
Hover me
</div>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-translate-3d { position: relative; transform-style: preserve-3d; transform: perspective(800px); transition: transform .6s cubic-bezier(.2, .8, .2, 1); }
.fx-translate-3d > * { transition: transform .6s cubic-bezier(.2, .8, .2, 1); }
.fx-translate-3d:hover { transform: perspective(800px) rotateX(48deg) rotateZ(-28deg); }
.fx-translate-3d:hover > :nth-child(1) { transform: translate3d(0, 0, 0); }
.fx-translate-3d:hover > :nth-child(2) { transform: translate3d(0, 0, 30px); }
.fx-translate-3d:hover > :nth-child(3) { transform: translate3d(0, 0, 60px); }
.fx-translate-3d:hover > :nth-child(4) { transform: translate3d(0, 0, 90px); }
@media (prefers-reduced-motion: reduce) {
.fx-translate-3d, .fx-translate-3d::before, .fx-translate-3d::after, .fx-translate-3d *, .fx-translate-3d *::before, .fx-translate-3d *::after { animation: none !important; transition: none !important; }
}3Transform Chain
Chains translate, rotate and scale as separate properties with staggered timing for a lively hover. Class: fx-transform-chain
Update notes for clients
$67.55
<div class="flex flex-wrap items-center justify-center gap-4 p-4 h-full" style="min-height:200px">
<div class="card border-0 shadow-sm fx-transform-chain" style="width:12rem">
<img src="../assets/img/photo-4.svg" alt="Card cover" class="card-img-top object-cover" style="height:100px">
<div class="card-body py-2">
<h3 class="h6 mb-0">Update notes for clients</h3>
<p class="small text-muted mb-0">$67.55</p>
</div>
</div>
</div>
/* CSS (already in assets/css/effects.css) */
.fx-transform-chain { transition: translate .3s ease, rotate .5s cubic-bezier(.34, 1.56, .64, 1) .08s, scale .4s ease .16s; }
.fx-transform-chain:hover, .fx-transform-chain:focus-visible { translate: 0 -8px; rotate: -3deg; scale: 1.06; }
@supports not (translate: 0) {
.fx-transform-chain { transition: transform .4s cubic-bezier(.34, 1.56, .64, 1); }
.fx-transform-chain:hover, .fx-transform-chain:focus-visible { transform: translateY(-8px) rotate(-3deg) scale(1.06); }
}
@media (prefers-reduced-motion: reduce) {
.fx-transform-chain, .fx-transform-chain::before, .fx-transform-chain::after, .fx-transform-chain *, .fx-transform-chain *::before, .fx-transform-chain *::after { animation: none !important; transition: none !important; }
}