Packages
Components for the framework you already use
Nine native packages, one source. Each is generated from a single component spec and tested against the same 826 golden cases, so a Boxy Button in Angular and a Boxy Button in Elm render the markup the stylesheet expects.
npm install @boxy-design/react
React 19 · setup
- Import the stylesheet once at the app root:
import "@boxy-design/react/styles.css". - Render
<IconSprite />once near the root so<Icon name="..." />resolves. - Optional:
<ThemeScript />in<head>(SSR) anduseBoxy()for theme, density and mode. - Render
<Behaviors />once near the root to make menus, tooltips, combobox, calendar, kanban and the other interactive widgets respond. Tabs, Modal and Drawer are components and need nothing.
import { Button, Field, Input } from "@boxy-design/react";
<Field label="Service name" help="Lowercase, hyphens only.">
<Input />
</Field>
<Button variant="primary">Deploy</Button>
Notes
- Field wires
id,aria-describedbyandaria-invalidinto the control inside it; do not set them by hand. - Extend with
className, never by overridingbx-*classes.buttonClass()and the other*Class()builders give the same classes for a non-Boxy element.
npm install @boxy-design/next
Next.js 16 (App Router) · setup
- In app/layout.tsx:
import "@boxy-design/next/styles.css", render<ThemeScript />inside<head>,<IconSprite />first in<body>, and putsuppressHydrationWarningon<html>. - Button, Card, Table, Tag, Callout and the rest of the stateless components are Server Components. Field, Input, Select, Textarea, Tabs and Modal are client components and carry their own
"use client"; import them from a Server Component directly, no wrapper needed. - Render
<Behaviors />(a client component) once in the root layout if the app uses menus, tooltips, combobox, calendar, kanban, grid, picker, inbox or charts.
import { Button, ButtonLink, Card, CardBody } from "@boxy-design/next";
export default function Page() {
return (
<Card>
<CardBody>
<Button variant="primary">Deploy</Button>
<ButtonLink href="/logs" size="sm">Logs</ButtonLink>
</CardBody>
</Card>
);
}
Notes
ButtonLinkis anext/linkstyled as a Button; use it for navigation instead of wrapping a<Link>in a<Button>.useBoxy()(client) reads and sets theme, density and mode; theThemeScriptprevents a flash on first paint.
npm install @boxy-design/preact
Preact 11 · setup
- Import the stylesheet once:
import "@boxy-design/preact/styles.css". - Render
<IconSprite />once near the root. - Render
<Behaviors />once near the root to make menus, tooltips, combobox, calendar, kanban and the other interactive widgets respond.
import { Button, Field, Input } from "@boxy-design/preact";
<Field label="Service name">
<Input />
</Field>
<Button variant="primary">Deploy</Button>
Notes
- Native Preact:
class, notclassName, and nopreact/compatneeded.
npm install @boxy-design/vue
Vue 3.5 · setup
- Import the stylesheet once in main.ts:
import "@boxy-design/vue/styles.css". - Render
<IconSprite />once in App.vue. - Components are named exports (
Button,Field,Input, ...); import the ones you use and put them in a template. - Render
<Behaviors />once in App.vue (or calluseBehaviors()) to make menus, tooltips, combobox, calendar, kanban and the other interactive widgets respond.
<script setup lang="ts">
import { Button, Field, Input } from "@boxy-design/vue";
</script>
<template>
<Field label="Service name" help="Lowercase, hyphens only.">
<Input />
</Field>
<Button variant="primary">Deploy</Button>
</template>
Notes
Tabstakesv-model,Modaltakesv-model:openand a#footerslot.useBoxy()returns the current theme, density and mode as a ref plus setters.
npm install @boxy-design/nuxt
Nuxt 4 · setup
- Add
modules: ["@boxy-design/nuxt"]to nuxt.config.ts. That registers every component (prefixBx:<BxButton>), the stylesheet, the no-flash theme script and the icon sprite. Nothing else to import. - Options live under
boxy: { prefix, css, themeScript, sprite, mode }. - Behaviours (menus, tooltips, combobox, calendar, kanban, ...) start in the browser automatically; turn them off with
boxy: { behaviors: false }.
<template>
<BxCard>
<BxCardBody>
<BxField label="Service name" help="Lowercase, hyphens only.">
<BxInput />
</BxField>
<BxButton variant="primary">Deploy</BxButton>
</BxCardBody>
</BxCard>
</template>
Notes
useBoxy()is auto-imported.
npm install @boxy-design/svelte
Svelte 5 · setup
- Import the stylesheet once in the root layout:
import "@boxy-design/svelte/styles.css". - Render
<IconSprite />once in the root layout. - Render
<Behaviors />once near the root to make menus, tooltips, combobox, calendar, kanban and the other interactive widgets respond.
<script lang="ts">
import { Button, Field, Input } from "@boxy-design/svelte";
</script>
<Field label="Service name" help="Lowercase, hyphens only.">
<Input />
</Field>
<Button variant="primary">Deploy</Button>
Notes
- Runes-based; content is passed as children and the Modal footer is a
footersnippet. Textareatakesvalue, not child text.
npm install @boxy-design/angular
Angular 22 · setup
- Add
node_modules/@boxy-design/angular/styles.cssto thestylesarray in angular.json (or@importit from your global stylesheet). - Add
<svg bxIconSprite></svg>once in the root template. - Import what you use into each standalone component's
imports(BxButton,BxField, ...), or...BOXYwhile prototyping. - Add
provideBoxyBehaviors()to the application providers to make menus, tooltips, combobox, calendar, kanban and the other interactive widgets respond.
import { BxButton, BxField, BxInput } from "@boxy-design/angular";
@Component({
imports: [BxButton, BxField, BxInput],
template: `
<div bxField label="Service name" help="Lowercase, hyphens only.">
<input bxInput />
</div>
<button bxButton variant="primary">Deploy</button>
`
})
export class DeployForm {}
Notes
- Directives sit on the native element (
<button bxButton>), so the DOM is exactly the design system's markup. Components with internal structure (Icon, Field, Callout) are components on a native element too. - Tabs:
<ng-container bxTabs defaultValue="a">. Modal:<bx-modal [(open)]="open" title="...">with<ng-container bxModalFooter>.
npm install @boxy-design/htmx
HTMX / server-rendered HTML · setup
- Link the stylesheet (
styles.cssfrom the package) and includeboxy-htmx.jsonce; it handles tab keys and turns any.bx-dialogswapped into the page into a real modal (focus trap, inert page, scroll lock, Esc, focus return). - Render
iconSprite()once in the base layout. - Not a Node server? Use the HTML in
partials/*.html, orspec.json, to template the same markup in any language. boxy-htmx.jsalso starts every interactive widget (menus, tooltips, combobox, calendar, kanban, ...), tabs and modal dialogs; there is nothing more to wire.
import { button, field, input, tabs, tabList, tab, tabPanel } from "@boxy-design/htmx";
field({ id: "svc", label: "Service name", help: "Lowercase." }, (f) => input({ field: f }));
button({ variant: "primary", attrs: { "hx-post": "/deploy", "hx-target": "#out" } }, "Deploy");
Notes
- Every function takes
(props, children)and returns an HTML string. Passhx-*and any other attribute throughattrs. - Escape user text with
esc(): children are trusted HTML. field()andtabs()hand their wiring to a callback because a server has no context.
npm install @boxy-design/elm
Elm 0.19 · setup
- Add the package's
srctosource-directoriesin elm.json (npm install) orelm install avestura/boxy-design. - Link
boxy.css+boxy-components.cssfrom your HTML, and putBoxy.IconSprite.viewonce in the view. - Include
boxy-htmx.js(from@boxy-design/htmx) if you want focus trapping and an inert page for modals. - Load
@boxy-design/elm/boxy-behaviors.jswith a plain<script>so menus, tooltips, combobox, calendar, kanban and dialogs respond; it picks up what Elm renders.
import Boxy.Button as Button
import Boxy.Field as Field
import Boxy.Input as Input
view model =
Field.view "svc" [ Field.label "Service name" ] [] (\f -> [ Input.view [ Input.field f ] [] ])
-- and: Button.view [ Button.primary ] [ onClick Deploy ] [ text "Deploy" ]
Notes
- One module per component, an options list for variants:
Button.view [ Button.primary, Button.sm ] attrs children. - Elm has no context, so Field and Tabs hand their wiring to their children explicitly.
- Also published to the Elm registry as
avestura/boxy-design(elm install avestura/boxy-design) when the release mirror is configured. - Use the
classNameoption instead ofHtml.Attributes.classon the root, which would replace the component's classes.
MIT · one version across every package · shares the stylesheet and tokens
01 / Why they match
Generated, then checked five ways
Nobody ports Boxy by hand. A component is described once, as a framework-neutral element tree, and each package is emitted from that description. Then every layer that could drift has its own test.
boxy-components.css the bx-* classes: what it looks like
spec/components.json element tree, props, a11y: what it is
|
v node tools/gen/build.mjs
packages/<framework>/src/generated/ emitted, committed, diffed in CI
01
Spec against CSS
The build fails if the spec names a class or icon that does not exist, or if the stylesheet has a modifier no component exposes.
02
Spec against this site
Every spec'd component is parsed out of these showcase pages. A usage the spec cannot produce, such as a different role or child order, fails the build.
03
Output against spec
CI regenerates everything and fails if a committed file differs. A markup change shows up as a diff in the golden cases, so it gets reviewed.
04
Every package, same cases
Each package renders all 826 cases through its own compiler and runtime, then diffs the DOM against the oracle. Angular is built AOT, Elm is type-checked.
05
Behaviour
Keyboard navigation, focus trap, scroll lock, inert page and focus return are tested in a DOM. Next and Nuxt are also built as real apps and their server HTML is asserted.
06
Sabotage checks
Breaking a class name, a role or a child element on purpose must turn the suites red. That is how a stale test cache in the Angular runner was found.
02 / Coverage
What the packages include today
230 component parts, the same in every package: everything in the stylesheet, from buttons and forms to menus, the combobox, the calendar, kanban boards, the data grid and the agent chat. The twelve page patterns are compositions of these, so they are built from the same parts.
| Group | Components |
|---|---|
| Buttons | Button, ButtonGroup |
| Forms | Label, HelpText, Field, Input, Textarea, Select, Choice, Checkbox, Radio, Toggle, SearchField, DateField, Slider, SliderScale, SliderTicks, Stepper, Segmented |
| Combobox and pickers | Combo, ComboToggle, Listbox, ListboxGroup, ListboxEmpty, ListboxOption, OptionMeta, MultiSelect, MultiInput, MultiClear, Calendar, RangeCalendar, CalendarFoot, RangePicker, RangePresets, ColorPicker, PickerField, PickerHue, PickerRow, PickerSwatches, PickerContrast, Swatch, SwatchButton |
| Layout | Stack, Cluster, Grid, AutoGrid, Split, Collapse, Container, Rail, Section, GridBackground, DotBackground, Ticks |
| Text and content | LabelText, Mono, Num, ScreenReaderText, Kbd, Mark, Code, Caret, Prose, Quote, Cite, List, OrderedList, ListItem, DescriptionList, Empty, EmptyGlyph, Banner |
| Status and feedback | Icon, Tag, TagRemove, Status, Callout, Spinner, Skeleton, Progress, ProgressRow, Meter, MeterCell, StepBar, StepBarCell, Toast, ToastBody, ToastStack, Tooltip |
| People and activity | Avatar, AvatarGroup, Timeline, TimelineItem, TimelineTime, TimelineTitle, TimelineBody |
| Cards and data | Card, CardHead, CardBody, CardFoot, Table, TableWrap, Th, Td, DataGrid, DataGridBar, Chart, ChartLegend, ChartKey, ChartTip, Sparkline, UptimeStrip |
| Navigation | Tabs, TabList, Tab, TabPanel, Breadcrumb, BreadcrumbMore, Tree, TreeHeading, TreeLink, TreeSummary, TreeCount, Toc, TocLink, Accordion, AccordionNumber, AccordionBody |
| Menus and popovers | Menu, MenuEntry, MenuItem, MenuLink, MenuItemCheckbox, MenuItemRadio, MenuIcon, MenuHint, MenuLabel, MenuSeparator, MenuHeader, MenuFooter, Popover, PopoverHead, PopoverBody, PopoverFoot, FloatingPanel |
| Dialogs, drawers and palette | Modal, Dialog, DialogHeader, DialogTitle, DialogBody, DialogFooter, Scrim, Drawer, DrawerPanel, DrawerHead, DrawerBody, DrawerFoot, PaletteSearch, PaletteList, PaletteGroup, PaletteLink, PaletteAction, PaletteMeta, PaletteEmpty, PaletteFoot |
| Code | CodeBlock, CodeBlockHead, CodeBlockSpacer, CodeBlockButton, CodeBlockFoot, CodeLines, CodeLine, Token |
| Files | Dropzone, DropzoneGlyph, UploadList, UploadItem, UploadType, UploadName, UploadMeta |
| Agent and chat | Thread, Message, MessageWho, MessageMeta, MessageBody, MessageActions, Thought, ThoughtBody, ToolList, Tool, ToolStatus, ToolName, ToolArgs, ToolDuration, ToolBody, ToolSummary, Approval, ApprovalHead, ApprovalBody, ApprovalFoot, Composer, ComposerFiles, ComposerBar |
| Boards and panes | Kanban, KanbanColumn, KanbanHead, KanbanCount, KanbanList, KanbanSlot, KanbanCard, KanbanCardTitle, KanbanCardMeta, KanbanPriority, Panes, Pane, PaneHead, Resizer |
| Notifications | BellButton, BellCount, Inbox, InboxHead, InboxTabs, InboxList, InboxDay, InboxFoot, Note, NoteIcon, NoteText, NoteActions, NoteTime |
Names follow each framework's convention: Angular prefixes with Bx,
Elm has one module per component, HTMX uses lower-case functions and ships
ready-made partials/*.html.
03 / Behaviours
One behaviour layer under all of them
Menus with submenus and type-ahead, tooltips, the combobox and multi-select, the
calendar and range picker, kanban drag, resizable panes, the editable grid, the
colour picker, the inbox, charts and toasts need JavaScript. It is written once, in
@boxy-design/behaviors, finds widgets by their classes and data
attributes, and has no framework in it. Every package starts it with one line.
React, Next, Preact, Svelte <Behaviors /> Vue <Behaviors /> or useBehaviors() Angular provideBoxyBehaviors() Nuxt automatic HTMX, Elm <script src="boxy-htmx.js"> / boxy-behaviors.js
Tabs, Modal and Drawer are native components in every framework package, so they need
nothing. Your app hears about changes through bubbling bx:* events, such as
bx:select, bx:date and bx:move (cancelable, so a
framework that owns the cards can move them itself).
04 / Theme
Theme, density and mode work the same everywhere
Every web package exports the same no-flash script and helpers. They set
data-theme, data-density and data-mode
on <html> and keep them under boxy:theme,
boxy:density and boxy:mode, which is also what this
site does. React, Next, Vue and Nuxt add a reactive useBoxy().
05 / For agents
Your agent picks the right package
Installing the skill also installs the rule: if the project uses one of these
frameworks, use its package and do not hand-write components. The CLI reads
package.json (and elm.json) to say which one.
$ npx boxy-design packages # prints the package for this project, with setup and an example
npx boxy-design init prints the same note, and the skill's
references/frameworks.md carries the per-framework setup. Agents still
run boxy-check before they report the work done.
Start
Install one package and keep the system intact
The stylesheet, the tokens and the components move together, so an upgrade is one version number.