Skip to content

View Layouts ​

Add a layout attribute to a container and its views are arranged in 3D — no manual positioning or CSS transforms needed.

The layout runs at Fold.init(). It sets each panel's position and transform as inline styles and, for most layouts, adds a scroll spacer so there's enough scroll distance to visit every view.

Supported layout types: ring, grid, random, helix, scatter.


Quick Start ​

html
<div class="foldpage"
     data-foldpage-view-layout="ring">

  <header data-foldpage-view="0">Center view</header>  <!-- head -->
  <section data-foldpage-view>Panel 1</section>
  <section data-foldpage-view>Panel 2</section>
  <section data-foldpage-view>Panel 3</section>

</div>

<script>
  Fold.init({ foldpage: true });
</script>

No viewLayout config is needed in Fold.init — data attributes on the container drive the layout automatically.

Auto Views — let Fold discover view elements automatically:

html
<div class="foldpage"
     data-foldpage-view-layout="ring"
     data-foldpage-view-auto=".card">

  <header data-foldpage-view="0">Center view</header>
  <div class="card">Panel 1</div>   <!-- auto-tagged as views at runtime -->
  <div class="card">Panel 2</div>
  <div class="card">Panel 3</div>

</div>

See Auto Views for full details including heading-based discovery.

Framework components — Angular, React, Vue, and Web Component elements can be used as view panels directly. Fold discovers them by data-foldpage-view regardless of tag name:

html
<!-- Angular SPA example -->
<div class="foldpage"
     data-foldpage-view-layout="ring"
     data-foldpage-view-layout-direction="row-reverse">

  <app-hero    data-foldpage-view="0" />   <!-- head / center view -->
  <app-features data-foldpage-view />
  <app-demos    data-foldpage-view />
  <app-contact  data-foldpage-view />

</div>

Each <app-*> element gets no 3D mesh of its own; the layout positions it and its inner HTML renders normally.


Generic Attributes ​

These attributes apply to all layout types.

Container attributes (on the data-foldpage-view-layout element):

AttributeTypeDefaultDescription
data-foldpage-view-layout"ring" / "grid" / "random" / "helix" / "scatter"—Layout type. Required.
data-foldpage-view-layout-absolutebooleantype-specific¹Whether panels are absolutely positioned. See Absolute Positioning.
data-foldpage-view-autoCSS selector—Automatically discover view elements. See Auto Views.
data-foldpage-view-layout-countintegertype-specific²Ring: total slots. Grid: columns per row (0 = auto). Random/Scatter: PRNG seed (0 = unseeded).
data-foldpage-view-layout-spacingnumber1.0Uniform spacing multiplier. Fallback for -x, -y, -z overrides. Scatter: also controls the per-panel Z step.
data-foldpage-view-layout-spacing-xnumber→ spacingX-axis spacing override.
data-foldpage-view-layout-spacing-ynumber→ spacingY-axis spacing override. Random: direction bias — positive = scatter only below head; negative = only above; 0 = symmetric ±Y.
data-foldpage-view-layout-spacing-znumber→ spacingZ-axis spacing override. Negative reverses Z direction. Random: direction bias — positive = scatter only behind; negative = only in front; 0 = symmetric ±Z. Scatter: per-panel Z step override (magnitude only, no direction bias — always ordered front-to-back).
data-foldpage-view-layout-offset-xpx0Ring/Grid/Helix: X shift per successive step. Random/Scatter: global X bias added after scatter.
data-foldpage-view-layout-offset-ypx0Ring/Grid/Helix: Y shift per step. Positive = lower on screen. Random/Scatter: global Y bias.
data-foldpage-view-layout-offset-zpx0Ring/Grid/Helix: Z stagger per step. Random/Scatter: global Z bias.
data-foldpage-view-layout-rotationdegrees0Base rotation value. Fallback for -x/y/z overrides. Meaning varies per layout — see Rotation. Scatter: a boolean toggle instead (any nonzero value), not a magnitude.
data-foldpage-view-layout-rotation-xdegrees→ rotationX rotation (pitch) override. Not used by scatter.
data-foldpage-view-layout-rotation-ydegrees→ rotationY rotation (yaw) override. Not used by scatter.
data-foldpage-view-layout-rotation-zdegrees→ rotationZ rotation (roll) override. Not used by scatter.
data-foldpage-view-layout-align"center" / "start""center"Horizontal alignment of panels. Partial last rows follow this setting.
data-foldpage-view-layout-headboolean"true"See Head View.
data-foldpage-view-layout-scroll-distancepx1000Scroll distance per view transition. Only applies when absolute=true.
data-foldpage-view-layout-toppxautoPanel top offset. Auto = head view bottom + 40 px (or head view top for random/scatter). Only applies when absolute=true.

