@mvoom/sdk
Turns a spec into real motion in production. Agnostic core, adapters per framework.
Mount a measured motion mechanism on real DOM. Zero dependencies, no framework, and the trigger is interpreted rather than approximated.
npm i @mvoom/sdk
The component you buy from mvoom is self-contained. It brings its own mounting code and depends on nothing, on purpose. This package is the other path: you have a VUI Spec — from the API or from the MCP connector — and you want to run it inside an app that already exists, without generating files.
That is the agent's path. A model asks for a mechanism and mounts it where the markup already is.
import { montar } from "@mvoom/sdk";
const detener = montar(receta, document.querySelector("#hero")!);
// later, when the component goes away
detener();
The trigger is the whole point
viewport-enter and scroll-scrub look identical in a single frame. They are not the same mechanism, and telling them apart cost the engine an entire pass.
| Trigger | What it does here |
|---|---|
viewport-enter | fires once when the element enters, then runs on the clock |
scroll-scrub | never runs on its own — the scroll drives it, and it runs backwards when you scroll up |
load loop timer | runs immediately, on the clock |
hover click focus | the person fires it |
unknown | measured, not attributable — the resting state is applied and nothing animates |
unknown does not get a guessed trigger. Picking a reasonable-looking one would produce motion the original never had, and from the outside that is indistinguishable from a bad measurement. It is a result, not a gap.The one thing that is not literally what was measured
Reduced motion
If the system asks for reduced motion, nothing animates and the final state is applied.
That distinction matters more than it sounds: an entrance that starts at opacity: 0 and simply does not animate leaves the content invisible forever. That is not respecting the preference, it is hiding the page.
// only for a test bench — never in production
montar(receta, el, { respetarPreferencia: false });
React and Vue
Thin adapters over the same core. Both are separate entry points, so if you use neither you do not even pull the import.
import { useMvoom } from "@mvoom/sdk/react";
export function Hero({ receta }) {
const ref = useMvoom(receta);
return <div ref={ref}>{/* elements carrying data-mv="…" */}</div>;
}
<script setup>
import { useMvoom } from "@mvoom/sdk/vue";
const raiz = useMvoom(receta);
</script>
<template><div ref="raiz"><!-- … --></div></template>
React and Vue are optional peer dependencies: the copy your app already has, never a second one. Two copies of React in a bundle do not produce a clear error — they produce hooks that fail somewhere unrelated.
What this package does not do
| Not included | Why |
|---|---|
| Measuring | That needs a real browser, five passes and a queue. This mounts what was already measured |
| Any network call | It takes a recipe you already have. It never phones home, and it has no idea a service exists |
| Generating code | The purchased component does that, and it is self-contained. This is for running a spec in place |
| A scroll library | The scrub uses a passive scroll listener and the browser's own animation API. Smoothing is your app's decision, not ours |
Zero dependencies, and why it matters here
This runs inside someone else's project. Every dependency is a decision imposed on them, and the whole file is short enough to read in one sitting — which is the only honest way to trust code that touches every element you point it at.
It validates the recipe before mounting, too. Types do not survive JSON.parse, and this receives data from the network: mounting a half-built object "as best it can" would produce motion nobody measured.
Support
| Versioning | Semantic, with a hand-written changelog |
| Security | security@vorluno.dev — first response within 48 hours |
| Licence | MIT. The engine that produces the measurements is not — see ADR-MV016 |
<sub>Built and maintained by Vorluno Software, S.A. The mechanism this mounts was measured in a real browser, never inferred from a screenshot.</sub>