Wardian docs

Components and Arrange

Wardian has a small library of interface parts. Like shadcn/ui, you copy them into your app, and then they are your code. Every app that uses them looks and works alike, and still works on a host that has no library at all.

See each one live, in light and dark, with its markup, at Components in Wardian's app list, or at /ui/.

Add components#

wardian add button field card toast apps/my-app
wardian add --list

wardian add copies each component's files into the package's ui/ folder, with ui/theme.css.

It never replaces a file that is already there unless you give --force. So your changes to a component are safe.

The components#

ComponentWhat it isFiles
buttonbuttons in five variants and three sizesbutton.css
fieldtext inputs, selects, text areas, labels and hintsfield.css
carda bordered box with a title, a description, content and a footercard.css
badgea small label for a state or a countbadge.css
tablea data table with a sticky header and right-aligned numberstable.css
switchan on/off switch made from a checkboxswitch.css
tabstabs with arrow-key movement between themtabs.css, tabs.js
dialoga modal dialog, and WardianUI.confirm()dialog.css, dialog.js
toastshort messages in the corner: WardianUI.toast()toast.css, toast.js
tooltipa hint on hover and keyboard focustooltip.css, tooltip.js
progressthe <wardian-progress> bar (also built in)progress.js
arrangeArrange for a page apparrange.js

Classes start with w-. Variants and sizes are attributes:

<button class="w-button">Run</button>
<button class="w-button" data-variant="outline" data-size="sm">Reset</button>
<button class="w-button" data-variant="destructive">Delete</button>

The variants are secondary, outline, ghost, destructive and success; the sizes are sm, lg and icon.

The scripted parts have small calls:

WardianUI.toast('Saved', { variant: 'success' });
if (await WardianUI.confirm('Delete this habit?', { destructive: true })) remove();

Change the look#

One file, ui/theme.css, sets the colours, corners and spacing for every component, through tokens. Change the tokens, not each class:

:root {
  --w-primary: #2f6b50;
  --w-primary-fg: #ffffff;
  --w-radius: 12px;
}
TokenSets
--w-bg, --w-fgpage background and text
--w-muted, --w-muted-bgquiet text and quiet backgrounds
--w-border, --w-ringlines and the focus ring
--w-primary, --w-primary-fgthe main action
--w-secondary, --w-secondary-fgthe other actions
--w-destructive, --w-successdanger and success
--w-radiuscorners
--w-space-1 … --w-space-6spacing, 4 px to 32 px
--w-font, --w-font-mono, --w-text-sm, --w-text, --w-text-lgtype

theme.css has a dark set of the same tokens, used when the viewer's system is dark.

The progress bar#

<wardian-progress> is built into every suite frame and into /sdk/wardian.js, so it needs no copy. Use it for any work longer than a second, instead of drawing your own.

const bar = ctx.$('wardian-progress');
bar.start('Simulating');
bar.update({ value: done, max: total, detail: `${done} of ${total}` });
bar.done('Finished');           // or bar.fail('Stopped')
bar.addEventListener('cancel', () => worker.terminate());
AttributeDoes
label, detailthe text above and beside the bar
value, maxfill the bar; without value, it shows work of unknown length
elapseda running clock
cancelablea Stop button that fires cancel

It has the progressbar role, and it stops moving when the viewer asks for reduced motion.

Arrange#

Every app offers Arrange: each viewer may reorder its panels, move them between two columns, hide them, or use one column. The layout belongs to the viewer. Wardian keeps it in its data folder, and the package never changes.

<div data-arrange-grid>
  <div data-arrange-column="side">
    <section data-panel="input" data-panel-label="Your text">…</section>
  </div>
  <div data-arrange-column="main">
    <section data-panel="stats">…</section>
    <section data-panel="hash">…</section>
  </div>
</div>
<script src="ui/arrange.js"></script>

A page is sandboxed and cannot keep the layout itself. So arrange.js asks the host page to keep it, through postMessage. Outside Wardian, the layout lasts until the page closes.

A hidden panel still runs. An app must never depend on where a panel sits, or on being visible. Results must never wait on a panel the viewer may hide.

This page is docs/site/components.md in the repository. Something wrong or missing? Change that file.