Cover
Fills the screen and centres one thing, with a header and footer at the edges.
SourcePackage @kerf-js/veneer
Preview
tsx
import { Cover } from '@kerf-js/veneer'export function Parts() { return ( <Cover style={SCREEN}> <Box>Header hugs the top</Box> <Box data-part="center" tall={96} > data-part="center" </Box> <Box>Footer hugs the bottom</Box> </Cover> )}type Status = | 'Live' | 'Building' | 'Failed' | 'Paused'const PROJECTS: { id: string name: string owner: string region: string status: Status deploys: number uptime: number latency: number cost: number updated: string}[] = [ { id: 'storefront', name: 'storefront', owner: 'Priya Raman', region: 'eu-west', status: 'Live', deploys: 412, uptime: 99.98, latency: 84, cost: 1240, updated: '2026-10-31', }, { id: 'checkout', name: 'checkout', owner: 'Tomás Oliveira', region: 'eu-west', status: 'Live', deploys: 288, uptime: 99.95, latency: 112, cost: 980, updated: '2026-10-31', }, { id: 'search-api', name: 'search-api', owner: 'Lena Fischer', region: 'us-east', status: 'Building', deploys: 196, uptime: 99.91, latency: 146, cost: 1610, updated: '2026-10-30', }, { id: 'billing', name: 'billing', owner: 'Mara Lindqvist', region: 'us-east', status: 'Live', deploys: 154, uptime: 99.99, latency: 73, cost: 720, updated: '2026-10-29', }, { id: 'mobile-bff', name: 'mobile-bff', owner: 'Sam Okafor', region: 'ap-south', status: 'Failed', deploys: 233, uptime: 99.42, latency: 208, cost: 860, updated: '2026-10-29', }, { id: 'notifier', name: 'notifier', owner: 'Jun Tanaka', region: 'eu-north', status: 'Live', deploys: 97, uptime: 99.97, latency: 58, cost: 310, updated: '2026-10-27', }, { id: 'reports', name: 'reports', owner: 'Noor Haddad', region: 'us-west', status: 'Paused', deploys: 41, uptime: 99.8, latency: 321, cost: 190, updated: '2026-10-22', }, { id: 'edge-cache', name: 'edge-cache', owner: 'Jun Tanaka', region: 'global', status: 'Live', deploys: 318, uptime: 100, latency: 21, cost: 1430, updated: '2026-10-31', }, { id: 'auth', name: 'auth', owner: 'Mara Lindqvist', region: 'eu-west', status: 'Live', deploys: 176, uptime: 99.99, latency: 66, cost: 540, updated: '2026-10-28', }, { id: 'ingest', name: 'ingest', owner: 'Lena Fischer', region: 'us-east', status: 'Live', deploys: 264, uptime: 99.93, latency: 131, cost: 2120, updated: '2026-10-30', },]const min = 60_000const BOX = 'display:grid;align-content:center;' + 'gap:2px;' + 'padding:var(--veneer-space-related);' + 'border:var(--veneer-border-hairline) ' + 'solid var(--veneer-color-border);' + 'border-radius:' + 'var(--veneer-radius-surface);' + 'background:var(--veneer-color-surface);' + 'color:var(--veneer-color-text);' + 'font-size:var(--veneer-text-caption-size);' + 'min-inline-size:0'const projectOf = (c: unknown) => { const n = Number(typeof c === 'function' ? c() : c) return Number.isInteger(n) && n > 0 ? PROJECTS[(n - 1) % PROJECTS.length] : undefined}function Box({ tall, children, ...rest}: { tall?: number children?: unknown [attr: string]: unknown}) { return ( <div style={ tall ? `${BOX};min-block-size:${tall}px` : BOX } {...rest} > {projectOf(children) ? ( <> <strong>{projectOf(children)!.name}</strong> <span style={ 'color:var(--veneer-color-text-subtle)' } > {projectOf(children)!.region} </span> </> ) : ( children )} </div> )}const SCREEN = '--veneer-viewport-block:360px;' + 'border:var(--veneer-border-hairline) ' + 'solid var(--veneer-color-border);' + 'border-radius:' + 'var(--veneer-radius-surface);' + 'padding-inline:var(--veneer-space-group)'Installation
npm install @kerf-js/veneerUsage
tsx
import { Cover } from '@kerf-js/veneer'<Cover style={SCREEN}> <Box>Header hugs the top</Box> <Box data-part="center" tall={96} > data-part="center" </Box> <Box>Footer hugs the bottom</Box></Cover>Inside a box
min="fill" fills its parent instead of the screen.
tsx
import { Cover } from '@kerf-js/veneer'export function Fill() { return ( <div style={FILL}> <Cover min="fill"> <Box data-part="center"> min="fill" takes its parent's height </Box> </Cover> </div> )}type Status = | 'Live' | 'Building' | 'Failed' | 'Paused'const PROJECTS: { id: string name: string owner: string region: string status: Status deploys: number uptime: number latency: number cost: number updated: string}[] = [ { id: 'storefront', name: 'storefront', owner: 'Priya Raman', region: 'eu-west', status: 'Live', deploys: 412, uptime: 99.98, latency: 84, cost: 1240, updated: '2026-10-31', }, { id: 'checkout', name: 'checkout', owner: 'Tomás Oliveira', region: 'eu-west', status: 'Live', deploys: 288, uptime: 99.95, latency: 112, cost: 980, updated: '2026-10-31', }, { id: 'search-api', name: 'search-api', owner: 'Lena Fischer', region: 'us-east', status: 'Building', deploys: 196, uptime: 99.91, latency: 146, cost: 1610, updated: '2026-10-30', }, { id: 'billing', name: 'billing', owner: 'Mara Lindqvist', region: 'us-east', status: 'Live', deploys: 154, uptime: 99.99, latency: 73, cost: 720, updated: '2026-10-29', }, { id: 'mobile-bff', name: 'mobile-bff', owner: 'Sam Okafor', region: 'ap-south', status: 'Failed', deploys: 233, uptime: 99.42, latency: 208, cost: 860, updated: '2026-10-29', }, { id: 'notifier', name: 'notifier', owner: 'Jun Tanaka', region: 'eu-north', status: 'Live', deploys: 97, uptime: 99.97, latency: 58, cost: 310, updated: '2026-10-27', }, { id: 'reports', name: 'reports', owner: 'Noor Haddad', region: 'us-west', status: 'Paused', deploys: 41, uptime: 99.8, latency: 321, cost: 190, updated: '2026-10-22', }, { id: 'edge-cache', name: 'edge-cache', owner: 'Jun Tanaka', region: 'global', status: 'Live', deploys: 318, uptime: 100, latency: 21, cost: 1430, updated: '2026-10-31', }, { id: 'auth', name: 'auth', owner: 'Mara Lindqvist', region: 'eu-west', status: 'Live', deploys: 176, uptime: 99.99, latency: 66, cost: 540, updated: '2026-10-28', }, { id: 'ingest', name: 'ingest', owner: 'Lena Fischer', region: 'us-east', status: 'Live', deploys: 264, uptime: 99.93, latency: 131, cost: 2120, updated: '2026-10-30', },]const min = 60_000const BOX = 'display:grid;align-content:center;' + 'gap:2px;' + 'padding:var(--veneer-space-related);' + 'border:var(--veneer-border-hairline) ' + 'solid var(--veneer-color-border);' + 'border-radius:' + 'var(--veneer-radius-surface);' + 'background:var(--veneer-color-surface);' + 'color:var(--veneer-color-text);' + 'font-size:var(--veneer-text-caption-size);' + 'min-inline-size:0'const projectOf = (c: unknown) => { const n = Number(typeof c === 'function' ? c() : c) return Number.isInteger(n) && n > 0 ? PROJECTS[(n - 1) % PROJECTS.length] : undefined}function Box({ tall, children, ...rest}: { tall?: number children?: unknown [attr: string]: unknown}) { return ( <div style={ tall ? `${BOX};min-block-size:${tall}px` : BOX } {...rest} > {projectOf(children) ? ( <> <strong>{projectOf(children)!.name}</strong> <span style={ 'color:var(--veneer-color-text-subtle)' } > {projectOf(children)!.region} </span> </> ) : ( children )} </div> )}const FILL = 'block-size:280px;' + 'border:var(--veneer-border-hairline) ' + 'solid var(--veneer-color-border);' + 'border-radius:' + 'var(--veneer-radius-surface);' + 'padding-inline:var(--veneer-space-group)'A sign-in page
Previews pin the screen's height with --veneer-viewport-block. In an app, leave that out.
tsx
import { state } from '@kerf-js/core'import { Button, Center, Cover, Heading, Input, Row, Stack, Text} from '@kerf-js/veneer'export function SignIn() { const sent = state(false) const send = (e: Event) => { e.preventDefault() sent.value = true } return ( <Cover style={`${SCREEN};--veneer-viewport-block:520px`} gap="group" > <Row justify="between" gap="tight" > <Text role="heading">kerf</Text> <Button emphasis="ghost" tone="neutral" size="sm" > Help </Button> </Row> <Center data-part="center" max="narrow" > {sent.value ? ( <Stack gap="related" align="center" > <Heading level={3}>Check your email</Heading> <Text subtle> We sent a sign-in link to you. </Text> <Button emphasis="ghost" tone="neutral" onClick={() => (sent.value = false)} > Use another address </Button> </Stack> ) : ( signInForm(send) )} </Center> <Text role="caption" subtle > © 2026 kerf </Text> </Cover> )}const SCREEN = '--veneer-viewport-block:360px;' + 'border:var(--veneer-border-hairline) ' + 'solid var(--veneer-color-border);' + 'border-radius:' + 'var(--veneer-radius-surface);' + 'padding-inline:var(--veneer-space-group)'function signInForm(send: (e: Event) => void) { return ( <Stack as="form" gap="group" onSubmit={send} > <Stack gap="hair"> <Heading level={3}>Sign in to kerf</Heading> <Text subtle> We'll email you a link. No password needed. </Text> </Stack> <Input label="Work email" type="email" required /> <Button type="submit" full > Email me a link </Button> </Stack> )}A not found page
Use Cover to fill the screen and Center to put a message in the middle.
tsx
import { Button, Center, Cover, Heading, Stack, Text} from '@kerf-js/veneer'export function NotFound() { return ( <Cover style={SCREEN}> <Center data-part="center" max="narrow" intrinsic > <Stack gap="related" align="center" > <Text role="eyebrow" subtle > 404 </Text> <Heading level={3} role="title" > No such project </Heading> <Text subtle> It may have been renamed, or you may not have access. </Text> <Button icon="arrow-left" emphasis="outline" tone="neutral" > Back to projects </Button> </Stack> </Center> </Cover> )}const SCREEN = '--veneer-viewport-block:360px;' + 'border:var(--veneer-border-hairline) ' + 'solid var(--veneer-color-border);' + 'border-radius:' + 'var(--veneer-radius-surface);' + 'padding-inline:var(--veneer-space-group)'When to use
Accessibility
- It sets no landmark. Pass
as="main"when it is the page.
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 least space kept between the parts, from the space scale. |
min | "screen" | "fill" | — | screen fills the viewport, fill fills its parent. |