@box-kite/react 2.2.18 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/.claude/rules/box-kite-rules.md +6 -2
  2. package/.claude/skills/box-kite/SKILL.md +19 -15
  3. package/.claude/skills/box-kite/references/patterns.md +1 -1
  4. package/.claude/skills/box-kite/references/styling.md +3 -3
  5. package/.cursor/rules/box-kite.mdc +7 -3
  6. package/AGENTS.md +7 -3
  7. package/BOX_AI_CONTEXT.md +81 -41
  8. package/BOX_KITE_AI_CONTEXT.md +81 -41
  9. package/README.md +13 -7
  10. package/behavior.cjs +1 -1
  11. package/behavior.mjs +1 -1
  12. package/box.cjs +1 -1
  13. package/box.d.ts +1 -0
  14. package/box.mjs +1 -1
  15. package/catalog.cjs +1 -1
  16. package/catalog.mjs +4 -1
  17. package/client.cjs +1 -1
  18. package/client.mjs +14 -14
  19. package/components/calendar.cjs +1 -1
  20. package/components/calendar.mjs +21 -20
  21. package/components/dashboard.cjs +1 -1
  22. package/components/dashboard.mjs +12 -12
  23. package/components/dataGrid/components/dataGridFooter.d.ts +6 -2
  24. package/components/dataGrid/models/gridModel.d.ts +21 -2
  25. package/components/dataGrid/models/groupRowModel.d.ts +1 -1
  26. package/components/dataGrid/models/rowModel.d.ts +1 -1
  27. package/components/dataGrid/models/sourceGroupRowModel.d.ts +5 -1
  28. package/components/dataGrid/models/sourceRowModel.d.ts +5 -1
  29. package/components/dataGrid.cjs +3 -3
  30. package/components/dataGrid.mjs +821 -771
  31. package/components/dateField.cjs +1 -1
  32. package/components/dateField.mjs +6 -6
  33. package/components/datePicker.cjs +1 -1
  34. package/components/datePicker.mjs +116 -111
  35. package/components/dialog.cjs +1 -1
  36. package/components/dialog.d.ts +1 -1
  37. package/components/dialog.mjs +1 -1
  38. package/components/dropdown.cjs +1 -1
  39. package/components/dropdown.mjs +8 -3
  40. package/components/menu.cjs +1 -1
  41. package/components/menu.mjs +10 -10
  42. package/components/overlay.cjs +1 -1
  43. package/components/overlay.mjs +6 -6
  44. package/components/popover.cjs +1 -1
  45. package/components/popover.d.ts +8 -2
  46. package/components/popover.mjs +58 -55
  47. package/components/slider.cjs +1 -1
  48. package/components/slider.mjs +8 -8
  49. package/components/sortableList.cjs +1 -1
  50. package/components/sortableList.mjs +1 -1
  51. package/components/svg.d.ts +2 -2
  52. package/components/swipeItem.cjs +1 -1
  53. package/components/swipeItem.mjs +11 -11
  54. package/components/tabs.cjs +1 -1
  55. package/components/tabs.mjs +9 -9
  56. package/dataGridList.cjs +1 -1
  57. package/dataGridList.mjs +3 -5
  58. package/docs/props.md +49 -25
  59. package/interactionListening.cjs +1 -1
  60. package/interactionListening.mjs +3 -3
  61. package/layer.cjs +1 -1
  62. package/layer.mjs +1 -1
  63. package/package.json +5 -5
  64. package/placement.cjs +1 -1
  65. package/placement.mjs +1 -1
  66. package/platform.cjs +1 -1
  67. package/platform.mjs +16 -11
  68. package/react-shared.cjs +1 -1
  69. package/react-shared.mjs +1 -1
  70. package/rsc.cjs +1 -1
  71. package/rsc.d.ts +1 -0
  72. package/rsc.mjs +1 -1
  73. package/utils/dom/domUtils.d.ts +2 -0
package/BOX_AI_CONTEXT.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @box-kite/react - AI Assistant Context
2
2
 
3
- Runtime CSS-in-JS library. `Box` component accepts 235 CSS props and generates CSS classes at runtime. Same prop values share a single class.
3
+ Runtime CSS-in-JS library. `Box` component accepts CSS properties as typed props and generates CSS classes at runtime. Same prop values share a single class.
4
4
 
5
5
  ---
6
6
 
@@ -191,7 +191,7 @@ All sizing, spacing, and positioning props also accept percentage strings: `p="5
191
191
  | `fieldSizing` | field-sizing | `'content'` — a control that grows with what is typed into it, no JavaScript measuring anything — or `'fixed'` |
192
192
  | `scrollbarGutter` / `scrollbarWidth` / `scrollbarColor` | scrollbar-gutter / -width / -color | `'stable'` / `'stable both-edges'` reserve the scrollbar's space before there is one, so nothing shifts when content overflows. `scrollbarWidth`: `'auto'`/`'thin'`/`'none'`; `scrollbarColor`: `[thumb, track]`, each a token, a `Box.extend()` colour or either with `/50` |
193
193
  | `willChange` | will-change | `'transform'`, `'opacity'`, `'filter'`, `'scroll-position'`, `'contents'`, `'auto'`. A hint with a real cost (a promoted layer holds memory), so it belongs on the few elements that animate, not on a list |
194
- | `cursor` / `pointerEvents` / `userSelect` | misc | string values. For `transition`, `animation` and the transform props see _Animation and transitions_ below |
194
+ | `cursor` / `pointerEvents` / `userSelect` / `touchAction` | misc | string values. For `transition`, `animation` and the transform props see _Animation and transitions_ below |
195
195
 
196
196
  ### Anchor positioning — a floating layer with no JavaScript
197
197
 
@@ -468,26 +468,27 @@ one, nested wherever a prop nests, rendered on a server.
468
468
 
469
469
  Twenty-three SVG properties, on any Box, plus twenty element components in `components/svg` — `Svg`, `G`, `Defs`, `Path`, `Circle`, `Ellipse`, `Rect`, `Line`, `Polyline`, `Polygon`, `SvgText`, `TSpan`, `LinearGradient`, `RadialGradient`, `Stop`, `ClipPath`, `Mask`, `Use`, `SvgSymbol`, `Marker`. **Never `<Box tag="path">`.** For an icon from somebody else's set, `<Icon>` (`components/icon`) — see below. (`BaseSvg` is deprecated: it is `Svg` with a 24×24 preset.)
470
470
 
471
- | Prop | CSS Property | Accepts |
472
- | ------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
473
- | `fill` / `stroke` | fill / stroke | any colour variable, or `'none'` |
474
- | `fillOpacity` / `strokeOpacity` | fill-opacity / stroke-opacity | `0`–`1` in tenths, same scale as `opacity` |
475
- | `fillRule` | fill-rule | `'nonzero'`, `'evenodd'` |
476
- | `strokeWidth` | stroke-width | number, **user units — no divider** (`strokeWidth={2}` → `2`) |
477
- | `strokeLinecap` | stroke-linecap | `'butt'`, `'round'`, `'square'` |
478
- | `strokeLinejoin` | stroke-linejoin | `'miter'`, `'round'`, `'bevel'` |
479
- | `strokeMiterlimit` | stroke-miterlimit | number, 1 or greater |
480
- | `strokeDasharray` | stroke-dasharray | number (`{12}` = dash 12, gap 12) or the pattern as a string (`"12 4"`) |
481
- | `strokeDashoffset` | stroke-dashoffset | number, or a percentage of the path length (`'40%'`) |
482
- | `paintOrder` | paint-order | `'normal'`, `'fill'`, `'stroke'`, `'markers'` |
483
- | `vectorEffect` | vector-effect | `'none'`, `'non-scaling-stroke'` |
484
- | `shapeRendering` | shape-rendering | `'auto'`, `'optimizeSpeed'`, `'crispEdges'`, `'geometricPrecision'` |
485
- | `textAnchor` | text-anchor | `'start'`, `'middle'`, `'end'` — which part of a label sits on its `x` |
486
- | `dominantBaseline` | dominant-baseline | `'auto'`, `'alphabetic'`, `'central'`, `'middle'`, `'hanging'`, `'text-top'`, `'text-bottom'`, `'ideographic'`, `'mathematical'` |
487
- | `cx` / `cy` | cx / cy | number in user units, or a percentage — `<circle>`, `<ellipse>` |
488
- | `r` | r | number in user units, or a percentage — `<circle>` |
489
- | `rx` / `ry` | rx / ry | number, a percentage, or `'auto'` — `<ellipse>` radii, `<rect>` corners |
490
- | `x` / `y` | x / y | number in user units, or a percentage — `<rect>`, `<image>`, `<use>`, `<foreignObject>`, nested `<svg>`. NOT `<text>` |
471
+ | Prop | CSS Property | Accepts |
472
+ | ------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
473
+ | `fill` / `stroke` | fill / stroke | any colour variable, or `'none'` |
474
+ | `fillOpacity` / `strokeOpacity` | fill-opacity / stroke-opacity | `0`–`1` in tenths, same scale as `opacity` |
475
+ | `fillRule` | fill-rule | `'nonzero'`, `'evenodd'` |
476
+ | `strokeWidth` | stroke-width | number, **user units — no divider** (`strokeWidth={2}` → `2`) |
477
+ | `strokeLinecap` | stroke-linecap | `'butt'`, `'round'`, `'square'` |
478
+ | `strokeLinejoin` | stroke-linejoin | `'miter'`, `'round'`, `'bevel'` |
479
+ | `strokeMiterlimit` | stroke-miterlimit | number, 1 or greater |
480
+ | `strokeDasharray` | stroke-dasharray | number (`{12}` = dash 12, gap 12), the pattern as a string (`"12 4"`), or `'dashed'`/`'dotted'`, which scale with `strokeWidth` |
481
+ | `strokeDashoffset` | stroke-dashoffset | number, or a percentage of the path length (`'40%'`) |
482
+ | `paintOrder` | paint-order | `'normal'`, or up to three of `'fill'`/`'stroke'`/`'markers'` in paint order (`'stroke fill markers'`) |
483
+ | `pointerEvents` | pointer-events | `'none'`, `'auto'`, `'all'`, and which part of a shape takes the press: `'visiblePainted'`, `'visibleFill'`, `'visibleStroke'`, `'visible'`, `'painted'`, `'fill'`, `'stroke'`, `'bounding-box'` (not in Firefox) |
484
+ | `vectorEffect` | vector-effect | `'none'`, `'non-scaling-stroke'` |
485
+ | `shapeRendering` | shape-rendering | `'auto'`, `'optimizeSpeed'`, `'crispEdges'`, `'geometricPrecision'` |
486
+ | `textAnchor` | text-anchor | `'start'`, `'middle'`, `'end'` — which part of a label sits on its `x` |
487
+ | `dominantBaseline` | dominant-baseline | `'auto'`, `'alphabetic'`, `'central'`, `'middle'`, `'hanging'`, `'text-top'`, `'text-bottom'`, `'ideographic'`, `'mathematical'` |
488
+ | `cx` / `cy` | cx / cy | number in user units, or a percentage — `<circle>`, `<ellipse>` |
489
+ | `r` | r | number in user units, or a percentage — `<circle>` |
490
+ | `rx` / `ry` | rx / ry | number, a percentage, or `'auto'` — `<ellipse>` radii, `<rect>` corners |
491
+ | `x` / `y` | x / y | number in user units, or a percentage — `<rect>`, `<image>`, `<use>`, `<foreignObject>`, nested `<svg>`. NOT `<text>` |
491
492
 
492
493
  ```tsx
