Skip to content
kerfjsGitHub

Center

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

SourcePackage @kerf-js/veneerTry it

Preview

tsx
import { Center, Stack } from '@kerf-js/veneer'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(--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 MAXES = [  'narrow',  'measure',  'content',] as const

Installation

npm install @kerf-js/veneer

Try 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
Max
Codeupdates as you change the controls
import { Center } from '@kerf-js/veneer'<Center max="narrow">  <Box>A measured column</Box></Center>

Usage

tsx
import { Center } from '@kerf-js/veneer'<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 '@kerf-js/veneer'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(--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>  )}

Centre the content too

intrinsic also centres each child at its own width.

tsx
import {  Button,  Center,  Heading,  Stack,  Text} from '@kerf-js/veneer'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 '@kerf-js/veneer'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>  )}

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

Accessibility

  • This has no shortcuts of its own: controls inside it use Tab, Enter and Space as usual, and veneer'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 veneer'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