CSS Transitions Demo

A focused reference for the standard CSS transition feature set: properties, duration, timing functions, delay, multi-property shorthand, triggers (:hover, :focus, :active, class toggles), transform variety, and interruption/reversal behavior.

1. Transitionable Properties

The same hover trigger, applied to a different CSS property each time. Hover any card.

opacity

Fades out on hover.

A
transition: opacity .35s ease;

background-color

Fill color crossfades.

A
transition: background-color .35s ease;

color

Text/border color crossfades.

Aa
transition: color .35s ease;

transform

Lifts and scales up — the example named in the original request.

A
transition: transform .35s ease; transform: translateY(-10px);

border-radius

Square morphs into a circle.

A
transition: border-radius .35s ease;

box-shadow

Shadow grows and tints.

A
transition: box-shadow .35s ease;

width / height

Two length properties animating together.

A
transition: width .35s ease, height .35s ease;

border-color / border-width

Two properties on one selector, same duration.

A
transition: border-color .35s ease, border-width .35s ease;

2. Transition Duration

Identical transform, three different transition-duration values. Hover each box.

150ms — snappy

.15s
transition: transform .15s ease;

500ms — default-ish

.5s
transition: transform .5s ease;

1500ms — slow

1.5s
transition: transform 1.5s ease;

3. Timing Functions

Same 120px translate, same .9s duration, seven curves. Hover each track to compare acceleration.

linear
•
ease
•
ease-in
•
ease-out
•
ease-in-out
•
cubic-bezier()
•
steps(6, end)
•

4. Transition Delay & Stagger

transition-delay offsets when a property's animation starts. A per-child delay ramp produces a cascade effect.

Staggered cascade

Hover the row — each box's transform starts 60ms after the previous one.

1
2
3
4
5
6
transition: transform .4s ease calc(var(--i) * .06s);

Single delayed transition

Nothing happens for 500ms after hover starts, then it animates.

Wait…
transition: transform .4s ease .5s;

5. Multi-Property Shorthand

One rule, four properties, each with its own duration/easing/delay — the general form of the transition shorthand.

Combined transform + color + shadow + radius

Hover
transition: transform .3s ease, background-color .8s ease, box-shadow .8s ease .1s, border-radius 1.1s ease-in-out;

6. Triggers: hover, focus, active

Transitions fire on any computed-style change, not just :hover — including keyboard focus and pointer-down.

:hover

Pointer over the element.

Hover
.el:hover { transform: translateY(-8px); }

:focus

Click or Tab into the input.

.el:focus { transform: scale(1.03); }

:active

Press and hold the mouse button down.

Press
.el:active { transform: scale(.92); }

7. Transform Variety

Every transform function type, transitioned. All use the same underlying transform property.

translate()

Move
transform: translate(20px, -12px);

scale()

Scale
transform: scale(1.3, .8);

rotate()

Rotate
transform: rotate(35deg);

skew()

Skew
transform: skew(-12deg, 4deg);

rotateX/Y + translateZ

3D
transform: rotateY(28deg) rotateX(14deg) translateZ(10px);

transform-origin

Pivot is offset to 15% 85% instead of center.

Pivot
transform-origin: 15% 85%; transform: rotate(-25deg) scale(1.1);

8. Interruption & Reversal

Move the pointer on and off quickly. A spec-correct transition reverses smoothly from whatever position it is currently at — it never jumps back to the start before reversing.

2 second transition — try tapping hover repeatedly

Go
transition: transform 2s ease;

9. JS-Driven Class Toggle

Transitions aren't limited to pseudo-classes — any computed-style change triggers them, including a class added/removed by JavaScript.

Click →
el.classList.toggle('is-active'); /* .js-toggle.is-active { transform: rotate(180deg) scale(1.15); } */

10. Transform Function Coverage