¹ Absolute positioning defaults: ring = true, grid = true, random = true, helix = false, scatter = true.
² Ring: panel count. Grid: ceil(sqrt(panelCount)). Random/Scatter: PRNG seed.

Per-panel view navigation attributes (on data-foldpage-view elements):

These change the camera's angle when it flies to a view, not the panel's own transform.

AttributeTypeDefaultDescription
data-foldpage-rotate-xdegrees0Camera X tilt when navigating to this view.
data-foldpage-rotate-ydegrees0Camera Y rotation when navigating to this view. Use 180 to approach from inside a ring (outward-facing).
data-foldpage-rotate-zdegrees0Camera Z roll when navigating to this view.

Head View ​

The element with data-foldpage-view="0" (or the first data-foldpage-view found) is the head view.

data-foldpage-view-layout-headRingGridHelix
"true" (default)Head sits at center, position unchanged. Panels arranged around it.Head stays in normal document flow at Z=0. Panels start one layer back.Head is untouched (step 0). Each panel receives step × rotation.
"false"Head included as slot 0 in the ring.Head is the first grid panel at (col=0, layer=0).Head is panel at step 1.

View Ordering ​

data-foldpage-view="<n>" sets explicit slot order for panels. Panels with numeric values are sorted by that number and assigned first; unnumbered panels follow in DOM order.

html
<section data-foldpage-view="2">Appears third</section>
<section data-foldpage-view="1">Appears second</section>
<section data-foldpage-view>Appears last (DOM order)</section>

Auto Views ​

data-foldpage-view-auto instructs Fold to automatically discover view elements within a layout container and assign data-foldpage-view="" to each at runtime. No markup changes are needed on individual panels.

The discovery pass runs before the layout engine, so the layout sees the same view structure as if the attributes were hand-authored.

Selector Mode ​

Any valid querySelectorAll string tags every matching element within the container:

html
<div class="foldpage"
     data-foldpage-view-layout="ring"
     data-foldpage-view-auto=".card">

  <header data-foldpage-view="0">Head</header>
  <div class="card">Card 1</div>  <!-- auto-tagged as data-foldpage-view="" -->
  <div class="card">Card 2</div>
  <div class="card">Card 3</div>

</div>

Comma-separated and compound selectors work:

html
data-foldpage-view-auto=".card"
data-foldpage-view-auto="section.panel"
data-foldpage-view-auto="[data-panel]"

Heading Mode ​

When the selector is a single heading tag (h1–h6), Fold uses ancestor-walk mode: it finds each heading's highest ancestor within the container that contains exactly that one heading, and tags that ancestor as the view. This naturally maps to section-structured markup.

html
<div class="foldpage"
     data-foldpage-view-layout="helix"
     data-foldpage-view-layout-rotation="30"
     data-foldpage-view-auto="h2">

  <header data-foldpage-view="0">Head</header>

  <!-- section contains exactly one h2 → section is auto-tagged -->
  <section class="panel">
    <h2>Introduction</h2>
    <p>Content...</p>
  </section>

  <section class="panel">
    <h2>Getting Started</h2>
    <p>Content...</p>
  </section>

</div>

Ancestor walk: starting from the <h2>, Fold walks up the DOM tree. Any ancestor that contains exactly one h2 becomes the candidate. The highest such ancestor (closest to the layout container) is tagged as the view.

Flat structure / Content Splitting: when multiple headings share the same direct parent (no wrapping sections), Fold automatically injects wrapper <div>s to group each heading with the siblings that follow it:

html
<!-- Source: headings are direct children of the container -->
<div class="foldpage" data-foldpage-view-auto="h2">
  <p>Intro paragraph — left untouched</p>
  <h2>Section A</h2>
  <p>Content A</p>
  <h2>Section B</h2>
  <p>Content B</p>
