Components#

Every component imports from onejs-ui and is styled by the active theme. Controlled inputs follow a value + onChange(value) pattern — onChange receives a plain value (a boolean, number, or string), not an event.

import { Button, Card, Checkbox, Slider /* … */ } from "onejs-ui"

Layout#

Card#

A surface container. variant="raised" lifts it above the page with surfaceRaised; variant="surface" (default) sits flush.

<Card variant="raised" style={{ width: 360 }}>
    {/* content */}
</Card>

Stack, HStack, VStack#

Flex containers with a gap (applied as margins, since UI Toolkit has no gap). HStack/VStack are Stack with direction preset to row/column.

<VStack gap={16}>
    <HStack gap={8}>
        <Button>Save</Button>
        <Button intent="ghost">Cancel</Button>
    </HStack>
</VStack>
PropTypeDefault
direction"row" \| "column""column"
gapnumber (px)0
aligncross-axis alignment
justifymain-axis alignment

Divider#

A 1px separator line in the border color.

<Divider />
<Divider orientation="vertical" />

Spacer#

Empty space inside a flex container. With size it's fixed; without, it flexes to fill.

<HStack>
    <Text>Left</Text>
    <Spacer />
    <Text>Right</Text>
</HStack>

Typography#

Text#

Body text with a tone and size scale.

<Text>Default body text</Text>
<Text tone="muted" size="sm">Secondary detail</Text>
  • tone: "default" | "muted" | "subtle"
  • size: "sm" | "md" | "lg" | "xl"

Heading#

Section headings at three levels.

<Heading level={1}>Settings</Heading>
<Heading level={3}>Audio</Heading>
  • level: 1 | 2 | 3

Form controls#

Button#

intent sets the visual style; size sets the size step. Content is passed as children.

<Button onClick={save}>Primary</Button>
<Button intent="secondary" onClick={cancel}>Secondary</Button>
<Button intent="ghost" size="sm">Ghost</Button>
<Button intent="danger" onClick={remove}>Delete</Button>
  • intent: "primary" | "secondary" | "ghost" | "danger" (default "primary")
  • size: "sm" | "md" | "lg" (default "md")
  • onClick: (e) => void

Checkbox#

A labeled checkbox on the native UI Toolkit toggle.

const [agree, setAgree] = useState(false)
<Checkbox label="I agree" value={agree} onChange={setAgree} />
  • label: string
  • value: boolean · onChange: (checked: boolean) => void

Switch#

A toggle switch; the label renders to the left.

const [on, setOn] = useState(true)
<Switch label="Notifications" value={on} onChange={setOn} />
  • value: boolean · onChange: (checked: boolean) => void

Input#

A text field on the native UI Toolkit TextField — a proper focus target with full keyboard/IME behavior.

const [name, setName] = useState("")
<Input value={name} onChange={setName} />
  • value: string · onChange: (value: string) => void
  • Other TextField props (e.g. placeholder) pass through.

Slider#

A slider on the native UI Toolkit Slider, with pointer drag and keyboard steps.

const [vol, setVol] = useState(60)
<Slider value={vol} lowValue={0} highValue={100} onChange={setVol} />
  • value: number · onChange: (value: number) => void
  • lowValue / highValue: range bounds.

RadioGroup#

A single-choice group. value is the selected index (-1 for none).

const [plan, setPlan] = useState(1)
<RadioGroup value={plan} choices={["Free", "Pro", "Team"]} onChange={setPlan} />
  • choices: string[]
  • value: number (index, -1 = none) · onChange: (index: number) => void

Select#

A dropdown of options. value is the selected option's value string.

const [fruit, setFruit] = useState("apple")
<Select
    value={fruit}
    options={[
        { label: "Apple", value: "apple" },
        { label: "Orange", value: "orange" },
    ]}
    onChange={setFruit}
/>
  • options: { label: string; value: string }[]
  • value: string · onChange: (value: string) => void

Feedback#

Badge#

A small status pill.

<Badge intent="success">Active</Badge>
<Badge intent="danger">Error</Badge>
  • intent: "neutral" | "primary" | "success" | "warning" | "danger"

Toast#

Transient notifications. Wrap your app in <ToastProvider> once, then call useToast() (which returns a toast function) from anywhere.

import { ToastProvider, useToast } from "onejs-ui"

function SaveButton() {
    const toast = useToast()
    return (
        <Button onClick={() => toast({ message: "Saved", tone: "success" })}>
            Save
        </Button>
    )
}

// at the root:
<ToastProvider placement="bottom-right">
    <App />
</ToastProvider>
  • toast(options){ message, title?, tone?, duration? }
  • tone: "default" | "success" | "warning" | "danger" | "info"
  • duration: ms before auto-dismiss; 0 keeps it open. Default 4000.

Overlays#

All overlays render into a portal and share positioning, dismissal, and motion. placement accepts sides and alignments like "bottom-start" or "top".

Tooltip#

A label shown on hover/focus of its child.

<Tooltip label="Delete permanently">
    <Button intent="danger">Delete</Button>
</Tooltip>

Popover#

A floating panel anchored to a trigger. Uncontrolled by default; pass open + onOpenChange to control it.

<Popover trigger={<Button>Options</Button>} placement="bottom">
    <Text>Popover content</Text>
</Popover>

A menu of actions anchored to a trigger.

import { DropdownMenu, MenuItem, MenuSeparator } from "onejs-ui"

<DropdownMenu trigger={<Button>Actions</Button>}>
    <MenuItem onSelect={duplicate}>Duplicate</MenuItem>
    <MenuItem onSelect={rename}>Rename</MenuItem>
    <MenuSeparator />
    <MenuItem danger onSelect={remove}>Delete</MenuItem>
</DropdownMenu>

MenuItem: onSelect, disabled, danger. The menu closes after a selection.

Dialog#

A centered modal with a backdrop and a focus trap. Controlled via open / onClose.

const [open, setOpen] = useState(false)
<Dialog open={open} onClose={() => setOpen(false)} title="Delete file?">
    <Text tone="muted">This can't be undone.</Text>
    <HStack gap={8}>
        <Button intent="ghost" onClick={() => setOpen(false)}>Cancel</Button>
        <Button intent="danger" onClick={confirm}>Delete</Button>
    </HStack>
</Dialog>
  • open: boolean · onClose: () => void
  • title, dismissOnEscape (default true), backdrop-dismiss (default true).

Drawer#

A panel that slides in from an edge. Same control model as Dialog.

const [open, setOpen] = useState(false)
<Drawer open={open} onClose={() => setOpen(false)} side="right" title="Filters">
    {/* content */}
</Drawer>
  • side: "left" | "right" | "top" | "bottom" (default "right")
  • open, onClose, title, dismissOnEscape.