# Center

> Centers its children horizontally and vertically inside a relative container. Single-purpose alternative to ad-hoc `flex items-center justify-center` snippets scattered through the codebase.

- Status: beta
- Group: structure
- Page: https://www.digitaltableteur.com/design-system/components/center
- Contract: https://www.digitaltableteur.com/contracts/v1/Center.contract.json

## When to use

Center puts one focal thing in the middle of a box; a spinner, an empty state, a logo. It only centers within the space it has, so give it a height (or place it in a sized parent). For text flows or multi-item layouts use Stack; Center is for a single focal point.

## When not to use

- Do not center large layout sections; use `Section` + `Container` instead.
- Do not rely on this to vertically center text inside a button or input; those already center via their own padding.
- Do not use Center as a page layout root; that is Container + Section.

## Look-alikes

- Container: Do not use Center as a page layout root; that is Container + Section.
- EmptyState: Use it for single focal content: Spinner, EmptyState, a logo lockup.
- Section: Do not use Center as a page layout root; that is Container + Section.
- Spinner: Use it for single focal content: Spinner, EmptyState, a logo lockup.

## Props

| Prop | Type | Default | Description | Required |
| --- | --- | --- | --- | --- |
| `children` | `ReactNode` | none | Content to center inside the flex container. | Yes |
| `className` | `string` | none | Additional CSS class names (often a height constraint). | No |
| `as` | `"symbol" \| "object" \| "image" \| "form" \| "slot" \| "style" \| "title" \| "button" \| "search" \| "article" \| "dialog" \| "figure" \| "img" \| "link" \| "main" \| "menu" \| "menuitem" \| "option" \| "switch" \| "table" \| "text" \| "time" \| "div" \| "section" \| "aside" \| "li" \| "a" \| "abbr" \| "address" \| "area" \| "audio" \| "b" \| "base" \| "bdi" \| "bdo" \| "big" \| "blockquote" \| "body" \| "br" \| "canvas" \| "caption" \| "center" \| "cite" \| "code" \| "col" \| "colgroup" \| "data" \| "datalist" \| "dd" \| "del" \| "details" \| "dfn" \| "dl" \| "dt" \| "em" \| "embed" \| "fieldset" \| "figcaption" \| "footer" \| "h1" \| "h2" \| "h3" \| "h4" \| "h5" \| "h6" \| "head" \| "header" \| "hgroup" \| "hr" \| "html" \| "i" \| "iframe" \| "input" \| "ins" \| "kbd" \| "keygen" \| "label" \| "legend" \| "map" \| "mark" \| "meta" \| "meter" \| "nav" \| "noindex" \| "noscript" \| "ol" \| "optgroup" \| "output" \| "p" \| "param" \| "picture" \| "pre" \| "progress" \| "q" \| "rp" \| "rt" \| "ruby" \| "s" \| "samp" \| "script" \| "select" \| "small" \| "source" \| "span" \| "strong" \| "sub" \| "summary" \| "sup" \| "template" \| "tbody" \| "td" \| "textarea" \| "tfoot" \| "th" \| "thead" \| "tr" \| "track" \| "u" \| "ul" \| "var" \| "video" \| "wbr" \| "webview" \| "svg" \| "animate" \| "animateMotion" \| "animateTransform" \| "circle" \| "clipPath" \| "defs" \| "desc" \| "ellipse" \| "feBlend" \| "feColorMatrix" \| "feComponentTransfer" \| "feComposite" \| "feConvolveMatrix" \| "feDiffuseLighting" \| "feDisplacementMap" \| "feDistantLight" \| "feDropShadow" \| "feFlood" \| "feFuncA" \| "feFuncB" \| "feFuncG" \| "feFuncR" \| "feGaussianBlur" \| "feImage" \| "feMerge" \| "feMergeNode" \| "feMorphology" \| "feOffset" \| "fePointLight" \| "feSpecularLighting" \| "feSpotLight" \| "feTile" \| "feTurbulence" \| "filter" \| "foreignObject" \| "g" \| "line" \| "linearGradient" \| "marker" \| "mask" \| "metadata" \| "mpath" \| "path" \| "pattern" \| "polygon" \| "polyline" \| "radialGradient" \| "rect" \| "set" \| "stop" \| "textPath" \| "tspan" \| "use" \| "view"` | `div` | Polymorphic wrapper element. | No |

## Variants