</div>

<!-- Runtime result after auto-views runs: -->
<div class="foldpage" data-foldpage-view-auto="h2">
  <p>Intro paragraph — left untouched</p>
  <div data-foldpage-view-auto-wrapper="" data-foldpage-view="">
    <h2>Section A</h2>
    <p>Content A</p>
  </div>
  <div data-foldpage-view-auto-wrapper="" data-foldpage-view="">
    <h2>Section B</h2>
    <p>Content B</p>
  </div>
</div>

Content before the first heading is left in place. Injected wrappers are removed and their content is unwrapped when Fold.destroy() is called.

Merge Approach ​

Elements that already carry data-foldpage-view are never overwritten — auto-views and hand-authored markup coexist safely. The head view (data-foldpage-view="0") should always be manually marked and is unaffected by the discovery pass.

html
<div data-foldpage-view-layout="ring"
     data-foldpage-view-auto=".card">

  <!-- Manually pinned at slot 3 — skipped by auto-views -->
  <div class="card" data-foldpage-view="3">Pinned</div>

  <!-- Auto-discovered — no existing attribute -->
  <div class="card">Auto A</div>
  <div class="card">Auto B</div>

</div>

JS API ​

js
Fold.init({
  viewLayout: {
    type:     'ring',
    viewAuto: '.card',  // global default selector
  }
});

The data-foldpage-view-auto attribute on the container takes precedence over the viewAuto JS option when both are set.


Absolute Positioning ​

Layout types differ in how they position panels:

LayoutDefaultEffect when trueEffect when false
ringtrueposition:absolute + scroll spacerFlow-positioned; elements stay in document flow
gridtrueposition:absolute + scroll spacerFlow-positioned
randomtrueposition:absolute + scroll spacerFlow-positioned
helixfalseposition:absolute + scroll spacerOnly CSS transform applied; elements in flow
scattertrueposition:absolute + scroll spacerFlow-positioned

When absolute=true, panels are absolutely positioned at a shared top offset and a scroll spacer is added so there's enough scroll distance to visit every view.

When absolute=false, only CSS transforms are applied (rotation and translation). Elements stay in their natural vertical order in the document — page height is determined by content. This is the helix default.

Overriding the Default ​

Data attribute (per-container):

html
<!-- Helix with absolute positioning (opt-in) -->
<div data-foldpage-view-layout="helix"
     data-foldpage-view-layout-absolute="true"
     data-foldpage-view-layout-rotation="40">...</div>

<!-- Ring without absolute positioning (opt-out) -->
<div data-foldpage-view-layout="ring"
     data-foldpage-view-layout-absolute="false">...</div>

JS API (global override):

js
Fold.init({
  viewLayout: {
    type:     'ring',
    absolute: false,   // override for all containers
  }
});

Priority: JS absolute option > data-foldpage-view-layout-absolute attribute > layout-type default.

When to use absolute=false

Use absolute=false when you want a layout type's 3D rotation/offset effects but prefer the page scroll height to be determined by actual content (not a synthetic spacer). The helix layout is designed around this mode — panels visually spiral in 3D while staying in normal document flow.


Ring Layout ​

Panels are placed at evenly-spaced angles around the head view in a circle (or partial arc). Each panel is auto-rotated to face inward by default.

How It Works ​

  • The head element's position is unchanged (when head="true").
  • All other data-foldpage-view descendants become ring panels, assigned to slots in order.
  • Ring radius = 700 px × (spacing + panels × 0.05). The ring grows automatically as panels are added.
  • Angles are measured clockwise from North (−Z axis): 0° = North, 90° = East, 180° = South, 270° = West.
  • Each panel's auto-rotation faces toward the origin (inward).

Ring Attribute Reference ​

Container attributes (in addition to generic attributes):

AttributeTypeDefaultDescription
data-foldpage-view-layout-startdegrees0Angle of the first slot. 0 = North, 90 = East, 180 = South.
data-foldpage-view-layout-direction"row" / "row-reverse""row-reverse"Slot stepping direction viewed from above ("row" = clockwise, "row-reverse" = counter-clockwise). Shares grid's vocabulary — ring has no "layer" concept, so only these two apply.
data-foldpage-view-layout-emptycomma-separated indices—Slot indices to skip, e.g. "5,6,7". Enables arc configurations.