493
494
  import { Circle, Path, Rect, Svg, SvgText } from '@box-kite/react/components/svg';
@@ -515,17 +516,18 @@ import { Circle, Path, Rect, Svg, SvgText } from '@box-kite/react/components/svg
515
516
  <Rect x={16} y={32} width={24} height={64} rx={3} fill="sky-600" />
516
517
  ```
517
518
 
518
- **Nine things to get right:**
519
+ **Ten things to get right:**
519
520
 
520
- 1. **No divider on SVG lengths.** `strokeWidth`, `strokeDasharray`, `strokeDashoffset` and `strokeMiterlimit` pass the number through unchanged — they are measured in the coordinate system the `viewBox` sets up.
521
+ 1. **No divider on SVG lengths.** `strokeWidth`, `strokeDasharray`, `strokeDashoffset` and `strokeMiterlimit` pass the number through unchanged — they are measured in the coordinate system the `viewBox` sets up. The geometry props (`cx`/`cy`/`r`/`rx`/`ry`/`x`/`y`) write that number as `px` — a user unit in SVG — because Firefox and Safari drop a bare number in them.
521
522
  2. **Inheritance does the work.** Put `fill`/`stroke`/`strokeWidth` on the `<svg>`, not on every shape. The exception is a shape carrying its own `fill=` attribute — a presentation attribute on an element beats a value inherited from its parent, so style that shape directly.
522
523
  3. **`vectorEffect` and `dominantBaseline` are not inherited**, so their rules target the element _and_ its descendants (`.cls, .cls *`). `vectorEffect="non-scaling-stroke"` on the `<svg>` keeps a hairline one pixel wide at any scale; `dominantBaseline="central"` on the `<svg>` reaches every label inside it.
523
524
  4. **Geometry belongs to the shape, and it transitions.** `cx`/`cy`/`r`/`rx`/`ry`/`x`/`y` are real CSS in SVG 2, so a gauge or a growing bar is a pseudo-class and no JavaScript. They are not inherited and should not be — set them on the shape.
524
- 5. **Each component settles the names SVG and Box both use, for its own element.** On `Path`, `d` is path data (not `flexDirection`); on `Rect`, `width`/`height` are user units (not the ÷4 layout scale); on `SvgText` and `TSpan`, `x`/`y`/`dx`/`dy` are attributes, because the CSS geometry properties do not apply to text; on `RadialGradient`, `cx`/`cy`/`r` are attributes, because they do not apply to a gradient either — while on `Circle` the same names stay CSS and transition. `transform` is a prop on every shape and group (`transform="rotate(-90 48 48)"` carries its own centre, which the CSS `rotate` prop cannot).
525
+ 5. **Each component settles the names SVG and Box both use, for its own element.** On `Path`, `d` is path data (not `flexDirection`); on `Rect`, `width`/`height` are user units (not the ÷4 layout scale); on `SvgText` and `TSpan`, `x`/`y`/`dx`/`dy` are attributes, because the CSS geometry properties do not apply to text; on `RadialGradient`, `cx`/`cy`/`r` are attributes, because they do not apply to a gradient either — while on `Circle` the same names stay CSS and transition. `transform` is a prop on every shape and group, for a matrix or an angle that is data (`transform="rotate(-90 48 48)"`); a fixed turn about the shape's own centre is CSS (item 10).
525
526
  6. **`Svg` sizes with attributes and names itself with `label`.** `viewBox`, `preserveAspectRatio`, `width` and `height` are the SVG attributes (`width="100%"`, `width={200}`), so the ÷4 layout `width`/`height` are not available on it. No `label` means `aria-hidden` — decoration, which is what most SVG is; `label` makes it `role="img"` with that name. A role or `aria-*` of your own in `props` wins over both.
526
527
  7. **An icon from a set is not SVG you draw — it is `<Icon>`.** `<Icon size={5} color="amber-500" label="Sunny"><Sun /></Icon>` from `components/icon`, wrapping one element from lucide, Tabler, react-icons, or a raw `<svg>`. `size` is the ÷4 scale (`size={6}` is 24px, the default) and lands in the _class_, where a CSS declaration outranks the `width`/`height` attributes the icon set writes — which is why `Icon` needs to know no set's API. `strokeWidth` is the same: an ordinary Box prop, so it can change on hover. No `label` means `aria-hidden`; a `label` means `role="img"`. **Reach for `Svg` rather than wrapping one in `Icon`** — `Svg` takes these props directly. (Wrapping one does work: `Icon` asks the child which convention it follows and routes `role`/`aria-label` into `props` for a component of ours. It is still a layer nobody needs.)
527
528
  8. **A paint server is a value, not an attribute.** `fill`, `stroke` and `clipPath` take `url(#id)` (a `<LinearGradient>`, a pattern, a `<ClipPath>` the document defines) and `var(--name)` (a variable somebody else declared) beside the colour tokens — so `fill="url(#sky)"` and `clipPath="url(#frame)"`, and both can differ per theme, on `hover` and per breakpoint. Only those two shapes are accepted: anything else emits no rule at all rather than a broken declaration. A gradient stop is still themed the same way — `<Stop stopColor="currentColor" color="amber-300" />`.
528
529
  9. **An icon outside lucide comes through the same `<Icon>`.** Iconify carries 300,000+ icons in 200-plus sets, and the choice is only about _when_ the icon becomes markup. One icon: copy its SVG and paste it into an `<Icon>` — no dependency, renders on a server. A set: `unplugin-icons` compiles `~icons/<set>/<name>` into a component at build time from an `@iconify-json/<set>` devDependency (`npm i -D unplugin-icons @iconify-json/<set> @svgr/core @svgr/plugin-jsx`, the plugin with `{ compiler: 'jsx', jsx: 'react' }`, and `/// <reference types="unplugin-icons/types/react" />` in a `.d.ts`) — nothing fetched at runtime, server-renders. A name that is data (from a CMS, from a user): `@iconify/react`'s `<Icon icon="set:name"/>` inside our `<Icon>` — it fetches in the browser, so it is a client component and the server sends no icon. **Turbopack runs no unplugin**, so under Next.js 16 the build-time recipe needs `next build --webpack`; the other two work as they are.
530
+ 10. **A shape turns about the viewBox's corner unless told otherwise.** An SVG element's `transform-box` is `view-box`, so `rotate={45}` alone swings a shape round `0 0`. `<G rotate={45} transformBox="fill-box" transformOrigin="center">` turns it about its own centre (measured in Chrome, Firefox and WebKit). `transformOrigin` takes 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'` — in SVG a px is a user unit). An edge you can press is `pointerEvents="visibleStroke"` over a wide transparent stroke, and a drag on a touch screen needs `touchAction="none"` on the element pressed, or the page pans instead.
529
531
 
530
532
  ---
531
533
 
@@ -534,20 +536,21 @@ import { Circle, Path, Rect, Svg, SvgText } from '@box-kite/react/components/svg
534
536
  Every Box already transitions `all` its properties over `--transitionTime` (0.25s), which is why a
535
537
  `hover` colour fades without being asked. On top of that:
536
538
 
537
- | Prop | CSS Property | Notes |
538
- | ----------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
539
- | `animation` | animation | One of four presets: `'spin'`, `'pulse'`, `'bounce'`, `'ping'`, or `'none'`. Keyframes come with the engine |
540
- | `animationName` | animation-name | A sequence registered with `Box.keyframes()`, a preset name, or a name from any stylesheet |
541
- | `animationDuration` / `animationDelay` | ms | **Milliseconds, no divider**: `animationDuration={1100}` → `1100ms` |
542
- | `animationIterationCount` | number | A number or `'infinite'` |
543
- | `animationDirection` / `animationFillMode` / `animationPlayState` | keywords | `'normal'`/`'reverse'`/`'alternate'`/`'alternate-reverse'`; `'none'`/`'forwards'`/`'backwards'`/`'both'`; `'running'`/`'paused'` |
544
- | `animationTimingFunction` / `transitionTimingFunction` | timing function | Keywords, the four springs (`'spring'`, `'spring-gentle'`, `'spring-bouncy'`, `'spring-snappy'`), or a curve: `'cubic-bezier(0.4, 0, 0.6, 1)'`, `'steps(4, end)'`, `'linear(0, 0.5, 1)'` |
545
- | `transition` | transition-property | `'all'`, `'none'`, or a group: `'colors'`, `'opacity'`, `'shadow'`, `'transform'`, `'size'`, `'filter'` |
546
- | `transitionDuration` / `transitionDelay` | ms | Milliseconds, same as the animation pair. Both duration props also take a spring name — that spring's settling time |
547
- | `transitionBehavior` | transition-behavior | `'normal'` or `'allow-discrete'`, which lets `display`, `overlay` and `content-visibility` transition — the property that makes an element animate _out_ without unmounting |
548
- | `interpolateSize` | interpolate-size | `'numeric-only'` or `'allow-keywords'`, which is what makes `height: auto` animate. Inherited, so it belongs on a container. Chromium-only; elsewhere the size snaps |
549
- | `startingStyle` | `@starting-style` | A nested block of plain props: what they start from the first time the element is styled. `startingStyle={{ opacity: 0, translateY: 2 }}` is an entrance with no JavaScript |
550
- | `translateX` / `translateY` / `rotate` / `scale` / `flip` | transform longhands | Each writes its own CSS property, so they compose. `scale={1.05}` is unitless; `rotate={45}` is degrees |
539
+ | Prop | CSS Property | Notes |
540
+ | ----------------------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
541
+ | `animation` | animation | One of four presets: `'spin'`, `'pulse'`, `'bounce'`, `'ping'`, or `'none'`. Keyframes come with the engine |
542
+ | `animationName` | animation-name | A sequence registered with `Box.keyframes()`, a preset name, or a name from any stylesheet |
543
+ | `animationDuration` / `animationDelay` | ms | **Milliseconds, no divider**: `animationDuration={1100}` → `1100ms` |
544
+ | `animationIterationCount` | number | A number or `'infinite'` |
545
+ | `animationDirection` / `animationFillMode` / `animationPlayState` | keywords | `'normal'`/`'reverse'`/`'alternate'`/`'alternate-reverse'`; `'none'`/`'forwards'`/`'backwards'`/`'both'`; `'running'`/`'paused'` |
546
+ | `animationTimingFunction` / `transitionTimingFunction` | timing function | Keywords, the four springs (`'spring'`, `'spring-gentle'`, `'spring-bouncy'`, `'spring-snappy'`), or a curve: `'cubic-bezier(0.4, 0, 0.6, 1)'`, `'steps(4, end)'`, `'linear(0, 0.5, 1)'` |
547
+ | `transition` | transition-property | `'all'`, `'none'`, or a group: `'colors'`, `'opacity'`, `'shadow'`, `'transform'`, `'size'`, `'filter'` |
548
+ | `transitionDuration` / `transitionDelay` | ms | Milliseconds, same as the animation pair. Both duration props also take a spring name — that spring's settling time |
549
+ | `transitionBehavior` | transition-behavior | `'normal'` or `'allow-discrete'`, which lets `display`, `overlay` and `content-visibility` transition — the property that makes an element animate _out_ without unmounting |
550
+ | `interpolateSize` | interpolate-size | `'numeric-only'` or `'allow-keywords'`, which is what makes `height: auto` animate. Inherited, so it belongs on a container. Chromium-only; elsewhere the size snaps |
551
+ | `startingStyle` | `@starting-style` | A nested block of plain props: what they start from the first time the element is styled. `startingStyle={{ opacity: 0, translateY: 2 }}` is an entrance with no JavaScript |
552
+ | `translateX` / `translateY` / `rotate` / `scale` / `flip` | transform longhands | Each writes its own CSS property, so they compose. `scale={1.05}` is unitless; `rotate={45}` is degrees |
553
+ | `transformOrigin` / `transformBox` | transform-origin / transform-box | The point `rotate`/`scale` turn about (`'center'`, `'top left'`, `'1/2 1/4'`, `'48px 48px'`, a ÷4 number) and the box it is measured in (`'fill-box'` for an SVG shape's own) |
551
554
 
552
555
  ```tsx
553
556
  // A preset: no registration, and it stops under prefers-reduced-motion on its own
@@ -1453,6 +1456,42 @@ Full reference: `docs/a11y-primitives.md`.
1453
1456
 
1454
1457
  ---
1455
1458
 
1459
+ ## Sizes: compact, normal, large
1460
+
1461
+ Every built-in component is drawn at one of three sizes, and a size is two CSS variables —
1462
+ `--control-height` and `--control-font-size` — so it inherits. Before 2.3.0 each control's height fell out of
1463
+ its own padding (a text box 46px, a button 36px, a combobox 34px), and nothing lined up.
1464
+
1465
+ | Size | Variant | Height | Text | Declared by |
1466
+ | ------- | ------------------- | ------ | ---- | -------------------------------------------------------- |
1467
+ | Compact | `variant="compact"` | 32px | 13px | `--control-height-compact`/`--control-font-size-compact` |
1468
+ | Normal | — (the default) | 40px | 14px | `--control-height-normal`/`--control-font-size-normal` |
1469
+ | Large | `variant="large"` | 48px | 16px | `--control-height-large`/`--control-font-size-large` |
1470
+
1471
+ - **What follows it.** Fields and buttons (`Button`, `Textbox`, `Dropdown`/`Select`, `Combobox`/`TimeZoneSelect`,
1472
+ `DateField`/`DatePicker`) take the height; items and rows (menu items, dropdown and combobox options, tabs,
1473
+ accordion headers, calendar days, a picker's times and presets) take it as a minimum; `Textarea` takes the text
1474
+ size; `Checkbox`, `RadioButton`, `Switch` and `Slider` scale whole; `Progress` changes its thickness.
1475
+ `Popover`, `Dialog`, `Menu`, `Tabs`, `Accordion`, `Collapsible`, `RadioGroup` and `Calendar` take the variant and
1476
+ pass it to everything inside. A `DataGrid` is drawn compact.
1477
+ - **A variant sizes the subtree**: `<Menu variant="compact">` makes every item compact, and `<DatePicker variant="large">`
1478
+ its calendar too (the picker hands its size to the panel, which is a sibling). `variant="normal"` restores the
1479
+ default inside a sized subtree.
1480
+ - **The project's scale is declared once**, on the app's root:
1481
+
1482
+ ```tsx
1483
+ <Box vars={Box.sizes({ compact: 8, normal: 9, large: { height: 11, fontSize: 15 } })}>{app}</Box>
1484
+ ```
1485
+
1486
+ A number is the ÷4 scale for a height and ÷16 for a text size; a string is CSS. `Box.sizes({ size: 'compact' })`
1487
+ draws a whole subtree at that size.
1488
+
1489
+ - **Never size one control by hand.** `height={9}` on one button wins as any prop does, which is how a row ends up
1490
+ with three heights. Padding no longer sizes a control: `py` changes nothing, `height="auto"` hands it back.
1491
+ - **Your own control joins** with `height="control"` (`minHeight` where it wraps) and `fontSize="control"`;
1492
+ `'control-compact'`/`'control-normal'`/`'control-large'` name one size outright.
1493
+ - `variant="square"` on a `Button` is an icon button as wide as it is tall, at whichever size.
1494
+
1456
1495
  ## Form controls: Checkbox, Switch, RadioGroup
1457
1496
 
1458
1497
  All three render **real native inputs**, so focus, the checked state, the disabled state and form
@@ -1572,7 +1611,8 @@ positioning only and light-dismisses nothing.
1572
1611
  | `offset` | `2` | The gap, on the ÷4 scale — 8px. |
1573
1612
  | `flip` | `true` | Whether a side with no room is swapped for its opposite. |
1574
1613
  | `matchWidth` | `false` | Whether the panel is at least as wide as its trigger. |
1575
- | `autoFocus` | `true` | Whether opening moves focus into the panel. An `autofocus` inside wins. |
1614
+ | `autoFocus` | `true` | Whether opening moves focus onto the panel's first control (the panel if none). |
1615
+ | `initialFocus` | — | A ref to land on instead. React's `autoFocus` writes no attribute, so use this. |
1576
1616
 
1577
1617
  What the component supplies, because the `popover` attribute supplies none of it: `role="dialog"`, the
1578
1618
  name, and `aria-expanded`/`aria-haspopup="dialog"`/`aria-controls` on the trigger. What the **platform**
@@ -3793,7 +3833,7 @@ allowed.tokens; // the colours, the @keyframes names and the style-tree nodes a
3793
3833
  `DataGrid`'s `def` (required — the columns and the grid-wide flags) and `data`, `DashboardGrid`'s
3794
3834
  `layout`/`defaultLayout`/`columns`, `Widget`'s `empty`, `ChartContainer`'s `series`. A column's nested
3795
3835
  `columns`, and `dataSource`/`onCellEdit`/`rowDetail`/`treeData`, stay out — they are the app's to pass.
3796
- - **The allow-list is the app's.** `catalog()` with no options is all 78 components and all 235 props —
3836
+ - **The allow-list is the app's.** `catalog()` with no options is every component and every prop —
3797
3837
  ~4.6 MB serialized. `include`, `exclude` and `styleProps` are how an app narrows it.
3798
3838
 
3799
3839
  Full reference: `docs/catalog.md`.
@@ -1,6 +1,6 @@
1
1
  # @box-kite/react - AI Assistant Context
2
2
 
3
- Runtime CSS-in-JS library. `Box` component accepts 235 CSS props and generates CSS classes at runtime. Same prop values share a single class.
3
+ Runtime CSS-in-JS library. `Box` component accepts CSS properties as typed props and generates CSS classes at runtime. Same prop values share a single class.
4
4
 
5
5
  ---
6
6
 
@@ -191,7 +191,7 @@ All sizing, spacing, and positioning props also accept percentage strings: `p="5
191
191
  | `fieldSizing` | field-sizing | `'content'` — a control that grows with what is typed into it, no JavaScript measuring anything — or `'fixed'` |
192
192
  | `scrollbarGutter` / `scrollbarWidth` / `scrollbarColor` | scrollbar-gutter / -width / -color | `'stable'` / `'stable both-edges'` reserve the scrollbar's space before there is one, so nothing shifts when content overflows. `scrollbarWidth`: `'auto'`/`'thin'`/`'none'`; `scrollbarColor`: `[thumb, track]`, each a token, a `Box.extend()` colour or either with `/50` |
193
193
  | `willChange` | will-change | `'transform'`, `'opacity'`, `'filter'`, `'scroll-position'`, `'contents'`, `'auto'`. A hint with a real cost (a promoted layer holds memory), so it belongs on the few elements that animate, not on a list |
194
- | `cursor` / `pointerEvents` / `userSelect` | misc | string values. For `transition`, `animation` and the transform props see _Animation and transitions_ below |
194
+ | `cursor` / `pointerEvents` / `userSelect` / `touchAction` | misc | string values. For `transition`, `animation` and the transform props see _Animation and transitions_ below |
195
195
 
196
196
  ### Anchor positioning — a floating layer with no JavaScript
197
197
 
@@ -468,26 +468,27 @@ one, nested wherever a prop nests, rendered on a server.
468
468
 
469
469
  Twenty-three SVG properties, on any Box, plus twenty element components in `components/svg` — `Svg`, `G`, `Defs`, `Path`, `Circle`, `Ellipse`, `Rect`, `Line`, `Polyline`, `Polygon`, `SvgText`, `TSpan`, `LinearGradient`, `RadialGradient`, `Stop`, `ClipPath`, `Mask`, `Use`, `SvgSymbol`, `Marker`. **Never `<Box tag="path">`.** For an icon from somebody else's set, `<Icon>` (`components/icon`) — see below. (`BaseSvg` is deprecated: it is `Svg` with a 24×24 preset.)
470
470
 
471
- | Prop | CSS Property | Accepts |
472
- | ------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
473
- | `fill` / `stroke` | fill / stroke | any colour variable, or `'none'` |
474
- | `fillOpacity` / `strokeOpacity` | fill-opacity / stroke-opacity | `0`–`1` in tenths, same scale as `opacity` |
475
- | `fillRule` | fill-rule | `'nonzero'`, `'evenodd'` |
476
- | `strokeWidth` | stroke-width | number, **user units — no divider** (`strokeWidth={2}` → `2`) |
477
- | `strokeLinecap` | stroke-linecap | `'butt'`, `'round'`, `'square'` |
478
- | `strokeLinejoin` | stroke-linejoin | `'miter'`, `'round'`, `'bevel'` |
479
- | `strokeMiterlimit` | stroke-miterlimit | number, 1 or greater |
480
- | `strokeDasharray` | stroke-dasharray | number (`{12}` = dash 12, gap 12) or the pattern as a string (`"12 4"`) |
481
- | `strokeDashoffset` | stroke-dashoffset | number, or a percentage of the path length (`'40%'`) |
482
- | `paintOrder` | paint-order | `'normal'`, `'fill'`, `'stroke'`, `'markers'` |
483
- | `vectorEffect` | vector-effect | `'none'`, `'non-scaling-stroke'` |
484
- | `shapeRendering` | shape-rendering | `'auto'`, `'optimizeSpeed'`, `'crispEdges'`, `'geometricPrecision'` |
485
- | `textAnchor` | text-anchor | `'start'`, `'middle'`, `'end'` — which part of a label sits on its `x` |
486
- | `dominantBaseline` | dominant-baseline | `'auto'`, `'alphabetic'`, `'central'`, `'middle'`, `'hanging'`, `'text-top'`, `'text-bottom'`, `'ideographic'`, `'mathematical'` |
487
- | `cx` / `cy` | cx / cy | number in user units, or a percentage — `<circle>`, `<ellipse>` |
488
- | `r` | r | number in user units, or a percentage — `<circle>` |
489
- | `rx` / `ry` | rx / ry | number, a percentage, or `'auto'` — `<ellipse>` radii, `<rect>` corners |
490
- | `x` / `y` | x / y | number in user units, or a percentage — `<rect>`, `<image>`, `<use>`, `<foreignObject>`, nested `<svg>`. NOT `<text>` |
471
+ | Prop | CSS Property | Accepts |
472
+ | ------------------------------- | ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
473
+ | `fill` / `stroke` | fill / stroke | any colour variable, or `'none'` |
474
+ | `fillOpacity` / `strokeOpacity` | fill-opacity / stroke-opacity | `0`–`1` in tenths, same scale as `opacity` |
475
+ | `fillRule` | fill-rule | `'nonzero'`, `'evenodd'` |
476
+ | `strokeWidth` | stroke-width | number, **user units — no divider** (`strokeWidth={2}` → `2`) |
477
+ | `strokeLinecap` | stroke-linecap | `'butt'`, `'round'`, `'square'` |
478
+ | `strokeLinejoin` | stroke-linejoin | `'miter'`, `'round'`, `'bevel'` |
479
+ | `strokeMiterlimit` | stroke-miterlimit | number, 1 or greater |
480
+ | `strokeDasharray` | stroke-dasharray | number (`{12}` = dash 12, gap 12), the pattern as a string (`"12 4"`), or `'dashed'`/`'dotted'`, which scale with `strokeWidth` |
481
+ | `strokeDashoffset` | stroke-dashoffset | number, or a percentage of the path length (`'40%'`) |
482
+ | `paintOrder` | paint-order | `'normal'`, or up to three of `'fill'`/`'stroke'`/`'markers'` in paint order (`'stroke fill markers'`) |
483
+ | `pointerEvents` | pointer-events | `'none'`, `'auto'`, `'all'`, and which part of a shape takes the press: `'visiblePainted'`, `'visibleFill'`, `'visibleStroke'`, `'visible'`, `'painted'`, `'fill'`, `'stroke'`, `'bounding-box'` (not in Firefox) |
484
+ | `vectorEffect` | vector-effect | `'none'`, `'non-scaling-stroke'` |
485
+ | `shapeRendering` | shape-rendering | `'auto'`, `'optimizeSpeed'`, `'crispEdges'`, `'geometricPrecision'` |
486
+ | `textAnchor` | text-anchor | `'start'`, `'middle'`, `'end'` — which part of a label sits on its `x` |
487
+ | `dominantBaseline` | dominant-baseline | `'auto'`, `'alphabetic'`, `'central'`, `'middle'`, `'hanging'`, `'text-top'`, `'text-bottom'`, `'ideographic'`, `'mathematical'` |
488
+ | `cx` / `cy` | cx / cy | number in user units, or a percentage — `<circle>`, `<ellipse>` |
489
+ | `r` | r | number in user units, or a percentage — `<circle>` |
490
+ | `rx` / `ry` | rx / ry | number, a percentage, or `'auto'` — `<ellipse>` radii, `<rect>` corners |
491
+ | `x` / `y` | x / y | number in user units, or a percentage — `<rect>`, `<image>`, `<use>`, `<foreignObject>`, nested `<svg>`. NOT `<text>` |
491
492
 
492
493
  ```tsx
493
494
  import { Circle, Path, Rect, Svg, SvgText } from '@box-kite/react/components/svg';
@@ -515,17 +516,18 @@ import { Circle, Path, Rect, Svg, SvgText } from '@box-kite/react/components/svg
515
516
  <Rect x={16} y={32} width={24} height={64} rx={3} fill="sky-600" />
516
517
  ```
517
518
 
518
- **Nine things to get right:**
519
+ **Ten things to get right:**
519
520
 
520
- 1. **No divider on SVG lengths.** `strokeWidth`, `strokeDasharray`, `strokeDashoffset` and `strokeMiterlimit` pass the number through unchanged — they are measured in the coordinate system the `viewBox` sets up.
521
+ 1. **No divider on SVG lengths.** `strokeWidth`, `strokeDasharray`, `strokeDashoffset` and `strokeMiterlimit` pass the number through unchanged — they are measured in the coordinate system the `viewBox` sets up. The geometry props (`cx`/`cy`/`r`/`rx`/`ry`/`x`/`y`) write that number as `px` — a user unit in SVG — because Firefox and Safari drop a bare number in them.
521
522
  2. **Inheritance does the work.** Put `fill`/`stroke`/`strokeWidth` on the `<svg>`, not on every shape. The exception is a shape carrying its own `fill=` attribute — a presentation attribute on an element beats a value inherited from its parent, so style that shape directly.
522
523
  3. **`vectorEffect` and `dominantBaseline` are not inherited**, so their rules target the element _and_ its descendants (`.cls, .cls *`). `vectorEffect="non-scaling-stroke"` on the `<svg>` keeps a hairline one pixel wide at any scale; `dominantBaseline="central"` on the `<svg>` reaches every label inside it.
523
524
  4. **Geometry belongs to the shape, and it transitions.** `cx`/`cy`/`r`/`rx`/`ry`/`x`/`y` are real CSS in SVG 2, so a gauge or a growing bar is a pseudo-class and no JavaScript. They are not inherited and should not be — set them on the shape.
524
- 5. **Each component settles the names SVG and Box both use, for its own element.** On `Path`, `d` is path data (not `flexDirection`); on `Rect`, `width`/`height` are user units (not the ÷4 layout scale); on `SvgText` and `TSpan`, `x`/`y`/`dx`/`dy` are attributes, because the CSS geometry properties do not apply to text; on `RadialGradient`, `cx`/`cy`/`r` are attributes, because they do not apply to a gradient either — while on `Circle` the same names stay CSS and transition. `transform` is a prop on every shape and group (`transform="rotate(-90 48 48)"` carries its own centre, which the CSS `rotate` prop cannot).
525
+ 5. **Each component settles the names SVG and Box both use, for its own element.** On `Path`, `d` is path data (not `flexDirection`); on `Rect`, `width`/`height` are user units (not the ÷4 layout scale); on `SvgText` and `TSpan`, `x`/`y`/`dx`/`dy` are attributes, because the CSS geometry properties do not apply to text; on `RadialGradient`, `cx`/`cy`/`r` are attributes, because they do not apply to a gradient either — while on `Circle` the same names stay CSS and transition. `transform` is a prop on every shape and group, for a matrix or an angle that is data (`transform="rotate(-90 48 48)"`); a fixed turn about the shape's own centre is CSS (item 10).
525
526
  6. **`Svg` sizes with attributes and names itself with `label`.** `viewBox`, `preserveAspectRatio`, `width` and `height` are the SVG attributes (`width="100%"`, `width={200}`), so the ÷4 layout `width`/`height` are not available on it. No `label` means `aria-hidden` — decoration, which is what most SVG is; `label` makes it `role="img"` with that name. A role or `aria-*` of your own in `props` wins over both.
526
527
  7. **An icon from a set is not SVG you draw — it is `<Icon>`.** `<Icon size={5} color="amber-500" label="Sunny"><Sun /></Icon>` from `components/icon`, wrapping one element from lucide, Tabler, react-icons, or a raw `<svg>`. `size` is the ÷4 scale (`size={6}` is 24px, the default) and lands in the _class_, where a CSS declaration outranks the `width`/`height` attributes the icon set writes — which is why `Icon` needs to know no set's API. `strokeWidth` is the same: an ordinary Box prop, so it can change on hover. No `label` means `aria-hidden`; a `label` means `role="img"`. **Reach for `Svg` rather than wrapping one in `Icon`** — `Svg` takes these props directly. (Wrapping one does work: `Icon` asks the child which convention it follows and routes `role`/`aria-label` into `props` for a component of ours. It is still a layer nobody needs.)
527
528
  8. **A paint server is a value, not an attribute.** `fill`, `stroke` and `clipPath` take `url(#id)` (a `<LinearGradient>`, a pattern, a `<ClipPath>` the document defines) and `var(--name)` (a variable somebody else declared) beside the colour tokens — so `fill="url(#sky)"` and `clipPath="url(#frame)"`, and both can differ per theme, on `hover` and per breakpoint. Only those two shapes are accepted: anything else emits no rule at all rather than a broken declaration. A gradient stop is still themed the same way — `<Stop stopColor="currentColor" color="amber-300" />`.
528
529
  9. **An icon outside lucide comes through the same `<Icon>`.** Iconify carries 300,000+ icons in 200-plus sets, and the choice is only about _when_ the icon becomes markup. One icon: copy its SVG and paste it into an `<Icon>` — no dependency, renders on a server. A set: `unplugin-icons` compiles `~icons/<set>/<name>` into a component at build time from an `@iconify-json/<set>` devDependency (`npm i -D unplugin-icons @iconify-json/<set> @svgr/core @svgr/plugin-jsx`, the plugin with `{ compiler: 'jsx', jsx: 'react' }`, and `/// <reference types="unplugin-icons/types/react" />` in a `.d.ts`) — nothing fetched at runtime, server-renders. A name that is data (from a CMS, from a user): `@iconify/react`'s `<Icon icon="set:name"/>` inside our `<Icon>` — it fetches in the browser, so it is a client component and the server sends no icon. **Turbopack runs no unplugin**, so under Next.js 16 the build-time recipe needs `next build --webpack`; the other two work as they are.
530
+ 10. **A shape turns about the viewBox's corner unless told otherwise.** An SVG element's `transform-box` is `view-box`, so `rotate={45}` alone swings a shape round `0 0`. `<G rotate={45} transformBox="fill-box" transformOrigin="center">` turns it about its own centre (measured in Chrome, Firefox and WebKit). `transformOrigin` takes 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'` — in SVG a px is a user unit). An edge you can press is `pointerEvents="visibleStroke"` over a wide transparent stroke, and a drag on a touch screen needs `touchAction="none"` on the element pressed, or the page pans instead.
529
531
 
530
532
  ---
531
533
 
@@ -534,20 +536,21 @@ import { Circle, Path, Rect, Svg, SvgText } from '@box-kite/react/components/svg
534
536
  Every Box already transitions `all` its properties over `--transitionTime` (0.25s), which is why a
535
537
  `hover` colour fades without being asked. On top of that:
536
538
 
537
- | Prop | CSS Property | Notes |
538
- | ----------------------------------------------------------------- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
539
- | `animation` | animation | One of four presets: `'spin'`, `'pulse'`, `'bounce'`, `'ping'`, or `'none'`. Keyframes come with the engine |
540
- | `animationName` | animation-name | A sequence registered with `Box.keyframes()`, a preset name, or a name from any stylesheet |
541
- | `animationDuration` / `animationDelay` | ms | **Milliseconds, no divider**: `animationDuration={1100}` → `1100ms` |
542
- | `animationIterationCount` | number | A number or `'infinite'` |
543
- | `animationDirection` / `animationFillMode` / `animationPlayState` | keywords | `'normal'`/`'reverse'`/`'alternate'`/`'alternate-reverse'`; `'none'`/`'forwards'`/`'backwards'`/`'both'`; `'running'`/`'paused'` |
544
- | `animationTimingFunction` / `transitionTimingFunction` | timing function | Keywords, the four springs (`'spring'`, `'spring-gentle'`, `'spring-bouncy'`, `'spring-snappy'`), or a curve: `'cubic-bezier(0.4, 0, 0.6, 1)'`, `'steps(4, end)'`, `'linear(0, 0.5, 1)'` |
545
- | `transition` | transition-property | `'all'`, `'none'`, or a group: `'colors'`, `'opacity'`, `'shadow'`, `'transform'`, `'size'`, `'filter'` |
546
- | `transitionDuration` / `transitionDelay` | ms | Milliseconds, same as the animation pair. Both duration props also take a spring name — that spring's settling time |
547
- | `transitionBehavior` | transition-behavior | `'normal'` or `'allow-discrete'`, which lets `display`, `overlay` and `content-visibility` transition — the property that makes an element animate _out_ without unmounting |
548
- | `interpolateSize` | interpolate-size | `'numeric-only'` or `'allow-keywords'`, which is what makes `height: auto` animate. Inherited, so it belongs on a container. Chromium-only; elsewhere the size snaps |
549
- | `startingStyle` | `@starting-style` | A nested block of plain props: what they start from the first time the element is styled. `startingStyle={{ opacity: 0, translateY: 2 }}` is an entrance with no JavaScript |
550
- | `translateX` / `translateY` / `rotate` / `scale` / `flip` | transform longhands | Each writes its own CSS property, so they compose. `scale={1.05}` is unitless; `rotate={45}` is degrees |
539
+ | Prop | CSS Property | Notes |
540
+ | ----------------------------------------------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
541
+ | `animation` | animation | One of four presets: `'spin'`, `'pulse'`, `'bounce'`, `'ping'`, or `'none'`. Keyframes come with the engine |
542
+ | `animationName` | animation-name | A sequence registered with `Box.keyframes()`, a preset name, or a name from any stylesheet |
543
+ | `animationDuration` / `animationDelay` | ms | **Milliseconds, no divider**: `animationDuration={1100}` → `1100ms` |
544
+ | `animationIterationCount` | number | A number or `'infinite'` |
545
+ | `animationDirection` / `animationFillMode` / `animationPlayState` | keywords | `'normal'`/`'reverse'`/`'alternate'`/`'alternate-reverse'`; `'none'`/`'forwards'`/`'backwards'`/`'both'`; `'running'`/`'paused'` |
546
+ | `animationTimingFunction` / `transitionTimingFunction` | timing function | Keywords, the four springs (`'spring'`, `'spring-gentle'`, `'spring-bouncy'`, `'spring-snappy'`), or a curve: `'cubic-bezier(0.4, 0, 0.6, 1)'`, `'steps(4, end)'`, `'linear(0, 0.5, 1)'` |
547
+ | `transition` | transition-property | `'all'`, `'none'`, or a group: `'colors'`, `'opacity'`, `'shadow'`, `'transform'`, `'size'`, `'filter'` |
548
+ | `transitionDuration` / `transitionDelay` | ms | Milliseconds, same as the animation pair. Both duration props also take a spring name — that spring's settling time |
549
+ | `transitionBehavior` | transition-behavior | `'normal'` or `'allow-discrete'`, which lets `display`, `overlay` and `content-visibility` transition — the property that makes an element animate _out_ without unmounting |
550
+ | `interpolateSize` | interpolate-size | `'numeric-only'` or `'allow-keywords'`, which is what makes `height: auto` animate. Inherited, so it belongs on a container. Chromium-only; elsewhere the size snaps |
551
+ | `startingStyle` | `@starting-style` | A nested block of plain props: what they start from the first time the element is styled. `startingStyle={{ opacity: 0, translateY: 2 }}` is an entrance with no JavaScript |
552
+ | `translateX` / `translateY` / `rotate` / `scale` / `flip` | transform longhands | Each writes its own CSS property, so they compose. `scale={1.05}` is unitless; `rotate={45}` is degrees |
553
+ | `transformOrigin` / `transformBox` | transform-origin / transform-box | The point `rotate`/`scale` turn about (`'center'`, `'top left'`, `'1/2 1/4'`, `'48px 48px'`, a ÷4 number) and the box it is measured in (`'fill-box'` for an SVG shape's own) |
551
554
 
552
555
  ```tsx
553
556
  // A preset: no registration, and it stops under prefers-reduced-motion on its own
@@ -1453,6 +1456,42 @@ Full reference: `docs/a11y-primitives.md`.
1453
1456
 
1454
1457
  ---
1455
1458
 
1459
+ ## Sizes: compact, normal, large
1460
+
1461
+ Every built-in component is drawn at one of three sizes, and a size is two CSS variables —
1462
+ `--control-height` and `--control-font-size` — so it inherits. Before 2.3.0 each control's height fell out of
1463
+ its own padding (a text box 46px, a button 36px, a combobox 34px), and nothing lined up.
1464
+
1465
+ | Size | Variant | Height | Text | Declared by |
1466
+ | ------- | ------------------- | ------ | ---- | -------------------------------------------------------- |
1467
+ | Compact | `variant="compact"` | 32px | 13px | `--control-height-compact`/`--control-font-size-compact` |
1468
+ | Normal | — (the default) | 40px | 14px | `--control-height-normal`/`--control-font-size-normal` |
1469
+ | Large | `variant="large"` | 48px | 16px | `--control-height-large`/`--control-font-size-large` |
1470
+
1471
+ - **What follows it.** Fields and buttons (`Button`, `Textbox`, `Dropdown`/`Select`, `Combobox`/`TimeZoneSelect`,
1472
+ `DateField`/`DatePicker`) take the height; items and rows (menu items, dropdown and combobox options, tabs,
1473
+ accordion headers, calendar days, a picker's times and presets) take it as a minimum; `Textarea` takes the text
1474
+ size; `Checkbox`, `RadioButton`, `Switch` and `Slider` scale whole; `Progress` changes its thickness.
1475
+ `Popover`, `Dialog`, `Menu`, `Tabs`, `Accordion`, `Collapsible`, `RadioGroup` and `Calendar` take the variant and
1476
+ pass it to everything inside. A `DataGrid` is drawn compact.
1477
+ - **A variant sizes the subtree**: `<Menu variant="compact">` makes every item compact, and `<DatePicker variant="large">`
1478
+ its calendar too (the picker hands its size to the panel, which is a sibling). `variant="normal"` restores the
1479
+ default inside a sized subtree.
1480
+ - **The project's scale is declared once**, on the app's root:
1481
+
1482
+ ```tsx
1483
+ <Box vars={Box.sizes({ compact: 8, normal: 9, large: { height: 11, fontSize: 15 } })}>{app}</Box>
1484
+ ```
1485
+
1486
+ A number is the ÷4 scale for a height and ÷16 for a text size; a string is CSS. `Box.sizes({ size: 'compact' })`
1487
+ draws a whole subtree at that size.
1488
+
1489
+ - **Never size one control by hand.** `height={9}` on one button wins as any prop does, which is how a row ends up
1490
+ with three heights. Padding no longer sizes a control: `py` changes nothing, `height="auto"` hands it back.
1491
+ - **Your own control joins** with `height="control"` (`minHeight` where it wraps) and `fontSize="control"`;
1492
+ `'control-compact'`/`'control-normal'`/`'control-large'` name one size outright.
1493
+ - `variant="square"` on a `Button` is an icon button as wide as it is tall, at whichever size.
1494
+
1456
1495
  ## Form controls: Checkbox, Switch, RadioGroup
1457
1496
 
1458
1497
  All three render **real native inputs**, so focus, the checked state, the disabled state and form
@@ -1572,7 +1611,8 @@ positioning only and light-dismisses nothing.
1572
1611
  | `offset` | `2` | The gap, on the ÷4 scale — 8px. |
1573
1612
  | `flip` | `true` | Whether a side with no room is swapped for its opposite. |
1574
1613
  | `matchWidth` | `false` | Whether the panel is at least as wide as its trigger. |
1575
- | `autoFocus` | `true` | Whether opening moves focus into the panel. An `autofocus` inside wins. |
1614
+ | `autoFocus` | `true` | Whether opening moves focus onto the panel's first control (the panel if none). |
1615
+ | `initialFocus` | — | A ref to land on instead. React's `autoFocus` writes no attribute, so use this. |
1576
1616
 
1577
1617
  What the component supplies, because the `popover` attribute supplies none of it: `role="dialog"`, the
1578
1618
  name, and `aria-expanded`/`aria-haspopup="dialog"`/`aria-controls` on the trigger. What the **platform**
@@ -3793,7 +3833,7 @@ allowed.tokens; // the colours, the @keyframes names and the style-tree nodes a
3793
3833
  `DataGrid`'s `def` (required — the columns and the grid-wide flags) and `data`, `DashboardGrid`'s
3794
3834
  `layout`/`defaultLayout`/`columns`, `Widget`'s `empty`, `ChartContainer`'s `series`. A column's nested
3795
3835
  `columns`, and `dataSource`/`onCellEdit`/`rowDetail`/`treeData`, stay out — they are the app's to pass.
3796
- - **The allow-list is the app's.** `catalog()` with no options is all 78 components and all 235 props —
3836
+ - **The allow-list is the app's.** `catalog()` with no options is every component and every prop —
3797
3837
  ~4.6 MB serialized. `include`, `exclude` and `styleProps` are how an app narrows it.
3798
3838
 
3799
3839
  Full reference: `docs/catalog.md`.
package/README.md CHANGED
@@ -5,11 +5,16 @@
5
5
  [![Tests](https://github.com/box-kite/box-kite/actions/workflows/test.yml/badge.svg)](https://github.com/box-kite/box-kite/actions/workflows/test.yml)
6
6
  [![license](https://img.shields.io/npm/l/@box-kite/react)](LICENSE)
7
7
 
8
- `Box` is a single React component with 235 typed CSS props. It generates the CSS for the values you
8
+ `Box` is a single React component that takes CSS properties as typed props. It generates the CSS for the values you
9
9
  pass at runtime and caches every rule by its content, so the same value anywhere in the app reuses
10
10
  one class — a project styles itself in TypeScript, with no CSS files to write and no class-name
11
11
  convention to remember.
12
12
 
13
+ **It renders in React Server Components** with no `'use client'`, no provider and no compiler: each
14
+ rule travels with the markup as a `<style href precedence>` element, which React 19 hoists and
15
+ dedupes — [how](#react-server-components-react-19) ·
16
+ [what it costs, measured against CSS modules](https://www.box-kite.dev/blog/styling-server-components/).
17
+
13
18
  [Docs and live demos](https://www.box-kite.dev) · [Blog](https://www.box-kite.dev/blog/) · [Contributing](CONTRIBUTING.md) ·
14
19
  [Support](SUPPORT.md) · [Security](SECURITY.md) ·
15
20
  [Releases](https://github.com/box-kite/box-kite/releases)
@@ -46,10 +51,10 @@ target lives with the prop registry it extends, not with a binding.
46
51
  Spacing counts in quarters of a rem: `p={3}` is `0.75rem`, `m={2}` is `0.5rem`. The example below is
47
52
  a box with `margin: 0.5rem` and `padding: 1.75rem`.
48
53
 
49
- ```JS
50
- import Box from "@box-kite/react";
54
+ ```tsx
55
+ import Box from '@box-kite/react';
51
56
 
52
- export default function Component(props: Props) {
57
+ export default function Component() {
53
58
  return (
54
59
  <Box className="custom-class" m={2} p={7}>
55
60
  basic example
@@ -69,8 +74,9 @@ are generated by running the engine, so the editor cannot tell you a scale the l
69
74
  ## Colours
70
75
 
71
76
  The palette is Tailwind's, in OKLCH: twenty-six families of eleven steps — `slate-50` through
72
- `rose-950`, the four Tailwind 4.3 added (`mauve`, `mist`, `olive`, `taupe`) included — declared as CSS
73
- variables the first time a page asks for one. Any colour value takes an opacity modifier:
77
+ `rose-950`, the four Tailwind 4.3 added (`mauve`, `mist`, `olive`, `taupe`) included — each one a CSS
78
+ variable written with its own value as the fallback, so a page declares none of them in `:root` and
79
+ any stylesheet can override one. Any colour value takes an opacity modifier:
74
80
 
75
81
  ```JS
76
82
  <Box bgColor="blue-500/40" borderColor="black/10" hover={{ bgColor: 'blue-500/60' }} />
@@ -687,7 +693,7 @@ A complete page — props, pseudo-classes, breakpoints, themes, `extend`, `compo
687
693
 
688
694
  ## Architecture
689
695
 
690
- The styling engine is framework-free. Everything that generates CSS — the 235 prop definitions,
696
+ The styling engine is framework-free. Everything that generates CSS — the prop definitions,
691
697
  the value formatters, class-name generation, rule ordering, the style sinks (CSSOM, `textContent`,
692
698
  string for SSR, style elements for React 19), the flush scheduler, CSS variables and the theme
693
699
  runtime — lives in `src/core/` and imports no React at all. CI fails the build if it ever does
package/behavior.cjs CHANGED
@@ -1 +1 @@
1
- const e=require("./platform.cjs"),t=require("./effects.cjs"),n=require("./keyboard.cjs");let r=require("react");function i(e){let n=(0,r.useRef)(e);return t.n(()=>{n.current=e}),n}function a(e){let t=i(e);return(0,r.useCallback)((...e)=>t.current?.(...e),[t])}function o(e){let{value:t,defaultValue:n,onChange:o}=e,s=t!==void 0,[c,l]=(0,r.useState)(n),u=s?t:c,d=i(u),f=a(o);return[u,(0,r.useCallback)((e,t)=>{let n=typeof e==`function`?e(d.current):e;Object.is(n,d.current)||(s||(d.current=n),l(n),f(n,t))},[f,d,s])]}var s=[];function c(e,t){let n=t.elements();return e.elements().some(e=>n.some(t=>e!==t&&e.contains(t)))}function l(){let e=s.filter(e=>!s.some(t=>t!==e&&c(e,t)));return e[e.length-1]}function u(n){let{enabled:r=!0,onDismiss:o,inside:c,escapeKey:u=!0,outsidePointer:d=!0}=n,f=i(c),p=a(o);t.n(()=>{if(!r)return;let t={elements:()=>(f.current??[]).map(t=>e.p(t)).filter(e=>e!==null)};s.push(t);let n=new AbortController,i=(e,t)=>document.addEventListener(e,t,{signal:n.signal});return u&&i(`keydown`,e=>{e.key!==`Escape`||e.defaultPrevented||l()===t&&p(`escape`,e)}),d&&i(`pointerdown`,t=>{e.h(t,f.current??[])||p(`outside-pointer`,t)}),()=>{n.abort();let e=s.indexOf(t);e!==-1&&s.splice(e,1)}},[r,u,d,p,f])}function d(){let t=e.t()?.activeElement;return t&&t!==document.body?t:null}function f(t={}){let{enabled:n=!0,returnTo:i,preventScroll:o}=t,[s,c]=(0,r.useState)(null),[l,u]=(0,r.useState)(!1);n!==l&&(u(n),n&&c(d()));let f=a(()=>{(e.m(i)??s)?.focus({preventScroll:o})});return(0,r.useEffect)(()=>{if(n)return()=>{let e=document.activeElement;(!e||e===document.body||!document.contains(e))&&f()}},[n,f]),{returnFocus:f}}var p=10;function m(e,t){return t<0?-1:Math.max(0,Math.min(e,t))}function h(e,t,n,r,i){if(n===0)return-1;let a=e;for(let e=0;e<n;e++){if(a+=t,a<0||a>=n){if(!r)return-1;a=(a%n+n)%n}if(!i?.(a))return a}return-1}function g(e,t,n){return h(e===1?-1:t,e,t,!1,n)}function _(t){let{count:a,columns:s,columnIndexOf:c,pageSize:l=p,defaultActiveColumn:u=0,onActiveCellChange:d,orientation:f=`vertical`,loop:_=!0,activeIndex:v,defaultActiveIndex:y=0,onActiveIndexChange:b,isDisabled:x,textOf:S,focusItems:C=!0,onSelect:w}=t,[T,E]=o({value:v,defaultValue:y,onChange:b}),D=Math.min(T,a-1),O=s!==void 0,k=(0,r.useCallback)(e=>typeof s==`function`?s(e):s??0,[s]),A=(0,r.useCallback)((e,t)=>{let n=k(e);if(!c)return m(t,n-1);if(n<=0)return-1;let r=0;for(;r+1<n&&c(e,r+1)<=t;)r++;return r},[c,k]),[j,M]=(0,r.useState)(u),N=O&&D>=0?A(D,j):-1,P=(0,r.useRef)([]),F=(0,r.useRef)(new Map),I=(0,r.useRef)(new Map),L=(0,r.useRef)(new Map),R=(0,r.useRef)({query:``,at:0}),z=(0,r.useCallback)(e=>{P.current[e]?.focus()},[]),B=(0,r.useRef)(!1),V=(0,r.useCallback)((e,t)=>{B.current=!0,I.current.get(`${e}:${t}`)?.focus(),B.current=!1},[]),H=(0,r.useCallback)(()=>O?I.current.get(`${D}:${N}`)??null:P.current[D]??null,[N,D,O]),U=i({row:D,column:N}),W=i(j),G=i(d),K=i(k),q=i(A),J=i(c),Y=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=Math.max(0,t),o=r<0?-1:q.current(r,i),s=U.current;return r===s.row&&o===s.column?s:(E(r,n),M(i),G.current?.({row:r,column:o},n),{row:r,column:o})},[U,q,G,a,E]),X=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=r<0?-1:m(Math.max(0,t),K.current(r)-1);return Y(r,i<0?0:J.current?.(r,i)??i,n)},[J,a,Y,K]),Z=(0,r.useCallback)((e,t)=>{let n=`${e}:${t}`;return L.current.has(n)||L.current.set(n,e=>{e?I.current.set(n,e):(I.current.delete(n),L.current.delete(n))}),{ref:L.current.get(n),tabIndex:e===D&&t===N?0:-1,onFocus:()=>{B.current||X(e,t,{reason:`focus`})}}},[N,D,X]),Q=(0,r.useCallback)(e=>(F.current.has(e)||F.current.set(e,t=>{P.current[e]=t}),{ref:F.current.get(e),tabIndex:C?e===D?0:-1:void 0,onFocus:C?()=>E(e,{reason:`focus`}):void 0}),[D,C,E]),$=(0,r.useCallback)(t=>{let{row:n,column:r}=U.current,i=(e,n)=>{t.preventDefault();let r=X(e,n,{reason:`keyboard`,event:t});C&&V(r.row,r.column)},o=e=>{t.preventDefault();let n=Y(e,W.current,{reason:`keyboard`,event:t});C&&V(n.row,n.column)},s=2**53-1,c=()=>e.g(t.currentTarget)?-1:1;switch(t.key){case`ArrowRight`:return i(n,r+c());case`ArrowLeft`:return i(n,r-c());case`ArrowDown`:return o(n+1);case`ArrowUp`:return o(n-1);case`Home`:return t.ctrlKey||t.metaKey?i(0,0):i(n,0);case`End`:return t.ctrlKey||t.metaKey?i(a-1,s):i(n,s);case`PageDown`:return o(n+l);case`PageUp`:return o(n-l)}},[U,a,W,V,C,l,Y,X]);return{activeIndex:D,activeColumn:N,setActiveIndex:E,setActiveCell:X,onKeyDown:(0,r.useCallback)(t=>{if(t.defaultPrevented)return;if(O){$(t);return}let r=f!==`horizontal`,i=f!==`vertical`,o=R.current,s=n.t.isOpen(o,t.timeStamp),c=(e,n)=>{e!==-1&&e!==D&&(t.preventDefault(),E(e,{reason:n,event:t}),C&&z(e))};if(i&&(t.key===`ArrowRight`||t.key===`ArrowLeft`)){let n=t.key===`ArrowRight`!==e.g(t.currentTarget);c(h(D,n?1:-1,a,_,x),`keyboard`);return}if(t.key===`ArrowDown`&&r){c(h(D,1,a,_,x),`keyboard`);return}if(t.key===`ArrowUp`&&r){c(h(D,-1,a,_,x),`keyboard`);return}if(t.key===`Home`){c(g(1,a,x),`keyboard`);return}if(t.key===`End`){c(g(-1,a,x),`keyboard`);return}if(t.key===`Enter`||t.key===` `&&!(s&&S)){if(!w||D===-1)return;t.preventDefault(),w(D,t);return}if(S&&n.t.isPrintable(t)){let e=n.t.push(o,t.key,t.timeStamp);c(n.t.target(e,D,a,S,x),`typeahead`)}},[D,a,z,C,x,O,_,$,w,f,E,S]),itemProps:Q,cellProps:Z,activeItem:H}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return _}});
1
+ const e=require("./platform.cjs"),t=require("./effects.cjs"),n=require("./keyboard.cjs");let r=require("react");function i(e){let n=(0,r.useRef)(e);return t.n(()=>{n.current=e}),n}function a(e){let t=i(e);return(0,r.useCallback)((...e)=>t.current?.(...e),[t])}function o(e){let{value:t,defaultValue:n,onChange:o}=e,s=t!==void 0,[c,l]=(0,r.useState)(n),u=s?t:c,d=i(u),f=a(o);return[u,(0,r.useCallback)((e,t)=>{let n=typeof e==`function`?e(d.current):e;Object.is(n,d.current)||(s||(d.current=n),l(n),f(n,t))},[f,d,s])]}var s=[];function c(e,t){let n=t.elements();return e.elements().some(e=>n.some(t=>e!==t&&e.contains(t)))}function l(){let e=s.filter(e=>!s.some(t=>t!==e&&c(e,t)));return e[e.length-1]}function u(n){let{enabled:r=!0,onDismiss:o,inside:c,escapeKey:u=!0,outsidePointer:d=!0}=n,f=i(c),p=a(o);t.n(()=>{if(!r)return;let t={elements:()=>(f.current??[]).map(t=>e.p(t)).filter(e=>e!==null)};s.push(t);let n=new AbortController,i=(e,t)=>document.addEventListener(e,t,{signal:n.signal});return u&&i(`keydown`,e=>{e.key!==`Escape`||e.defaultPrevented||l()===t&&p(`escape`,e)}),d&&i(`pointerdown`,t=>{e.g(t,f.current??[])||p(`outside-pointer`,t)}),()=>{n.abort();let e=s.indexOf(t);e!==-1&&s.splice(e,1)}},[r,u,d,p,f])}function d(){let t=e.t()?.activeElement;return t&&t!==document.body?t:null}function f(t={}){let{enabled:n=!0,returnTo:i,preventScroll:o}=t,[s,c]=(0,r.useState)(null),[l,u]=(0,r.useState)(!1);n!==l&&(u(n),n&&c(d()));let f=a(()=>{(e.h(i)??s)?.focus({preventScroll:o})});return(0,r.useEffect)(()=>{if(n)return()=>{let e=document.activeElement;(!e||e===document.body||!document.contains(e))&&f()}},[n,f]),{returnFocus:f}}var p=10;function m(e,t){return t<0?-1:Math.max(0,Math.min(e,t))}function h(e,t,n,r,i){if(n===0)return-1;let a=e;for(let e=0;e<n;e++){if(a+=t,a<0||a>=n){if(!r)return-1;a=(a%n+n)%n}if(!i?.(a))return a}return-1}function g(e,t,n){return h(e===1?-1:t,e,t,!1,n)}function _(t){let{count:a,columns:s,columnIndexOf:c,pageSize:l=p,defaultActiveColumn:u=0,onActiveCellChange:d,orientation:f=`vertical`,loop:_=!0,activeIndex:v,defaultActiveIndex:y=0,onActiveIndexChange:b,isDisabled:x,textOf:S,focusItems:C=!0,onSelect:w}=t,[T,E]=o({value:v,defaultValue:y,onChange:b}),D=Math.min(T,a-1),O=s!==void 0,k=(0,r.useCallback)(e=>typeof s==`function`?s(e):s??0,[s]),A=(0,r.useCallback)((e,t)=>{let n=k(e);if(!c)return m(t,n-1);if(n<=0)return-1;let r=0;for(;r+1<n&&c(e,r+1)<=t;)r++;return r},[c,k]),[j,M]=(0,r.useState)(u),N=O&&D>=0?A(D,j):-1,P=(0,r.useRef)([]),F=(0,r.useRef)(new Map),I=(0,r.useRef)(new Map),L=(0,r.useRef)(new Map),R=(0,r.useRef)({query:``,at:0}),z=(0,r.useCallback)(e=>{P.current[e]?.focus()},[]),B=(0,r.useRef)(!1),V=(0,r.useCallback)((e,t)=>{B.current=!0,I.current.get(`${e}:${t}`)?.focus(),B.current=!1},[]),H=(0,r.useCallback)(()=>O?I.current.get(`${D}:${N}`)??null:P.current[D]??null,[N,D,O]),U=i({row:D,column:N}),W=i(j),G=i(d),K=i(k),q=i(A),J=i(c),Y=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=Math.max(0,t),o=r<0?-1:q.current(r,i),s=U.current;return r===s.row&&o===s.column?s:(E(r,n),M(i),G.current?.({row:r,column:o},n),{row:r,column:o})},[U,q,G,a,E]),X=(0,r.useCallback)((e,t,n)=>{let r=m(e,a-1),i=r<0?-1:m(Math.max(0,t),K.current(r)-1);return Y(r,i<0?0:J.current?.(r,i)??i,n)},[J,a,Y,K]),Z=(0,r.useCallback)((e,t)=>{let n=`${e}:${t}`;return L.current.has(n)||L.current.set(n,e=>{e?I.current.set(n,e):(I.current.delete(n),L.current.delete(n))}),{ref:L.current.get(n),tabIndex:e===D&&t===N?0:-1,onFocus:()=>{B.current||X(e,t,{reason:`focus`})}}},[N,D,X]),Q=(0,r.useCallback)(e=>(F.current.has(e)||F.current.set(e,t=>{P.current[e]=t}),{ref:F.current.get(e),tabIndex:C?e===D?0:-1:void 0,onFocus:C?()=>E(e,{reason:`focus`}):void 0}),[D,C,E]),$=(0,r.useCallback)(t=>{let{row:n,column:r}=U.current,i=(e,n)=>{t.preventDefault();let r=X(e,n,{reason:`keyboard`,event:t});C&&V(r.row,r.column)},o=e=>{t.preventDefault();let n=Y(e,W.current,{reason:`keyboard`,event:t});C&&V(n.row,n.column)},s=2**53-1,c=()=>e._(t.currentTarget)?-1:1;switch(t.key){case`ArrowRight`:return i(n,r+c());case`ArrowLeft`:return i(n,r-c());case`ArrowDown`:return o(n+1);case`ArrowUp`:return o(n-1);case`Home`:return t.ctrlKey||t.metaKey?i(0,0):i(n,0);case`End`:return t.ctrlKey||t.metaKey?i(a-1,s):i(n,s);case`PageDown`:return o(n+l);case`PageUp`:return o(n-l)}},[U,a,W,V,C,l,Y,X]);return{activeIndex:D,activeColumn:N,setActiveIndex:E,setActiveCell:X,onKeyDown:(0,r.useCallback)(t=>{if(t.defaultPrevented)return;if(O){$(t);return}let r=f!==`horizontal`,i=f!==`vertical`,o=R.current,s=n.t.isOpen(o,t.timeStamp),c=(e,n)=>{e!==-1&&e!==D&&(t.preventDefault(),E(e,{reason:n,event:t}),C&&z(e))};if(i&&(t.key===`ArrowRight`||t.key===`ArrowLeft`)){let n=t.key===`ArrowRight`!==e._(t.currentTarget);c(h(D,n?1:-1,a,_,x),`keyboard`);return}if(t.key===`ArrowDown`&&r){c(h(D,1,a,_,x),`keyboard`);return}if(t.key===`ArrowUp`&&r){c(h(D,-1,a,_,x),`keyboard`);return}if(t.key===`Home`){c(g(1,a,x),`keyboard`);return}if(t.key===`End`){c(g(-1,a,x),`keyboard`);return}if(t.key===`Enter`||t.key===` `&&!(s&&S)){if(!w||D===-1)return;t.preventDefault(),w(D,t);return}if(S&&n.t.isPrintable(t)){let e=n.t.push(o,t.key,t.timeStamp);c(n.t.target(e,D,a,S,x),`typeahead`)}},[D,a,z,C,x,O,_,$,w,f,E,S]),itemProps:Q,cellProps:Z,activeItem:H}}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return a}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return o}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return f}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return u}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return _}});
package/behavior.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { g as e, h as t, m as n, p as r, t as i } from "./platform.mjs";
1
+ import { _ as e, g as t, h as n, p as r, t as i } from "./platform.mjs";
2
2
  import { n as a } from "./effects.mjs";
3
3
  import { t as o } from "./keyboard.mjs";
4
4
  import { useCallback as s, useEffect as c, useRef as l, useState as u } from "react";
package/box.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./rolldown-runtime.cjs"),t=require("./react-shared.cjs"),n=require("./client.cjs"),r=require("./interactions.cjs");let i=require("react");i=e.t(i,1);let a=require("@box-kite/core");function o({tag:e,tagProps:t,render:n}){let[r,a]=(0,i.useState)(!1);return i.default.createElement(e,{...t,onMouseEnter:()=>a(!0),onMouseLeave:()=>a(!1)},n({isHover:r}))}function s(e,r){let{tag:a=`div`,children:s}=e,{classNames:c,styleElements:l}=n.i(e,a===`svg`),u=t.d(e,c);r&&(u.ref=r);let d=typeof s==`function`?i.default.createElement(o,{tag:a,tagProps:u,render:s}):i.default.createElement(a,u,s);return l?i.default.createElement(i.default.Fragment,null,l,d):d}var c=t.f(s,`Box`);c.extend=a.BoxExtends.extend,c.components=a.BoxExtends.components,c.palette=a.BoxExtends.palette,c.createComponents=a.BoxExtends.createComponents,c.Components=n.o,c.keyframes=a.BoxExtends.keyframes,c.spring=a.Springs.spring,c.Theme=n.n,c.useTheme=n.n.useTheme,c.useDesign=n.n.useDesign,c.getVariableValue=e=>(0,a.getDefaultEngine)().getVariableValue(e),c.configure=n.r.configure,c.viewTransition=a.startViewTransition,c.onInteraction=r.i,c.reportInteraction=r.r,c.flushInteractions=r.n;function l(e,r){return t.p(n.i(e,r?.svg===!0),e.className)}exports.default=c,exports.useBoxElement=s,exports.useClassNames=l,exports.useVisibility=n.a;
1
+ Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:`Module`}});const e=require("./rolldown-runtime.cjs"),t=require("./react-shared.cjs"),n=require("./client.cjs"),r=require("./interactions.cjs");let i=require("react");i=e.t(i,1);let a=require("@box-kite/core");function o({tag:e,tagProps:t,render:n}){let[r,a]=(0,i.useState)(!1);return i.default.createElement(e,{...t,onMouseEnter:()=>a(!0),onMouseLeave:()=>a(!1)},n({isHover:r}))}function s(e,r){let{tag:a=`div`,children:s}=e,{classNames:c,styleElements:l}=n.i(e,a===`svg`),u=t.d(e,c);r&&(u.ref=r);let d=typeof s==`function`?i.default.createElement(o,{tag:a,tagProps:u,render:s}):i.default.createElement(a,u,s);return l?i.default.createElement(i.default.Fragment,null,l,d):d}var c=t.f(s,`Box`);c.extend=a.BoxExtends.extend,c.components=a.BoxExtends.components,c.palette=a.BoxExtends.palette,c.sizes=a.BoxExtends.sizes,c.createComponents=a.BoxExtends.createComponents,c.Components=n.o,c.keyframes=a.BoxExtends.keyframes,c.spring=a.Springs.spring,c.Theme=n.n,c.useTheme=n.n.useTheme,c.useDesign=n.n.useDesign,c.getVariableValue=e=>(0,a.getDefaultEngine)().getVariableValue(e),c.configure=n.r.configure,c.viewTransition=a.startViewTransition,c.onInteraction=r.i,c.reportInteraction=r.r,c.flushInteractions=r.n;function l(e,r){return t.p(n.i(e,r?.svg===!0),e.className)}exports.default=c,exports.useBoxElement=s,exports.useClassNames=l,exports.useVisibility=n.a;
package/box.d.ts CHANGED
@@ -24,6 +24,7 @@ interface BoxType {
24
24
  components: typeof BoxExtends.components;
25
25
  /** The `vars` record that points the components' `accent` and `base` families at others, for an element and its subtree. */
26
26
  palette: typeof BoxExtends.palette;
27
+ sizes: typeof BoxExtends.sizes;
27
28
  /** A design for one subtree, registered nowhere: hand it to `<Box.Components value>` or `<Box.Theme components>`. */
28
29
  createComponents: typeof BoxExtends.createComponents;
29
30
  /** Every Box below resolves its `component` against this registry instead of the app's `Box.components()`. */
package/box.mjs CHANGED
@@ -23,7 +23,7 @@ function v(t, n) {
23
23
  return s ? d.createElement(d.Fragment, null, s, l) : l;
24
24
  }
25
25
  var y = t(v, "Box");
26
- y.extend = p.extend, y.components = p.components, y.palette = p.palette, y.createComponents = p.createComponents, y.Components = o, y.keyframes = p.keyframes, y.spring = m.spring, y.Theme = a, y.useTheme = a.useTheme, y.useDesign = a.useDesign, y.getVariableValue = (e) => h().getVariableValue(e), y.configure = s.configure, y.viewTransition = g, y.onInteraction = c, y.reportInteraction = u, y.flushInteractions = l;
26
+ y.extend = p.extend, y.components = p.components, y.palette = p.palette, y.sizes = p.sizes, y.createComponents = p.createComponents, y.Components = o, y.keyframes = p.keyframes, y.spring = m.spring, y.Theme = a, y.useTheme = a.useTheme, y.useDesign = a.useDesign, y.getVariableValue = (e) => h().getVariableValue(e), y.configure = s.configure, y.viewTransition = g, y.onInteraction = c, y.reportInteraction = u, y.flushInteractions = l;
27
27
  function b(e, t) {
28
28
  return n(i(e, t?.svg === !0), e.className);
29
29
  }