Frame
A box that holds its shape, for images, video and previews.
Preview
tsx
import { Frame, Grid, Text } from '@kerf-js/veneer'export function Ratios() { return ( <Grid min="xs" cols={6} gap="related" > {RATIOS.map((ratio) => ( <Frame key={ratio} ratio={ratio} > <Text role="caption" subtle > {ratio} </Text> </Frame> ))} </Grid> )}const min = 60_000const RATIOS = [ '21:9', '16:9', '3:2', '4:3', 'square', '3:4',] as constInstallation
npm install @kerf-js/veneerTry it
Change the controls: the preview and its code update together. The address keeps what you set, so you can link to it.
Live preview
Controls
Codeupdates as you change the controls
import { Frame } from '@kerf-js/veneer'<Frame> <img src="/preview.png" alt="" /></Frame>Usage
tsx
import { Frame } from '@kerf-js/veneer'Media crops to fit
An image, video, canvas or SVG directly inside fills the frame and crops.
tsx
import { Frame, Grid } from '@kerf-js/veneer'export function Media() { return ( <Grid min="xs" cols={3} gap="related" > <Frame>{art('1', '3')}</Frame> <Frame ratio="square">{art('2', '5')}</Frame> <Frame ratio="3:4">{art('4', '7')}</Frame> </Grid> )}const min = 60_000const art = (a: string, b: string) => ( <svg viewBox="0 0 320 180" preserveAspectRatio="xMidYMid slice" aria-hidden="true" > <rect width="320" height="180" fill={`var(--veneer-color-data-${a})`} /> <circle cx="96" cy="150" r="130" fill={`var(--veneer-color-data-${b})`} /> <rect x="176" y="36" width="104" height="108" rx="8" fill="var(--veneer-color-surface)" opacity="0.85" /> </svg>)Deploy previews
Use ratio to give each preview the same shape, here 16:9.
tsx
import { Card, Frame, Grid, Stack, Text} from '@kerf-js/veneer'export function Previews() { return ( <Grid min="sm" cols={3} gap="related" > {DEPLOYS.slice(0, 3).map((d, n) => ( <Card key={d.id}> <Stack gap="related"> <Frame ratio="16:9"> {art(String(n + 1), String(n + 4))} </Frame> <Stack gap="hair"> <Text role="heading">{d.project}</Text> <Text role="caption" subtle > {d.message} </Text> </Stack> </Stack> </Card> ))} </Grid> )}const DEPLOYS = [ { id: 'd-4821', project: 'storefront', branch: 'main', commit: '9f3ac21', message: 'Cache product images at the edge', author: 'Priya Raman', status: 'Live', minutes: 2, at: '2026-10-31T16:42', }, { id: 'd-4820', project: 'search-api', branch: 'ranking-v2', commit: '1be07d4', message: 'Rank exact matches first', author: 'Lena Fischer', status: 'Building', minutes: 6, at: '2026-10-31T16:10', }, { id: 'd-4819', project: 'mobile-bff', branch: 'main', commit: 'c44e190', message: 'Retry timed out upstream calls', author: 'Sam Okafor', status: 'Failed', minutes: 4, at: '2026-10-31T15:27', }, { id: 'd-4818', project: 'checkout', branch: 'main', commit: '70d2fa8', message: 'Add Apple Pay to the last step', author: 'Tomás Oliveira', status: 'Live', minutes: 3, at: '2026-10-31T14:03', }, { id: 'd-4817', project: 'edge-cache', branch: 'main', commit: 'e81b6c2', message: 'Lower the stale window to 30s', author: 'Jun Tanaka', status: 'Live', minutes: 1, at: '2026-10-31T11:55', }, { id: 'd-4816', project: 'billing', branch: 'main', commit: '2ad9e03', message: 'Prorate seats on plan change', author: 'Mara Lindqvist', status: 'Live', minutes: 3, at: '2026-10-29T17:20', },]const min = 60_000const change = (a: number, b: number) => { const sign = a >= b ? '+' : '' const percent = (((a - b) / b) * 100).toFixed(1) return `${sign}${percent}%`}const art = (a: string, b: string) => ( <svg viewBox="0 0 320 180" preserveAspectRatio="xMidYMid slice" aria-hidden="true" > <rect width="320" height="180" fill={`var(--veneer-color-data-${a})`} /> <circle cx="96" cy="150" r="130" fill={`var(--veneer-color-data-${b})`} /> <rect x="176" y="36" width="104" height="108" rx="8" fill="var(--veneer-color-surface)" opacity="0.85" /> </svg>)When to use
Use it for
- Images and video that must not shift the page as they load
- Thumbnails of mixed sizes that should line up
Reach for something else
- Card, when it needs a surface and padding
Accessibility
- Frame draws nothing itself, so give an image its
altas usual.
API Reference
| Prop | Type | Default | Description |
|---|---|---|---|
children | unknown | — | What it contains. |
as | "div" | "section" | "article" | "main" | "header" | "footer" | "nav" | "… | — | The element it renders as. |
gap | "section" | "none" | "hair" | "tight" | "related" | "group" | "page" | — | The space between children, from veneer's space scale. |
ratio | "square" | "4:3" | "3:2" | "16:9" | "21:9" | "3:4" | "9:16" | '16:9' | Width to height. 16:9 by default. |