Skip to content
Docs
Open Promoeyes
scrolling

scrolling

Scrolling primitives — every way content MOVES INSIDE A VIEWPORT, one scenario per scene. The subject is the `scroll` / `carousel-scroll` track and the overflow mask that makes it read: a container whose subtree scrolls becomes a clip box (CSS `overflow: hidden`), so content taller/wider than the box is cut at its edges instead of bleeding over the rest of the frame.

Read the rig's notes
THE CURRENT GRAMMAR — the `scroll` property channel on the viewport itself:

  card(x=… y=… w=… h=…)               <- the VIEWPORT (clips by default)
    scroll                             <- x/y only; moves the CHILDREN
      0 0 0
      6 easeInOutCubic 0 620
    …rows, taller than the box…

  positive scroll.y scrolls DOWN (content travels up, lower rows come in);
  positive scroll.x scrolls RIGHT. A bare scalar is the vertical amount.
  The travel is CLAMPED at both ends: however large a value is authored,
  the last row parks on the viewport's midline and the box is never left
  empty. NEVER animate `position` to scroll a card — that moves the card,
  its border and its shadow across the frame instead of its content.

The scenes below still use the older `<animate><scroll by=…>` track, which
translates a content GROUP inside a clip box rather than the container's
children, and is unclamped:

  card(x=… y=… w=… h=… clip=true)             <- the VIEWPORT (the mask)
    item(component=group w=… layout=column …)  <- the CONTENT (taller than it)
      animate
        scroll(by=620 start=1.2 end=6.6 axis=y easing=easeInOutCubic)
      children
        …rows…

  axis=y  · positive `by` scrolls content UP   (lower content comes into view)
  axis=x  · positive `by` scrolls content LEFT (righter content comes in)
  from/to · scroll the other way — from=520 to=0 walks content back DOWN
  A <card> clips automatically once its subtree scrolls; clip=true is the
  explicit opt-in used everywhere here so the mask is visible in the source.

PIXEL-SPACE SECTION (scenes 01–05) — one full-stage <ui3d> per scene, world
mapped 1:1 to the 1920x1080 stage (camera at 960 540 1320).
  01. list      — the baseline: one column taller than its card, masked
  02. rails     — axis=x, two strips travelling in opposite directions
  03. carousel  — the <carousel> component + its carousel-scroll track
  04. parallax  — three lanes, three rates, one of them running backwards
  05. nested    — a vertical viewport whose rows are horizontal viewports;
                  the clip boxes INTERSECT instead of the inner replacing
                  the outer

DEVICE SECTION (scenes 06–10) — real devices at true physical size, the same
tracks running on real glass. Scene 05 owns the boundary cut (transition=none),
since match-move cannot carry a haze rig across a coordinate-system change.
  06. chrome    — a sticky nav bar: the page scrolls UNDER a bar drawn after
                  (and therefore on top of) the viewport
  07. iphone    — a feed between a pinned status header and a pinned tab bar
  08. ipad      — split view: the master list scrolls, the detail pane does not
  09. macbook   — two speeds on one screen: the doc runs fast, its contents
                  rail crawls
  10. imac      — three viewports at once: a log up, a thumbnail wall up
                  slowly, a ticker sideways

Matched haze rides every scene (haze-a/haze-b + dot-a/b/c in px scenes,
wz0..wz2 in device scenes) so transition=match-move carries the backdrop.
Preview: /packages/runtime/index.html?project=promos/primitives/scrolling/scrolling.promo
Topic
uis
Scenes
10
Kind
reference rig
//
    Scrolling primitives — every way content MOVES INSIDE A VIEWPORT, one
    scenario per scene. The subject is the `scroll` / `carousel-scroll` track
    and the overflow mask that makes it read: a container whose subtree scrolls
    becomes a clip box (CSS `overflow: hidden`), so content taller/wider than
    the box is cut at its edges instead of bleeding over the rest of the frame.

    THE CURRENT GRAMMAR — the `scroll` property channel on the viewport itself:

      card(x=… y=… w=… h=…)               <- the VIEWPORT (clips by default)
        scroll                             <- x/y only; moves the CHILDREN
          0 0 0
          6 easeInOutCubic 0 620
        …rows, taller than the box…

      positive scroll.y scrolls DOWN (content travels up, lower rows come in);
      positive scroll.x scrolls RIGHT. A bare scalar is the vertical amount.
      The travel is CLAMPED at both ends: however large a value is authored,
      the last row parks on the viewport's midline and the box is never left
      empty. NEVER animate `position` to scroll a card — that moves the card,
      its border and its shadow across the frame instead of its content.

    The scenes below still use the older `<animate><scroll by=…>` track, which
    translates a content GROUP inside a clip box rather than the container's
    children, and is unclamped:

      card(x=… y=… w=… h=… clip=true)             <- the VIEWPORT (the mask)
        item(component=group w=… layout=column …)  <- the CONTENT (taller than it)
          animate
            scroll(by=620 start=1.2 end=6.6 axis=y easing=easeInOutCubic)
          children
            …rows…

      axis=y  · positive `by` scrolls content UP   (lower content comes into view)
      axis=x  · positive `by` scrolls content LEFT (righter content comes in)
      from/to · scroll the other way — from=520 to=0 walks content back DOWN
      A <card> clips automatically once its subtree scrolls; clip=true is the
      explicit opt-in used everywhere here so the mask is visible in the source.

    PIXEL-SPACE SECTION (scenes 01–05) — one full-stage <ui3d> per scene, world
    mapped 1:1 to the 1920x1080 stage (camera at 960 540 1320).
      01. list      — the baseline: one column taller than its card, masked
      02. rails     — axis=x, two strips travelling in opposite directions
      03. carousel  — the <carousel> component + its carousel-scroll track
      04. parallax  — three lanes, three rates, one of them running backwards
      05. nested    — a vertical viewport whose rows are horizontal viewports;
                      the clip boxes INTERSECT instead of the inner replacing
                      the outer

    DEVICE SECTION (scenes 06–10) — real devices at true physical size, the same
    tracks running on real glass. Scene 05 owns the boundary cut (transition=none),
    since match-move cannot carry a haze rig across a coordinate-system change.
      06. chrome    — a sticky nav bar: the page scrolls UNDER a bar drawn after
                      (and therefore on top of) the viewport
      07. iphone    — a feed between a pinned status header and a pinned tab bar
      08. ipad      — split view: the master list scrolls, the detail pane does not
      09. macbook   — two speeds on one screen: the doc runs fast, its contents
                      rail crawls
      10. imac      — three viewports at once: a log up, a thumbnail wall up
                      slowly, a ticker sideways

    Matched haze rides every scene (haze-a/haze-b + dot-a/b/c in px scenes,
    wz0..wz2 in device scenes) so transition=match-move carries the backdrop.
    Preview: /packages/runtime/index.html?project=promos/primitives/scrolling/scrolling.promo
promo#scrolling
  // =======================================================================
  // SCENE 01 · LIST — the baseline vertical scroll
  // =======================================================================
  scene#s01-list(duration=7 transition=match-mo