Skip to content

@mistri/ui on its own

Draw a page of your own with the buttons, fields, dialogs and menus an App is drawn with, without a server behind it.

Terminal window
npm install @mistri/ui react react-dom @base-ui/react
Invite.tsx
import '@mistri/ui/style.css';
import { useState } from 'react';
import { Button, Field, Modal } from '@mistri/ui';
export function Invite() {
const [open, setOpen] = useState(false);
const [email, setEmail] = useState('');
return (
<>
<Button intent="primary" onClick={() => setOpen(true)}>Invite</Button>
{open && (
<Modal className="invite" aria-label="Invite a teammate" onClose={() => setOpen(false)}>
<Field label="Email" hint="They get a link to sign in.">
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
</Field>
<Button intent="primary" onClick={() => setOpen(false)}>Send</Button>
</Modal>
)}
</>
);
}

A primitive reads no data, no address and no message catalog: its words, its icons and its answers are props. React, React DOM and Base UI are peers, so your project installs them. An App does not need this package: mistri brings it.

  • The App’s own button, field and dialog, in the platform’s tokens, light or dark by the reader’s setting.
  • A field that wires its label, hint and problem into the control (id, aria-describedby, aria-invalid), so no form joins them by hand.
  • A dialog that holds focus, closes on Escape and a click on its scrim, and draws a menu or a listbox opened inside it within itself.
Export What it is
Button intent (primary, secondary, quiet, danger), size (compact, default, large), busy
Field A label over one control, a hint under it, then a problem; children is the control, or a function of its wiring for a control of several parts
Modal Open while mounted; onClose is told once, however it closed
DropMenu, DropItem, DropGroup, DropSeparator A menu on a button
Dropdown A listbox: one choice from a list
Typeahead A box with a list that narrows as you type
DateField A date and its calendar, in the words it is given
Icon, registerIcons Draws the glyphs you register, by name

style.css is the tokens and the primitives’ rules in a mistri-ui cascade layer, so any rule of your page’s own wins. To place them in a layer of your own, import the two files it joins: @import '@mistri/ui/tokens.css' layer(base); and @mistri/ui/primitives.css the same way.

  • An icon draws nothing → register its glyph first with registerIcons; the package ships none.
  • A dialog will not close from its own button → unmount it: a Modal is open for as long as it is rendered.
  • A rule of your own loses to a primitive’s → your stylesheet is in a cascade layer declared before mistri-ui; import the two files into your layer instead of style.css.