The motion layer for developer websites. Ovio is a shadcn registry of animated components for showing off a project: contribution graphs, star history, release notes, download charts and more. The CLI copies the TypeScript source into your repo, so you own and can edit every line.
Every component comes in four design worlds, picked with the variant prop or a site-wide
OvioProvider:
- Minimal: clean digital objects. Whitespace, thin rules, quick motion with no overshoot.
- Craft: physical artefacts. Paper, tape and printed labels that settle with a soft overshoot.
- Retro: nostalgic hardware. Phosphor terminals and pixels that move in hard steps.
- Toy: playful plastic. Raised keys, knobs and pieces driven by springs and physics.
All motion respects prefers-reduced-motion.
| Component | Status | Description |
|---|---|---|
| Contribution Graph | Available | A year of activity, one cell per day. |
| Liquid Tabs | Available | A tab bar with a moving indicator and a deploy status. |
| Star History | Available | Stargazers over time, with annotated spikes. |
| Repository Card | Available | A repo at a glance: name, description, stars, forks and language. |
| Top Contributors | Available | The people behind a project, ranked by commits. |
| npm Downloads | Available | Weekly installs with trend and goal. |
| Physical Knob | Available | A rotary control you can drag, flick, scroll or turn with the keyboard. |
| Bundle Size | Available | Package size with gzip, brotli and the change from the previous version. |
| Changelog | Available | Releases with dates and notes. |
| Event Ticket | Available | A pass for launches and conferences: book it, tear the stub, flip it for the QR code. |
| Sponsor Wall | Available | Sponsors by tier, from a logo wall to blocks on a pegboard. |
| Developer ID Card | Available | An identity card for a developer: name, role, stack, availability and a QR code. |
| Git Branch Visualizer | Available | Branches and commits as a graph you can select, merge and branch off. |
| Now Playing | Available | What you're listening to, with play, seek and volume. |
| Toast | Available | Notifications in every world, from a taped note to a terminal line. |
| Letter | Available | A newsletter issue as a letter, sent as a paper plane, sealed envelope, Morse wire or mailbox drop. |
| Compare Slider | Available | Two versions of anything, one over the other, split by a divider you drag. |
| Liquid Magnifier | Available | A liquid lens that trails the pointer and magnifies whatever it covers. |
| Scratch to Reveal | Available | A coating you scratch away with a pointer or finger to find what is under it. |
| Dial Menu | Available | A radial menu that opens around the pointer; aim by direction and flick to pick. |
The catalogue lives in content/components.ts.
Ovio needs a project set up for shadcn (React 19, Tailwind CSS v4). This GitHub repository is the
registry, so there is nothing to configure. Add a component in the world you want, as
<component>-<world>:
npx shadcn add JanaSundar/ovio/star-history-craftThat installs Star History with the Craft world alone: the component, worlds/craft.tsx and only
the shared files and dependencies Craft uses. Every component has -minimal, -craft, -retro
and -toy items. A single-world install only accepts its own world as variant, so asking for
another is a type error; a world set by OvioProvider that isn't installed falls back to it.
To get all four worlds and switch between them with variant, add the component by its name
alone:
npx shadcn add JanaSundar/ovio/star-historyAdding a second single world later replaces worlds/index.ts, which lists the installed worlds;
add the component by name instead to have them all.
Append #<tag> or a commit SHA to pin a version, for example
npx shadcn add JanaSundar/ovio/star-history-craft#v0.1.0.
The CLI installs the component under components/ovio/<name>/, its npm dependencies (motion,
@number-flow/react, uqr for QR codes) and the shared Ovio items it needs, including the theme at styles/ovio.css.
Import the theme from your global stylesheet (the path is relative to that file):
@import "tailwindcss";
@import "../styles/ovio.css";The worlds use Geist, Geist Mono, Bricolage Grotesque, IBM Plex Mono, Caveat, VT323 and Archivo.
The theme reads them from next/font variables (--font-geist, --font-vt323 and so on) and
falls back to the family names, so load them with next/font/google or Google Fonts.
npx shadcn add JanaSundar/ovio/all adds every available component, in all four worlds, at once.
import { StarHistory } from "@/components/ovio/star-history/star-history";
<StarHistory variant="craft" data={stars} repo="ada-dev/lumen" />;To set the world for a whole site, wrap it in OvioProvider. A component's own variant still
wins.
import { OvioProvider } from "@/components/shared/world-provider";
<OvioProvider world="retro">{children}</OvioProvider>;Three optional registry items fetch real data on the server, already shaped for the components:
npx shadcn add JanaSundar/ovio/github # lib/github.ts
npx shadcn add JanaSundar/ovio/npm # lib/npm.ts
npx shadcn add JanaSundar/ovio/music # lib/music.tslib/github.ts:getRepository,getStarHistory,getContributors,getReleases,getContributions,getCommitGraphandgetDeveloper(a profile as Developer ID Card props).lib/npm.ts:getWeeklyDownloads(npm registry) andgetBundleSizes(bundlephobia).lib/music.ts:getNowPlaying(services)asks whichever music service is attached.spotify()andlastfm()read their credentials from the environment (see.env.example), andmusicService({ name, url, map })adapts any other JSON API.
All are server-only and use native fetch through lib/ovio-fetch.ts. The GitHub helpers read
GITHUB_TOKEN from the environment, or take { token }. The npm helpers need no key.
# .env.local
GITHUB_TOKEN=github_pat_...import { RepositoryCard } from "@/components/ovio/repository-card/repository-card";
import { getRepository } from "@/lib/github";
export default async function Page() {
return <RepositoryCard repository={await getRepository("honojs/hono")} />;
}- Responses are cached for an hour by default. Pass
{ revalidate }to change it. A static or ISR page calls each API at most once per window, however much traffic it gets. If a refresh fails, Next keeps serving the last good page. - A spent limit throws
RateLimitError, withresetAtand a message like "github.com rate limit reached, resets in 12 min". Every request times out after 10 seconds. - GitHub: 60 requests an hour without a token, 5,000 with one.
getContributionsuses GraphQL, which needs a token.getStarHistorymakes up to 16 requests, and GitHub lists stargazers only to the repository's owner, so it charts your own repos, not someone else's. - npm: the downloads API is keyless and serves up to 18 months.
- bundlephobia: unofficial, and it answers 429 after a few quick requests. Call
getBundleSizeswithoutversionsto read its history in one request.
Each component's docs page has a Data section with its source, auth and limits.
To fetch from your own API route in the browser, useLiveData shares one request per page and
shows a fallback until the response arrives, or if it fails. Pair it with the Toast to report
errors:
npx shadcn add JanaSundar/ovio/live-data JanaSundar/ovio/toastconst { data } = useLiveData("/api/stars", sampleStars, {
onError: (error, retry) =>
ovioToast.error(error.message, { action: { label: "Retry", onClick: retry } }),
});Mount <OvioToaster /> once, for example in your root layout.
Three components also come as images for a GitHub README, in any world. There's nothing to install: paste the Markdown, and the image refreshes every hour.
[](https://github.com/honojs/hono)
[](https://www.npmjs.com/package/hono)
[](https://github.com/JanaSundar)| Embed | Subject | Data |
|---|---|---|
| Repository Card | ?repo=owner/name |
GitHub's REST API: the repository |
| npm Downloads | ?package=name or @scope/name |
npm's downloads API: the last 12 weeks |
| Developer ID Card | ?user=login |
GitHub's REST API: the profile and the languages of the user's own repos |
&world=isminimal(default),craft,retroortoy;&format=pngreturns a PNG at 2× instead of an SVG.- Only public data anyone can read makes an embed. A user's contributions need GitHub's GraphQL API and their own token, so the Contribution Graph has no embed: render the component instead.
- Each docs page with an embed has a field to try a name and copy its Markdown.
- A name that doesn't exist, an organisation given as a developer, or an outage shows a small card saying so rather than a broken image.
This repo is the docs site at ovioui.vercel.app and the registry source.
pnpm install # also installs the git hooks (Lefthook)
pnpm dev # docs site at http://localhost:3000
pnpm build # builds the site
pnpm registry:validate # checks registry.json and that every file it lists exists
pnpm registry:check # checks every registry item installs with all the files it imports
pnpm lint # oxlint
pnpm format # oxfmt
pnpm typecheck # next typegen && tsc --noEmit
pnpm test # unit tests (Vitest)Git hooks run through Lefthook (lefthook.yml): before a commit, the
staged files are formatted and linted; before a push, the project is type-checked, unit-tested and
registry-checked. Skip them once with LEFTHOOK=0, e.g. LEFTHOOK=0 git commit -m "wip".
components/ovio/<name>/: each component, its shared state, and one file per world inworlds/.components/shared/,lib/,styles/ovio.css: the shared registry items (world, motion tokens, theme, formatting, keyboard handling, rolling numbers, Toy primitives).registry.json: the registry. The shadcn CLI reads it and the source files straight from this repository.app/,components/site/,content/: the docs site.app/embed/,lib/embed/: the README embeds, drawn for each world and rendered with Takumi.
- Next.js 16 (App Router) and React 19
- Motion for React and NumberFlow
- Tailwind CSS v4
- shadcn registry
- oxlint and oxfmt
- Vitest and Lefthook
- TypeScript, pnpm
MIT © Jana Sundar