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.