boardweaver/ui)The platform bundles Radix UI — the radix-ui package,
version 1.6.7 — and exposes it as boardweaver/ui. Import it like any module:
it resolves to a single shared copy the frame provides, so nothing is added to your bundle, and
your game is version-locked to that copy. The platform upgrades it deliberately, never underneath
a committed game.
import { Dialog, ContextMenu, Tooltip } from "boardweaver/ui";
Reach for these instead of hand-rolling. They are unstyled — you own every pixel — and they
already handle focus traps, escape/outside-click dismissal, roving tabindex, hover-and-focus
parity, and the aria-* wiring that hand-written versions almost always omit.
| Import | Use it for |
|---|---|
Dialog |
Modal surfaces: rules, settings, setup |
AlertDialog |
Confirming something irreversible ("resign?"). Unlike Dialog it does not dismiss on outside click and focuses cancel first |
Tooltip |
Short text on hover and keyboard focus |
HoverCard |
Rich hover content — hover a card, show it enlarged |
Popover |
Anchored non-modal panels: piece detail, bid entry |
ContextMenu |
Right-click menus |
DropdownMenu |
The click-triggered menu. Use this for piece actions — there is no right-click on touch, so ContextMenu alone is desktop-only |
VisuallyHidden |
Screen-reader-only text. Use it to announce state changes a sighted player reads from the board |
Accordion, AccessibleIcon, AspectRatio, Avatar, Checkbox, Collapsible, Direction,
Form, Label, Menubar, NavigationMenu, Portal, Progress, RadioGroup, ScrollArea,
Select, Separator, Slider, Slot, Switch, Tabs, Toast, Toggle, ToggleGroup,
Toolbar.
Each import is a namespace of subcomponents (Dialog.Root, Dialog.Trigger, Dialog.Content, …).
The prop types are Radix's own, so the upstream documentation applies exactly — look up any
component at https://www.radix-ui.com/primitives/docs/components/<name>.
Three notes on that upstream documentation:
It documents Radix's current release, which may be newer than the 1.6.7 bundled here. If a prop the site describes is a type error in your game, that is the likely reason — it landed after the bundled version. Trust the type error.
Where it says import * as Dialog from "@radix-ui/react-dialog", use
import { Dialog } from "boardweaver/ui" instead. Importing @radix-ui/* or radix-ui
directly is a compile error — those specifiers do not exist inside a game.
Anything under Radix's "internal" or "unstable" entry points is not available. If you cannot
import it from boardweaver/ui, it is not part of the platform surface.
import { useState } from "react";
import { Dialog, DropdownMenu, VisuallyHidden } from "boardweaver/ui";
import { useMatch, useSelf } from "boardweaver/react";
export default function App() {
const match = useMatch();
const { isActive } = useSelf();
const [rulesOpen, setRulesOpen] = useState(false);
return (
<div>
{/* Announce turn changes to screen readers without drawing anything. */}
<VisuallyHidden.Root>
{isActive ? "Your turn" : "Waiting for your opponent"}
</VisuallyHidden.Root>
<DropdownMenu.Root>
<DropdownMenu.Trigger aria-label="Piece actions">…</DropdownMenu.Trigger>
<DropdownMenu.Portal>
<DropdownMenu.Content side="bottom" style={{ background: "#fff", padding: 4 }}>
<DropdownMenu.Item
disabled={!isActive}
onSelect={() => match.click({ type: "Click", pieceId: "tile-1" })}
>
Play tile
</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
<Dialog.Root open={rulesOpen} onOpenChange={setRulesOpen}>
<Dialog.Trigger>Rules</Dialog.Trigger>
<Dialog.Portal>
<Dialog.Overlay style={{ position: "fixed", inset: 0, background: "#0008" }} />
<Dialog.Content style={{ position: "fixed", inset: "20%", background: "#fff" }}>
<Dialog.Title>How to play</Dialog.Title>
<Dialog.Description>Place a tile, then end your turn.</Dialog.Description>
<Dialog.Close>Close</Dialog.Close>
</Dialog.Content>
</Dialog.Portal>
</Dialog.Root>
</div>
);
}
Every primitive renders a plain element with no styles attached, so style it however you style the
rest of your game — inline style, a <style> tag, className plus your own CSS. Radix also sets
data-state attributes (data-state="open", data-state="checked") you can target for
open/closed and enter/exit styling.
Dialog.Portal and friends render into document.body — which, inside the game frame, is your
document. Nothing can clip or escape your UI, and you never need to think about the host page.