Video components
Install polished, temporal React components as source and compose them directly inside video.tsx.
Odori’s component registry follows the shadcn model: installation copies source into your project instead of hiding implementation behind a package.
pnpm odori add \
@odori/stage \
@odori/title-reveal \
@odori/code-proof \
@odori/terminal \
@odori/browser-demo \
@odori/connection-story \
@odori/end-card
pnpm odori registry prints the catalog with each component’s aspect ratios,
minimum duration, and reduced-motion behavior. Registry dependencies install
with the component that needs them.
Every component also has a page in the component catalog, one per
registry entry, where the real component plays in the browser at any frame and
format, next to its timing contract, props, and the exact source odori add
copies.
- videos/
- components/
- stage/
- stage.tsx
- stage.preview.tsx
- title-reveal/
- title-reveal.tsx
- title-reveal.preview.tsx
- code-proof/
- code-proof.tsx
- code-proof.preview.tsx
- terminal/
- terminal.tsx
- terminal.preview.tsx
- end-card/
- end-card.tsx
- end-card.preview.tsx
- stage/
- components/
Use the components as ordinary React:
<Scene id="proof" duration="8s">
<Stage grid={false}>
<CodeProof
code={'export const preview = "instant";'}
focus={[1]}
language="tsx"
title="videos/launch/video.tsx"
/>
</Stage>
</Scene>
Temporal contracts
Video components need more than prop types. Registry metadata describes:
- supported aspect ratios
- recommended and minimum duration
- natural entrance and exit frames
- content limits, such as maximum headline length
- reduced-motion behavior
- required fonts and audio
odori test and the registry test suite check these contracts, while the
copied implementation stays fully editable.
Scale with the frame, not the width
Components multiply their design values by useDesignScale(), which measures
the shorter side of the frame against a 1080px reference. The same component
reads correctly at 1920x1080, 1080x1920, and 1080x1080 without a separate
vertical variant.
const scale = useDesignScale();
<div style={{fontSize: 116 * scale, letterSpacing: "-0.045em"}}>{title}</div>;
Component development in Studio
The component source stays independent from its development fixture. A sibling
*.preview.tsx file defines representative props, controls, duration, canvas
size, and edge cases for Studio. The fixture is similar to a Storybook story
with a deterministic frame clock.
Reuse application components
Videos can import deterministic components from an application’s root
components/ directory. Keep those components under their existing product
ownership instead of duplicating them for video.
Add an adapter under videos/components/product/ when the application
component expects routing, live data, or browser interaction. The adapter
supplies frozen data and disables behavior that depends on wall-clock or
application state.
Initial component library
| Family | Components |
|---|---|
| Typography | Title reveal, metric callout, captions |
| Developer proof | Terminal, code proof |
| Product UI | Browser demo |
| Narrative | Connection story |
| Brand | Stage, end card |
Install the agent quickstart
The repository also ships an agent skill that turns project evidence into a coherent first cut and installs a small, story-specific component set:
npx skills add allenzhou101/odori \
--skill odori-quickstart \
--yes
Source ownership
Registry provenance is recorded in .odori/components.json, so the CLI can tell
a pristine component from one you have edited and from one the registry has
moved past.
odori diff # what changed, in your project and upstream
odori diff terminal --full # the diff itself
odori update # apply upstream changes to untouched components
Updates are opt-in and source-aware. A component that is both edited locally and
changed upstream is reported as diverged and left alone until you pass
--force.