Box Kite 2.4.0
7 November 2026 · npm · Compare v2.3.1...v2.4.0
Box Kite now installs nothing but its own engine and asks for React 18 or newer, which is what its components already needed. The rules an AI agent loads are a fifth of the size, and six reported bugs are fixed, among them a system theme that would not switch to dark and a Combobox that screen readers named after its placeholder.
Box Kite now needs React 18 or newer
The peer range was React 16.14 and newer, but the DataGrid, the date controls, Popover, Dialog, Menu and Toaster already needed React 18, and CI only ever tested 18 and 19. The peer range is now >=18.0.0, which is what was really supported. No API changed, so this is not a major. But an app on React 16 or 17 that used only Box and the simpler components did work before, and npm will now refuse to install this version alongside it.
On React 16 or 17: stay on 2.3.x, or upgrade React to 18 or newer first.
Box Kite installs no third-party packages
@box-kite/react now depends on @box-kite/core and nothing else. The css prop's types used to come from the csstype package; they are now a generated copy inside @box-kite/core, so css={{ … }} is typed exactly as before and nothing extra is installed. The use-sync-external-store shim is gone too: the components use React's own useSyncExternalStore.
npm view @box-kite/react dependencies
# { '@box-kite/core': '<this version>' }The rules your agent loads are a fifth of the size
The rules file, the skill and the Cursor rule that ship in the package were a 90 KB wall loaded on every request that touched a component, and the rules that prevent most mistakes were buried in it. Each rule is now what to write and the one trap, about 19 KB in all, with the same numbers. Nothing was dropped: the reasons, measurements and edge cases behind each rule moved to references/rules.md in the skill, under the same number, and get_rules in @box-kite/mcp reads that long form.
| File | Before | After |
|---|---|---|
.claude/rules/box-kite-rules.md | 89 KB | 19 KB |
.cursor/rules/box-kite.mdc | 91 KB | 20 KB |
.claude/skills/box-kite/SKILL.md | 97 KB | 27 KB |
If you copied the Cursor rule or installed the skill, copy or install it again to pick up the short version.
Breaking changes
None.
Fixes
@box-kite/core's types pointed at a package it did not install. Itscssprop types importedcsstype, which the package never listed, so a TypeScript app using the engine without React got a missing-module error (or, withskipLibCheck, acssprop that accepted anything). The types are inside the package now.- A
<Box.Theme>that started on the system theme ignored athemegiven later. Passingtheme="dark"after starting with nothemeleft the page light, because React replayed the provider's own no-op update over the new value. It switches now, and adesignprop change after a stored design of the same name is no longer lost the same way. - A
Comboboxwas named after its placeholder, and its list after what was typed. Anidwritten inpropswas replaced by a generated one, so a<label htmlFor>of your own named nothing and the browser fell back to the placeholder. And without alabelprop the list was labelled by the field, which reads out the field's text. Theidis kept now, and the list carries the field's name: thelabel, youraria-labelledby, youraria-labelor the text of your own<label>. - Restyling every
Textboxreached the DataGrid's page number.Box.components({ textbox: { styles: { width: 'fit' } } })stretched the page-number field and wrapped the pagination bar. The field sets its own width now. - A
Skeletoncould not be taller than one line.<Skeleton height={40} />drew one thin bar at the top. With one line the bar fills the skeleton now, so a height makes a block. - A horizontal
RadioGroupput its label in the row with the radios. The label has a line of its own now, and the radios wrap when the row runs out of room. - A colour of your own refused the opacity modifier in TypeScript.
bgColor="ui-primary/35"rendered correctly but was a type error for a colour added throughBox.extend()and declared inAugmented.BoxPropTypes. Every colour prop that takesblue-500/40now takes your colour with a modifier too. - Docs. A custom DataGrid
Cellis drawn edge to edge with no padding (the built-in text pads itselfpx={3}), which was never written down. TheGridexample used agridColsprop that does not exist (gridTemplateColumns), andTextbox,Textarea,ComboboxandTimeZoneSelectsaid anidgoes inprops, where the types refuse it: it is the top-level prop.