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.
npx shadcn@latest add https://webberui.com/r/velocity-skew.jsonPlayground
Tune the props live — the code snippet updates as you go, so you can dial in the look you want before copying it.
<VelocitySkew />
Installation
npx shadcn@latest add https://webberui.com/r/velocity-skew.jsonOr, 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
| Prop | Type | Default | Description |
|---|---|---|---|
children | React.ReactNode | — | The content that reacts to scroll speed |
container | React.RefObject<HTMLElement | null> | — | Ref of the nested scroll container; the window is the scroll container by default |
maxSkew | number | 8 | Maximum skew angle (degrees); set to 0 to disable skewing |
maxBlur | number | 4 | Maximum blur strength (px); set to 0 to disable blurring |
maxVelocity | number | 1200 | Scroll speed required to reach the maximum effect (px/s); lower is more sensitive |
stiffness | number | 350 | Spring stiffness used for smoothing; higher springs back faster |
damping | number | 60 | Spring damping used for smoothing; higher oscillates less |
combine | boolean | false | When true, the whole block skews as one; by default each child skews on its own |
className | string | — | Class for the outer container |
itemClassName | string | — | Class for each child's wrapper (has no effect in combine mode) |
How it works
useScrollprovides the scroll displacement,useVelocityconverts it into a speed, anduseSpringsmooths 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
maxBluris0, nofilteris 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
tabIndexand anaria-labelso it can be scrolled by keyboard.
Scroll Progress Set
Three scroll indicators in one — a top bar, a bottom-right ring, and section dots on the right, all sharing the same scroll progress.
Scroll Timeline
A scroll-driven timeline where the growing line extends along the main axis and each node lights up and reveals its content as the line arrives.