Box Kite 2.3.1
6 November 2026 · npm · Compare v2.3.0...v2.3.1
An SVG shape can now turn about its own centre, take a press on its stroke alone and keep a finger's drag, and the SVG geometry props draw in Firefox and Safari. A large DataGrid also scrolls with no blank frames, re-renders far less, and numbers its rows correctly inside nested and server-side groups.
An SVG shape turns about its own centre, an edge takes the press on its stroke, and a drag keeps the finger
Three new props, and three that take more values. Everything below was checked in Chrome, Firefox and Safari's engine.
transformOriginandtransformBox. An SVG shape measures its origin against the whole viewBox, sorotate={45}on its own swings it round the corner. WithtransformBox="fill-box"andtransformOrigin="center"it turns about its own centre.transformOrigintakes a keyword ('center','top left'), a fraction or a percentage, a number on the ÷4 scale, or two or three positions as CSS writes them ('48px 48px').touchAction:auto,none,manipulation,pan-x,pan-y,pinch-zoomand their combinations. WithouttouchAction="none", a finger dragging an element pans the page instead of moving the element.pointerEventstakes the SVG keywords:visiblePainted,visibleFill,visibleStroke,visible,painted,fill,strokeandbounding-box(Firefox doesn't supportbounding-boxand uses the default). PutvisibleStrokeon a wide, transparent stroke and only the line takes the press, not the area it encloses.paintOrdertakes up to three keywords in the order they paint ('stroke fill markers').strokeDasharray="dashed"and"dotted"grow with the line.strokeWidthnow also sets--boxStrokeWidth, and the two named patterns are multiples of it, so a thicker line keeps the same pattern.
<Svg viewBox="0 0 200 200" width={200}>
<G rotate={45} transformBox="fill-box" transformOrigin="center">
<Rect x={120} y={120} width={40} height={40} fill="red-500" />
</G>
<Path d="M20 180 L180 20" stroke="transparent" strokeWidth={16} pointerEvents="visibleStroke" touchAction="none" />
<Line x1={0} y1={190} x2={200} y2={190} stroke="slate-500" strokeWidth={2} strokeDasharray="dashed" />
</Svg>Breaking changes
None.
Fixes
The SVG geometry props drew nothing in Firefox and Safari.
cx,cy,r,rx,ry,xandywere written as bare numbers (r: 20), which only Chromium accepts in CSS, so aProgressRingor aMiniDonuthad no radius there. They are written inpxnow (r: 20px), which in SVG is the same user unit, so nothing changes in Chromium.A DataGrid grouped two levels deep numbered its rows wrongly once an inner group was open — the row-number column repeated and skipped numbers, and every "Select row N" label followed it. The rows are now numbered in the order they appear, group rows included, with inner groups open or shut in any combination; copying a group row's row-number cell copies the number it shows instead of a blank.
Resizing a DataGrid column widened the row number, row selection and row detail columns. The space a resize freed was shared out to them along with the data columns, so the checkbox column grew with every drag. Those three now keep their own width, and the space goes to the data columns.
A fast wheel scroll through a DataGrid showed blank body. About one frame in five had rows missing under 800px wheel steps, because the rows a scroll revealed were drawn a frame after the scroll. They are now drawn in the same frame: measured on the docs demo, 0 blank frames where there were 19–21%.
After a filter, the DataGrid's scrollbar thumb slid to its new size instead of jumping there. The spacers that give the scrollbar its range picked up every Box's 250ms transition, so a reader scrolled deep was dragged back over a quarter of a second. They resize at once now, and a scroll left past the end of a shortened list draws its last rows rather than an empty window.
Every DataGrid cell's text was a scroll container, which could draw a stray scrollbar on Windows and catch the mouse wheel when a cell's content overflowed. It clips instead, as the grouping cell of a group row now does too.
Opening a detail panel or a group in a large DataGrid re-filtered and re-sorted every row. Opening a row changes which rows are shown, not which rows there are, so it now only works out the new list. A detail panel in a sorted grid of 100,000 rows opens in about 5ms instead of about 210ms.
A DataGrid whose
defis written inline re-filtered and re-sorted every row on every render of its parent. Adefbuilt in the render body is a new object each time, and that rebuilt everything behind the rows. Filtering, sorting and the row models are now kept on the values they actually read, so a parent re-render costs about 2.5ms on 100,000 sorted rows instead of about 47ms. Keepingdefat module scope is still the cheapest choice.Resizing the window re-rendered every row of a DataGrid on every frame, and so did letting go of a column's resize handle. The rows take their widths from CSS variables that are already written straight to the grid, so now only the header redraws. On the docs demo, the script work in each frame of a window drag is about half what it was.
A DataGrid re-rendered whenever the component holding it did, even with the same props. It is memoized now, and its totals row no longer redraws on every scroll frame.
A DataGrid fed by a
dataSourcerestarted its row numbers inside every open group, and inside every open row of a lazy tree — a row's number was its place in the block the server sent. It is now its place on screen, the same count a grid grouped in the browser gives, and it follows when a group above it opens or shuts.