# Box

_Box Kite 2.2.5 · a markdown copy of https://www.box-kite.dev/box/_

One component, 235 CSS properties as typed props, and no stylesheet anywhere. This page is the whole of it: what a Box is, the five things that are not what you would guess, and every prop with the CSS it writes.

## A Box is one element, and every style is a prop

`Box` renders a `<div>` and takes 235 CSS properties as props. There is no stylesheet to import, no class string to compose and no build step: the value you write is checked by the compiler, turned into one CSS rule, and given a class name that every element writing the same value shares.

**Import it, and write CSS as props**

```jsx
import Box from '@box-kite/react';

<Box p={4} borderRadius={2} bgColor="indigo-600" color="white" fontSize={14} fontWeight={600}>
  Ship it
</Box>
```

That is the entire idea, and most of what follows is consequences of it. The one thing worth knowing before you write anything is where a memory of another library will mislead you — which is the next five cards, and then you are done.

## Five things to know first

Each of these typechecks either way, so the compiler will not catch it. They are the whole list.

1

You might expect · A number is a pixel value.

A number is a step on that prop’s own scale

Spacing, sizing and radius divide by 4; fontSize divides by 16; border width, line height and every duration are written out as they stand. The table two sections down is all nine families with one number in them.

`<Box p={16} fontSize={14} b={1} /> // 16px of padding?`

`<Box p={4} fontSize={14} b={1} /> // padding: 1rem · font-size: 0.875rem · border-width: 1px`

2

You might expect · Anything the props miss goes in style={{ }}.

There is no style prop — css is, and it is a class

It takes the same style object and compiles it into a shared class, so it nests inside hover, a breakpoint or a theme like every other prop and renders on a server. An inline style can do none of that.

`<Box style={{ mixBlendMode: 'multiply' }} />`

`<Box css={{ mixBlendMode: 'multiply' }} />`

