Skip to content
weftGitHub

Cover

Fills the screen and centres one thing, with a header and footer at the edges.

SourcePackage @weft-js/warp

Preview

tsx
import { Cover } from '@weft-js/warp'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(--warp-space-related);border:var(--warp-border-hairline) solid var(--warp-color-border);border-radius:var(--warp-radius-surface);background:var(--warp-color-surface);color:var(--warp-color-text);font-size:var(--warp-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(--warp-color-text-subtle)">{projectOf(children)!.region}</span></> : children}    </div>  )}const SCREEN = '--warp-viewport-block:360px;border:var(--warp-border-hairline) solid var(--warp-color-border);border-radius:var(--warp-radius-surface);padding-inline:var(--warp-space-group)'

Installation

npm install @weft-js/warp

Usage

tsx
import { Cover } from '@weft-js/warp'<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 '@weft-js/warp'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(--warp-space-related);border:var(--warp-border-hairline) solid var(--warp-color-border);border-radius:var(--warp-radius-surface);background:var(--warp-color-surface);color:var(--warp-color-text);font-size:var(--warp-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(--warp-color-text-subtle)">{projectOf(children)!.region}</span></> : children}    </div>  )}const FILL = 'block-size:280px;border:var(--warp-border-hairline) solid var(--warp-color-border);border-radius:var(--warp-radius-surface);padding-inline:var(--warp-space-group)'

A sign-in page

Previews pin the screen's height with --warp-viewport-block. In an app, leave that out.

tsx
import { state } from '@weft-js/core'import { Button, Center, Cover, Heading, Input, Row, Stack, Text } from '@weft-js/warp'export function SignIn() {  const sent = state(false)  const send = (e: Event) => { e.preventDefault(); sent.value = true }  return (    <Cover style={`${SCREEN};--warp-viewport-block:520px`} gap="group">      <Row justify="between" gap="tight">        <Text role="heading">weft</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>          )          : (            <Stack as="form" gap="group" onSubmit={send}>              <Stack gap="hair">                <Heading level={3}>Sign in to weft</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>          )}      </Center>      <Text role="caption" subtle>© 2026 weft</Text>    </Cover>  )}const SCREEN = '--warp-viewport-block:360px;border:var(--warp-border-hairline) solid var(--warp-color-border);border-radius:var(--warp-radius-surface);padding-inline:var(--warp-space-group)'

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 '@weft-js/warp'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 = '--warp-viewport-block:360px;border:var(--warp-border-hairline) solid var(--warp-color-border);border-radius:var(--warp-radius-surface);padding-inline:var(--warp-space-group)'

When to use

Use it for

  • Sign-in and sign-up pages
  • Error and empty pages
  • A landing hero

Reach for something else

  • Center, when it only needs to centre across, not down
  • Shell, when the page has navigation

Accessibility

  • It sets no landmark. Pass as="main" when it is the page.

API Reference

PropTypeDefaultDescription
childrenunknown—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.

RelatedCenterShellSign in