JS API ​

js
Fold.init({
  viewLayout: {
    type:           'ring',
    spacing:        1.5,     // ring radius multiplier
    count:          6,       // total slots
    start:          0,               // first slot angle (degrees)
    direction:      'row-reverse',   // 'row' | 'row-reverse'
    empty:          [5],     // leave slot 5 empty
    scrollDistance: 1000,
    top:            400,     // px — omit for auto
    absolute:       true,    // default for ring
  }
});

// Live update — patches transforms in place
Fold.set({ viewLayout: { spacing: 0.8 } });

Facing: Inward vs Outward ​

Panels face inward toward the center of the ring by default.

To make the camera approach from inside the ring, add data-foldpage-rotate-y="180" to the panel:

html
<!-- Camera approaches from inside (outward-facing view) -->
<section data-foldpage-view data-foldpage-rotate-y="180">...</section>

data-foldpage-rotate-y only changes the camera's approach; the panel's own position and facing come from the layout.

Arc Configuration ​

Leave slots empty to form a half-circle, quarter-circle, or any partial arc:

html
<div class="foldpage"
     data-foldpage-view-layout="ring"
     data-foldpage-view-layout-count="8"
     data-foldpage-view-layout-start="90"
     data-foldpage-view-layout-direction="row"
     data-foldpage-view-layout-empty="5,6,7">
  <!-- 5 panels fill slots 0–4: East → SE → S → SW → West -->
</div>

Slot math: step = 360° / count. With count=8 (step = 45°) and start=90°, slots step East → SE → S → SW → West → NW → N → NE. Marking slots 5–7 empty leaves a front-facing arc from East to West.


Grid Layout ​

Panels are placed in a rectangular grid. Columns spread along X; depth layers spread along Z. An optional Y offset per layer creates a staircase effect.

How It Works ​

  • Panels are assigned to [column, layer] positions in order, following the direction fill convention.
  • Column gap = 800 px × (spacing-x or spacing). Layer gap = 700 px × (spacing-z or spacing).
  • Default count = ceil(sqrt(panelCount)) — a roughly square grid:
PanelsAuto columnsShape
422 × 2
633 × 2
933 × 3
1244 × 3
  • Panels face the camera by default (no auto-rotation). Use data-foldpage-rotate-y for per-panel camera orientation.
  • Partial last rows follow the align setting.
  • When head="true" (default): the head view stays in normal flow at Z=0; panels start at Z = −layerGap. When head="false": all views are grid panels.

Direction ​

Controls the fill axis and which end of the Z axis is first:

ValueFill axisZ startsFirst panel
"row" (default)X (columns)front (Z=0)front-left
"row-reverse"X (columns)backback-left → toward viewer
"layer"Z (depth)front (Z=0)front-left, fills depth-first
"layer-reverse"Z (depth)backback-left, fills toward viewer

Left/right is controlled by align.

Grid Attribute Reference ​

All generic attributes apply. No grid-specific additions beyond direction (which takes different values from ring).

html
<div class="foldpage"
     data-foldpage-view-layout="grid"
     data-foldpage-view-layout-count="3"
     data-foldpage-view-layout-spacing="1.2"
     data-foldpage-view-layout-spacing-z="0.8"
     data-foldpage-view-layout-offset-y="60"
     data-foldpage-view-layout-direction="row">

  <header data-foldpage-view="0">Center</header>
  <section data-foldpage-view>P1</section>
  <!-- ... 8 more panels for a 3×3 grid -->

</div>

JS API ​

js
Fold.init({
  viewLayout: {
    type:           'grid',
    count:          3,        // columns per row
    spacing:        1.0,      // base multiplier for both X and Z gaps
    spacingX:       1.2,      // column gap override (800px × 1.2 = 960px)
    spacingZ:       0.8,      // layer gap override (700px × 0.8 = 560px)
    offsetY:        60,       // staircase: each layer 60px lower on screen
    align:          'center',
    direction:      'row',
    head:           true,
    scrollDistance: 1000,
    absolute:       true,     // default for grid
  }
});

