Skip to main content
Glama

Box Kite styles React in TypeScript. Every CSS property is a prop on Box, every value it takes is a type, and the CSS is written for you at runtime β€” one shared class per value, no stylesheet to import, no class names to invent, no build plugin.

It is built to be written by an AI: the rules and a reference measured from the engine ship inside the package, an MCP server checks a prop bag against the real engine, and the compiler refuses a value the library would not render.

import Button from '@box-kite/react/components/button';
import Flex from '@box-kite/react/components/flex';
import { H2, P } from '@box-kite/react/components/semantics';

export function PlanCard() {
  return (
    <Flex d="column" gap={3} p={6} md={{ p: 8 }} borderRadius={3} shadow="md"
          bgColor="white" theme={{ dark: { bgColor: 'slate-900' } }}>
      <H2 fontSize={20} fontWeight={600}>Pro plan</H2>
      <P color="slate-600">Every value on this card is checked by the compiler.</P>
      <Button hover={{ bgColor: 'indigo-600' }}>Upgrade</Button>
    </Flex>
  );
}

Each prop becomes one shared class: p={6} is padding: 1.5rem, md wraps it in a media query, theme scopes it to the dark subtree, and the same value anywhere else in the app reuses the same rule.

⚑ Quick start

Start a new Vite + React app with a design, a sample page and the agent setup already in it:

npm create @box-kite@latest

Or add it to the app you have:

npm install @box-kite/react

React 18 or newer, and no third-party dependencies: the install is Box Kite and its own engine, nothing else. TypeScript is optional, but it is where the library shines.

Related MCP server: MCP-Knowledge-Toolbox

✨ Why Box Kite

πŸ€– Built for AI agents

A model's memory of a CSS-in-JS API is usually a memory of a different API β€” the prop names here collide with Tailwind's and Chakra's while the numbers mean something else. So Box Kite does not rely on what a model remembers:

What

How you get it

AGENTS.md + docs/ inside the npm package

Automatic β€” node_modules/@box-kite/react/AGENTS.md, with every prop and the CSS it writes

Project wiring (AGENTS.md, CLAUDE.md, MCP)

npm create @box-kite@latest -- --agents-only β€” merges into an existing repo, and updates on a rerun

Skill for Claude Code, Cursor, Codex, Copilot…

npx skills add box-kite/box-kite

MCP server β€” no key, no network

claude mcp add box-kite -- npx -y @box-kite/mcp (or the stdio config your editor takes)

llms.txt and markdown docs

box-kite.dev/llms.txt β€” append .md to any docs URL

check_styles runs your props through the real engine. A value the library does not accept writes no rule and no class name β€” by design, so a typo never emits a broken declaration. The MCP tool makes that visible before anything renders:

βœ… p         β†’ .p-4{padding:1rem}
❌ bgColor   does not accept "blue-550" β€” no rule and no class name were written.
βœ… fontSize  β†’ .fontSize-14{font-size:0.875rem}
⚠️ href      an HTML attribute, not a style prop. Written at the top level it typechecks
             and is then dropped β€” it goes in props={{ "href": … }}.

The dev console is written as a prompt. Each warning says what rendered and the line to write instead, so an agent reading a test run fixes itself:

[box-kite] bgColor="bleu-500" wrote no CSS: bgColor does not take that value, so the prop
           was dropped along with its class. Did you mean "blue-500"?
[box-kite] fontSize={4} is 4px text: fontSize's divider is 16, not the spacing scale's 4,
           so the number is the pixel size. For 16px write fontSize={16}.

And a UI a model writes at runtime is a first-class citizen. catalog() turns every component and every value its props take into JSON Schema β€” the shape structured output, json-render and A2UI want β€” and <SpecRenderer> renders what comes back, checking each node against an allow-list you control. toolPart() and a2uiToSpec() read AI SDK, assistant-ui, CopilotKit, AG-UI and A2UI, and <ToolCallCard>, <ApprovalCard>, <Reasoning> and <StreamingText> draw an agent's turn. β†’ Generative UI Β· Agent components Β· Interop

πŸ”’ Typed down to the value

Props are not string. Colours are palette tokens (with an optional /opacity), spacing is a scale, keywords are unions β€” so the editor autocompletes them and the compiler catches the rest:

<Box bgColor="bleu-500" />
//   ~~~~~~~ ts(2820): Type '"bleu-500"' is not assignable… Did you mean '"blue-500"'?

<Box display="flexx" />
//   ~~~~~~~ ts(2820): … Did you mean '"flex"'?

<Box opacity={50} />
//   ~~~~~~~ ts(2322): Type '50' is not assignable to type '0 | 1 | 0.1 | 0.2 | …'

