Xanui-core ships a small imperative animation engine (animate) plus a set of Easing presets and three React hooks built on top of it: useTransition, useTransitionGroup, and useInView. All of these are exported directly from xanui-core.
Unlike CSS-in-JS keyframe helpers, animate interpolates plain numeric values (not CSS strings) frame by frame via requestAnimationFrame, and calls your onUpdate callback with the current value on every frame so you can apply it however you like (inline styles, a ref, a CSS variable, etc.).
animate(options)ts
import { animate, Easing } from 'xanui-core'import type { AnimateOptions } from 'xanui-core'animate accepts a single AnimateOptions<T> object, where T is a record of numeric values (e.g. { scale: number; opacity: number }).
animate returns a cancel function: calling it clears the pending requestAnimationFrame/setTimeout and stops the animation.
tsx
import { animate, Easing } from 'xanui-core' const cancel = animate({ from: { scale: , opacity: }, to: { scale: , opacity: }, duration: , easing: Easing.standard, onUpdate: (value) => { el.style.transform = `scale(${value.scale})` el.style.opacity = String(value.opacity) }, onDone: () => console.log('done'),}) // later, to cancel mid-flight:cancel()tsx
animate({ from: { x: , y: }, to: { x: , y: }, duration: , easing: { x: Easing.smooth, y: Easing.easeOutBounce }, breakpoints: { x: [{ value: , callback: () => console.log('halfway across') }], }, onUpdate: (value) => { box.style.transform = `translate(${value.x}px, ${value.y}px)` },})EasingEasing is an object of ready-made timing functions, each with the signature (t: number) => number:
useTransitionuseTransition<T>(props: UseTransitionProps<T>) wraps animate with enter/exit state management for a single value.
UseTransitionProps<T> = AnimateOptions<T> plus:
Return value:
tsx
import { useTransition } from 'xanui-core' const Collapse = ({ open, children }: { open: boolean; children: React.ReactNode }) => { const { state, enter, exit, status } = useTransition({ from: { height: , opacity: }, to: { height: , opacity: }, duration: , onUpdate: (value) => { el.current!.style.height = `${value.height}px` el.current!.style.opacity = String(value.opacity) }, }) useEffect(() => { open ? enter() : exit() }, [open]) return <div ref={el} data-status={status}>{children}</div>}useTransitionGroupuseTransitionGroup<T>(options: UseTransitionGroupProps<T>) runs staggered enter/exit animations across a list of keyed items.
Returns { statuses, mounted, enter, exit, toggle }, where statuses and mounted are records keyed by item key (statuses[key] is a UseTransitionStatus, mounted[key] is a boolean), and enter/exit/toggle (each () => void) run the staggered animation across all items.
tsx
import { useTransitionGroup } from 'xanui-core' const items = list.map((item, i) => ({ key: item.id, from: { y: , opacity: }, to: { y: , opacity: } })) const { statuses, mounted, enter } = useTransitionGroup({ items, stagger: , mountOnEnter: true, onUpdate: (value, key) => applyStyle(key, value),}) useEffect(() => { enter() }, [])useInViewuseInView<T extends HTMLElement>(options?: UseInViewOptions) tracks whether an element is intersecting its scroll container using IntersectionObserver.
Returns { ref, inView }, where ref must be attached to the element you want to observe and inView is a boolean.
tsx
import { useInView } from 'xanui-core' const Reveal = ({ children }: { children: React.ReactNode }) => { const { ref, inView } = useInView({ threshold: , once: true }) return ( <div ref={ref} style={{ opacity: inView ? : , transition: 'opacity 400ms' }}> {children} </div> )}