- Allowed `as` values: `symbol`, `object`, `image`, `form`, `slot`, `style`, `title`, `button`, `search`, `article`, `dialog`, `figure`, `img`, `link`, `main`, `menu`, `menuitem`, `option`, `switch`, `table`, `text`, `time`, `div`, `section`, `aside`, `li`, `a`, `abbr`, `address`, `area`, `audio`, `b`, `base`, `bdi`, `bdo`, `big`, `blockquote`, `body`, `br`, `canvas`, `caption`, `center`, `cite`, `code`, `col`, `colgroup`, `data`, `datalist`, `dd`, `del`, `details`, `dfn`, `dl`, `dt`, `em`, `embed`, `fieldset`, `figcaption`, `footer`, `h1`, `h2`, `h3`, `h4`, `h5`, `h6`, `head`, `header`, `hgroup`, `hr`, `html`, `i`, `iframe`, `input`, `ins`, `kbd`, `keygen`, `label`, `legend`, `map`, `mark`, `meta`, `meter`, `nav`, `noindex`, `noscript`, `ol`, `optgroup`, `output`, `p`, `param`, `picture`, `pre`, `progress`, `q`, `rp`, `rt`, `ruby`, `s`, `samp`, `script`, `select`, `small`, `source`, `span`, `strong`, `sub`, `summary`, `sup`, `template`, `tbody`, `td`, `textarea`, `tfoot`, `th`, `thead`, `tr`, `track`, `u`, `ul`, `var`, `video`, `wbr`, `webview`, `svg`, `animate`, `animateMotion`, `animateTransform`, `circle`, `clipPath`, `defs`, `desc`, `ellipse`, `feBlend`, `feColorMatrix`, `feComponentTransfer`, `feComposite`, `feConvolveMatrix`, `feDiffuseLighting`, `feDisplacementMap`, `feDistantLight`, `feDropShadow`, `feFlood`, `feFuncA`, `feFuncB`, `feFuncG`, `feFuncR`, `feGaussianBlur`, `feImage`, `feMerge`, `feMergeNode`, `feMorphology`, `feOffset`, `fePointLight`, `feSpecularLighting`, `feSpotLight`, `feTile`, `feTurbulence`, `filter`, `foreignObject`, `g`, `line`, `linearGradient`, `marker`, `mask`, `metadata`, `mpath`, `path`, `pattern`, `polygon`, `polyline`, `radialGradient`, `rect`, `set`, `stop`, `textPath`, `tspan`, `use`, `view` (default `div`).

## Invalid combinations

The contract records no invalid combinations.

## Keyboard

The contract records no keyboard interaction.

## Rules

Every item under When not to use is a MUST NOT. MUST and SHOULD follow RFC 2119.

| Level | Rule | How it is checked |
| --- | --- | --- |
| MUST | ARIA: No own ARIA; inherits semantics from its children. | Not yet verified: no evidence record. WCAG 4.1.2. |
| MUST | Required stories produce no axe violations at the configured severity. | Automated by `npm run test:stories:matrix:ci`. WCAG 2.5.8. |
| MUST | The accessibility tree (role, name, state) matches the committed snapshot for every required story. | Automated by `npm run test:stories:matrix:ci`. WCAG 1.3.1, 4.1.2. |
| MUST | The component remains legible and operable under a real browser forced-colors: active pass. | Automated by `npm run test:stories:hc`. |
| MUST | A human completed an end-to-end accessibility review of this component. | Manual review, last on 2026-05-28. |
| SHOULD | Give Center a box: a min-height class or a sized parent; centering inside an auto-height div is a no-op. | Not stated in the contract. |
| SHOULD | Use it for single focal content: Spinner, EmptyState, a logo lockup. | Not stated in the contract. |
| SHOULD | Compose EmptyState inside Center for the canonical empty view. | Not stated in the contract. |
| SHOULD | Use as to keep semantics when the centered block is a section or figure. | Not stated in the contract. |
| SHOULD NOT | Do not center long prose; ragged both-side edges hurt reading; align text blocks left. | Not stated in the contract. |

## Install and import

- Install: `npm install @digitaltableteur/react` (version 0.9.1, restricted access on npm).
- Import: `import { Center } from "@digitaltableteur/react";`
- Entry point: `@digitaltableteur/react/layout` exports it too.
- Styles: import `@digitaltableteur/react/style.css` once at the app boundary.
- Also available as the custom element `<dt-center>`; see the custom elements manifest.

## Links

- Machine-readable contract (Design System Contract 1.0): https://www.digitaltableteur.com/contracts/v1/Center.contract.json
- Markdown version of this page: https://www.digitaltableteur.com/design-system/components/center.md
- Component index as JSON: https://www.digitaltableteur.com/design-system/components.json
- All components: https://www.digitaltableteur.com/design-system/components
- Source: The implementation lives in a private repository. This page, its Markdown twin and the contract are generated from it at build time and are the public source of truth.