Every individual CSS transform function, hover-triggered in isolation, auditing exactly which ones Fold's matrix decomposition (cssTransform.service.ts's decomposeMatrix16) can faithfully reproduce in the 3D scene. The browser always resolves any transform list to a single matrix()/matrix3d() before Fold ever sees it, so every card below is really testing the same decomposition step — just fed a different resulting matrix shape. Legend: supported edge case not supported

matrix() supported*

*Exact when the matrix is a pure translate+rotate+scale composite — this example is rotate(15deg) translate(20px,-10px) pre-multiplied. A hand-authored matrix with shear or a projective row inherits the skew/perspective gaps below.

A
transform: matrix(0.966, 0.259, -0.259, 0.966, 20, -10);

matrix3d() supported*

*Same caveat as matrix() above — this example is rotateY(25deg) translateZ(15px), no shear/perspective.

3D
transform: matrix3d(0.9063,0,-0.4226,0, 0,1,0,0, 0.4226,0,0.9063,0, 0,0,15,1);

perspective() not supported

The projective row of the resolved matrix is never read by decomposeMatrix16 at all — no depth-foreshortening is applied, silently. Expect this to look identical to a plain rotateY(40deg).

3D
transform: perspective(400px) rotateY(40deg);

rotate() supported

2-D rotation (Z-axis) — exact.

A
transform: rotate(45deg);

rotate3d() supported

Fixed: the tween now interpolates rotation via quaternion slerp (FoldGroupElement.tsx) instead of independent per-axis Euler lerp, matching how the CSS spec itself resolves a multi-axis rotation transition — correct at every frame, including through gimbal-lock-adjacent orientations. (Previously: per-axis lerp only coincidentally matched slerp for single-axis rotation; combined-axis frames could visibly diverge mid-transition.)

3D
transform: rotate3d(1, 1, 0, 45deg);

rotateX() supported

Single-axis 3-D rotation — exact.

X
transform: rotateX(45deg);

rotateY() supported

Single-axis 3-D rotation — exact.

Y
transform: rotateY(45deg);

rotateZ() supported

Single-axis rotation, same as rotate() — exact.

Z
transform: rotateZ(45deg);

translate() supported

Pure translation is read directly off the matrix's translation column — always exact.

A
transform: translate(30px, -15px);

translate3d() supported

Same as translate() with an explicit Z — exact.

A
transform: translate3d(20px, -10px, 15px);

scale() supported

Positive uniform/non-uniform scale — exact.

A
transform: scale(1.4);

scale() negative supported

Fixed: decomposition now checks the transform matrix's determinant sign and folds it into scaleX (same convention as Three.js's own Matrix4.decompose()) instead of losing it to sqrt(). Reconstructs the exact same final matrix — e.g. this decomposes to scaleX=-1 + rotateZ=180°, which recomposes to the identical mirror. (Previously: sign was silently dropped, misread as an implicit ~180° rotation — visibly wrong for asymmetric content like text.)

Aa
transform: scale(-1, 1); /* mirror, not a flip */

scale3d() supported

Per-axis scale including Z — exact.

A
transform: scale3d(1.3, .7, 1);

scaleX() supported

Single-axis scale — exact.

X
transform: scaleX(1.6);

scaleY() supported

Single-axis scale — exact.

Y
transform: scaleY(1.6);

scaleZ() supported

Combined with rotateX so a pure Z-scale has a visible effect on a flat plane — exact.

Z
transform: rotateX(50deg) scaleZ(2.2);

skew() not supported

ParsedCssTransform has no shear field at all. The decomposition doesn't ignore skew — it misreads it: a shear column gets measured as if it were rotation + scale, so this renders as a rotated/stretched rectangle rather than a slanted parallelogram.

A
transform: skew(-15deg, 8deg);

skewX() not supported

Same underlying gap as skew() above.

X
transform: skewX(20deg);

skewY() not supported

Same underlying gap as skew() above.

Y
transform: skewY(15deg);