onejs-ui#
onejs-ui is a themeable React component library for OneJS — built for Unity UI Toolkit, GPU-rendered, and gamepad/keyboard-native. It's a separate package on top of onejs-react, not a web library ported over.
You get ~20 components (layout, typography, form controls, overlays), light/dark theming you can swap instantly, and a real focus-visible keyboard/gamepad focus ring that's wired up for you.
Why a separate library?#
onejs-react gives you the primitives (View, Label, Button, Toggle, ...) that map 1:1 to UI Toolkit elements. onejs-ui is the layer above: opinionated, themed components you compose into real screens — a settings panel, a HUD, a dialog — without restyling primitives by hand each time.
- Game-engine first. Designed for UI Toolkit and controller/keyboard navigation. Web-only patterns (data grids, breadcrumbs, calendars) are intentionally out of scope.
- Instant theming. Components style themselves through USS classes that read
--ojs-*custom properties. Swapping a theme recompiles one small variables sheet and Unity re-resolves the cascade natively — no React re-render. - Native-backed controls. Checkbox, Switch, Slider, Input, and Radio are built on the matching UI Toolkit controls, so they're genuine focus targets with full keyboard/IME behavior.
- Cheap by construction. Most components are pure-TSX composites over
onejs-react, styled by embedded CSS Modules. No custom C# required.
Requirements#
- OneJS v3 runtime. The focus ring's reliability depends on the runtime's tick-based
focuschangesignal. Use a recent OneJS build that includes it; with an older runtime the ring degrades gracefully to nav-event-driven only. - Peer dependencies:
react(18 or 19) andonejs-react.
Install#
npm install onejs-uionejs-ui ships raw TS/TSX — there's no build step. Your app's esbuild bundles it, the same model as onejs-react.
Quickstart#
Wrap your app in <ThemeProvider>. That applies the theme and initializes the focus-visible manager, so everything inside gets themed components with working focus rings — no extra wiring.
import { useState } from "react"
import { render } from "onejs-react"
import {
ThemeProvider, Card, Heading, Text, Button, Checkbox, HStack,
useTheme, darkTheme, lightTheme,
} from "onejs-ui"
function App() {
const { tokens, setTheme } = useTheme()
const [agree, setAgree] = useState(false)
return (
<Card variant="raised" style={{ width: 360 }}>
<Heading level={1}>onejs-ui</Heading>
<Text tone="muted">Themed via USS variables.</Text>
<Checkbox label="I agree" value={agree} onChange={setAgree} />
<HStack gap={8}>
<Button onClick={() => {}}>Primary</Button>
<Button intent="secondary" onClick={() => setTheme(tokens === darkTheme ? lightTheme : darkTheme)}>
Toggle theme
</Button>
</HStack>
</Card>
)
}
render(
<ThemeProvider theme={darkTheme}>
<App />
</ThemeProvider>,
__root,
)What's included#
| Group | Components |
|---|---|
| Layout | Card, Stack / HStack / VStack, Divider, Spacer |
| Typography | Text, Heading |
| Form controls | Button, Checkbox, Switch, Input, Slider, RadioGroup, Select |
| Feedback | Badge, Toast |
| Overlays | Popover, Dialog, DropdownMenu, Drawer, Tooltip |
| Foundations | ThemeProvider, FocusScope, Overlay, motion (usePresence) |
Next steps#
- Theming — tokens, custom themes, and live theme swaps.
- Focus & navigation — the focus-visible model and
FocusScope. - Components — the full reference with usage for every component.