Motion primitives
motiq/primitivesFreeUtility
Shared, dependency-free hooks used across workflow components: useReducedMotion, useVisibilityPause (offscreen pause), useSequence (drive live data over time), formatNumber (locale), useAnimatedNumber.
Install it
npx shadcn@latest add https://www.motiq.dev/r/primitives.jsonSource
1"use client";23/**4 * Motiq shared primitives — small, dependency-free hooks/utilities reused5 * across workflow components so each one doesn't re-implement reduced-motion,6 * offscreen pausing, or locale number formatting. Installs as editable source7 * (`@motiq/primitives`). No React import of `motion` here — this is engine-8 * agnostic. Clean-room original.9 */10import * as React from "react";11import { createPortal } from "react-dom";1213/**14 * SSR-safe `prefers-reduced-motion`. Reads synchronously on the client so a15 * reduced-motion user never sees a frame of motion; the value is never rendered16 * into markup, so there is no hydration-mismatch risk.17 */18export function useReducedMotion(): boolean {19 const [reduced, setReduced] = React.useState(20 () => typeof window !== "undefined" && window.matchMedia("(prefers-reduced-motion: reduce)").matches,21 );22 React.useEffect(() => {23 const mq = window.matchMedia("(prefers-reduced-motion: reduce)");24 setReduced(mq.matches);25 const onChange = (e: MediaQueryListEvent) => setReduced(e.matches);26 mq.addEventListener("change", onChange);27 return () => mq.removeEventListener("change", onChange);28 }, []);29 return reduced;30}3132/**33 * Scroll `el` into view within its NEAREST scrollable ancestor only — never the34 * window/page. `element.scrollIntoView()` bubbles through every scroll container35 * up to the document, so a live feed/timeline that follows its active row would36 * yank the WHOLE PAGE to itself (e.g. a "Running" demo advancing steps inside a37 * catalog card). This scrolls just the owning scroll region and no-ops when the38 * element has no scrollable ancestor, so it can never move the page.39 */40export function scrollIntoViewWithin(41 el: HTMLElement | null | undefined,42 { smooth = false, margin = 8 }: { smooth?: boolean; margin?: number } = {},43): void {44 if (!el || typeof el.getBoundingClientRect !== "function" || typeof window === "undefined") return;45 let c: HTMLElement | null = el.parentElement;46 while (c) {47 const oy = getComputedStyle(c).overflowY;48 if ((oy === "auto" || oy === "scroll") && c.scrollHeight > c.clientHeight + 1) break;49 c = c.parentElement;50 }51 if (!c) return; // no inner scroll region → do nothing (never scroll the page)52 const er = el.getBoundingClientRect();53 const cr = c.getBoundingClientRect();54 let delta = 0;55 if (er.top < cr.top + margin) delta = er.top - cr.top - margin;56// … truncated
Files it writes
More from motiq
All 100 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| cn() utilityutils | motiq | Utilities | Free | 2 deps |
