
Horizontal spatial navigation — panels spread along the X axis.
Each section cancels its page Y position and offsets along X (and Z).
foldpage-el
When Fold processes an element it adds the foldpage-el class to it.
This class is the hook for all CSS-driven 3D positioning. Any rule that targets
.myclass.foldpage-el is invisible to the browser's layout engine but
drives the element's position, rotation, and scale in the Three.js scene.
The approach keeps 3D authoring entirely in CSS — no JavaScript, no separate
coordinate system to learn. You write familiar transform rules,
and Fold extracts and applies them as group transforms in world space.
/* Only active when Fold is running */ .hero.foldpage-el { transform: rotateY(-30deg) translateX(600px); } /* Children can add their own depth pops */ .hero h1.foldpage-el { transform: translateZ(28px); } .hero img.foldpage-el { transform: translateZ(48px); }
Positions the whole section in 3D — rotates and translates the group origin.
Children add a local translateZ to float in front of their parent plane.
Fold reads computed styles at render time — plain CSS, no script.
Rules with .foldpage-el are ignored by the browser until Fold adds that class.
panel-far-left —
far left at rotateY(42deg) translateX(-860px), with
translateY(-1360px) applied first to cancel its page Y position.
Styling in 3D
Fold reads each element's computed style at render time, so any
CSS you write — colors, gradients, borders, opacity, box-shadow — is honored in
the 3D scene. Background planes use MeshStandardMaterial; colors are
converted from CSS rgba() to Three.js Color.
An element's background color, gradient, or image becomes the texture of a rounded-rectangle plane mesh. Border radius is preserved. Box shadows render as separate offset planes at a slight Z depth behind the main mesh.
/* All of these render faithfully in 3D */ .card { background: rgba(12, 8, 30, 0.9); border: 1px solid rgba(160, 100, 240, 0.3); border-radius: 16px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); }
Text color, text-shadow, opacity, and
letter-spacing all flow through to Troika. Multiple shadow layers
render as stacked text meshes slightly behind the main text, using their CSS
color and blur values.
getComputedStyle() returns after your media queries resolve.
Switching themes just re-renders with the new computed values.
Horizontal Layout Technique
HTML elements flow vertically — each section sits lower on the page than the
one before it. In a standard Fold layout that natural Y offset becomes part of
the element's 3D world position. For a horizontal layout, where
all panels should sit at roughly the same height, you need to cancel that offset
with a negative translateY.
The key rule: put translateY(-offset) before any rotation in
the transform chain. CSS transforms apply left-to-right, each one changing the
local coordinate frame. If translateY comes first — before
rotateY — it operates in the original world frame and cancels the
element's page-scroll Y position exactly. If it comes after a rotation, the Y
axis is no longer aligned with the world Y axis and the compensation will be off.
/* ✓ translateY FIRST — works in world frame */ .panel.foldpage-el { transform: translateY(-1880px) /* cancel page Y, world frame */ rotateY(20deg) /* now rotate local frame */ translateX(-440px) /* move in rotated local X */ translateZ(-200px); /* depth in rotated local Z */ } /* ✗ translateY AFTER rotation — compensates wrong axis */ .panel.foldpage-el { transform: rotateY(20deg) translateY(-1880px) /* now tilted — wrong! */ translateX(-440px); }
The offset is the vertical center of the element in the page, measured in CSS pixels from the top of the document. A rough estimate:
For this demo, with a min-height: 115vh home section (~1050px at a
900px viewport) and panels averaging ~520px slot height (content + 100px margin):
These estimates are calculated for a ~900px tall viewport. At different viewport
heights, min-height: 115vh produces different absolute pixel values,
shifting all subsequent panels. If the horizontal alignment looks off, adjust the
translateY values in increments of a few hundred pixels.
An alternative approach: set a fixed pixel height on the home section
(e.g. height: 900px) rather than min-height: 115vh.
This makes the Y offsets viewport-independent and easier to calculate.
After the Y compensation, the remaining transforms position the panel in the
horizontal plane. The outer panels in this demo use a stronger
rotateY and are pushed slightly forward in Z
(translateZ(160px)) so they arc toward the viewer — creating the
impression of a curved horizontal stage rather than a flat row.
The inner panels are pushed slightly back
(translateZ(-200px)) to reinforce the curve.
panel-near-right:
translateY(-2540px) rotateY(-20deg) translateX(440px) translateZ(-200px).
It is the tallest view, so the camera pans through it before moving to the far right.
Template
A minimal horizontal views layout needs three things: a tall home section, panels with predictable heights, and Y compensation on each panel.
<header class="home" data-foldpage-view="0"> <h1>Page Title</h1> <nav> <a href="#left" data-foldpage-view-link="#left">Left</a> <a href="#right" data-foldpage-view-link="#right">Right</a> </nav> </header> <section id="left" class="panel-left" data-foldpage-view>...</section> <section id="right" class="panel-right" data-foldpage-view>...</section> <style> .home { height: 900px; } /* fixed height for predictable Y offsets */ .panel-left.foldpage-el { transform: translateY(-1100px) /* 900 + 100 margin + 100 half-panel */ rotateY(40deg) translateX(-800px) translateZ(150px); } .panel-right.foldpage-el { transform: translateY(-1700px) /* 900 + 100 + 200 panel + 100 + 100 half-panel */ rotateY(-40deg) translateX(800px) translateZ(150px); } </style>
See the demos index for the full set of view layout patterns, or explore Views & Navigation for the radial layout where panels face a central origin from all directions.
panel-far-right —
translateY(-3300px) rotateY(-42deg) translateX(860px) translateZ(160px).
The Y compensation brought it from ~3300px down the page to the same height as the origin.