Brand provider
The resolved brand rendered as a sheet: color, type, and motion.
Customize
import {Scene, Video} from "odori";
import {BrandProvider} from "../components/brand-provider/brand-provider";
<Video>
<Scene id="brand-provider" duration="4s">
<BrandProvider
title="Brand tokens"
/>
</Scene>
</Video>Installation
pnpm odori add component brand-providernpx odori add component brand-providerbunx odori add component brand-providerEdit the source in videos/components/brand-provider/. Use odori diff to compare upstream changes and odori update to apply updates.
Copy these files into your project
videos/components/brand-provider/brand-provider.tsx
import {Fill, Easing, interpolate, useBrand, useFrame, useDesignScale} from "odori";
export type BrandProviderProps = {
/** Which token groups to show. */
show?: Array<"colors" | "type" | "motion">;
title?: string;
};
/**
* Reads the resolved brand and renders it. Every token on screen is the value
* the surrounding video actually uses, so this doubles as a brand sheet and as
* a check that a layout inherited what you expected.
*/
export const BrandProvider = ({show = ["colors", "type", "motion"], title = "Brand tokens"}: BrandProviderProps) => {
const frame = useFrame();
const brand = useBrand();
const scale = useDesignScale();
const swatches = Object.entries(brand.colors);
const row = (index: number) => {
const delay = 8 + index * 4;
const progress = interpolate(frame, [delay, delay + 20], [0, 1], {easing: Easing.standard});
return {opacity: progress, transform: `translateY(${(1 - progress) * 18 * scale}px)`};
};
return (
<Fill
style={{
gap: 40 * scale,
justifyContent: "center",
padding: `${110 * scale}px ${130 * scale}px`,
}}
>
<div style={{fontSize: 62 * scale, fontWeight: 570, letterSpacing: "-0.04em", ...row(0)}}>{title}</div>
{show.includes("colors") ? (
<div style={{display: "flex", flexWrap: "wrap", gap: 18 * scale}}>
{swatches.map(([name, value], index) => (
<div key={name} style={{alignItems: "center", display: "flex", gap: 12 * scale, ...row(index + 1)}}>
<span
style={{
background: value,
border: `1px solid ${brand.colors.border}`,
borderRadius: 12 * scale,
height: 56 * scale,
width: 56 * scale,
}}
/>
<span style={{fontFamily: brand.typography.mono, fontSize: 24 * scale}}>
{name}
<span style={{color: brand.colors.muted, display: "block"}}>{value}</span>
</span>
</div>
))}
</div>
) : null}
{show.includes("type") ? (
<div style={{display: "grid", gap: 8 * scale, ...row(swatches.length + 1)}}>
<span style={{fontFamily: brand.typography.sans, fontSize: 44 * scale}}>Sans, for statements</span>
<span style={{color: brand.colors.muted, fontFamily: brand.typography.mono, fontSize: 30 * scale}}>
Mono, for evidence
</span>
</div>
) : null}
{show.includes("motion") ? (
<div
style={{
color: brand.colors.muted,
fontFamily: brand.typography.mono,
fontSize: 26 * scale,
...row(swatches.length + 2),
}}
>
standard [{brand.motion.standard.join(", ")}] · stagger {brand.motion.staggerFrames}f
</div>
) : null}
</Fill>
);
};
videos/components/brand-provider/brand-provider.preview.tsx
import {defineComponentPreview} from "odori/preview";
import {BrandProvider} from "./brand-provider";
export default defineComponentPreview({
title: "Brand provider",
category: "Brand",
description: "The resolved brand rendered as a sheet: color, type, and motion.",
component: BrandProvider,
canvas: {width: 1920, height: 1080, duration: "4s"},
controls: {
title: {type: "text", defaultValue: "Brand tokens"},
},
examples: [
{name: "Everything", props: {}},
{name: "Colors only", props: {show: ["colors"], title: "Palette"}},
],
});
Add it to a scene
import {Scene, Video} from "odori";
import {BrandProvider} from "../components/brand-provider/brand-provider";
<Video>
<Scene id="brand-provider" duration="4s">
<BrandProvider
title="Brand tokens"
/>
</Scene>
</Video>Timing contract
Use these durations and content limits when composing a scene. Run odori test to check your video.
- Family
- Brand
- Recommended duration
- 120 frames · 4s
- Minimum duration
- 60 frames · 2s
- Entrance
- 40 frames
- Exit
- 0 frames
- Reduced motion
- rows appear together
- Requires
- sans font, mono font
- Content limits
- none
Props
| Prop | Type | Default | Required |
|---|---|---|---|
showWhich token groups to show. | Array<"colors" | "type" | "motion"> | ["colors" | — |
title | string | "Brand tokens" | — |
Related components
All componentsIcon setA stroked icon set that draws itself on, as editable SVG source.Identity revealAn animated product logo and name.Logo orbitProduct identity surrounded by connected integrations.Marks starterPlaceholder marks that draw themselves on, as editable SVG source.Absolute stageFull-frame surface with stable clipping and positioning.AccordionSections opening one at a time, the outgoing one closing as the next grows.