The whole story: [Escape Hatch](https://www.box-kite.dev/escape-hatch.md)

3

You might expect · You pick the element with tag.

The element comes from the component

There is one per tag you are likely to want, each with its defaults already decided. tag is the fallback for the rest, and a display value is never how you pick one.

`<Box tag="button"> · <Box tag="h1"> · <Box display="flex">`

`<Button> · <H1> · <Flex>`

4

You might expect · HTML attributes sit beside the style props.

HTML attributes go in props

Box forwards that object and nothing else. An href or a data-state written at the top level still typechecks — JSX allows any hyphenated attribute — and is then dropped, with nothing anywhere to say so.

`<Link href="/about" data-state="open">`

`<Link props={{ href: '/about', 'data-state': 'open' }}>`

5

You might expect · A hover style needs a second prop, a class or a state hook.

Every prop nests

hover, focus, a breakpoint, a theme and a dozen others each take the same props again, so one element carries every state it has. None of them re-renders anything: they were all in the stylesheet before the page loaded.

`<Box bgColor="slate-100" hover={{ bgColor: 'slate-200' }} md={{ p: 6 }} theme={{ dark: { bgColor: 'slate-800' } }} />`

## The numbers, measured

The divider belongs to the prop, not to the library. Put one number in and see what each family does with it — every line below is the CSS this page asked the real engine for, not a formula written out beside it.

| You write | The CSS it writes | Measured in | Why |
| --- | --- | --- | --- |
| `p={4}` | `padding: 1rem` | rem, ÷4 | The spacing scale. 4 is 1rem, which is the step everything else is a multiple of. |
| `gap={4}` | `gap: 1rem` | rem, ÷4 | Same scale as padding and margin, so a gap lines up with the space around it. |
| `width={4}` | `width: 1rem` | rem, ÷4 | Sizes are on the spacing scale too — and take "1/2", "fit" and "fit-screen" besides. |
| `borderRadius={4}` | `border-radius: 1rem` | rem, ÷4 | The spacing scale again, so a radius matches the padding inside it. |
| `fontSize={4}` | `font-size: 0.25rem` | rem, ÷16 | Divided by 16, so the number you write is the pixel size you meant. |
| `lineHeight={4}` | `line-height: 4px` | px | A line box is a measurement, not a step on a scale. |
| `b={4}` | `border-width: 4px` | px | A border is drawn in device pixels. 1 means one pixel, not a quarter of a rem. |
| `strokeWidth={4}` | `stroke-width: 4` | user units | SVG has its own coordinate system, so no divider and no unit at all. |
| `transitionDuration={4}` | `transition-duration: 4ms` | ms | Every time on every prop is milliseconds. |

A value a prop does not accept writes **nothing** — no rule and no class name — rather than a broken declaration. That is why the reference below prints the CSS each prop really emits: a typed prop is a promise that what typechecks works.

## Build a card, one prop at a time

Six steps, each adding one idea. Copy any of them; they all run as they stand.

### 1 · A Box is a div

No props at all. Box resets the browser's margins and padding to nothing and decides nothing else.

```jsx
<Box>Monthly revenue</Box>
```

### 2 · Space and a surface — p, bgColor, borderRadius

p={4} is 1rem on all four sides. A colour is a palette token rather than a hex value, which is what lets a theme move it later.

```jsx
<Box p={4} bgColor="white" borderRadius={2}>
  Monthly revenue
</Box>
```

### 3 · Edges — b, borderColor, shadow

b={1} is one pixel, not a quarter of a rem: border width is the family written out in pixels. shadow takes a named step rather than four numbers.

```jsx
<Box p={4} bgColor="white" borderRadius={2} b={1} borderColor="slate-200" shadow="small">
  Monthly revenue
</Box>
```

### 4 · Lay the content out — Flex, never display='flex'

Flex is a Box that has already decided its display, so the intent is in the element. Everything else is still a Box prop.

```jsx
<Flex d="column" gap={1} p={4} bgColor="white" borderRadius={2} b={1} borderColor="slate-200" shadow="small">
  <Box fontSize={13} fontWeight={500} color="slate-500">Monthly revenue</Box>
  <Box fontSize={28} fontWeight={700} color="slate-900">$48,200</Box>
</Flex>
```

### 5 · Answer the pointer — hover, transitionDuration

hover takes the same props again. Nothing re-renders when the pointer arrives: both states were in the stylesheet before the page loaded.

```jsx
<Flex
  d="column"
  gap={1}
  p={4}
  bgColor="white"
  borderRadius={2}
  b={1}
  borderColor="slate-200"
  shadow="small"
  transitionDuration={150}
  hover={{ shadow: 'medium', translateY: -0.5, borderColor: 'indigo-300' }}
>
  <Box fontSize={13} fontWeight={500} color="slate-500">Monthly revenue</Box>
  <Box fontSize={28} fontWeight={700} color="slate-900">$48,200</Box>
</Flex>
```

### 6 · Answer the theme and the width — theme, md

A theme and a breakpoint nest the way hover does. Six props deep, one element, and still no stylesheet — switch this page's theme and watch the card follow.

```jsx
<Flex
  d="column"
  gap={1}
  p={4}
  md={{ p: 6 }}
  bgColor="white"
  borderRadius={2}
  b={1}
  borderColor="slate-200"
  shadow="small"
  transitionDuration={150}
  hover={{ shadow: 'medium', translateY: -0.5, borderColor: 'indigo-300' }}
  theme={{ dark: { bgColor: 'slate-800', borderColor: 'slate-700', hover: { borderColor: 'indigo-500' } } }}
>
  <Box fontSize={13} fontWeight={500} color="slate-500">Monthly revenue</Box>
  <Box fontSize={28} fontWeight={700} color="slate-900" theme={{ dark: { color: 'white' } }}>$48,200</Box>
</Flex>
```

## One prop, many states

A nesting key takes the same props again, and they compose in either direction — a breakpoint inside a theme, a hover inside a container query. Every combination is one more rule in the stylesheet and no more work at runtime.

**A button that answers four different things**

```jsx
<Box
  tag="button"
  px={4}
  py={2}
  borderRadius={2}
  bgColor="indigo-600"
  color="white"
  transitionDuration={150}
  hover={{ bgColor: 'indigo-500' }}
  focus={{ outline: 2, outlineColor: 'indigo-400', outlineOffset: 2 }}
  md={{ px: 6 }}
  theme={{ dark: { bgColor: 'indigo-500', hover: { bgColor: 'indigo-400' } } }}
>
  Hover me, focus me, resize me
</Box>
```

| Key | What it selects | Written |
| --- | --- | --- |
| `hover, focus, active, disabled, checked, visited, open, …` | A state the browser knows the element is in. | `hover={{ bgColor: 'slate-200' }}` |
| `sm, md, lg, xl, xxl` | A viewport at least that wide — 640, 768, 1024, 1280, 1536px. | `md={{ p: 6 }}` |
| `cq` | The width of the nearest container, rather than of the window. [Container Queries](https://www.box-kite.dev/container-queries.md) | `cq={{ md: { d: 'row' } }}` |
| `theme` | A named theme on an ancestor — light and dark, or one of your own. [Theme Setup](https://www.box-kite.dev/theme-setup.md) | `theme={{ dark: { bgColor: 'slate-800' } }}` |
| `dataAttr, ariaAttr, has, not, nth` | A state your own code sets, what the element contains, or where it sits among its siblings. [State Variants](https://www.box-kite.dev/variants.md) | `dataAttr={{ 'state=open': { rotate: 180 } }}` |
| `group, peer` | Something an ancestor or a preceding sibling is doing. [State Variants](https://www.box-kite.dev/variants.md) | `group={{ 'card/hover': { opacity: 1 } }}` |
| `before, after, placeholder, selection, marker, …` | A pseudo-element. CSS allows one, so the types allow one. [Pseudo-Elements](https://www.box-kite.dev/pseudo-elements.md) | `after={{ content: 'New', color: 'amber-500' }}` |
| `startingStyle` | What a just-mounted element starts from, so it transitions in. [Animation](https://www.box-kite.dev/animation.md) | `startingStyle={{ opacity: 0, translateY: 2 }}` |
| `motionReduce, forcedColors, contrastMore, pointerCoarse, pointerFine` | What the reader asked their operating system for. Ranked above every breakpoint. | `motionReduce={{ transition: 'none' }}` |
| `rtl, ltr` | The direction this element resolved to, read off dir. [Right to Left](https://www.box-kite.dev/rtl.md) | `rtl={{ flip: 'xAxis' }}` |

## The props that are not styles

Nine names on Box mean something other than CSS. If you are looking for one of these in the reference below, this is why it is not there.

| Prop | Type | What it does |
| --- | --- | --- |
| `tag` | `'div' \| 'section' \| 'button' \| …` | The element to render. The fallback for a tag no component covers. |
| `props` | `attributes of that tag` | Every HTML attribute: href, type, name, onClick, role, data-* and aria-*. Box forwards this object and nothing else. |
| `id` | `string` | Lifted to the top level because an id is written so often. It reaches the element unchanged. |
| `className` | `string \| string[] \| Record<string, boolean>` | Merged with the classes the engine resolved — for a class from somewhere else, not for styling. |
| `component, variant` | `a registered name` | The styles a Box.components() entry holds, and one of its variants. Both names are typed. |
| `clean` | `boolean` | Drop the component defaults this element would otherwise inherit and start from nothing. |
| `children` | `ReactNode \| (({ isHover }) => ReactNode)` | The content. A function form is handed the hover state. |
| `style` | `CSSProperties` | It exists, and it is almost never the answer — use css instead. The library itself reaches for it three times, all per instance and per frame: a slider thumb’s offset, an anchor’s name and a dragged widget’s position. |

## Every prop, searchable

All 235 of them, with the CSS each one writes — measured from the engine when the reference was generated, so an example here is never a guess. Search by prop name, by CSS property or by a value you are looking for.

The same table as one file, for an editor or an agent, is [/props.md](https://www.box-kite.dev/props.md). Everything Box carries that is _not_ a prop — `Box.extend()`, `Box.components()`, `Box.Theme` and the rest — is on [Box functions](https://www.box-kite.dev/box-functions.md).

---

_Every page: https://www.box-kite.dev/llms.txt · every prop, measured: https://www.box-kite.dev/props.md_

