Skip to content
weftGitHub

Center

A column of readable width, centred in whatever holds it.

SourcePackage @weft-js/warpPlayground

Preview

tsx
import { Center, Stack } from '@weft-js/warp'export function Widths() {  return (    <Stack gap="related">      {MAXES.map(max => <Center key={max} max={max}><Box>max="{max}"</Box></Center>)}    </Stack>  )}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 MAXES = ['narrow', 'measure', 'content'] as const

Installation

npm install @weft-js/warp

Usage

tsx
import { Center } from '@weft-js/warp'<Stack gap="related">  {MAXES.map(max => <Center key={max} max={max}><Box>max="{max}"</Box></Center>)}</Stack>

Gutter

gutter adds the page's side margin and clears a phone's notch.

tsx
import { Center, Inset } from '@weft-js/warp'export function Gutter() {  return (    <Inset>      <Center gutter>        <Box tall={72}>gutter keeps the page's side margin at any width</Box>      </Center>    </Inset>  )}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>  )}

Centre the content too

intrinsic also centres each child at its own width.

tsx
import { Button, Center, Heading, Stack, Text } from '@weft-js/warp'export function Intrinsic() {  return (    <Center max="narrow" intrinsic>      <Stack gap="related" align="center">        <Heading level={3}>Nothing failed this week</Heading>        <Text subtle>intrinsic centres each child at its own width, and centres the text.</Text>        <Button emphasis="outline" tone="neutral">See all deploys</Button>      </Stack>    </Center>  )}

An article

Use max set to measure to hold text to a comfortable line length.

tsx
import { Center, Heading, Stack, Text } from '@weft-js/warp'export function Article() {  return (    <Center max="measure">      <Stack gap="related">        <Heading level={3}>Lower stale windows at the edge</Heading>        <Text subtle>By Jun Tanaka</Text>        <Text>We moved the edge cache's stale window from two minutes to thirty seconds. Fresh pages now reach people sooner, and the origin barely noticed.</Text>        <Text>Prose is easiest to read at about seventy characters a line, so articles sit in the measure rather than the page's full width.</Text>      </Stack>    </Center>  )}const TRAFFIC = [  { month: 'Nov', requests: 41.2, errors: 0.42, deploys: 88, cost: 6120 },  { month: 'Dec', requests: 52.8, errors: 0.51, deploys: 74, cost: 6480 },  { month: 'Jan', requests: 47.1, errors: 0.38, deploys: 96, cost: 6390 },  { month: 'Feb', requests: 49.6, errors: 0.33, deploys: 104, cost: 6710 },  { month: 'Mar', requests: 58.3, errors: 0.47, deploys: 118, cost: 7240 },  { month: 'Apr', requests: 61.9, errors: 0.29, deploys: 126, cost: 7380 },  { month: 'May', requests: 66.4, errors: 0.31, deploys: 131, cost: 7690 },  { month: 'Jun', requests: 64.2, errors: 0.44, deploys: 122, cost: 7620 },  { month: 'Jul', requests: 71.5, errors: 0.27, deploys: 139, cost: 8010 },  { month: 'Aug', requests: 74.8, errors: 0.25, deploys: 144, cost: 8230 },  { month: 'Sep', requests: 79.3, errors: 0.36, deploys: 152, cost: 8560 },  { month: 'Oct', requests: 86.1, errors: 0.22, deploys: 167, cost: 8930 },]const now = TRAFFIC[11]!, before = TRAFFIC[10]!

When to use

Use it for

  • Articles and other long text
  • Sign-in and other single forms
  • Keeping a page's content from getting too wide

Reach for something else

  • Cover, when it should centre vertically too

Playground

Max
import { Center } from '@weft-js/warp'<Center max="narrow">  <Box>A measured column</Box></Center>

Accessibility

  • This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and warp's parts keep their roles and labels.

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 space between children, from warp's space scale.
max"measure" | "narrow" | "content" | "wide"—How wide it may get: narrow, measure, content or wide.
gutterbooleanfalseAdds the page's side margin, and the safe areas on a phone.
intrinsicbooleanfalseCentres each child at its own width, and centres text.

RelatedCoverStackSplit