WebberUI

Velocity Skew

Skews and blurs content in real time based on scroll speed, springing back level when the scroll stops, for a reading feel full of kinetic energy.

Loading preview…
npx shadcn@latest add https://webberui.com/r/velocity-skew.json

Playground

Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.

8
4
1200
350
60
<VelocitySkew />

Installation

npx shadcn@latest add https://webberui.com/r/velocity-skew.json

Or, once registries are configured in components.json, install it as @webberui/velocity-skew.

Usage

import { VelocitySkew } from "@/components/ui/velocity-skew";

<VelocitySkew className="flex flex-col gap-3">
  <Card>First row</Card>
  <Card>Second row</Card>
  <Card>Third row</Card>
</VelocitySkew>

By default each direct child skews on its own; if the scrolling happens inside a nested overflow container, pass that container's ref to container:

const scrollerRef = React.useRef<HTMLDivElement>(null);

<div ref={scrollerRef} className="h-[300px] overflow-y-auto">
  <VelocitySkew container={scrollerRef} maxSkew={9} maxBlur={3}>
    {/* … */}
  </VelocitySkew>
</div>

Props

PropTypeDefaultDescription
childrenReact.ReactNodeThe content that reacts to scroll speed
containerReact.RefObject<HTMLElement | null>Ref of the nested scroll container; the window is the scroll container by default
maxSkewnumber8Maximum skew angle (degrees); set to 0 to disable skewing
maxBlurnumber4Maximum blur strength (px); set to 0 to disable blurring
maxVelocitynumber1200Scroll speed required to reach the maximum effect (px/s); lower is more sensitive
stiffnessnumber350Spring stiffness used for smoothing; higher springs back faster
dampingnumber60Spring damping used for smoothing; higher oscillates less
combinebooleanfalseWhen true, the whole block skews as one; by default each child skews on its own
classNamestringClass for the outer container
itemClassNamestringClass for each child's wrapper (has no effect in combine mode)

How it works

  • useScroll provides the scroll displacement, useVelocity converts it into a speed, and useSpring smooths the result — which avoids jitter and returns level naturally once you let go.
  • The skew direction flips with the scroll direction (negative angle scrolling down, positive scrolling up); the faster the scroll, the stronger the skew and blur, capping out at maxVelocity.
  • When maxBlur is 0, no filter is applied at all, which saves the cost of creating a compositing layer.

Accessibility

  • When the user has "reduce motion" enabled at the system level, the original layout is preserved and every velocity-driven skew and blur is removed.
  • The component does not intercept focus or keyboard behavior, so the content keeps its original semantics and readability; the scroll container in the example carries tabIndex and an aria-label so it can be scrolled by keyboard.

On this page