Baseline — no rotation
Control case. Offset-z with zero rotation always pushes straight toward the viewer — world space and local space are identical here.
Every card below rotates and/or scales an element and then pushes it with an offset
(data-foldpage-offset-* or CSS translateZ/translate*).
Each card names the expected result before you look — compare what you see
against the description. Best viewed at desktop width; rotate your view (drag) to see
the 3D relationships clearly.
Plain <div> boxes, positioned purely with data-foldpage-rotate-* and data-foldpage-offset-*. No CSS transform involved.
Control case. Offset-z with zero rotation always pushes straight toward the viewer — world space and local space are identical here.
Tilted right edge back. The push should slide the box diagonally toward its own (rotated) front face — down-and-right of straight-out, not straight out.
Mirror of the previous card. Same push magnitude, opposite tilt — should look like an exact mirror image, confirming the push direction always tracks the element's own rotation.
Tipped forward around X instead of Y. The push should slide up/down along the tilt (not sideways) — the reorientation happens on whichever axis is actually rotated.
Diagonal tilt on two axes at once. The push follows the single combined face normal — a diagonal slide, not two separate movements.
Offsetting along local X on a Y-rotated element. Because rotateY tilts the X/Z plane, this push should visibly move both sideways and toward/away from the camera — not purely sideways.
A roll (rotate-z) tilts the X/Y plane. The vertical push should come out slightly diagonal, following the rolled local "up" direction instead of straight up.
data-foldpage-rotate-origin moves the rotation pivot away from the element's center. The offset is applied after that pivoted rotation, so it should still track the element's own (now off-center-rotated) orientation.
Rotates in place around its own center. Included only as a visual reference for the next card.
Same rotation, but pivoting around the box's left edge instead of its center — swings like a door on a hinge rather than spinning in place.
Combines the previous card with a Z push. The push should follow the face normal of the swung box (post-pivot-rotation), not the box's original unrotated orientation.
Same hinge point, rotated the other way — the box should swing toward the viewer instead of away, and the offset push should follow suit.
Scale wraps rotation which wraps the local offset (see FoldGroupElement's
group structure) — so a scaled element's offset distance scales with it too.
offset-z="40" on a scale="1.5" element
renders as if it were pushed ~60 world-units — 1.5× the authored value.
No offset yet — just confirms the box renders 1.5× larger and still rotates correctly around its own (now larger) center.
Same as above plus a Z push. Compare the apparent push distance against the 1.0-scale card in Section 1 with the same offset-z="60" value (30° card) — this one should look similarly far despite authoring only 40, because the 1.5× scale amplifies it.
Same authored offset as the card to the left, but scaled down. The push should look noticeably shorter (~24 effective units) — the amplification runs both directions.
Scaling from the left edge instead of the center — the box should grow/rotate outward from that edge rather than swelling symmetrically.
A parent box carries the rotation; a small child badge sits inside it. Three.js scene-graph nesting means the child inherits the parent's rotation automatically — these cards test whether the child's own offset/rotation additionally composes correctly on top of that.
Child badge has no transform of its own — it should simply sit in place on the parent's face and tilt with the parent, at zero relative offset.
The child's offset-z should push it further along the parent's already-rotated face normal — the same local-frame composition as Section 1, just one nesting level deeper.
Rotations should add up: the child tilts by its own 20° on top of the parent's 25°, for a combined ~45° lean relative to the page — not 20° measured from flat.
Parent uses the Section 2 "door hinge" pivot; the child has its own small offset. The child should ride along with the parent's swing and still push along its own local axis on top of that.
transform + Hover TransitionsSame composition, driven by CSS transform (parsed from getComputedStyle()) instead of data attributes — including the live hover transition path. Hover each card.
On hover, the box should lift toward the viewer along its own tilt — sliding diagonally, matching the data-attribute version in Section 1.
Mirror of the previous card — should hover in the mirrored diagonal direction, at the same magnitude.
rotateY() translateZ()Authored with rotate listed first. Should look identical to the card on the right.
translateZ() rotateY()
Authored with translate listed first — in raw CSS this ordering has different
math meaning (translate happens in the pre-rotation frame). Fold can't see that
distinction: getComputedStyle() always resolves transform
to a single matrix before Fold reads it, so authoring order is invisible to the
engine. Both cards render identically here by design.
data-foldpage-model-src hosts go through the exact same FoldGroupElement composition as any other element — these confirm the fix holds for 3D geometry, not just flat CSS planes.
Rotated 35° and pushed forward. The model should slide toward the camera along the direction it's already facing, matching the flat-box cards in Section 1.
Same scale-composition consequence from Section 3, on a real model: scaled 1.6×, the authored offset-z="30" should appear to push roughly 1.6× as far as it would unscaled.
Same "door hinge" pivot from Section 2, applied to a model instead of a flat box — it should swing around its own left edge rather than spinning in place.