apx-dsv0.1Local renderer · live
K
Foundations4
  • Getting started
  • Theming
  • Templates
  • Icons
  • Avatar
  • Badge
  • DataGrid
  • Scheduler
  • Stat
  • Table
  • Timeline
  • TreeView
  • Accordion
  • Alert
  • EmptyState
  • Progress
  • Skeleton
  • Spinner
  • SplashScreen
  • Toast
  • ColorPicker
  • FileUpload
  • Form
  • Rating
  • TagsInput
  • Combobox
  • Field
  • Select
  • Toggle
  • Button
  • Calendar
  • Checkbox
  • DatePicker
  • Input
  • NumberInput
  • Radio
  • Slider
  • Switch
  • Textarea
  • AppShell
  • Div
  • Divider
  • Marquee
  • Parallax
  • Reveal
  • Section
  • SectionHeading
  • Sidebar
  • Stack
  • Surface
  • Typography
  • Image
  • Breadcrumbs
  • Carousel
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Toolbar
  • CommandPalette
  • Confirm
  • Drawer
  • HoverCard
  • Menu
  • Modal
  • Popover
  • Tooltip
  • Icon
  • Card
  • PricingCard
66 componentsapx-ds/renderer
Parallax
Variant↳ other

Layout

Parallax

Scroll-linked layer that drifts against the scroll, for hero backdrops and decorative imagery. Drift is clamped to a tenth of the travel — enforced, not documented, because larger parallax makes a page feel cheap and is a vestibular trigger above the reduced-motion threshold. Under prefers-reduced-motion the layer sits at its neutral position and no scroll listener is attached. For backgrounds only, never body copy.

Parallax

A scroll-linked layer that drifts against the scroll, for hero backdrops and decorative imagery.

The backdrop moves; the copy does not

Loading preview…
Basic.tsx
tsx
import { Parallax } from 'apx-ds';

<Div className="relative overflow-hidden">
  <Parallax speed={-0.08} className="absolute inset-x-0 -top-[10%] h-[120%]">
    <Image src="/hero.jpg" alt="" />
  </Parallax>
  <Div className="relative">…copy, which stays locked to the surface…</Div>
</Div>
import { Parallax } from 'apx-ds';

<Div className="relative overflow-hidden">
  <Parallax speed={-0.08} className="absolute inset-x-0 -top-[10%] h-[120%]">
    <Image src="/hero.jpg" alt="" />
  </Parallax>
  <Div className="relative">…copy, which stays locked to the surface…</Div>
</Div>

For backgrounds, never for content

Wrap the atmosphere — a backdrop, a decorative image, a gradient wash. Never body copy or a heading.

Text moving at a different rate from the surface it sits on is hard to read while scrolling, and it's the clearest tell of a template that reached for an effect instead of a design. If a section needs more presence, it needs better contrast or better type, not a second rate of motion.

The speed ceiling is enforced

A layer asking for 0.5 renders identically to one asking for 0.1

Loading preview…
SpeedCeiling.tsx

speed is clamped to ±0.1 — a tenth of the distance the layer travels through the viewport. This is a bound in code, not a note in the docs, and that's deliberate: a documented limit holds until the first template that wants just a bit more, and parallax is exactly the effect where a bit more is the whole difference between a page that feels deep and one that feels cheap. Past that point the layer visibly disagrees with the scroll gesture, and the page stops feeling like a surface being moved.

It's an accessibility bound as much as a taste one. Large parallax is a vestibular trigger for people who would never think to enable prefers-reduced-motion — so the ceiling has to hold for everyone, not only for those who asked.

Negative values drift against the scroll (the usual "background lags behind" reading); positive drift with it. A non-finite speed falls back to no drift and warns.

Reduced motion

Under prefers-reduced-motion: reduce the layer renders at its neutral position with no transform at all, and no scroll listener is ever attached.

Worth spelling out why neutral and not zero-progress: useScrollProgress freezes at 0.5, its midpoint, rather than at 0. 0 is one end of the range — the maximum displacement — so a hook that froze there would park every backdrop at its most extreme offset forever. That reads as a layout bug, not as motion being switched off.

API

PropTypeDefaultDescription
childrenReactNode—The layer to displace. Backgrounds and imagery only.
speednumber-0.06Drift as a fraction of travel. Clamped to ±0.1. Negative drifts against the scroll.
reduceMotionboolean—Force the reduced-motion behaviour on/off, bypassing the media query. For tests and docs.
as'div' | 'span''div'Element rendered for the wrapper.
className / style / sx——Standard escape hatches.

Layout notes

The wrapper only sets a transform — it owns no positioning of its own, so the drift is composited and never triggers layout.

Two things the consumer must supply:

  • A clipping ancestor. The layer moves beyond its authored box, so the parent needs overflow-hidden or the drift shows as an edge.
  • Overscan. Size the layer slightly larger than its container (-top-[10%] h-[120%] in the example above) so there's material to reveal at both ends of the drift.

useScrollProgress

Parallax is a thin consumer of useScrollProgress(ref) from @apx-ui/engine, which returns an element's progress through the viewport as 0 → 1: 0 when its top reaches the viewport bottom, 1 when its bottom leaves the top. Reach for the hook directly for other scroll-linked effects — a docs reading-progress bar, a scrubbed image sequence.

It uses a passive scroll listener coalesced into one requestAnimationFrame per frame rather than IntersectionObserver, which reports threshold crossings rather than a continuous position and would need an impractical number of thresholds to scrub smoothly.

Props

PropTypeDefaultDescription
children*ReactNode—The layer to displace. Backgrounds and imagery — never body copy.
asenum'div'Element rendered for the wrapper.
classNamestring——
reduceMotionboolean—Force the reduced-motion behaviour on (`true`) or off (`false`), bypassing the media query. Mirrors `useReducedMotion(forceValue)`; for tests and side-by-side documentation.
refRef<HTMLElement>—Allows getting a ref to the component instance. Once the component unmounts, React will set `ref.current` to `null` (or call the ref with `null` if you passed a callback ref). @see {@link https://react.dev/learn/referencing-values-with-refs#refs-and-the-dom React Docs}
speednumber-0.06How far the layer drifts, as a fraction of the distance it travels through the viewport. **Clamped to ±0.1**, and that ceiling is the point of the prop rather than a safety net. Large parallax is the single fastest way to make a page feel cheap: the layer visibly disagrees with the scroll, so the page stops feeling like a surface being moved and starts feeling like it is fighting the gesture. It is also a vestibular trigger well above the threshold where someone would think to turn on `prefers-reduced-motion`. Negative drifts against the scroll (the usual "background lags behind" reading); positive drifts with it.
styleCSSProperties——
sxSx—Theme-aware inline style.