<Button variant="primaryy">Save</Button>
//      ~~~~~~~ ts(2322): a component's variants are typed too

<Box css={{ mixBlendMod: 'multiply' }} />
//          ~~~~~~~~~~~ ts(2561): … Did you mean to write 'mixBlendMode'?
  • Hover a prop, see its CSS. Every prop's JSDoc carries an example such as p={4} β†’ padding: 1rem β€” generated by running the engine, so the editor can never teach a scale the library stopped using.

  • Your own tokens join the types. Box.extend() adds variables, props and values; one declare module makes them autocomplete like the built-ins. Box.components() variants are typed the same way.

  • The escape hatch is typed too. css={{ … }} is checked against csstype and still compiles to a shared class β€” never a style attribute.

πŸ“ The scales

The one thing worth learning by heart β€” the divider is per prop:

Prop

Scale

Example

p, m, gap, width, borderRadius

Γ·4 (quarter rem)

p={4} β†’ 1rem (16px)

fontSize

Γ·16

fontSize={14} β†’ 0.875rem (14px)

b (border width), lineHeight

px

b={1} β†’ 1px

animationDuration, transitionDelay…

ms

transitionDuration={150} β†’ 150ms

colours

tokens + /opacity

bgColor="blue-500/40" β†’ color-mix(…)

Three conventions follow from "every visual value is a prop": the element comes from the component (<Flex>, <Button>, <H1>, <Path>), HTML attributes go in props (<Link props={{ href: '/about' }}>), and there is no style={{ }}.

🧱 Components

Every component is a Box underneath β€” same props, same theming, same variants β€” and each is its own entry point, so a Textbox never pulls the DataGrid into your bundle.

Area

Components

Layout & content

Flex, Grid, H1–H6, P, Link, Nav…, List, Svg + every SVG element, Icon, Skeleton

Forms

Button, Textbox, Checkbox, Switch, RadioGroup, Combobox, Slider, Form

Dates

DateField, Calendar, DatePicker, TimeZoneSelect β€” over a zone-safe date module

Overlays

Dialog, Popover, Menu, Tooltip, Toaster β€” in the top layer, no portal, no z-index

Disclosure

Tabs, Accordion, Progress

Data & dashboards

DataGrid (sort, filter, group, edit, paste, virtualized), DashboardGrid, Sparkline, Gauge, ChartContainer

AI

ToolCallCard, ApprovalCard, Reasoning, StreamingText, markdownComponents, SpecRenderer

The behaviour behind them ships on its own as @box-kite/react/a11y β€” focus return, roving focus, dismissal and controllable state β€” for the widgets you build yourself.

πŸ–₯️ Server Components

// app/page.tsx β€” a Server Component. No 'use client', no provider, no CSS import.
import Flex from '@box-kite/react/components/flex';
import { H1 } from '@box-kite/react/components/semantics';

export default function Page() {
  return (
    <Flex d="column" gap={2} p={6}>
      <H1 fontSize={24}>Rendered on the server</H1>
    </Flex>
  );
}

The react-server export condition resolves to a hook-free build of Box; each rule comes back as a <style href precedence> element that React 19 hoists into <head> and dedupes, so it is stream-safe and the cascade is held by @layer, not by element order. β†’ How it works Β· measured against CSS modules Β· a whole Next.js app

πŸ“¦ Packages

Package

What it is

@box-kite/react

Box, every component and hook; depends on @box-kite/core and nothing else

@box-kite/core

the styling engine with no framework and no dependencies β€” createStyleEngine().classNames(props) (example)

@box-kite/mcp

the MCP server: docs search, props, components, rules and check_styles

@box-kite/create

npm create @box-kite β€” a Vite + React app with a design and the agent setup

πŸ“š Learn more

🀝 Contributing

Issues and pull requests are welcome β€” start with CONTRIBUTING.md, which covers the architecture and the framework-free core boundary. Questions go to SUPPORT.md, vulnerabilities to SECURITY.md.

If Box Kite saves you time, a ⭐ on GitHub helps other people find it.

License

MIT Β© Maxim Procopenco

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      C
      maintenance
      A modular Retrieval-Augmented Generation (RAG) framework that provides hybrid search and knowledge retrieval capabilities via the Model Context Protocol. It enables users to integrate document-based knowledge into LLM workflows with support for dense/sparse retrieval, reranking, and observability.
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables multi-source search and verified download of public-domain and open-license ebooks (Chinese/English) through a unified MCP interface.
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables MCP clients to create, inspect, edit, render, and export SVG/Inkscape documents through typed drawing tools, with a deterministic offline backend and an optional bridge to a locally running Inkscape instance for limited live editing and snapshots.
      5
      BSD 3-Clause