Fold.set({ viewLayout: { spacingZ: 1.2 } });

Staircase Effect ​

Use offset-y to step each depth layer down the screen:

html
<div class="foldpage"
     data-foldpage-view-layout="grid"
     data-foldpage-view-layout-count="3"
     data-foldpage-view-layout-offset-y="80">
  <!-- Each layer is 80px lower than the previous -->
</div>

Random Layout ​

Panels are placed at random positions and rotations within configurable scatter bounds. A seeded PRNG produces a stable, reproducible layout on every load when count is set.

How It Works ​

  • Each panel receives a random (X, Y, Z) position and (rotX, rotY, rotZ) rotation independently.
  • Scatter is centered on the origin (head view position) by default.
  • spacing controls the overall scatter radius (700 px × spacing).
  • spacing-x/y/z override per-axis scatter independently. spacing-y defaults to spacing × 0.5.
  • count is repurposed as the PRNG seed. count = 0 (default) uses Math.random() for a different layout each load.

Random Attribute Reference ​

AttributeTypeDefaultDescription
data-foldpage-view-layout-countinteger0PRNG seed. 0 = unseeded. Any positive integer gives a fixed layout.
data-foldpage-view-layout-spacingnumber1.0Base scatter radius (700 px × spacing).
data-foldpage-view-layout-spacing-xnumber→ spacingX scatter override (800 px × spacing-x).
data-foldpage-view-layout-spacing-ynumber0Y scatter + direction bias. > 0 = only below head; < 0 = only above; = 0 = symmetric ±Y (uses spacing × 0.5).
data-foldpage-view-layout-spacing-znumber0Z scatter + direction bias. > 0 = only behind; < 0 = only in front; = 0 = symmetric ±Z.
data-foldpage-view-layout-offset-xpx0Global X bias added to every panel after scatter.
data-foldpage-view-layout-offset-ypx0Global Y bias. Positive = lower on screen.
data-foldpage-view-layout-offset-zpx0Global Z bias. Negative pushes all panels behind the head view.
data-foldpage-view-layout-rotationdegrees0Max rotation magnitude on all axes.
data-foldpage-view-layout-rotation-x/y/zdegrees→ rotationPer-axis max rotation override.

JS API ​

js
Fold.init({
  viewLayout: {
    type:           'random',
    count:          42,       // seed — omit for unseeded
    spacing:        0.4,
    spacingY:       0.6,      // scatter below head only (Y > 0)
    spacingZ:       0.2,
    offsetZ:        -700,     // push all panels 700px behind head
    rotation:       20,
    rotationY:      45,
    scrollDistance: 1000,
    absolute:       true,     // default for random
  }
});

Scatter Layout ​

Panels are placed at random X/Y positions within a bounded area — like the random layout — but Z is deliberately not random. Each panel is pushed back one fixed step further than the last, in panel order, so depth stays predictable even though X/Y don't.

How It Works ​

  • Each panel receives a random (X, Y) position, independently.
  • Z is an ordered step, not random: panel 0 sits at the default depth (Z = 0), panel 1 is one step further back, panel 2 two steps back, and so on.
  • spacing controls both the X/Y scatter extent (800 px × spacing for X, 700 px × spacing for Y) and the per-panel Z step (300 px × spacing).
  • spacing-x / spacing-y / spacing-z override their respective axis independently, same fallback pattern as the other layout types.
  • count is repurposed as the PRNG seed for the X/Y scatter (and rotation, when enabled) — count = 0 (default) uses Math.random() for a different scatter each load; any positive integer reproduces the exact same layout every time.
  • Rotation is off by default. Setting data-foldpage-view-layout-rotation to any value other than "false"/"0"/"" enables a small random in-place tilt on X and Y only (never Z — cards stay flat-facing, just tilted) within a fixed ±25° range. This differs from every other layout type, where rotation is a magnitude — here it's a toggle, because the tilt amount is a single shared constant, not something meant to be dialed per axis.

Scatter Attribute Reference ​

