# Box functions

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

Everything Box carries that is not a prop: add props of your own, name a set of styles, register keyframes, sample a spring, switch themes and configure the engine — with the smallest real example of each.

## The whole surface

Ten names, and you will use two of them. `Box.components()` is how a design system is written here, and `Box.extend()` is how the prop set grows. The other eight are for the day you need them.

| Name | What it is for |
| --- | --- |
| [`Box.extend()`](#extend) | Add CSS variables, props of your own, and extra values on props that already exist. Afterwards they are indistinguishable from built-ins. |
| [`Box.components()`](#components) | Name a set of styles — with variants, named parts and inheritance — and wear it with component="card". |
| [`Box.keyframes()`](#keyframes) | Register an @keyframes sequence whose steps are Box props. Nothing is emitted until a rule names it. |
| [`Box.spring()`](#spring) | Sample a damped oscillator into { easing, duration } — the two halves the timing-function and duration props take. No runtime. |
| [`Box.viewTransition()`](#viewtransition) | Run a DOM change inside a view transition where the browser has one, and plainly where it has not — the same three promises either way. |
| [`Box.Theme`](#theme) | The provider that writes a theme class onto <html> or onto a wrapper of its own, follows the system preference and persists a choice. |
| [`Box.useTheme()`](#usetheme) | Read the theme the nearest provider settled on, and set it. Passing null hands control back to the system preference. |
| [`Box.configure()`](#configure) | Tell the engine how to name classes, where to write rules and what the base class transitions. Call it once, before the first render. |
| [`Box.getVariableValue()`](#getvariablevalue) | The var(--…) reference behind a token, declared on first use — for handing a themed colour to something that takes a string. |
| [`useClassNames()`](#useclassnames) | Box props as a class attribute, for an element Box cannot render: a router’s NavLink, a motion.div, an icon from another library. |
| [`useVisibility()`](#usevisibility) | Open/closed state that closes itself on an outside press, on Escape, and optionally on scroll or resize. |

Everything is registered on one engine, shared by the whole app, so these are called once at module scope — not in a component, not in an effect. Register before the first render and nothing has to be re-emitted.

## Box.components()

`Box.components({ card: { styles, variants, children, extends } })`

A named set of styles with variants, named parts and inheritance. An element wears it with `component="card"`, and props written on that element still win — a default is a starting point, not a lock. It is how every pre-built component here is styled, so restyling `button` or `datagrid` is the same call.

**Register a card, and two variants of it**

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

export const components = Box.components({
  card: {
    styles: {
      p: 5,
      borderRadius: 3,
      b: 1,
      bgColor: 'white',
      borderColor: 'slate-200',
      theme: { dark: { bgColor: 'slate-800', borderColor: 'slate-700' } },
    },
    variants: {
      danger: { borderColor: 'rose-400', bgColor: 'rose-50' },
      flat: { b: 0, shadow: 'none' },
    },
    children: {
      title: { styles: { fontSize: 16, fontWeight: 600, mb: 2 } },
    },
  },
});
```

**Wear it**

```jsx
<Box component="card" variant="danger" p={6}>
  <Box component="card.title">Payment failed</Box>
  We could not charge the card on file.
</Box>
```

### A name is not a type until you say so

`variant="danger"` is a type error until a `declare module` hands TypeScript what you registered — the names exist at runtime only. One file, written once, and every later `Box.components()` call it exports from is picked up automatically.

**box.d.ts — the one file that teaches the names**

```jsx
import '@box-kite/react';
import { ExtractComponentsAndVariants } from '@box-kite/core/types';
import { components } from './boxExtends';

declare module '@box-kite/core/types' {
  namespace Augmented {
    interface ComponentsTypes extends ExtractComponentsAndVariants<typeof components> {}
  }
}
```

In depth: [Theme Setup](https://www.box-kite.dev/theme-setup.md)

## Box.extend()

`Box.extend(variables, newProps, newValues)`

Three arguments, and each one is a different kind of growth: **variables** declares CSS custom properties, **new props** adds props the registry does not have, and **new values** teaches a prop that already exists to accept one more. All three land in the same pipeline as the built-ins — typed, nestable, shared, server-rendered.

**A brand colour, a prop of your own, and a new value on an existing prop**

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

export const { extendedProps, extendedPropTypes } = Box.extend(
  // 1. Variables: declared in :root the first time something uses one.
  { 'brand-500': '#4f46e5', 'grid-gutter': '1.5rem' },
  // 2. Props of your own.
  {
    columnRule: [
      {
        values: ['thin', 'thick'] as const,
        styleName: 'column-rule-width',
        valueFormat: (value: string) => (value === 'thin' ? '1px' : '4px'),
      },
    ],
  },
  // 3. More values on props that already exist.
  {
    bgColor: [{ values: ['brand-500'] as const, styleName: 'background-color', valueFormat: (value, getVariable) => getVariable(value) }],
  },
);
```

**Use them like any other prop**

```jsx
<Box bgColor="brand-500" columnRule="thick" hover={{ bgColor: 'brand-500/80' }} md={{ columnRule: 'thin' }} />
```

### A variable is a value, not a second system

A token registered here is accepted wherever the palette is, opacity modifier included — `"brand-500/80"` is a `color-mix` over the variable, so it stays themeable. The types need the same `declare module` the components do, with `ExtractBoxStyles<typeof extendedProps>` for your new props and `ExtractBoxStyles<typeof extendedPropTypes>` for the new values.

In depth: [Style Grouping](https://www.box-kite.dev/style-grouping.md)

## Box.keyframes()

`Box.keyframes({ slideIn: { from: {…}, to: {…} } })`

An `@keyframes` sequence whose steps are Box props rather than CSS — so a step is written in the same scale, tokens and all. Registering costs nothing: the engine writes a sequence the first time a rule names it.

**Register it, then name it**

```jsx
Box.keyframes({
  slideIn: {
    from: { opacity: 0, translateY: 4 },
    to: { opacity: 1, translateY: 0 },
  },
});
```

**On an element**

```jsx
<Box animationName="slideIn" animationDuration={300} animationFillMode="both" />
```

In depth: [Animation](https://www.box-kite.dev/animation.md)

## Box.spring()

`Box.spring({ stiffness, damping, mass, velocity })`

Spring physics sampled into a `linear()` curve and a settling time — the two halves the timing-function and duration props already take. There is no runtime: the spring is a value, so it shares a class like any other and costs nothing per frame.

**A spring of your own — the curve on one prop, the settling time on the other**

```jsx
const bouncy = Box.spring({ stiffness: 220, damping: 12 });

<Box transitionTimingFunction={bouncy.easing} transitionDuration={bouncy.duration} hover={{ scale: 1.05 }} />;
```

**Two of the four named ones — hover either**

```jsx
<Box transitionTimingFunction="spring-bouncy" transitionDuration="spring-bouncy" hover={{ scale: 1.08 }}>
  Hover: spring-bouncy
</Box>
<Box transitionTimingFunction="spring-gentle" transitionDuration="spring-gentle" hover={{ scale: 1.08 }}>
  Hover: spring-gentle
</Box>
```

### Four are already named

`spring`, `spring-gentle`, `spring-bouncy` and `spring-snappy` are values on the timing-function and duration props, so the common case needs no call at all — and their durations are multiples of `--transitionTime`, which reduced motion sets to zero.

In depth: [Animation](https://www.box-kite.dev/animation.md)

## Box.Theme

`<Box.Theme use="global" storageKey="theme" globalStyles={…}>`

The provider. It reads `prefers-color-scheme`, follows it live, persists an explicit choice, and writes the theme class and `data-theme` onto `<html>` (`use="global"`) or onto a wrapper of its own (`use="local"`, the default). Switching costs no re-render — it is one class moving on an ancestor.

**Once, at the root**

```jsx
<Box.Theme use="global" storageKey="theme" globalStyles={{ colorScheme: 'light dark' }}>
  <App />
</Box.Theme>
```

### A theme name is any identifier

`light` and `dark` are only the two you get for free. A `theme={{ midnight: … }}` block on any element works the moment a provider above it is called `midnight`, and a nested provider owns its own subtree outright.

In depth: [Theme Setup](https://www.box-kite.dev/theme-setup.md)

## Box.useTheme()

`const [theme, setTheme] = Box.useTheme()`

Read what the nearest provider settled on, and set it. Passing `null` clears the stored choice and hands control back to the operating system.

**A theme toggle**

```jsx
function ThemeToggle() {
  const [theme, setTheme] = Box.useTheme();

  return <Button onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>{theme === 'dark' ? 'Light' : 'Dark'}</Button>;
}
```

In depth: [Theme Setup](https://www.box-kite.dev/theme-setup.md)

## Box.configure()

`Box.configure({ classNames, sink, transition })`

How the engine names classes, where it writes rules, and what the base class transitions. The defaults suit a browser and a Node server alike, so most apps never call it — reach for it to make class names reproducible across processes, or to turn the base transition off.

**Before the first render, once**

```jsx
Box.configure({
  // 'hashed' (default), 'readable' for tests, or 'stable' — content-hashed, so two processes agree.
  classNames: 'stable',
  // 'cssom' | 'textContent' | 'string' | 'element'. Defaults to the environment.
  sink: 'cssom',
  // What every Box transitions by default — a transition group, or false to declare nothing at all.
  transition: 'colors',
});
```

### It re-emits everything

Changing the sink or the base transition after rules have been written drops the class-name cache and writes them all again, which is why this belongs at module scope and not in a component.

In depth: [Server Components](https://www.box-kite.dev/server-components.md)

## Box.getVariableValue()

`Box.getVariableValue("sky-500")`

The `var(--…)` reference behind a palette token, declaring it in `:root` the first time it is asked for. For handing a themed colour to something that takes a plain string — a canvas, a chart library, an SVG attribute somebody else renders.

**A token as a string**

```jsx
const stroke = Box.getVariableValue('sky-500'); // 'var(--sky-500)'
```

### For markup you do render, vars is the prop

`vars={{ 'color-revenue': 'sky-500' }}` declares `--color-revenue` on an element and everything inside it, so it nests in a theme and a breakpoint like any other prop. Reach for `getVariableValue` only when a string is genuinely what is wanted.

## useClassNames()

`const { className, styles } = useClassNames(props)`

Box props as a `className`, for an element Box cannot render: a router’s `NavLink`, a `motion.div`, a component from another library that takes a class and nothing else. All the nesting works — hover, breakpoints, themes — because it is the same resolution Box does.

**Styling somebody else's component**

```jsx
import { useClassNames } from '@box-kite/react';

function Crumb({ to, children }: { to: string; children: React.ReactNode }) {
  const { className, styles } = useClassNames({ color: 'sky-600', hover: { color: 'sky-400' } });

  return (
    <>
      {styles}
      <NavLink to={to} className={className}>
        {children}
      </NavLink>
    </>
  );
}
```

### Render styles either way

`styles` is defined in element mode only, where the CSS travels as `<style>` elements React hoists. Everywhere else it is `undefined` and rendering it costs nothing — so that line is what to write in both.

## useVisibility()

`const [visible, setVisible, ref] = useVisibility(options)`

Open/closed state that closes itself: an outside press, Escape, and optionally a scroll or a resize. The ref goes on the element that counts as _inside_.

**A panel that dismisses itself**

```jsx
const [isVisible, setVisible, ref] = useVisibility<HTMLDivElement>({ hideOnScroll: true });

<Box ref={ref}>
  <Button onClick={() => setVisible(!isVisible)}>Filters</Button>
  {isVisible && <Box p={4}>…</Box>}
</Box>;
```

### For a real layer, reach past it

A dismissable panel that also needs focus return, layering or the top layer is `Popover`, and the primitive under it is `useDismiss` from `@box-kite/react/a11y`. `useVisibility` is the small case: a disclosure that owns nothing else.

---

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

