60fps/ui
A collection of headless interaction engines — carousel, drag, motion and scroll-reveal — written once in framework-agnostic TypeScript, then wrapped in thin, idiomatic adapters for React and Vue.
The engine owns the behaviour (DOM measurement, event listeners, data-* attributes, an event
emitter); the adapter owns the lifecycle and reactivity for your framework. Swap the adapter, keep
the engine.
The two layers
Every primitive is split the same way:
| Layer | What it does | Example |
|---|---|---|
| Engine | Vanilla class extending an Emitter, with init(el) / clean(). No framework imports. | new CarouselEngine() |
| Adapter | A hook/composable that instantiates the engine, drives init/clean, and mirrors state. | useCarousel(ref) |
This means the hard part — the interaction logic — has a single implementation and a single test suite, while each framework gets an API that feels native to it.
Packages
| Package | Contents |
|---|---|
@60fps/utils | Shared primitives: Emitter, DOM helpers, math, ResizeObserver / IntersectionObserver managers |
@60fps/motion | Motion — animate a value with spring, damping or inertia |
@60fps/drag | DragEngine — pointer & touch drag gestures |
@60fps/carousel | CarouselEngine — accessible scroll-snap carousel |
@60fps/inview | InViewEngine + portable CSS — reveal on viewport entry |
@60fps/video | VideoEngine + PosterVideoEngine — polite playback & lazy poster→video |
@60fps/react | React hooks for every engine (@60fps/react/carousel-engine, …) |
@60fps/vue | Vue composables for every engine (@60fps/vue/carousel-engine, …) |
Install
# React
pnpm add @60fps/react @60fps/motion @60fps/drag @60fps/carousel @60fps/inview @60fps/video
# Vue
pnpm add @60fps/vue @60fps/motion @60fps/drag @60fps/carousel @60fps/inview @60fps/video
Head to Motion to see the pattern in action, or jump straight to Carousel and InView.