AttributeTypeDefaultDescription
data-foldpage-view-layout-countinteger0PRNG seed. 0 = unseeded. Any positive integer gives a fixed, reproducible layout (position AND rotation).
data-foldpage-view-layout-spacingnumber1.0X/Y scatter extent (800/700 px × spacing) AND per-panel Z step (300 px × spacing).
data-foldpage-view-layout-spacing-xnumber→ spacingX scatter override (800 px × spacing-x).
data-foldpage-view-layout-spacing-ynumber→ spacingY downward scatter override (700 px × spacing-y).
data-foldpage-view-layout-spacing-znumber→ spacingPer-panel Z step override (300 px × spacing-z). Magnitude only — Z is always ordered front-to-back, never direction-biased like random's spacing-z.
data-foldpage-view-layout-offset-xpx0Global X bias added to every panel after scatter.
data-foldpage-view-layout-offset-ypx0Global Y bias. Positive = lower on screen.
data-foldpage-view-layout-offset-zpx0Global Z bias added after each panel's ordered step. Negative pushes all panels further behind the head view.
data-foldpage-view-layout-rotationbooleanoffToggle, not a magnitude. Any value other than "false"/"0"/"" enables a fixed ±25° random X/Y tilt per panel.

JS API ​

js
Fold.init({
  viewLayout: {
    type:           'scatter',
    count:          7,        // seed — omit for unseeded
    spacing:        1.0,      // X/Y extent + Z step multiplier
    spacingX:       1.4,      // wider X scatter only
    rotation:       1,        // any nonzero value enables the ±25° X/Y tilt
    scrollDistance: 1000,
    absolute:       true,     // default for scatter
  }
});

// Live update — patches transforms in place
Fold.set({ viewLayout: { spacing: 0.6, rotation: 0 } });

Helix Layout ​

Panels receive a cumulative CSS transform — each successive panel gets one additional step of rotation and/or translation. Elements stay in normal document flow by default (no position: absolute). The visual spiral is purely CSS transform-based; document structure and scroll height are unaffected.

How It Works ​

  • The head view (step = 0) is untouched — no transform applied.
  • Each panel at index i receives step = i + 1 increments:
    • Panel 1 → rotation × 1
    • Panel 2 → rotation × 2
    • Panel N → rotation × N
  • Transforms are additive on all axes independently.
  • By default (absolute=false), elements remain in normal flow and no scroll spacer is added.

Helix Attribute Reference ​

AttributeTypeDefaultDescription
data-foldpage-view-layout-rotationdegrees0Base Y-axis rotation per step. Fallback when rotation-y is not set.
data-foldpage-view-layout-rotation-xdegrees0Per-step X rotation (pitch).
data-foldpage-view-layout-rotation-ydegrees0Per-step Y rotation (yaw). Overrides rotation for Y when non-zero.
data-foldpage-view-layout-rotation-zdegrees0Per-step Z rotation (roll).
data-foldpage-view-layout-offset-xpx0Per-step X translate.
data-foldpage-view-layout-offset-ypx0Per-step Y translate. Positive = lower on screen.
data-foldpage-view-layout-offset-zpx0Per-step Z translate. Negative = push deeper per step.
data-foldpage-view-layout-absolutebooleanfalseOpt into position:absolute mode with scroll spacer.
data-foldpage-view-layout-scroll-distancepx1000Scroll distance per view transition. Only applies when absolute=true.
data-foldpage-view-layout-toppxautoPanel top offset. Only applies when absolute=true.

JS API ​

js
Fold.init({
  viewLayout: {
    type:      'helix',
    rotation:  40,    // 40° per step on Y axis
    rotationX: 0,     // pitch per step
    rotationZ: 0,     // roll per step
    offsetZ:   -200,  // push 200px deeper per step (true Z-helix)
    absolute:  false, // default for helix — elements stay in flow
  }
});

Full Circle ​

With rotation="40" and 9 panels: 9 × 40° = 360° — a complete spiral back to the starting orientation:

html
<div class="foldpage"
     data-foldpage-view-layout="helix"
     data-foldpage-view-layout-rotation="40">

  <header data-foldpage-view="0">0°</header>
  <section data-foldpage-view>40°</section>
  <section data-foldpage-view>80°</section>
  <!-- ... through 360° at panel 9 -->

</div>

Helix with Absolute Positioning ​

Opt into absolute positioning to get scroll-spacer-driven navigation:

html
<div class="foldpage"
     data-foldpage-view-layout="helix"
     data-foldpage-view-layout-rotation="40"
     data-foldpage-view-layout-absolute="true"
     data-foldpage-view-layout-scroll-distance="1200">

Rotation ​

rotation / rotation-x/y/z behave differently per layout type:

LayoutMeaning
Ringrotation / rotation-y spin every panel in place by the same flat amount, on top of the auto-facing angle — it does not accumulate per panel and does not move panels around the ring. rotation-x / rotation-z are the opposite: cumulative per sequential panel slot (panel 0 = 0°, panel 1 = 1×value, panel 2 = 2×value), which tilts the ring into a progressively steeper cone rather than staying a flat circle.
Gridrotation / rotation-y spin every panel in place by the same flat amount — it does not accumulate per layer and does not move panels off their grid slot. rotation-x / rotation-z are cumulative per depth layer (layer 0 = 0°, layer 1 = 1×value, layer 2 = 2×value) and tilt each layer's whole (x, y, z) position — a deliberate "fan of cards" effect.
RandomMaximum scatter magnitude. Each panel gets a random rotation uniformly distributed within ±value degrees on the given axis.
HelixPer-step additive rotation. Each panel receives step × value on the configured axes. The head view (step 0) is never rotated.
ScatterOn/off toggle, not a magnitude. Any nonzero value enables a fixed ±25° random tilt on X and Y (never Z) for every panel. No per-axis override — see Scatter Layout.

rotation is the base fallback when no per-axis override is set — for ring and grid this fallback only ever reaches rotation-y (never rotation-x/rotation-z, which are explicit-only). Set rotation-x="8" or rotation-z="8" directly to opt into the cumulative tilt effect on those axes.

html
<!-- Ring: every panel spins 15° in place (flat, not cumulative) -->
<div data-foldpage-view-layout="ring"
     data-foldpage-view-layout-rotation-y="15">

<!-- Grid: each depth layer pitches 8° more forward -->
<div data-foldpage-view-layout="grid"
     data-foldpage-view-layout-rotation-x="8">

<!-- Random: panels scatter with ±30° Y rotation, no X/Z -->
<div data-foldpage-view-layout="random"
     data-foldpage-view-layout-rotation-y="30"
     data-foldpage-view-layout-count="7">

<!-- Helix: each panel rotates 40° more on Y, diving 10° on X -->
<div data-foldpage-view-layout="helix"
     data-foldpage-view-layout-rotation="40"
     data-foldpage-view-layout-rotation-x="10">

<!-- Scatter: X/Y tilt enabled (toggle, not a magnitude), reproducible via seed -->
<div data-foldpage-view-layout="scatter"
     data-foldpage-view-layout-rotation="1"
     data-foldpage-view-layout-count="7">

Scroll Spacer ​

When absolute=true (the default for ring, grid, random, and scatter), a <div data-foldpage-scroll-spacer> is automatically appended to the container with height = panels × scrollDistance. This provides sufficient scroll distance to animate through all views.

If a [data-foldpage-scroll-spacer] or .scroll-spacer element already exists in the container, its height is updated in place. The spacer is removed (or its height restored) on Fold.destroy().

When absolute=false (the default for helix), no spacer is injected — page height comes from content.


View Dimming ​

Place data-foldpage-view-dim on any layout container to fade non-active panels:

html
<div data-foldpage-view-layout="ring"
     data-foldpage-view-dim="0.3">
  <!-- Inactive panels at 30% opacity; active panel at 100% -->
</div>

The default global dim value is 0.8. See View Dimming for full details, including the JS API (viewDim) and per-container override behavior.


Content Around Your Views ​

You don't need to wrap every piece of content in data-foldpage-view. Ordinary elements — a nav bar before your first view, a footer after your last one, or a plain section left between two views — are handled automatically as soon as at least one data-foldpage-view element exists on the page:

  • Fold positions this content to continue naturally from whichever view it's closest to, instead of leaving it in its raw, unadjusted spot on the page.
  • The scroll range automatically extends to include it, so it pans into view along with everything else — nothing gets skipped or stranded.
  • Several pieces of non-view content sitting next to each other keep their natural spacing relative to one another.

Fold.page — 3D UI for the web.