Table 4
Created 14:02
0.42 XMR
Native HTML foundations. Progressive enhancement. The same components from signup to the counter.
Use your browser’s JavaScript setting to review the native fallbacks. Every example and its HTML contract are present before scripts run.
The components below are written once, in Rust (crates/ui). Server pages render them through maud; the POS imports the Solid components cargo xtask ui translates from them. Each specimen is the Rust component's output, and the browser suite checks the Solid component renders the same.
A button for an action on the page. One that goes somewhere is a LinkButton: a link, not a button.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. What it says: the action, as a verb ("Save", "Delete store"). |
variant | Option<ButtonVariant (primary | danger | quiet)> | How much a button stands out. Without one it is secondary: most buttons are, and a page has at most one primary. |
size | Option<ControlSize (compact | touch)> | A control's size. Without one it is the normal size. |
kind | ButtonType (button | submit | reset) | Defaults to button, so it never submits a form by accident. Default: button. |
block | bool | Fills its container's width. Default: false. |
disabled | bool | Default: false. |
busy | bool | Marks it working (a save in flight): the cursor shows progress. Default: false. |
rest | Attrs |
Rust
Button::new("Save changes")
.variant(ButtonVariant::Primary)Solid
<Button variant="primary">Save changes</Button>HTML
<button class="mk-btn" type="button" data-variant="primary">Save changes</button>Rust
Button::new("Cancel")Solid
<Button>Cancel</Button>HTML
<button class="mk-btn" type="button">Cancel</button>Rust
Button::new("Delete store")
.variant(ButtonVariant::Danger)Solid
<Button variant="danger">Delete store</Button>HTML
<button class="mk-btn" type="button" data-variant="danger">Delete store</button>Rust
Button::new("Discard")
.variant(ButtonVariant::Quiet)Solid
<Button variant="quiet">Discard</Button>HTML
<button class="mk-btn" type="button" data-variant="quiet">Discard</button>Rust
Button::new("Log in")
.variant(ButtonVariant::Primary)
.kind(ButtonType::Submit)Solid
<Button variant="primary" kind="submit">Log in</Button>HTML
<button class="mk-btn" type="submit" data-variant="primary">Log in</button>Rust
Button::new("Discard")
.size(ControlSize::Compact)Solid
<Button size="compact">Discard</Button>HTML
<button class="mk-btn" type="button" data-size="compact">Discard</button>Rust
Button::new("Charge")
.variant(ButtonVariant::Primary)
.size(ControlSize::Touch)
.block(true)Solid
<Button variant="primary" size="touch" block>Charge</Button>HTML
<button class="mk-btn" type="button" data-variant="primary" data-size="touch" data-block>Charge</button>Rust
Button::new("Export orders")
.disabled(true)Solid
<Button disabled>Export orders</Button>HTML
<button class="mk-btn" type="button" disabled>Export orders</button>Rust
Button::new("Saving…")
.variant(ButtonVariant::Primary)
.disabled(true)
.busy(true)Solid
<Button variant="primary" disabled busy>Saving…</Button>HTML
<button class="mk-btn" type="button" data-variant="primary" disabled aria-busy="true">Saving…</button>A link that looks like a button: for a call to action that goes to another page. It needs somewhere to go.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
href | String | Required. |
variant | Option<ButtonVariant (primary | danger | quiet)> | How much a button stands out. Without one it is secondary: most buttons are, and a page has at most one primary. |
size | Option<ControlSize (compact | touch)> | A control's size. Without one it is the normal size. |
block | bool | Default: false. |
rest | Attrs |
Rust
LinkButton::new("Add a store", "#component-link-button")
.variant(ButtonVariant::Primary)Solid
<LinkButton href="#component-link-button" variant="primary">Add a store</LinkButton>HTML
<a class="mk-btn" href="#component-link-button" data-variant="primary">Add a store</a>Rust
LinkButton::new("Watch it live", "#component-link-button")Solid
<LinkButton href="#component-link-button">Watch it live</LinkButton>HTML
<a class="mk-btn" href="#component-link-button">Watch it live</a>Rust
LinkButton::new("Discard changes", "#component-link-button")
.size(ControlSize::Compact)Solid
<LinkButton href="#component-link-button" size="compact">Discard changes</LinkButton>HTML
<a class="mk-btn" href="#component-link-button" data-size="compact">Discard changes</a>A tile that opens a tool (the POS, an order form): an icon, a title and a line saying what it's for. Without href it can't be opened yet (a script checks the browser first): marked disabled, the line saying why, until the page gives it somewhere to go.
| Prop | Type | About |
|---|---|---|
title | String | Required. |
hint | String | Required. What it's for, or why it can't be opened. |
icon | Slot | Required. An inline SVG. |
href | Option<String> | |
hint_id | Option<String> | The hint's id, for a script that rewrites it. |
rest | Attrs |
Rust
LaunchTile::new("Create an order", "For one-off custom payments", "+")
.href("#component-launch-tile")Solid
<LaunchTile title="Create an order" hint="For one-off custom payments" icon={{
"$slot": [
"+"
]
}} href="#component-launch-tile" />HTML
<a class="mk-launch-tile" href="#component-launch-tile"><span class="mk-launch-tile-icon">+</span><span class="mk-launch-tile-text"><span class="mk-launch-tile-title">Create an order</span><span class="mk-launch-tile-hint">For one-off custom payments</span></span></a>Rust
LaunchTile::new("POS Terminal", "Requires JavaScript", "▦")Solid
<LaunchTile title="POS Terminal" hint="Requires JavaScript" icon={{
"$slot": [
"▦"
]
}} />HTML
<a class="mk-launch-tile" aria-disabled="true"><span class="mk-launch-tile-icon">▦</span><span class="mk-launch-tile-text"><span class="mk-launch-tile-title">POS Terminal</span><span class="mk-launch-tile-hint">Requires JavaScript</span></span></a>A choice of one view among a few (Active and Finished orders): its buttons are Buttons with aria-pressed, the pressed one shown. They filter what's on the page; panels to switch between are Tabs.
| Prop | Type | About |
|---|---|---|
label | String | Required. What is chosen ("Order status"). |
children | Slot | Required. The Buttons, each aria-pressed. |
Rust
SegmentedControl::new("Order status", Slot::many([Button::new("Active · 3")
.attr("aria-pressed", "true"), Button::new("Finished · 12")
.attr("aria-pressed", "false")]))Solid
<SegmentedControl label="Order status"><Button aria-pressed="true">Active · 3</Button><Button aria-pressed="false">Finished · 12</Button></SegmentedControl>HTML
<div class="mk-segmented" role="group" aria-label="Order status"><button class="mk-btn" type="button" aria-pressed="true">Active · 3</button><button class="mk-btn" type="button" aria-pressed="false">Finished · 12</button></div>A message about the page or what just happened: a refused save, a node that's down, a save that went through. An error is announced (role="alert"), the rest are statuses, unless quiet. A message that asks for something takes a title and actions (Dismiss, a link to fix it).
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
tone | Tone (error | warning | success | notice) | Required. What a message says about how something went. The words say it; the colour only backs them up. |
icon | Option<Slot> | Before everything, at the left: an icon (assets::icons::INFO). |
title | Option<String> | Bold, before the words. |
actions | Option<Slot> | Under the words: buttons or links. |
quiet | bool | Not announced: a message that is part of the page as it loads. Default: false. |
block | bool | A div, for a message holding lists or paragraphs; a p otherwise. Default: false. |
rest | Attrs |
Could not save. Check the highlighted fields and try again.
Rust
Alert::new("Could not save. Check the highlighted fields and try again.", Tone::Error)Solid
<Alert tone="error">Could not save. Check the highlighted fields and try again.</Alert>HTML
<p class="error" role="alert">Could not save. Check the highlighted fields and try again.</p>Connection interrupted. Payment status may be delayed.
Rust
Alert::new("Connection interrupted. Payment status may be delayed.", Tone::Warning)Solid
<Alert tone="warning">Connection interrupted. Payment status may be delayed.</Alert>HTML
<p class="warning" role="status">Connection interrupted. Payment status may be delayed.</p>Saved.
Rust
Alert::new("Saved.", Tone::Success)Solid
<Alert tone="success">Saved.</Alert>HTML
<p class="success" role="status">Saved.</p>No wallets yet.
Rust
Alert::new("No wallets yet.", Tone::Notice)
.quiet(true)Solid
<Alert tone="notice" quiet>No wallets yet.</Alert>HTML
<p class="notice">No wallets yet.</p>Rust
Alert::new("If a scam site embeds it, that site's victims would pay you. Verify your domains in Settings and allow only them.", Tone::Warning)
.title("Any website can show this store's checkout")
.actions(Button::new("Dismiss")
.size(ControlSize::Compact))
.quiet(true)
.block(true)Solid
<Alert tone="warning" title="Any website can show this store's checkout" actions={{
"$slot": [
{
"$component": "Button",
"props": {
"block": false,
"busy": false,
"children": {
"$slot": [
"Dismiss"
]
},
"disabled": false,
"kind": "button",
"size": "compact"
}
}
]
}} quiet block>If a scam site embeds it, that site's victims would pay you. Verify your domains in Settings and allow only them.</Alert>HTML
<div class="warning"><strong class="alert-title">Any website can show this store's checkout</strong> If a scam site embeds it, that site's victims would pay you. Verify your domains in Settings and allow only them.<span class="alert-actions"><button class="mk-btn" type="button" data-size="compact">Dismiss</button></span></div>A panel holding one part of a page: a form, a set of facts; or, as a tile, one of a grid of them.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
look | PanelLook (raised | tile) | A panel's look. Default: raised. |
rest | Attrs |
Each store gives Monokulo its view key, never its spend key.
Rust
Panel::new(Slot::many([Heading::new("Watch-only")
.level(HeadingLevel::H3), Hint::new("Each store gives Monokulo its view key, never its spend key.")]))
.look(PanelLook::Tile)Solid
<Panel look="tile"><Heading level="h3">Watch-only</Heading><Hint>Each store gives Monokulo its view key, never its spend key.</Hint></Panel>HTML
<div class="box" data-look="tile"><h3>Watch-only</h3><p class="hint">Each store gives Monokulo its view key, never its spend key.</p></div>Monokulo posts each order's changes here.
Rust
Panel::new(Slot::many([Heading::new("Webhook")
.level(HeadingLevel::H3), Hint::new("Monokulo posts each order's changes here.")]))Solid
<Panel><Heading level="h3">Webhook</Heading><Hint>Monokulo posts each order's changes here.</Hint></Panel>HTML
<div class="box"><h3>Webhook</h3><p class="hint">Monokulo posts each order's changes here.</p></div>A section's or a part's heading, under the page's PageHeader.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
level | HeadingLevel (h2 | h3 | h4) | A heading's level. A page's own title, the h1, is its PageHeader's. Default: h2. |
id | Option<String> |
Rust
Heading::new("Recent orders")Solid
<Heading>Recent orders</Heading>HTML
<h2>Recent orders</h2>Rust
Heading::new("Webhook")
.level(HeadingLevel::H3)Solid
<Heading level="h3">Webhook</Heading>HTML
<h3>Webhook</h3>Muted help beside or under what it explains. A span inside a line of text when inline; a paragraph otherwise.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
inline | bool | Default: false. |
rest | Attrs |
Shown to customers on the payment page.
Rust
Hint::new("Shown to customers on the payment page.")Solid
<Hint>Shown to customers on the payment page.</Hint>HTML
<p class="hint">Shown to customers on the payment page.</p>Rust
Hint::new("(optional)")
.inline(true)Solid
<Hint inline>(optional)</Hint>HTML
<span class="hint">(optional)</span>Facts about one thing, each named: a wallet's network, an order's amount.
| Prop | Type | About |
|---|---|---|
facts | Vec<Fact> | Required. One fact of [Facts]. |
rest | Attrs |
Rust
Facts::new(vec![
Fact { term: "Network".into(), value: NetworkBadge::new("Mainnet")
.main(true).into() },
Fact { term: "Watching since".into(), value: "block 3,412,001".into() },
Fact { term: "Balance".into(), value: "1.25 XMR".into() },
])Solid
<Facts facts={[
{
"term": "Network",
"value": {
"$slot": [
{
"$component": "NetworkBadge",
"props": {
"icon_only": false,
"main": true,
"name": "Mainnet"
}
}
]
}
},
{
"term": "Watching since",
"value": {
"$slot": [
"block 3,412,001"
]
}
},
{
"term": "Balance",
"value": {
"$slot": [
"1.25 XMR"
]
}
}
]} />HTML
<dl class="facts"><dt>Network</dt><dd><span class="tag-network is-main"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M7 15V9l5 5 5-5v6"></path></svg><span class="tag-network-name">Mainnet</span></span></dd><dt>Watching since</dt><dd>block 3,412,001</dd><dt>Balance</dt><dd>1.25 XMR</dd></dl>A short word or two about something's state: a node's health, an order's status. short is what a phone shows instead, the long words kept for screen readers.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
tone | Option<TagTone (ok | error | unknown | syncing | stalled | slow | muted | dashed)> | How a tag reads, beside its words. |
state | Option<OrderState (pending | unconfirmed | confirming | partial | paid | overpaid | expired | cancelled | double-spend | offline | unknown)> | An order's state: each its own colours, the same wherever an order's state is shown (the checkout, the POS, the dashboard). |
short | Option<String> | |
icon | Option<Slot> | Before the words: an inline SVG or a status symbol. |
rest | Attrs |
Rust
Tag::new("Default")Solid
<Tag>Default</Tag>HTML
<span class="tag">Default</span>Rust
Tag::new("Healthy")
.tone(TagTone::Ok)Solid
<Tag tone="ok">Healthy</Tag>HTML
<span class="tag tag-ok">Healthy</span>Rust
Tag::new("Down")
.tone(TagTone::Error)Solid
<Tag tone="error">Down</Tag>HTML
<span class="tag tag-error">Down</span>Rust
Tag::new("Not checked")
.tone(TagTone::Unknown)Solid
<Tag tone="unknown">Not checked</Tag>HTML
<span class="tag tag-unknown">Not checked</span>Rust
Tag::new("Syncing")
.tone(TagTone::Syncing)Solid
<Tag tone="syncing">Syncing</Tag>HTML
<span class="tag tag-syncing">Syncing</span>Rust
Tag::new("Slow")
.tone(TagTone::Slow)Solid
<Tag tone="slow">Slow</Tag>HTML
<span class="tag tag-slow">Slow</span>Rust
Tag::new("Options file")
.tone(TagTone::Muted)
.icon("⚙")Solid
<Tag tone="muted" icon={{
"$slot": [
"⚙"
]
}}>Options file</Tag>HTML
<span class="tag tag-muted"><span class="tag-icon" aria-hidden="true">⚙</span>Options file</span>Rust
Tag::new("Default")
.tone(TagTone::Dashed)Solid
<Tag tone="dashed">Default</Tag>HTML
<span class="tag tag-dashed">Default</span>Rust
Tag::new("Waiting for payment")
.state(OrderState::Pending)
.short("Waiting")Solid
<Tag state="pending" short="Waiting">Waiting for payment</Tag>HTML
<span class="tag state-pending"><span class="label-long">Waiting for payment</span><span class="label-short" aria-hidden="true">Waiting</span></span>Rust
Tag::new("Payment seen, unconfirmed")
.state(OrderState::Unconfirmed)
.short("Seen")Solid
<Tag state="unconfirmed" short="Seen">Payment seen, unconfirmed</Tag>HTML
<span class="tag state-unconfirmed"><span class="label-long">Payment seen, unconfirmed</span><span class="label-short" aria-hidden="true">Seen</span></span>Rust
Tag::new("Confirming")
.state(OrderState::Confirming)Solid
<Tag state="confirming">Confirming</Tag>HTML
<span class="tag state-confirming">Confirming</span>Rust
Tag::new("Paid")
.state(OrderState::Paid)Solid
<Tag state="paid">Paid</Tag>HTML
<span class="tag state-paid">Paid</span>Rust
Tag::new("Expired")
.state(OrderState::Expired)Solid
<Tag state="expired">Expired</Tag>HTML
<span class="tag state-expired">Expired</span>Rust
Tag::new("Cancelled")
.state(OrderState::Cancelled)Solid
<Tag state="cancelled">Cancelled</Tag>HTML
<span class="tag state-cancelled">Cancelled</span>A Monero network, the same wherever a network is shown: the word always, with an icon and an edge to back it up. Mainnet (real money) has the brand's orange tint, a coin and a solid edge; a test network a neutral tint, a flask and a dashed edge.
| Prop | Type | About |
|---|---|---|
name | String | Required. The network's name ("Mainnet", "Stagenet", "Test networks"). |
main | bool | Default: false. |
icon_only | bool | Only the icon and the edge, its name for screen readers and on hover: in a table's cell, beside a wallet's name. Default: false. |
Rust
NetworkBadge::new("Mainnet")
.main(true)Solid
<NetworkBadge name="Mainnet" main />HTML
<span class="tag-network is-main"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M7 15V9l5 5 5-5v6"></path></svg><span class="tag-network-name">Mainnet</span></span>Rust
NetworkBadge::new("Stagenet")Solid
<NetworkBadge name="Stagenet" />HTML
<span class="tag-network is-test"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 3h6M10 3v6l-5 9a2 2 0 0 0 1.7 3h10.6a2 2 0 0 0 1.7-3l-5-9V3"></path><path d="M7.5 15h9"></path></svg><span class="tag-network-name">Stagenet</span></span>Rust
NetworkBadge::new("Mainnet")
.main(true)
.icon_only(true)Solid
<NetworkBadge name="Mainnet" main icon_only />HTML
<span class="tag-network is-main" data-size="icon" title="Mainnet"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M7 15V9l5 5 5-5v6"></path></svg><span class="tag-network-name">Mainnet</span></span>Rust
NetworkBadge::new("Stagenet")
.icon_only(true)Solid
<NetworkBadge name="Stagenet" icon_only />HTML
<span class="tag-network is-test" data-size="icon" title="Stagenet"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 3h6M10 3v6l-5 9a2 2 0 0 0 1.7 3h10.6a2 2 0 0 0 1.7-3l-5-9V3"></path><path d="M7.5 15h9"></path></svg><span class="tag-network-name">Stagenet</span></span>A long value shortened on screen (an id, an address) whose whole value is still what a double-click selects, a copy takes, a screen reader reads and find-in-page matches: the short text shows, and the whole value lies over it, transparent. Short enough already, it is just the text.
| Prop | Type | About |
|---|---|---|
value | String | Required. |
short | String | Required. What shows. |
Rust
ShortValue::new("order_a8723b2e45b0d44ea8723b2e45b0d44e", "a8723b…b0d44e")Solid
<ShortValue value="order_a8723b2e45b0d44ea8723b2e45b0d44e" short="a8723b…b0d44e" />HTML
<span class="short-value" title="order_a8723b2e45b0d44ea8723b2e45b0d44e"><span class="short-value-text" aria-hidden="true">a8723b…b0d44e</span><span class="short-value-full">order_a8723b2e45b0d44ea8723b2e45b0d44e</span></span>Rust
ShortValue::new("a8723b", "a8723b")Solid
<ShortValue value="a8723b" short="a8723b" />HTML
a8723bMore to see behind a summary: a details, so it opens without JavaScript.
| Prop | Type | About |
|---|---|---|
summary | Slot | Required. |
children | Slot | Required. |
look | DisclosureLook (plain | boxed | more-options | compact | row) | A disclosure's look. Default: plain. |
open | bool | Default: false. |
alert | bool | Something in it needs attention: an error's edge (open it too). Default: false. |
rest | Attrs |
Rust
Disclosure::new("How it's checked", "Every block is checked against two nodes.")Solid
<Disclosure summary={{
"$slot": [
"How it's checked"
]
}}>Every block is checked against two nodes.</Disclosure>HTML
<details class="disclosure"><summary>How it's checked</summary>Every block is checked against two nodes.</details>Rust
Disclosure::new("Where do I find these keys?", "Settings, then Keys.")
.look(DisclosureLook::Boxed)Solid
<Disclosure summary={{
"$slot": [
"Where do I find these keys?"
]
}} look="boxed">Settings, then Keys.</Disclosure>HTML
<details class="keys-help"><summary>Where do I find these keys?</summary><div class="keys-help-body">Settings, then Keys.</div></details>Rust
Disclosure::new("More options", "Leave on mainnet unless this is a test wallet.")
.look(DisclosureLook::MoreOptions)Solid
<Disclosure summary={{
"$slot": [
"More options"
]
}} look="more-options">Leave on mainnet unless this is a test wallet.</Disclosure>HTML
<details class="more-options"><summary>More options</summary>Leave on mainnet unless this is a test wallet.</details>Rust
Disclosure::new(Slot::many([Tag::new("Scanning")
.tone(TagTone::Syncing), " 1 block behind"]), Facts::new(vec![
Fact { term: "Progress".into(), value: "1 block behind".into() },
Fact { term: "Requests".into(), value: "None yet".into() },
]))
.look(DisclosureLook::Compact)
.open(true)Solid
<Disclosure summary={{
"$slot": [
{
"$component": "Tag",
"props": {
"children": {
"$slot": [
"Scanning"
]
},
"tone": "syncing"
}
},
" 1 block behind"
]
}} look="compact" open><Facts facts={[
{
"term": "Progress",
"value": {
"$slot": [
"1 block behind"
]
}
},
{
"term": "Requests",
"value": {
"$slot": [
"None yet"
]
}
}
]} /></Disclosure>HTML
<details class="disclosure-compact" open><summary><span class="tag tag-syncing">Scanning</span> 1 block behind</summary><div class="disclosure-body"><dl class="facts"><dt>Progress</dt><dd>1 block behind</dd><dt>Requests</dt><dd>None yet</dd></dl></div></details>Rust
Disclosure::new("Primary · node.example.com:18081", "The node's address, and whether it uses TLS.")
.look(DisclosureLook::Row)Solid
<Disclosure summary={{
"$slot": [
"Primary · node.example.com:18081"
]
}} look="row">The node's address, and whether it uses TLS.</Disclosure>HTML
<details class="disclosure-row"><summary>Primary · node.example.com:18081</summary><div class="disclosure-body">The node's address, and whether it uses TLS.</div></details>A "?" that opens help over the page: a details, so it opens without JavaScript. Once its island attaches, a click outside it or Escape closes it, Escape putting focus back on the "?".
| Prop | Type | About |
|---|---|---|
label | String | Required. What the help is about ("What the page shows"): the "?"'s name. |
children | Slot | Required. |
id | Option<String> | |
wide | bool | Room for two columns of help, as wide as the page allows. Default: false. |
anchor | TipAnchor (tip | container) | Where the help opens from. Default: tip. |
rest | Attrs |
Rust
HelpTip::new("How to search", "Fields compare with =, !=, <, >; join them with and, or. Quote text: order.id = 'o_1'.")Solid
<HelpTip label="How to search">Fields compare with =, !=, <, >; join them with and, or. Quote text: order.id = 'o_1'.</HelpTip>HTML
<details class="help-tip" data-mk="1" data-island="HelpTip" data-props="{}"><summary aria-label="How to search" title="How to search">?</summary><div class="help-tip-body">Fields compare with =, !=, <, >; join them with and, or. Quote text: order.id = 'o_1'.</div></details>A list whose items can be put in another order. Without JavaScript the page reorders it (each item's own Move up and Move down, posted to the server). Once its island attaches, an item moves by dragging its SortHandle, or with the up and down arrows on the handle; the items it passes slide out of its way. Items marked fixed (a blank row to add one) stay at the end.
Each move sends mk-reorder from the list ({item}), for a page script to renumber what it names after the order; a drag that ends in a new order, or an arrow, sends mk-reordered ({item}).
| Prop | Type | About |
|---|---|---|
label | String | Required. What is ordered ("Stagenet nodes"). |
children | Slot | Required. SortableItems. |
rest | Attrs |
Rust
SortableList::new("Example nodes", Slot::many([SortableItem::new(Disclosure::new(Slot::many([SortHandle::new("Reorder Primary: press the up or down arrow"), "Primary · node-a.example:18081"]), Slot::many(["The node's address, and whether it uses TLS.", FormActions::new(Button::new("Move down")
.attr("data-only", "no-script"))]))
.look(DisclosureLook::Row)), SortableItem::new(Disclosure::new(Slot::many([SortHandle::new("Reorder Fallback 1: press the up or down arrow"), "Fallback 1 · node-b.example:18081"]), Slot::many(["The node's address, and whether it uses TLS.", FormActions::new(Slot::many([Button::new("Move up")
.attr("data-only", "no-script"), Button::new("Move down")
.attr("data-only", "no-script")]))]))
.look(DisclosureLook::Row)), SortableItem::new(Disclosure::new(Slot::many([SortHandle::new("Reorder Fallback 2: press the up or down arrow"), "Fallback 2 · node-c.example:18081"]), Slot::many(["The node's address, and whether it uses TLS.", FormActions::new(Button::new("Move up")
.attr("data-only", "no-script"))]))
.look(DisclosureLook::Row)), SortableItem::new(Disclosure::new("+ Add a node", "An address to try last.")
.look(DisclosureLook::Row))
.fixed(true)]))Solid
<SortableList label="Example nodes"><SortableItem><Disclosure summary={{
"$slot": [
{
"$component": "SortHandle",
"props": {
"label": "Reorder Primary: press the up or down arrow"
}
},
"Primary · node-a.example:18081"
]
}} look="row">The node's address, and whether it uses TLS.<FormActions><Button data-only="no-script">Move down</Button></FormActions></Disclosure></SortableItem><SortableItem><Disclosure summary={{
"$slot": [
{
"$component": "SortHandle",
"props": {
"label": "Reorder Fallback 1: press the up or down arrow"
}
},
"Fallback 1 · node-b.example:18081"
]
}} look="row">The node's address, and whether it uses TLS.<FormActions><Button data-only="no-script">Move up</Button><Button data-only="no-script">Move down</Button></FormActions></Disclosure></SortableItem><SortableItem><Disclosure summary={{
"$slot": [
{
"$component": "SortHandle",
"props": {
"label": "Reorder Fallback 2: press the up or down arrow"
}
},
"Fallback 2 · node-c.example:18081"
]
}} look="row">The node's address, and whether it uses TLS.<FormActions><Button data-only="no-script">Move up</Button></FormActions></Disclosure></SortableItem><SortableItem fixed><Disclosure summary={{
"$slot": [
"+ Add a node"
]
}} look="row">An address to try last.</Disclosure></SortableItem></SortableList>HTML
<ol class="mk-sortable" aria-label="Example nodes" data-mk="1" data-island="SortableList" data-props="{}"><li class="mk-sortable-item"><details class="disclosure-row"><summary><span class="mk-sort-handle" role="button" tabindex="0" title="Drag to reorder" aria-label="Reorder Primary: press the up or down arrow" data-sortable-handle="" data-only="script">⠿</span>Primary · node-a.example:18081</summary><div class="disclosure-body">The node's address, and whether it uses TLS.<div class="form-actions"><button class="mk-btn" type="button" data-only="no-script">Move down</button></div></div></details></li><li class="mk-sortable-item"><details class="disclosure-row"><summary><span class="mk-sort-handle" role="button" tabindex="0" title="Drag to reorder" aria-label="Reorder Fallback 1: press the up or down arrow" data-sortable-handle="" data-only="script">⠿</span>Fallback 1 · node-b.example:18081</summary><div class="disclosure-body">The node's address, and whether it uses TLS.<div class="form-actions"><button class="mk-btn" type="button" data-only="no-script">Move up</button><button class="mk-btn" type="button" data-only="no-script">Move down</button></div></div></details></li><li class="mk-sortable-item"><details class="disclosure-row"><summary><span class="mk-sort-handle" role="button" tabindex="0" title="Drag to reorder" aria-label="Reorder Fallback 2: press the up or down arrow" data-sortable-handle="" data-only="script">⠿</span>Fallback 2 · node-c.example:18081</summary><div class="disclosure-body">The node's address, and whether it uses TLS.<div class="form-actions"><button class="mk-btn" type="button" data-only="no-script">Move up</button></div></div></details></li><li class="mk-sortable-item" data-sortable-fixed=""><details class="disclosure-row"><summary>+ Add a node</summary><div class="disclosure-body">An address to try last.</div></details></li></ol>One item of a SortableList: usually a Disclosure with the Row look, its summary holding the item's SortHandle.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
fixed | bool | Stays at the end, and isn't moved: a blank row to add one. Default: false. |
rest | Attrs |
What drags a SortableList's item, and takes its arrow keys. Shown only with JavaScript: without it the list's Move buttons reorder.
| Prop | Type | About |
|---|---|---|
label | String | Required. What moving it does, for screen readers ("Reorder Primary: press the up or down arrow"). |
rest | Attrs |
What's done and what isn't, or what will happen: a list of rows each with a mark, which screen readers hear as "Done:" or "Not yet:".
| Prop | Type | About |
|---|---|---|
checks | Vec<Check> | Required. One row of [Checks]. |
rest | Attrs |
Rust
Checks::new(vec![
Check { mark: CheckMark::Ok, what: "No plugin is connected to it".into(), fix: None },
Check { mark: CheckMark::No, what: "No order from shop.example.com can still be paid".into(), fix: Some("2 can be paid until about 14:20".into()) },
Check { mark: CheckMark::Then, what: "Checkouts embedded on shop.example.com stop loading".into(), fix: None },
])Solid
<Checks checks={[
{
"mark": "ok",
"what": {
"$slot": [
"No plugin is connected to it"
]
}
},
{
"fix": {
"$slot": [
"2 can be paid until about 14:20"
]
},
"mark": "no",
"what": {
"$slot": [
"No order from shop.example.com can still be paid"
]
}
},
{
"mark": "then",
"what": {
"$slot": [
"Checkouts embedded on shop.example.com stop loading"
]
}
}
]} />HTML
<ul class="checks"><li class="ok"><span class="mark" aria-hidden="true">✓</span><span><span class="visually-hidden">Done: </span>No plugin is connected to it</span></li><li class="no"><span class="mark" aria-hidden="true">✕</span><span><span class="visually-hidden">Not yet: </span>No order from shop.example.com can still be paid<span class="fix">2 can be paid until about 14:20</span></span></li><li class="then"><span class="mark" aria-hidden="true">→</span><span>Checkouts embedded on shop.example.com stop loading</span></li></ul>What happened to something, in order, each with when.
| Prop | Type | About |
|---|---|---|
events | Vec<Event> | Required. One event of a [Timeline]. |
Rust
Timeline::new(vec![
Event { when: "12 Oct, 14:02".into(), what: "Added".into() },
Event { when: "12 Oct, 14:09".into(), what: "Backup checked".into() },
Event { when: "13 Oct, 09:41".into(), what: "First payment".into() },
])Solid
<Timeline events={[
{
"what": {
"$slot": [
"Added"
]
},
"when": "12 Oct, 14:02"
},
{
"what": {
"$slot": [
"Backup checked"
]
},
"when": "12 Oct, 14:09"
},
{
"what": {
"$slot": [
"First payment"
]
},
"when": "13 Oct, 09:41"
}
]} />HTML
<ol class="timeline"><li><time>12 Oct, 14:02</time><span>Added</span></li><li><time>12 Oct, 14:09</time><span>Backup checked</span></li><li><time>13 Oct, 09:41</time><span>First payment</span></li></ol>A table, in a box of its own that scrolls sideways when it's wider than the page. On a phone a column whose cells are optional goes, and look reshapes the rest. A page styling its columns names the table with data-table.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. The rows: Rows (or a page's trs). |
head | Option<Slot> | The header row. |
look | TableLook (plain | cards | stack | facts) | How a table changes on a phone. Default: plain. |
caption | Option<String> | For screen readers: what the table is. |
scroll | bool | Without its scroll box: a table already narrow enough. Default: true. |
rest | Attrs |
| Order | Status | Amount |
|---|---|---|
| Coffee | Paid | 0.02 XMR |
| Tea | Waiting for payment | 0.01 XMR |
Rust
Table::new(Slot::many([Row::new(Slot::many([Cell::new("Coffee")
.part(CellPart::Title), Cell::new(Tag::new("Paid")
.state(OrderState::Paid))
.part(CellPart::Status), Cell::new("0.02 XMR")
.part(CellPart::Amount)])), Row::new(Slot::many([Cell::new("Tea")
.part(CellPart::Title), Cell::new(Tag::new("Waiting for payment")
.state(OrderState::Pending)
.short("Waiting"))
.part(CellPart::Status), Cell::new("0.01 XMR")
.part(CellPart::Amount)]))]))
.head(Row::new(Slot::many([Cell::new("Order")
.header(true), Cell::new("Status")
.header(true), Cell::new("Amount")
.header(true)])))
.look(TableLook::Cards)Solid
<Table head={{
"$slot": [
{
"$component": "Row",
"props": {
"children": {
"$slot": [
{
"$component": "Cell",
"props": {
"children": {
"$slot": [
"Order"
]
},
"header": true,
"optional": false
}
},
{
"$component": "Cell",
"props": {
"children": {
"$slot": [
"Status"
]
},
"header": true,
"optional": false
}
},
{
"$component": "Cell",
"props": {
"children": {
"$slot": [
"Amount"
]
},
"header": true,
"optional": false
}
}
]
}
}
}
]
}} look="cards"><Row><Cell part="title">Coffee</Cell><Cell part="status"><Tag state="paid">Paid</Tag></Cell><Cell part="amount">0.02 XMR</Cell></Row><Row><Cell part="title">Tea</Cell><Cell part="status"><Tag state="pending" short="Waiting">Waiting for payment</Tag></Cell><Cell part="amount">0.01 XMR</Cell></Row></Table>HTML
<div class="table-scroll"><table class="table-cards"><thead><tr><th>Order</th><th>Status</th><th>Amount</th></tr></thead><tbody><tr><td class="card-title">Coffee</td><td class="card-status"><span class="tag state-paid">Paid</span></td><td class="card-amount">0.02 XMR</td></tr><tr><td class="card-title">Tea</td><td class="card-status"><span class="tag state-pending"><span class="label-long">Waiting for payment</span><span class="label-short" aria-hidden="true">Waiting</span></span></td><td class="card-amount">0.01 XMR</td></tr></tbody></table></div>| Network | Mainnet |
|---|---|
| Height | 3,412,001 |
Rust
Table::new(Slot::many([Row::new(Slot::many([Cell::new("Network")
.header(true), Cell::new("Mainnet")])), Row::new(Slot::many([Cell::new("Height")
.header(true), Cell::new("3,412,001")]))]))
.look(TableLook::Facts)Solid
<Table look="facts"><Row><Cell header>Network</Cell><Cell>Mainnet</Cell></Row><Row><Cell header>Height</Cell><Cell>3,412,001</Cell></Row></Table>HTML
<div class="table-scroll"><table class="kv-table"><tbody><tr><th>Network</th><td>Mainnet</td></tr><tr><th>Height</th><td>3,412,001</td></tr></tbody></table></div>Links to the pages before and after this one of a long list, and where this one is. Real links: with fixi_target, fixi swaps the next page into that element and keeps the address in step.
| Prop | Type | About |
|---|---|---|
label | String | Required. What is paged ("Orders pages"). |
previous | Option<String> | |
next | Option<String> | |
previous_label | String | Default: "Previous". |
next_label | String | Default: "Next". |
status | Option<String> | Where this page is ("Page 2 of 5"). |
fixi_target | Option<String> | |
rest | Attrs |
Rust
Pagination::new("Invite pages")
.previous("#component-pagination")
.next("#component-pagination")
.status("Page 2 of 5")Solid
<Pagination label="Invite pages" previous="#component-pagination" next="#component-pagination" status="Page 2 of 5" />HTML
<nav class="mk-pagination" aria-label="Invite pages"><a href="#component-pagination" rel="prev">← Previous</a><span class="mk-pagination-status">Page 2 of 5</span><a href="#component-pagination" rel="next">Next →</a></nav>Rust
Pagination::new("Orders pages")
.next("#component-pagination")
.next_label("Older")Solid
<Pagination label="Orders pages" next="#component-pagination" next_label="Older" />HTML
<nav class="mk-pagination" aria-label="Orders pages"><a href="#component-pagination" rel="next">Older →</a></nav>One thing in a list of them, as a card: a title and a line under it, its status at the right, a figure (an amount), and a foot with a detail and what to do with it. An order on the POS's list.
| Prop | Type | About |
|---|---|---|
title | Slot | Required. |
subtitle | Option<Slot> | |
status | Option<Slot> | At the head's right: a Tag. |
amount | Option<Slot> | The figure: an amount. |
detail | Option<Slot> | Muted, at the foot's left. |
action | Option<Slot> | At the foot's right: a link or a button. |
rest | Attrs |
Created 14:02
0.42 XMR
Rust
Card::new("Table 4")
.subtitle("Created 14:02")
.status(Tag::new("Waiting for payment")
.state(OrderState::Pending)
.short("Waiting"))
.amount("0.42 XMR")
.detail("Expires in 52 min")
.action(LinkButton::new("Open", "#component-card")
.variant(ButtonVariant::Quiet)
.size(ControlSize::Compact))Solid
<Card title={{
"$slot": [
"Table 4"
]
}} subtitle={{
"$slot": [
"Created 14:02"
]
}} status={{
"$slot": [
{
"$component": "Tag",
"props": {
"children": {
"$slot": [
"Waiting for payment"
]
},
"short": "Waiting",
"state": "pending"
}
}
]
}} amount={{
"$slot": [
"0.42 XMR"
]
}} detail={{
"$slot": [
"Expires in 52 min"
]
}} action={{
"$slot": [
{
"$component": "LinkButton",
"props": {
"block": false,
"children": {
"$slot": [
"Open"
]
},
"href": "#component-card",
"size": "compact",
"variant": "quiet"
}
}
]
}} />HTML
<article class="mk-card"><div class="mk-card-head"><div><h3 class="mk-card-title">Table 4</h3><p class="mk-card-sub">Created 14:02</p></div><span class="tag state-pending"><span class="label-long">Waiting for payment</span><span class="label-short" aria-hidden="true">Waiting</span></span></div><p class="mk-card-amount">0.42 XMR</p><div class="mk-card-foot"><small>Expires in 52 min</small><a class="mk-btn" href="#component-card" data-variant="quiet" data-size="compact">Open</a></div></article>A table's row.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
rest | Attrs |
A table's cell; a header cell with header.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
header | bool | Default: false. |
part | Option<CellPart (title | status | amount | when | meta | detail)> | Where a cell goes when its row is a card on a phone. |
label | Option<String> | Its column's name, shown above it in a Stack table on a phone. |
optional | bool | Left out on a phone. Default: false. |
rest | Attrs |
A modal dialog, opened by the links and buttons naming it in data-opens-dialog. It gives focus to its first field, and back to what opened it once it closes.
| Prop | Type | About |
|---|---|---|
id | String | Required. |
labelled_by | String | Required. The id of its title (a DialogTitle). |
children | Slot | Required. |
size | DialogSize (confirm | wide) | How wide a dialog is. Default: confirm. |
confirms | Option<String> | A form's field (its name) whose change the form's save asks
about with this dialog first: the field holds what was saved in
data-saved. The dialog's field marked data-confirmed gets
what was typed. |
confirms_when | ConfirmWhen (changed | emptied) | When a form's save opens a dialog instead (Dialog::confirms). Default: changed. |
light_dismiss | bool | A click on the backdrop closes it too. Default: false. |
rest | Attrs |
Rust
Dialog::new("book-dialog", "book-dialog-title", Slot::many([DialogTitle::new("book-dialog-title", "Retire this wallet?"), Hint::new("Takes it out of every list and deletes its keys from Monokulo."), DialogActions::new(Slot::many([Button::new("Cancel")
.attr("data-closes-dialog", ""), Button::new("Retire wallet")
.variant(ButtonVariant::Danger)]))]))Solid
<Dialog id="book-dialog" labelled_by="book-dialog-title"><DialogTitle id="book-dialog-title">Retire this wallet?</DialogTitle><Hint>Takes it out of every list and deletes its keys from Monokulo.</Hint><DialogActions><Button data-closes-dialog="">Cancel</Button><Button variant="danger">Retire wallet</Button></DialogActions></Dialog>HTML
<dialog id="book-dialog" class="settings-dialog confirm-dialog" aria-labelledby="book-dialog-title" data-mk="1" data-island="Dialog" data-props="{"confirms_when":"changed","id":"book-dialog"}"><h2 id="book-dialog-title" class="dialog-title">Retire this wallet?<button type="button" class="dialog-x" aria-label="Close" data-closes-dialog>×</button></h2><p class="hint">Takes it out of every list and deletes its keys from Monokulo.</p><div class="dialog-actions"><button class="mk-btn" type="button" data-closes-dialog="">Cancel</button><button class="mk-btn" type="button" data-variant="danger">Retire wallet</button></div></dialog>A dialog's title: an h2, with a ✕ that closes the dialog. On the content's own page, the title is the page's (PageHeader).
| Prop | Type | About |
|---|---|---|
id | String | Required. |
children | Slot | Required. |
aside | Option<Slot> | Beside the title, before the ✕: a status. |
Rust
DialogTitle::new("book-title-dialog", "Delete this webhook?")Solid
<DialogTitle id="book-title-dialog">Delete this webhook?</DialogTitle>HTML
<h2 id="book-title-dialog" class="dialog-title">Delete this webhook?<button type="button" class="dialog-x" aria-label="Close" data-closes-dialog>×</button></h2>Rust
DialogTitle::new("book-title-status", "Delivery of order.paid")
.aside(Tag::new("retrying")
.tone(TagTone::Slow))Solid
<DialogTitle id="book-title-status" aside={{
"$slot": [
{
"$component": "Tag",
"props": {
"children": {
"$slot": [
"retrying"
]
},
"tone": "slow"
}
}
]
}}>Delivery of order.paid</DialogTitle>HTML
<h2 id="book-title-status" class="dialog-title">Delivery of order.paid<span class="dialog-aside"><span class="tag tag-slow">retrying</span></span><button type="button" class="dialog-x" aria-label="Close" data-closes-dialog>×</button></h2>A dialog's answers, at its foot on the right: Cancel, then the action.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
Rust
DialogActions::new(Slot::many([LinkButton::new("Cancel", "#component-dialog-actions"), Button::new("Delete webhook")
.variant(ButtonVariant::Danger)]))Solid
<DialogActions><LinkButton href="#component-dialog-actions">Cancel</LinkButton><Button variant="danger">Delete webhook</Button></DialogActions>HTML
<div class="dialog-actions"><a class="mk-btn" href="#component-dialog-actions">Cancel</a><button class="mk-btn" type="button" data-variant="danger">Delete webhook</button></div>A dialog's content as a page of its own: where its opener goes without JavaScript. The page's header holds its title.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
size | DialogSize (confirm | wide) | How wide a dialog is. Default: confirm. |
Takes it out of every list and deletes its keys from Monokulo.
Rust
DialogPage::new(Slot::many([Hint::new("Takes it out of every list and deletes its keys from Monokulo."), DialogActions::new(Slot::many([LinkButton::new("Cancel", "#component-dialog-page"), Button::new("Retire wallet")
.variant(ButtonVariant::Danger)]))]))Solid
<DialogPage><Hint>Takes it out of every list and deletes its keys from Monokulo.</Hint><DialogActions><LinkButton href="#component-dialog-page">Cancel</LinkButton><Button variant="danger">Retire wallet</Button></DialogActions></DialogPage>HTML
<div class="confirm-dialog-page"><p class="hint">Takes it out of every list and deletes its keys from Monokulo.</p><div class="dialog-actions"><a class="mk-btn" href="#component-dialog-page">Cancel</a><button class="mk-btn" type="button" data-variant="danger">Retire wallet</button></div></div>A labelled text input, with what it's for and what's wrong with it tied to it for screen readers. The label is required: an input never goes without one.
| Prop | Type | About |
|---|---|---|
label | String | Required. What the input is ("Email"). |
id | String | Required. The input's id: the label, hint and error point at it. |
name | String | Required. The form field's name. |
kind | InputType (text | email | password | number | url | search | tel | datetime-local) | The kind of text an input takes: phones show the matching keyboard. Default: text. |
value | Option<String> | |
placeholder | Option<String> | |
autocomplete | Option<String> | |
hint | Option<String> | What it's for, under the input. |
error | Option<String> | What's wrong with what was entered. Marks the input invalid. |
required | bool | Default: false. |
readonly | bool | Default: false. |
disabled | bool | Default: false. |
size | Option<ControlSize (compact | touch)> | A control's size. Without one it is the normal size. |
multiline | bool | Several lines of text (a message, a note): a text area, rows
tall, instead of an input; kind doesn't apply. Default: false. |
rows | number | Default: 4. |
rest | Attrs | More attributes on the input: minlength, inputmode, fx-*. |
Who you are, and what you'll use Monokulo for.
Rust
Field::new("Message", "book-message", "message")
.hint("Who you are, and what you'll use Monokulo for.")
.required(true)
.multiline(true)Solid
<Field label="Message" id="book-message" name="message" hint="Who you are, and what you'll use Monokulo for." required multiline />HTML
<div class="mk-field"><label for="book-message">Message</label><textarea class="mk-input" id="book-message" name="message" rows="4" required aria-describedby="book-message-help"></textarea><p class="mk-hint" id="book-message-help">Who you are, and what you'll use Monokulo for.</p></div>Shown to customers on the payment page.
Rust
Field::new("Store name", "book-store-name", "name")
.placeholder("Your store")
.hint("Shown to customers on the payment page.")Solid
<Field label="Store name" id="book-store-name" name="name" placeholder="Your store" hint="Shown to customers on the payment page." />HTML
<div class="mk-field"><label for="book-store-name">Store name</label><input class="mk-input" id="book-store-name" name="name" type="text" placeholder="Your store" aria-describedby="book-store-name-help"><p class="mk-hint" id="book-store-name-help">Shown to customers on the payment page.</p></div>Enter a complete email address.
Rust
Field::new("Email", "book-email", "email")
.kind(InputType::Email)
.value("incomplete")
.error("Enter a complete email address.")Solid
<Field label="Email" id="book-email" name="email" kind="email" value="incomplete" error="Enter a complete email address." />HTML
<div class="mk-field"><label for="book-email">Email</label><input class="mk-input" id="book-email" name="email" type="email" value="incomplete" aria-invalid="true" aria-describedby="book-email-error"><p class="mk-error" id="book-email-error">Enter a complete email address.</p></div>At least 8 characters.
Rust
Field::new("Password", "book-password", "password")
.kind(InputType::Password)
.autocomplete("new-password")
.hint("At least 8 characters.")
.required(true)
.attr("minlength", "8")Solid
<Field label="Password" id="book-password" name="password" kind="password" autocomplete="new-password" hint="At least 8 characters." required minlength="8" />HTML
<div class="mk-field"><label for="book-password">Password</label><input class="mk-input" id="book-password" name="password" type="password" autocomplete="new-password" required aria-describedby="book-password-help" minlength="8"><p class="mk-hint" id="book-password-help">At least 8 characters.</p></div>Rust
Field::new("Store id", "book-readonly", "store")
.value("Store 123")
.readonly(true)Solid
<Field label="Store id" id="book-readonly" name="store" value="Store 123" readonly />HTML
<div class="mk-field"><label for="book-readonly">Store id</label><input class="mk-input" id="book-readonly" name="store" type="text" value="Store 123" readonly></div>Rust
Field::new("Instance URL", "book-disabled", "url")
.kind(InputType::Url)
.value("https://pay.example.com")
.disabled(true)Solid
<Field label="Instance URL" id="book-disabled" name="url" kind="url" value="https://pay.example.com" disabled />HTML
<div class="mk-field"><label for="book-disabled">Instance URL</label><input class="mk-input" id="book-disabled" name="url" type="url" value="https://pay.example.com" disabled></div>Rust
Field::new("Refund address", "book-compact", "refund")
.size(ControlSize::Compact)Solid
<Field label="Refund address" id="book-compact" name="refund" size="compact" />HTML
<div class="mk-field"><label for="book-compact">Refund address</label><input class="mk-input" id="book-compact" name="refund" type="text" data-size="compact"></div>A text input on its own, for a control whose label is elsewhere: a Setting's, a table column's (label, read by screen readers). A labelled input with its hint and error is a Field.
| Prop | Type | About |
|---|---|---|
name | String | Required. |
id | Option<String> | |
kind | InputType (text | email | password | number | url | search | tel | datetime-local) | The kind of text an input takes: phones show the matching keyboard. Default: text. |
value | Option<String> | |
placeholder | Option<String> | |
autocomplete | Option<String> | |
label | Option<String> | Its name for screen readers, when nothing on the page labels it. |
described_by | Option<String> | The ids of what describes it: its help, its problem. |
invalid | bool | What was entered was refused. Default: false. |
required | bool | Default: false. |
readonly | bool | Default: false. |
disabled | bool | Default: false. |
size | Option<ControlSize (compact | touch)> | A control's size. Without one it is the normal size. |
actions | Option<Slot> | Inside its right edge: IconButtons (scan a code, choose a
file). Their own names say what they do. |
rest | Attrs | More attributes: minlength, inputmode, data-saved, fx-*. |
Rust
Input::new("webhook_url")
.kind(InputType::Url)
.placeholder("https://shop.example/webhook")
.label("Webhook URL")Solid
<Input name="webhook_url" kind="url" placeholder="https://shop.example/webhook" label="Webhook URL" />HTML
<input class="mk-input" type="url" name="webhook_url" placeholder="https://shop.example/webhook" aria-label="Webhook URL">Rust
Input::new("confirmations")
.kind(InputType::Number)
.value("-1")
.label("Confirmations")
.invalid(true)Solid
<Input name="confirmations" kind="number" value="-1" label="Confirmations" invalid />HTML
<input class="mk-input" type="number" name="confirmations" value="-1" aria-label="Confirmations" aria-invalid="true">Rust
Input::new("q")
.kind(InputType::Search)
.label("Search")
.size(ControlSize::Compact)Solid
<Input name="q" kind="search" label="Search" size="compact" />HTML
<input class="mk-input" type="search" name="q" aria-label="Search" data-size="compact">Rust
Input::new("refund_address")
.placeholder("Your Monero refund address")
.label("Refund address")
.actions(Slot::many([IconButton::new("Scan refund QR", "⌗"), IconButton::new("Choose QR image", "▣")]))Solid
<Input name="refund_address" placeholder="Your Monero refund address" label="Refund address" actions={{
"$slot": [
{
"$component": "IconButton",
"props": {
"disabled": false,
"icon": {
"$slot": [
"⌗"
]
},
"kind": "button",
"label": "Scan refund QR"
}
},
{
"$component": "IconButton",
"props": {
"disabled": false,
"icon": {
"$slot": [
"▣"
]
},
"kind": "button",
"label": "Choose QR image"
}
}
]
}} />HTML
<span class="mk-input-with-actions"><input class="mk-input" type="text" name="refund_address" placeholder="Your Monero refund address" aria-label="Refund address"><span class="mk-input-actions"><button type="button" class="mk-btn icon-only" aria-label="Scan refund QR" title="Scan refund QR">⌗</button><button type="button" class="mk-btn icon-only" aria-label="Choose QR image" title="Choose QR image">▣</button></span></span>A note under a field: what it's for, or how what's in it stands. The control points at it with aria-describedby and its id.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
tone | NoteTone (help | problem | pending | fine) | What a note under a field says. Default: help. |
id | Option<String> | |
live | bool | Said as it changes (a check as you type): role="status". Default: false. |
rest | Attrs |
Rust
FieldNote::new("The node's host and port, like node.example.com:18089.")Solid
<FieldNote>The node's host and port, like node.example.com:18089.</FieldNote>HTML
<span class="field-help">The node's host and port, like node.example.com:18089.</span>Rust
FieldNote::new("Add the port, like node.example.com:18081.")
.tone(NoteTone::Problem)Solid
<FieldNote tone="problem">Add the port, like node.example.com:18081.</FieldNote>HTML
<span class="setting-problem">Add the port, like node.example.com:18081.</span>Rust
FieldNote::new("Saved - restart needed for it to take effect.")
.tone(NoteTone::Pending)Solid
<FieldNote tone="pending">Saved - restart needed for it to take effect.</FieldNote>HTML
<span class="setting-pending">Saved - restart needed for it to take effect.</span>Rust
FieldNote::new("✓ Free to use.")
.tone(NoteTone::Fine)
.live(true)Solid
<FieldNote tone="fine" live>✓ Free to use.</FieldNote>HTML
<span class="field-check" role="status">✓ Free to use.</span>A value the page shows but can't change (set on the command line, by a connected plugin): a disabled box with a padlock, and no name, so it is never sent with a form. A secret shows as dots, never its value.
| Prop | Type | About |
|---|---|---|
value | String | Required. |
id | Option<String> | For a label for to name it. |
labelled_by | Option<String> | What names it, without a label for. |
described_by | Option<String> | |
secret | bool | Default: false. |
Rust
LockedValue::new("https://pay.example.com")
.id("book-locked")Solid
<LockedValue value="https://pay.example.com" id="book-locked" />HTML
<span class="locked-input"><input class="mk-input" type="text" value="https://pay.example.com" id="book-locked" disabled><svg class="lock-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M5 7V5a3 3 0 0 1 6 0v2" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"></path><rect x="3" y="7" width="10" height="7" rx="1.5" fill="currentColor"></rect></svg></span>Rust
LockedValue::new("")
.id("book-locked-secret")
.secret(true)Solid
<LockedValue value="" id="book-locked-secret" secret />HTML
<span class="locked-input"><input class="mk-input" type="password" value="locked" id="book-locked-secret" disabled><svg class="lock-icon" viewBox="0 0 16 16" aria-hidden="true"><path d="M5 7V5a3 3 0 0 1 6 0v2" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"></path><rect x="3" y="7" width="10" height="7" rx="1.5" fill="currentColor"></rect></svg></span>Text over several lines. Its label is elsewhere, like an Input's.
| Prop | Type | About |
|---|---|---|
name | String | Required. |
id | Option<String> | |
value | Option<String> | |
rows | number | Default: 3. |
placeholder | Option<String> | |
label | Option<String> | |
described_by | Option<String> | |
invalid | bool | Default: false. |
required | bool | Default: false. |
readonly | bool | Default: false. |
disabled | bool | Default: false. |
rest | Attrs |
Rust
TextArea::new("note")
.placeholder("For the customer's receipt")
.label("Note")Solid
<TextArea name="note" placeholder="For the customer's receipt" label="Note" />HTML
<textarea class="mk-input" name="note" rows="3" placeholder="For the customer's receipt" aria-label="Note"></textarea>A checkbox and what ticking it means, beside it. Without words beside it (a table's column says it), label names it.
| Prop | Type | About |
|---|---|---|
text | Option<Slot> | What ticking it means: the label. |
name | Option<String> | |
id | Option<String> | |
value | String | What it sends when ticked. Default: "on". |
checked | bool | Default: false. |
label | Option<String> | Its name for screen readers, without text. |
described_by | Option<String> | |
disabled | bool | Default: false. |
rest | Attrs | More attributes on the checkbox: data-saved, a script's hook. |
Rust
Checkbox::new()
.text("I've written all 16 words down, in order, and put them somewhere safe.")Solid
<Checkbox text={{
"$slot": [
"I've written all 16 words down, in order, and put them somewhere safe."
]
}} />HTML
<label class="check-line"><input type="checkbox" value="on"><span>I've written all 16 words down, in order, and put them somewhere safe.</span></label>Rust
Checkbox::new()
.text("Use TLS")
.name("ssl")
.checked(true)Solid
<Checkbox text={{
"$slot": [
"Use TLS"
]
}} name="ssl" checked />HTML
<label class="check-line"><input type="checkbox" name="ssl" value="on" checked><span>Use TLS</span></label>Rust
Checkbox::new()
.name("use_node")
.label("Use node.example.com")Solid
<Checkbox name="use_node" label="Use node.example.com" />HTML
<input type="checkbox" class="mk-checkbox" name="use_node" value="on" aria-label="Use node.example.com">On or off, said as a switch: a checkbox with role="switch", so it works and submits without JavaScript. An unticked checkbox sends nothing; listed_in names a field each switch adds its name to, so the form's handler can tell off from absent.
| Prop | Type | About |
|---|---|---|
name | String | Required. |
id | String | Required. |
on | bool | Default: false. |
described_by | Option<String> | |
saved | Option<Saved (on | off)> | What is saved, when a refused save shows another value. |
listed_in | Option<String> | |
disabled | bool | Default: false. |
Rust
Switch::new("book.accept", "book-switch-on")
.on(true)Solid
<Switch name="book.accept" id="book-switch-on" on />HTML
<label class="switch"><input type="checkbox" role="switch" name="book.accept" value="true" id="book-switch-on" checked><span class="switch-track" aria-hidden="true"></span><span class="switch-on" aria-hidden="true">On</span><span class="switch-off" aria-hidden="true">Off</span></label>Rust
Switch::new("book.notify", "book-switch-off")Solid
<Switch name="book.notify" id="book-switch-off" />HTML
<label class="switch"><input type="checkbox" role="switch" name="book.notify" value="true" id="book-switch-off"><span class="switch-track" aria-hidden="true"></span><span class="switch-on" aria-hidden="true">On</span><span class="switch-off" aria-hidden="true">Off</span></label>Rust
Switch::new("book.locked", "book-switch-disabled")
.disabled(true)Solid
<Switch name="book.locked" id="book-switch-disabled" disabled />HTML
<label class="switch"><input type="checkbox" role="switch" name="book.locked" value="true" id="book-switch-disabled" disabled><span class="switch-track" aria-hidden="true"></span><span class="switch-on" aria-hidden="true">On</span><span class="switch-off" aria-hidden="true">Off</span></label>One of a few choices, each a card: a radio button, maybe a picture, and its label. Its label is elsewhere (a Setting group's).
| Prop | Type | About |
|---|---|---|
name | String | Required. |
cards | Vec<RadioCard> | Required. One choice of [RadioCards]. |
compact | bool | Small cards, side by side: the theme's three. Default: false. |
Rust
RadioCards::new("book_theme", vec![
RadioCard { value: "system".into(), label: "System".into(), picture: None, checked: true, saved: None },
RadioCard { value: "light".into(), label: "Light".into(), picture: None, checked: false, saved: None },
RadioCard { value: "dark".into(), label: "Dark".into(), picture: None, checked: false, saved: None },
])
.compact(true)Solid
<RadioCards name="book_theme" cards={[
{
"checked": true,
"label": {
"$slot": [
"System"
]
},
"value": "system"
},
{
"checked": false,
"label": {
"$slot": [
"Light"
]
},
"value": "light"
},
{
"checked": false,
"label": {
"$slot": [
"Dark"
]
},
"value": "dark"
}
]} compact />HTML
<div class="radio-cards is-compact"><label class="radio-card"><input type="radio" name="book_theme" value="system" checked>System</label><label class="radio-card"><input type="radio" name="book_theme" value="light">Light</label><label class="radio-card"><input type="radio" name="book_theme" value="dark">Dark</label></div>A button showing only an icon: its name, required, is what screen readers say and what hovering shows.
| Prop | Type | About |
|---|---|---|
label | String | Required. What it does ("Send again"). |
icon | Slot | Required. The icon: an inline SVG. |
kind | ButtonType (button | submit | reset) | What a button does in its form. Default: button. |
disabled | bool | Default: false. |
rest | Attrs |
Rust
IconButton::new("Send again", "↻")Solid
<IconButton label="Send again" icon={{
"$slot": [
"↻"
]
}} />HTML
<button type="button" class="mk-btn icon-only" aria-label="Send again" title="Send again">↻</button>A link showing only an icon, looking like an IconButton: its name, required, is what screen readers say and what hovering shows.
| Prop | Type | About |
|---|---|---|
label | String | Required. Where it goes, in words ("Refresh page"). |
href | String | Required. |
icon | Slot | Required. The icon: an inline SVG. |
rest | Attrs |
Rust
IconLink::new("Refresh page", "#component-icon-link", "↻")Solid
<IconLink label="Refresh page" href="#component-icon-link" icon={{
"$slot": [
"↻"
]
}} />HTML
<a class="mk-btn icon-only" href="#component-icon-link" aria-label="Refresh page" title="Refresh page">↻</a>A form's buttons in a row; a spacer pushes the ones after it to the right.
| Prop | Type | About |
|---|---|---|
children | Slot | Required. |
rest | Attrs |
Rust
FormActions::new(Slot::many([Button::new("Add webhook")
.variant(ButtonVariant::Primary)
.kind(ButtonType::Submit), LinkButton::new("Cancel", "#component-form-actions")]))Solid
<FormActions><Button variant="primary" kind="submit">Add webhook</Button><LinkButton href="#component-form-actions">Cancel</LinkButton></FormActions>HTML
<div class="form-actions"><button class="mk-btn" type="submit" data-variant="primary">Add webhook</button><a class="mk-btn" href="#component-form-actions">Cancel</a></div>The site's one dropdown. The page sends an ordinary select: without JavaScript it is all there is, and each option's text says everything. Once its island attaches, a button and a list that floats over the page draw the options' parts, with search, type-ahead and the keys a listbox takes; the select stays the field, so forms, required, fixi and other scripts that read it keep working.
| Prop | Type | About |
|---|---|---|
name | String | Required. The form field's name. |
options | Vec<Choice> | Required. One option of a [Select]. Its text says everything in words, for a
browser without JavaScript (Label (detail) [Network] [chip] - note);
the enhanced list draws the same parts on their own: a logo, a label, a
monospace detail, a network badge, a chip, a note. |
id | Option<String> | |
label | Option<String> | The accessible name, for a select with no <label>. |
compact | bool | The toolbar size. Default: false. |
search | SearchMode (auto | show | hide) | Whether the list has a find box. Default: auto. |
required | bool | Default: false. |
disabled | bool | Default: false. |
rest | Attrs | More attributes on the select: aria-describedby, data-saved,
fx-*. |
Rust
Select::new("currency", vec![
Choice::new("AUD", "Australian Dollar").detail("AUD"),
Choice::new("CAD", "Canadian Dollar").detail("CAD"),
Choice::new("CHF", "Swiss Franc").detail("CHF"),
Choice::new("EUR", "Euro").detail("EUR").selected(true),
Choice::new("GBP", "Pound Sterling").detail("GBP"),
Choice::new("JPY", "Japanese Yen").detail("JPY"),
Choice::new("NZD", "New Zealand Dollar").detail("NZD"),
Choice::new("SEK", "Swedish Krona").detail("SEK"),
Choice::new("USD", "United States Dollar").detail("USD"),
Choice::new("XMR", "Monero").detail("XMR"),
Choice::new("BRL", "Brazilian Real").detail("BRL"),
Choice::new("INR", "Indian Rupee").detail("INR"),
])
.id("book-currency")
.label("Base currency")Solid
<Select name="currency" options={[
{
"current": false,
"data_label": "Australian Dollar",
"detail": "AUD",
"disabled": false,
"label": "Australian Dollar",
"selected": false,
"text": "Australian Dollar (AUD)",
"value": "AUD"
},
{
"current": false,
"data_label": "Canadian Dollar",
"detail": "CAD",
"disabled": false,
"label": "Canadian Dollar",
"selected": false,
"text": "Canadian Dollar (CAD)",
"value": "CAD"
},
{
"current": false,
"data_label": "Swiss Franc",
"detail": "CHF",
"disabled": false,
"label": "Swiss Franc",
"selected": false,
"text": "Swiss Franc (CHF)",
"value": "CHF"
},
{
"current": false,
"data_label": "Euro",
"detail": "EUR",
"disabled": false,
"label": "Euro",
"selected": true,
"text": "Euro (EUR)",
"value": "EUR"
},
{
"current": false,
"data_label": "Pound Sterling",
"detail": "GBP",
"disabled": false,
"label": "Pound Sterling",
"selected": false,
"text": "Pound Sterling (GBP)",
"value": "GBP"
},
{
"current": false,
"data_label": "Japanese Yen",
"detail": "JPY",
"disabled": false,
"label": "Japanese Yen",
"selected": false,
"text": "Japanese Yen (JPY)",
"value": "JPY"
},
{
"current": false,
"data_label": "New Zealand Dollar",
"detail": "NZD",
"disabled": false,
"label": "New Zealand Dollar",
"selected": false,
"text": "New Zealand Dollar (NZD)",
"value": "NZD"
},
{
"current": false,
"data_label": "Swedish Krona",
"detail": "SEK",
"disabled": false,
"label": "Swedish Krona",
"selected": false,
"text": "Swedish Krona (SEK)",
"value": "SEK"
},
{
"current": false,
"data_label": "United States Dollar",
"detail": "USD",
"disabled": false,
"label": "United States Dollar",
"selected": false,
"text": "United States Dollar (USD)",
"value": "USD"
},
{
"current": false,
"data_label": "Monero",
"detail": "XMR",
"disabled": false,
"label": "Monero",
"selected": false,
"text": "Monero (XMR)",
"value": "XMR"
},
{
"current": false,
"data_label": "Brazilian Real",
"detail": "BRL",
"disabled": false,
"label": "Brazilian Real",
"selected": false,
"text": "Brazilian Real (BRL)",
"value": "BRL"
},
{
"current": false,
"data_label": "Indian Rupee",
"detail": "INR",
"disabled": false,
"label": "Indian Rupee",
"selected": false,
"text": "Indian Rupee (INR)",
"value": "INR"
}
]} id="book-currency" label="Base currency" />HTML
<div class="mk-select" data-mk="1" data-island="Select" data-props="{"label":"Base currency","search":"auto"}"><select class="mk-input" name="currency" id="book-currency" aria-label="Base currency" data-mk="2"><option value="AUD" data-label="Australian Dollar" data-detail="AUD">Australian Dollar (AUD)</option><option value="CAD" data-label="Canadian Dollar" data-detail="CAD">Canadian Dollar (CAD)</option><option value="CHF" data-label="Swiss Franc" data-detail="CHF">Swiss Franc (CHF)</option><option value="EUR" selected data-label="Euro" data-detail="EUR">Euro (EUR)</option><option value="GBP" data-label="Pound Sterling" data-detail="GBP">Pound Sterling (GBP)</option><option value="JPY" data-label="Japanese Yen" data-detail="JPY">Japanese Yen (JPY)</option><option value="NZD" data-label="New Zealand Dollar" data-detail="NZD">New Zealand Dollar (NZD)</option><option value="SEK" data-label="Swedish Krona" data-detail="SEK">Swedish Krona (SEK)</option><option value="USD" data-label="United States Dollar" data-detail="USD">United States Dollar (USD)</option><option value="XMR" data-label="Monero" data-detail="XMR">Monero (XMR)</option><option value="BRL" data-label="Brazilian Real" data-detail="BRL">Brazilian Real (BRL)</option><option value="INR" data-label="Indian Rupee" data-detail="INR">Indian Rupee (INR)</option></select><!--mk:3--></div>Rust
Select::new("wallet_id", vec![
Choice::new("", "Choose a wallet…").disabled(true),
Choice::new("w_1", "Copper Heron").detail("48xQ7…v3Rk").network("mainnet").chip("Current").current(true).note("2 stores").selected(true),
Choice::new("w_2", "Feather test").detail("5Bq2…9TtM").network("stagenet").note("1 store"),
Choice::new("w_3", "Old till").detail("47aa…0QeN").network("mainnet").note("retired").disabled(true),
])
.id("book-wallet")
.label("Wallet")
.required(true)Solid
<Select name="wallet_id" options={[
{
"current": false,
"disabled": true,
"label": "Choose a wallet…",
"selected": false,
"text": "Choose a wallet…",
"value": ""
},
{
"chip": "Current",
"current": true,
"data_label": "Copper Heron",
"detail": "48xQ7…v3Rk",
"disabled": false,
"label": "Copper Heron",
"network": "mainnet",
"note": "2 stores",
"selected": true,
"text": "Copper Heron (48xQ7…v3Rk) [Mainnet] [Current] - 2 stores",
"value": "w_1"
},
{
"current": false,
"data_label": "Feather test",
"detail": "5Bq2…9TtM",
"disabled": false,
"label": "Feather test",
"network": "stagenet",
"note": "1 store",
"selected": false,
"text": "Feather test (5Bq2…9TtM) [Stagenet] - 1 store",
"value": "w_2"
},
{
"current": false,
"data_label": "Old till",
"detail": "47aa…0QeN",
"disabled": true,
"label": "Old till",
"network": "mainnet",
"note": "retired",
"selected": false,
"text": "Old till (47aa…0QeN) [Mainnet] - retired",
"value": "w_3"
}
]} id="book-wallet" label="Wallet" required />HTML
<div class="mk-select" data-mk="1" data-island="Select" data-props="{"label":"Wallet","search":"auto"}"><select class="mk-input" name="wallet_id" id="book-wallet" aria-label="Wallet" required data-mk="2"><option value="" disabled>Choose a wallet…</option><option value="w_1" selected data-label="Copper Heron" data-detail="48xQ7…v3Rk" data-network="mainnet" data-chip="Current" data-chip-tone="current" data-note="2 stores">Copper Heron (48xQ7…v3Rk) [Mainnet] [Current] - 2 stores</option><option value="w_2" data-label="Feather test" data-detail="5Bq2…9TtM" data-network="stagenet" data-note="1 store">Feather test (5Bq2…9TtM) [Stagenet] - 1 store</option><option value="w_3" disabled data-label="Old till" data-detail="47aa…0QeN" data-network="mainnet" data-note="retired">Old till (47aa…0QeN) [Mainnet] - retired</option></select><!--mk:3--></div>Rust
Select::new("network", vec![
Choice::new("mainnet", "").network("mainnet"),
Choice::new("stagenet", "").network("stagenet").selected(true),
Choice::new("testnet", "").network("testnet"),
])
.id("book-network")
.label("Network")Solid
<Select name="network" options={[
{
"current": false,
"data_label": "",
"disabled": false,
"label": "",
"network": "mainnet",
"selected": false,
"text": "[Mainnet]",
"value": "mainnet"
},
{
"current": false,
"data_label": "",
"disabled": false,
"label": "",
"network": "stagenet",
"selected": true,
"text": "[Stagenet]",
"value": "stagenet"
},
{
"current": false,
"data_label": "",
"disabled": false,
"label": "",
"network": "testnet",
"selected": false,
"text": "[Testnet]",
"value": "testnet"
}
]} id="book-network" label="Network" />HTML
<div class="mk-select" data-mk="1" data-island="Select" data-props="{"label":"Network","search":"auto"}"><select class="mk-input" name="network" id="book-network" aria-label="Network" data-mk="2"><option value="mainnet" data-label="" data-network="mainnet">[Mainnet]</option><option value="stagenet" selected data-label="" data-network="stagenet">[Stagenet]</option><option value="testnet" data-label="" data-network="testnet">[Testnet]</option></select><!--mk:3--></div>Rust
Select::new("level", vec![
Choice::new("error", "Errors"),
Choice::new("warn", "Warnings and up").selected(true),
Choice::new("info", "Info and up"),
Choice::new("debug", "Everything"),
])
.id("book-level")
.label("Level")
.compact(true)Solid
<Select name="level" options={[
{
"current": false,
"disabled": false,
"label": "Errors",
"selected": false,
"text": "Errors",
"value": "error"
},
{
"current": false,
"disabled": false,
"label": "Warnings and up",
"selected": true,
"text": "Warnings and up",
"value": "warn"
},
{
"current": false,
"disabled": false,
"label": "Info and up",
"selected": false,
"text": "Info and up",
"value": "info"
},
{
"current": false,
"disabled": false,
"label": "Everything",
"selected": false,
"text": "Everything",
"value": "debug"
}
]} id="book-level" label="Level" compact />HTML
<div class="mk-select" data-compact data-mk="1" data-island="Select" data-props="{"label":"Level","search":"auto"}"><select class="mk-input" data-size="compact" name="level" id="book-level" aria-label="Level" data-mk="2"><option value="error">Errors</option><option value="warn" selected>Warnings and up</option><option value="info">Info and up</option><option value="debug">Everything</option></select><!--mk:3--></div>Rust
Select::new("language", vec![
Choice::new("en", "English").selected(true),
])
.id("book-language")
.label("Language")
.disabled(true)Solid
<Select name="language" options={[
{
"current": false,
"disabled": false,
"label": "English",
"selected": true,
"text": "English",
"value": "en"
}
]} id="book-language" label="Language" disabled />HTML
<div class="mk-select" data-mk="1" data-island="Select" data-props="{"label":"Language","search":"auto"}"><select class="mk-input" name="language" id="book-language" aria-label="Language" disabled data-mk="2"><option value="en" selected>English</option></select><!--mk:3--></div>Progress through a flow (setup: Store, Wallet, Done), as flat segments, each with a bar counting its tasks done. It shows where you are; it isn't navigation.
| Prop | Type | About |
|---|---|---|
label | String | Required. What the steps are of ("Setup progress"). |
steps | Vec<WizardStep> | Required. One segment of a wizard's strip. |
Rust
WizardSteps::new("Setup progress", vec![
WizardStep { label: "Store".into(), detail: "Complete".into(), done: 1, tasks: 1, state: WizardState::Done },
WizardStep { label: "Wallet".into(), detail: "Back up · step 2 of 3".into(), done: 1, tasks: 3, state: WizardState::Current },
WizardStep { label: "Done".into(), detail: "Next".into(), done: 0, tasks: 1, state: WizardState::Next },
])Solid
<WizardSteps label="Setup progress" steps={[
{
"detail": "Complete",
"done": 1,
"label": "Store",
"state": "done",
"tasks": 1
},
{
"detail": "Back up · step 2 of 3",
"done": 1,
"label": "Wallet",
"state": "current",
"tasks": 3
},
{
"detail": "Next",
"done": 0,
"label": "Done",
"state": "next",
"tasks": 1
}
]} />HTML
<ol class="mk-wizard-steps" data-design="strip" aria-label="Setup progress"><li class="done"><span>Store<small>Complete</small></span><progress class="mk-wizard-progress" value="1" max="1" aria-label="Store: 1 of 1 tasks complete"></progress></li><li aria-current="step"><span>Wallet<small>Back up · step 2 of 3</small></span><progress class="mk-wizard-progress" value="1" max="3" aria-label="Wallet: 1 of 3 tasks complete"></progress></li><li><span>Done<small>Next</small></span><progress class="mk-wizard-progress" value="0" max="1" aria-label="Done: 0 of 1 tasks complete"></progress></li></ol>Rust
WizardSteps::new("Setup progress", vec![
WizardStep { label: "Store".into(), detail: "Complete".into(), done: 1, tasks: 1, state: WizardState::Done },
WizardStep { label: "Wallet".into(), detail: "Keys · step 2 of 2".into(), done: 1, tasks: 2, state: WizardState::Current },
WizardStep { label: "Done".into(), detail: "Next".into(), done: 0, tasks: 1, state: WizardState::Next },
])Solid
<WizardSteps label="Setup progress" steps={[
{
"detail": "Complete",
"done": 1,
"label": "Store",
"state": "done",
"tasks": 1
},
{
"detail": "Keys · step 2 of 2",
"done": 1,
"label": "Wallet",
"state": "current",
"tasks": 2
},
{
"detail": "Next",
"done": 0,
"label": "Done",
"state": "next",
"tasks": 1
}
]} />HTML
<ol class="mk-wizard-steps" data-design="strip" aria-label="Setup progress"><li class="done"><span>Store<small>Complete</small></span><progress class="mk-wizard-progress" value="1" max="1" aria-label="Store: 1 of 1 tasks complete"></progress></li><li aria-current="step"><span>Wallet<small>Keys · step 2 of 2</small></span><progress class="mk-wizard-progress" value="1" max="2" aria-label="Wallet: 1 of 2 tasks complete"></progress></li><li><span>Done<small>Next</small></span><progress class="mk-wizard-progress" value="0" max="1" aria-label="Done: 0 of 1 tasks complete"></progress></li></ol>Rust
WizardSteps::new("Setup progress", vec![
WizardStep { label: "Store".into(), detail: "Complete".into(), done: 1, tasks: 1, state: WizardState::Done },
WizardStep { label: "Wallet".into(), detail: "Check · step 3 of 3".into(), done: 2, tasks: 3, state: WizardState::Current },
WizardStep { label: "Done".into(), detail: "Next".into(), done: 0, tasks: 1, state: WizardState::Next },
])Solid
<WizardSteps label="Setup progress" steps={[
{
"detail": "Complete",
"done": 1,
"label": "Store",
"state": "done",
"tasks": 1
},
{
"detail": "Check · step 3 of 3",
"done": 2,
"label": "Wallet",
"state": "current",
"tasks": 3
},
{
"detail": "Next",
"done": 0,
"label": "Done",
"state": "next",
"tasks": 1
}
]} />HTML
<ol class="mk-wizard-steps" data-design="strip" aria-label="Setup progress"><li class="done"><span>Store<small>Complete</small></span><progress class="mk-wizard-progress" value="1" max="1" aria-label="Store: 1 of 1 tasks complete"></progress></li><li aria-current="step"><span>Wallet<small>Check · step 3 of 3</small></span><progress class="mk-wizard-progress" value="2" max="3" aria-label="Wallet: 2 of 3 tasks complete"></progress></li><li><span>Done<small>Next</small></span><progress class="mk-wizard-progress" value="0" max="1" aria-label="Done: 0 of 1 tasks complete"></progress></li></ol>Rust
WizardSteps::new("Setup progress", vec![
WizardStep { label: "Store".into(), detail: "Complete".into(), done: 1, tasks: 1, state: WizardState::Done },
WizardStep { label: "Wallet".into(), detail: "Complete".into(), done: 1, tasks: 1, state: WizardState::Done },
WizardStep { label: "Done".into(), detail: "Complete".into(), done: 1, tasks: 1, state: WizardState::Current },
])Solid
<WizardSteps label="Setup progress" steps={[
{
"detail": "Complete",
"done": 1,
"label": "Store",
"state": "done",
"tasks": 1
},
{
"detail": "Complete",
"done": 1,
"label": "Wallet",
"state": "done",
"tasks": 1
},
{
"detail": "Complete",
"done": 1,
"label": "Done",
"state": "current",
"tasks": 1
}
]} />HTML
<ol class="mk-wizard-steps" data-design="strip" aria-label="Setup progress"><li class="done"><span>Store<small>Complete</small></span><progress class="mk-wizard-progress" value="1" max="1" aria-label="Store: 1 of 1 tasks complete"></progress></li><li class="done"><span>Wallet<small>Complete</small></span><progress class="mk-wizard-progress" value="1" max="1" aria-label="Wallet: 1 of 1 tasks complete"></progress></li><li aria-current="step"><span>Done<small>Complete</small></span><progress class="mk-wizard-progress" value="1" max="1" aria-label="Done: 1 of 1 tasks complete"></progress></li></ol>Rust
WizardSteps::new("Wallet progress", vec![
WizardStep { label: "Wallet".into(), detail: "Keys · step 2 of 2".into(), done: 1, tasks: 2, state: WizardState::Current },
])Solid
<WizardSteps label="Wallet progress" steps={[
{
"detail": "Keys · step 2 of 2",
"done": 1,
"label": "Wallet",
"state": "current",
"tasks": 2
}
]} />HTML
<ol class="mk-wizard-steps" data-design="strip" aria-label="Wallet progress"><li aria-current="step"><span>Wallet<small>Keys · step 2 of 2</small></span><progress class="mk-wizard-progress" value="1" max="2" aria-label="Wallet: 1 of 2 tasks complete"></progress></li></ol>Where something is in a short, fixed sequence (a payment: Send, Confirm, Paid), with a line saying what that means and what to do: the same place and shape in every state, so nothing moves as it changes. The colours follow state; the words say it all.
| Prop | Type | About |
|---|---|---|
label | String | Required. What the steps are of ("Payment progress"). |
steps | Vec<ProgressStep> | Required. One step of [ProgressSteps]. |
title | String | Required. The headline of the line under the steps ("Waiting for payment"). |
message | Slot | Required. The rest of the line. |
state | Option<OrderState (pending | unconfirmed | confirming | partial | paid | overpaid | expired | cancelled | double-spend | offline | unknown)> | The colours: an order's state. |
compact | bool | The POS's size: smaller type, the same shape. Default: false. |
rest | Attrs |
Send 0.42 XMR. Scan the code or copy the address. 58 min left
Rust
ProgressSteps::new("Payment progress", vec![
ProgressStep { label: "Send".into(), progress: StepProgress::Now },
ProgressStep { label: "Confirm".into(), progress: StepProgress::Todo },
ProgressStep { label: "Paid".into(), progress: StepProgress::Todo },
], "Send 0.42 XMR", "Scan the code or copy the address. 58 min left")
.state(OrderState::Pending)Solid
<ProgressSteps label="Payment progress" steps={[
{
"label": "Send",
"progress": "now"
},
{
"label": "Confirm",
"progress": "todo"
},
{
"label": "Paid",
"progress": "todo"
}
]} title="Send 0.42 XMR" message={{
"$slot": [
"Scan the code or copy the address. 58 min left"
]
}} state="pending" />HTML
<div class="mk-stage state-pending"><ol class="mk-track" aria-label="Payment progress"><li class="step-now"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Send<span class="visually-hidden">, now</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Confirm<span class="visually-hidden">, to come</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Paid<span class="visually-hidden">, to come</span></span></li></ol><p class="mk-stage-msg" role="status"><strong>Send 0.42 XMR.</strong> Scan the code or copy the address. 58 min left</p></div>Part paid. 0.2 XMR in; send the other 0.22 XMR.
Rust
ProgressSteps::new("Payment progress", vec![
ProgressStep { label: "Send".into(), progress: StepProgress::Part },
ProgressStep { label: "Confirm".into(), progress: StepProgress::Todo },
ProgressStep { label: "Paid".into(), progress: StepProgress::Todo },
], "Part paid", "0.2 XMR in; send the other 0.22 XMR.")
.state(OrderState::Partial)Solid
<ProgressSteps label="Payment progress" steps={[
{
"label": "Send",
"progress": "part"
},
{
"label": "Confirm",
"progress": "todo"
},
{
"label": "Paid",
"progress": "todo"
}
]} title="Part paid" message={{
"$slot": [
"0.2 XMR in; send the other 0.22 XMR."
]
}} state="partial" />HTML
<div class="mk-stage state-partial"><ol class="mk-track" aria-label="Payment progress"><li class="step-part"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Send<span class="visually-hidden">, partly done</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Confirm<span class="visually-hidden">, to come</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Paid<span class="visually-hidden">, to come</span></span></li></ol><p class="mk-stage-msg" role="status"><strong>Part paid.</strong> 0.2 XMR in; send the other 0.22 XMR.</p></div>Confirming. Payment seen; 3 of 10 confirmations.
Rust
ProgressSteps::new("Payment progress", vec![
ProgressStep { label: "Send".into(), progress: StepProgress::Done },
ProgressStep { label: "Confirm".into(), progress: StepProgress::Now },
ProgressStep { label: "Paid".into(), progress: StepProgress::Todo },
], "Confirming", "Payment seen; 3 of 10 confirmations.")
.state(OrderState::Confirming)Solid
<ProgressSteps label="Payment progress" steps={[
{
"label": "Send",
"progress": "done"
},
{
"label": "Confirm",
"progress": "now"
},
{
"label": "Paid",
"progress": "todo"
}
]} title="Confirming" message={{
"$slot": [
"Payment seen; 3 of 10 confirmations."
]
}} state="confirming" />HTML
<div class="mk-stage state-confirming"><ol class="mk-track" aria-label="Payment progress"><li class="step-done"><span class="mk-track-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 12.5 5 5 10-11"></path></svg></span><span class="mk-track-name">Send<span class="visually-hidden">, done</span></span></li><li class="step-now"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Confirm<span class="visually-hidden">, now</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Paid<span class="visually-hidden">, to come</span></span></li></ol><p class="mk-stage-msg" role="status"><strong>Confirming.</strong> Payment seen; 3 of 10 confirmations.</p></div>Paid. Thank you.
Rust
ProgressSteps::new("Payment progress", vec![
ProgressStep { label: "Send".into(), progress: StepProgress::Done },
ProgressStep { label: "Paid".into(), progress: StepProgress::Done },
], "Paid", "Thank you.")
.state(OrderState::Paid)Solid
<ProgressSteps label="Payment progress" steps={[
{
"label": "Send",
"progress": "done"
},
{
"label": "Paid",
"progress": "done"
}
]} title="Paid" message={{
"$slot": [
"Thank you."
]
}} state="paid" />HTML
<div class="mk-stage state-paid"><ol class="mk-track" aria-label="Payment progress"><li class="step-done"><span class="mk-track-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 12.5 5 5 10-11"></path></svg></span><span class="mk-track-name">Send<span class="visually-hidden">, done</span></span></li><li class="step-done"><span class="mk-track-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><path d="m4.5 12.5 5 5 10-11"></path></svg></span><span class="mk-track-name">Paid<span class="visually-hidden">, done</span></span></li></ol><p class="mk-stage-msg" role="status"><strong>Paid.</strong> Thank you.</p></div>Expired. Nothing arrived in time; ask for a new order.
Rust
ProgressSteps::new("Payment progress", vec![
ProgressStep { label: "Send".into(), progress: StepProgress::Fail },
ProgressStep { label: "Confirm".into(), progress: StepProgress::Todo },
ProgressStep { label: "Paid".into(), progress: StepProgress::Todo },
], "Expired", "Nothing arrived in time; ask for a new order.")
.state(OrderState::Expired)Solid
<ProgressSteps label="Payment progress" steps={[
{
"label": "Send",
"progress": "fail"
},
{
"label": "Confirm",
"progress": "todo"
},
{
"label": "Paid",
"progress": "todo"
}
]} title="Expired" message={{
"$slot": [
"Nothing arrived in time; ask for a new order."
]
}} state="expired" />HTML
<div class="mk-stage state-expired"><ol class="mk-track" aria-label="Payment progress"><li class="step-fail"><span class="mk-track-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round"><path d="M7 7l10 10M17 7 7 17"></path></svg></span><span class="mk-track-name">Send<span class="visually-hidden">, stopped</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Confirm<span class="visually-hidden">, to come</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Paid<span class="visually-hidden">, to come</span></span></li></ol><p class="mk-stage-msg" role="status"><strong>Expired.</strong> Nothing arrived in time; ask for a new order.</p></div>Send 0.42 XMR. Scan the code.
Rust
ProgressSteps::new("Payment progress", vec![
ProgressStep { label: "Send".into(), progress: StepProgress::Now },
ProgressStep { label: "Confirm".into(), progress: StepProgress::Todo },
ProgressStep { label: "Paid".into(), progress: StepProgress::Todo },
], "Send 0.42 XMR", "Scan the code.")
.state(OrderState::Pending)
.compact(true)Solid
<ProgressSteps label="Payment progress" steps={[
{
"label": "Send",
"progress": "now"
},
{
"label": "Confirm",
"progress": "todo"
},
{
"label": "Paid",
"progress": "todo"
}
]} title="Send 0.42 XMR" message={{
"$slot": [
"Scan the code."
]
}} state="pending" compact />HTML
<div class="mk-stage state-pending" data-size="compact"><ol class="mk-track" aria-label="Payment progress"><li class="step-now"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Send<span class="visually-hidden">, now</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Confirm<span class="visually-hidden">, to come</span></span></li><li class="step-todo"><span class="mk-track-dot" aria-hidden="true"></span><span class="mk-track-name">Paid<span class="visually-hidden">, to come</span></span></li></ol><p class="mk-stage-msg" role="status"><strong>Send 0.42 XMR.</strong> Scan the code.</p></div>Panels of one page, one shown at a time. Without JavaScript every panel shows, and the tabs are links to them. Once its island attaches, the tabs are a tablist: one panel at a time, picked with a click or the arrow keys, Home and End. Each pick sends mk-tab ({id}, the panel shown) from the tabs, for a page script to follow.
| Prop | Type | About |
|---|---|---|
label | String | Required. What the tabs are of. |
tabs | Vec<Tab> | Required. One tab of [Tabs]: its label and the panel it shows. |
rest | Attrs |
Rust
Tabs::new("Example POS views", vec![
Tab { id: "example-charge".into(), label: "Charge".into(), panel: "Enter an amount to start a payment.".into() },
Tab { id: "example-history".into(), label: "History".into(), panel: "No payments yet.".into() },
Tab { id: "example-terminal".into(), label: "Terminal".into(), panel: "This terminal is ready to receive payments.".into() },
])Solid
<Tabs label="Example POS views" tabs={[
{
"id": "example-charge",
"label": {
"$slot": [
"Charge"
]
},
"panel": {
"$slot": [
"Enter an amount to start a payment."
]
}
},
{
"id": "example-history",
"label": {
"$slot": [
"History"
]
},
"panel": {
"$slot": [
"No payments yet."
]
}
},
{
"id": "example-terminal",
"label": {
"$slot": [
"Terminal"
]
},
"panel": {
"$slot": [
"This terminal is ready to receive payments."
]
}
}
]} />HTML
<div class="mk-tabset" data-mk="1" data-island="Tabs" data-props="{"tabs":[{"id":"example-charge"},{"id":"example-history"},{"id":"example-terminal"}]}"><div class="mk-tabs" aria-label="Example POS views" data-mk="2"><a href="#example-charge" id="example-charge-tab" data-mk="3">Charge</a><a href="#example-history" id="example-history-tab" data-mk="3">History</a><a href="#example-terminal" id="example-terminal-tab" data-mk="3">Terminal</a></div><div class="mk-tab-panels"><section id="example-charge" data-tab-panel="" data-mk="4">Enter an amount to start a payment.</section><section id="example-history" data-tab-panel="" data-mk="4">No payments yet.</section><section id="example-terminal" data-tab-panel="" data-mk="4">This terminal is ready to receive payments.</section></div></div>Tabs that are separate pages: plain links, the current one marked. No behaviour: each is a page of its own.
| Prop | Type | About |
|---|---|---|
label | String | Required. What the pages are of. |
links | Vec<TabLink> | Required. A link of [TabLinks]. |
Rust
TabLinks::new("Example settings navigation", vec![
TabLink { href: "#component-tab-links".into(), label: "General".into(), current: true, count: None },
TabLink { href: "#component-select".into(), label: "Networks".into(), current: false, count: None },
TabLink { href: "#component-alert".into(), label: "Notifications".into(), current: false, count: Some("3".into()) },
])Solid
<TabLinks label="Example settings navigation" links={[
{
"current": true,
"href": "#component-tab-links",
"label": "General"
},
{
"current": false,
"href": "#component-select",
"label": "Networks"
},
{
"count": "3",
"current": false,
"href": "#component-alert",
"label": "Notifications"
}
]} />HTML
<nav class="mk-tabs" aria-label="Example settings navigation"><a href="#component-tab-links" aria-current="page">General</a><a href="#component-select">Networks</a><a href="#component-alert">Notifications<span class="mk-tab-count">3</span></a></nav>A long value to copy: an address, an id, a key. Without JavaScript it is selectable text, ellipsised, that focus shows in full. Once its island attaches, a Copy button copies the whole value (or, where the clipboard is refused, selects it to copy by hand), and a shortened form can stand in for it on screen.
| Prop | Type | About |
|---|---|---|
value | String | Required. The whole value: what is copied. |
copy_label | String | The Copy button's accessible name: what it copies. Default: "Copy full value". |
labelled_by | Option<String> | The id of what names the value, when something on the page does. |
truncate | Truncate (end | middle | segments) | How a long value is shortened on screen. The whole value is always what is copied, and what focus reveals. Default: end. |
head | number | Middle's characters kept at the start. Default: 8. |
tail | number | Middle's characters kept at the end. Default: 8. |
segments | Option<String> | Segments' ranges. |
Rust
CopyValue::new("order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789")
.copy_label("Copy payment reference")Solid
<CopyValue value="order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789" copy_label="Copy payment reference" />HTML
<div class="mk-copy" data-island="CopyValue" data-props="{"copy_label":"Copy payment reference","head":8,"tail":8,"truncate":"end","value":"order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"}"><span class="mk-copy-value mk-mono" data-copy-value tabindex="0" data-mk="1">order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789</span><!--mk:2--></div>Rust
CopyValue::new("order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789")
.copy_label("Copy middle example")
.truncate(Truncate::Middle)
.head(10)
.tail(10)Solid
<CopyValue value="order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789" copy_label="Copy middle example" truncate="middle" head={10} tail={10} />HTML
<div class="mk-copy" data-island="CopyValue" data-props="{"copy_label":"Copy middle example","head":10,"tail":10,"truncate":"middle","value":"order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"}"><span class="mk-copy-value mk-mono" data-copy-value tabindex="0" data-mk="1">order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789</span><!--mk:2--></div>Rust
CopyValue::new("order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789")
.copy_label("Copy segmented example")
.truncate(Truncate::Segments)
.segments("0:8,32:8,-8:8")Solid
<CopyValue value="order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789" copy_label="Copy segmented example" truncate="segments" segments="0:8,32:8,-8:8" />HTML
<div class="mk-copy" data-island="CopyValue" data-props="{"copy_label":"Copy segmented example","head":8,"segments":"0:8,32:8,-8:8","tail":8,"truncate":"segments","value":"order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789"}"><span class="mk-copy-value mk-mono" data-copy-value tabindex="0" data-mk="1">order_0123456789abcdefghijklmnopqrstuvwxyz_ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789</span><!--mk:2--></div>Rust
CopyValue::new("48xQ7v3Rk")
.copy_label("Copy wallet id")Solid
<CopyValue value="48xQ7v3Rk" copy_label="Copy wallet id" />HTML
<div class="mk-copy" data-island="CopyValue" data-props="{"copy_label":"Copy wallet id","head":8,"tail":8,"truncate":"end","value":"48xQ7v3Rk"}"><span class="mk-copy-value mk-mono" data-copy-value tabindex="0" data-mk="1">48xQ7v3Rk</span><!--mk:2--></div>Lines to copy whole: a command, a config file, a key. Without JavaScript it is a pre to select. Once its island attaches, a Copy button over its top right copies it; with comment ("#"), a note after that marker is left out (the marker starting a line or after a space: # inside a word stays).
| Prop | Type | About |
|---|---|---|
code | String | Required. |
copy_label | String | The Copy button's accessible name: what it copies. Default: "Copy to clipboard". |
comment | Option<String> | A line comment's marker: what follows it isn't copied. |
umask 077
openssl rand -hex 32 > monokulo.key # keep this file safeRust
CopyBlock::new("umask 077\nopenssl rand -hex 32 > monokulo.key # keep this file safe")
.copy_label("Copy the key commands")
.comment("#")Solid
<CopyBlock code="umask 077\nopenssl rand -hex 32 > monokulo.key # keep this file safe" copy_label="Copy the key commands" comment="#" />HTML
<div class="mk-copy-block" data-island="CopyBlock" data-props="{"comment":"#","copy_label":"Copy the key commands"}"><pre data-mk="1"><code>umask 077
openssl rand -hex 32 > monokulo.key # keep this file safe</code></pre><!--mk:2--></div>The page's settings form: one per page. label names what has unsaved changes when leaving is asked about ("Abuse protection has unsaved changes"). With fixi_target, a save swaps its answer into that element without reloading; without, it's a plain POST and the page it redirects to.
| Prop | Type | About |
|---|---|---|
action | String | Required. |
label | String | Required. |
fixi_target | Option<String> | |
children | Slot | Required. The cards, the save bar and anything else the form sends. |
method | FormMethod (post | get) | How a form is sent. Default: post. |
form_id | String | The form's id: a page has one settings form, settings-form. Default: "settings-form". |
Rust
SettingsForm::new("#component-settings-form", "Store settings", Slot::many([SettingsCard::new("book-details", "Store details", Setting::new("Store name", "book-settings-name-input", Input::new("name")
.id("book-settings-name-input")
.value("Corner store"))), SaveBar::new("Saving changes the store straight away.", "#component-settings-form")
.inline(true)
.id("book-form-save-bar")
.region_label("Example form's save changes")]))
.method(FormMethod::Get)
.form_id("book-settings-form")Solid
<SettingsForm action="#component-settings-form" label="Store settings" method="get" form_id="book-settings-form"><SettingsCard name="book-details" title="Store details"><Setting label="Store name" id="book-settings-name-input"><Input name="name" id="book-settings-name-input" value="Corner store" /></Setting></SettingsCard><SaveBar message={{
"$slot": [
"Saving changes the store straight away."
]
}} discard_href="#component-settings-form" inline id="book-form-save-bar" region_label="Example form's save changes" /></SettingsForm>HTML
<div class="mk-settings-form" data-label="Store settings" data-mk="1" data-island="SettingsForm" data-props="{"label":"Store settings"}"><form method="get" action="#component-settings-form" id="book-settings-form" data-mk="2"><section id="card-book-details" class="settings-card" data-name="book-details" role="region" aria-labelledby="card-book-details-title" data-mk="1" data-island="SettingsCard" data-props="{"failed":false,"hidden":false,"name":"book-details","title":"Store details"}"><header class="card-head"><h3 id="card-book-details-title">Store details</h3><span class="card-state" data-card-state><!--mk:2--></span><span class="card-spacer"></span><!--mk:5--></header><div class="card-body"><div class="setting-field" data-mk="1" data-island="Setting" data-props="{}"><div class="setting-about"><div class="setting-label-row"><label class="setting-label" for="book-settings-name-input">Store name</label><span class="changed-mark">changed</span></div></div><div class="setting-control"><input class="mk-input" type="text" name="name" id="book-settings-name-input" value="Corner store"></div></div></div></section><div id="book-form-save-bar" class="save-bar" data-placement="inline" role="region" aria-label="Example form's save changes" data-mk="1" data-island="SaveBar" data-props="{"failed":false,"save_label":"Save"}"><div class="save-bar-inner"><p class="save-bar-message" data-save-bar-message tabindex="-1"><span data-mk="2">Saving changes the store straight away.</span><!--mk:3--></p><div class="save-bar-actions" data-mk="4"><a class="mk-btn" href="#component-settings-form" data-discard-all data-mk="5">Discard changes</a><button type="submit" class="mk-btn" data-variant="primary" data-save data-mk="6"><!--mk:7-->Save<!--/mk:7--></button></div><!--mk:8--></div></div></form></div>One card of settings, saved together: its title, what its unsaved changes or a refusal say, "Saved" after a save, and Discard for its changes. Its id is card-{name}: links to it (the save bar's, a toast's Show) use it.
| Prop | Type | About |
|---|---|---|
name | String | Required. The card's name: card-{name} is its id, and events name it. |
title | String | Required. |
children | Slot | Required. Its settings. |
extra_class | Option<String> | Another class beside settings-card. |
head | Option<Slot> | After the title: a chip, a line of meta. |
link | Option<Slot> | At the right of the head: where more about it is shown. |
badge | Option<Slot> | In the head while the card isn't refused ("Restart needed"). |
failed | bool | The last save refused this card: "Not saved", and message. Default: false. |
message | Option<String> | |
saved | Option<String> | The last save saved this card: "Saved" and when (or nothing). |
focus_saved | bool | The "Saved" mark takes focus once fixi swaps it in. Default: false. |
readonly | bool | Nothing on it can be changed here. Default: false. |
hidden | bool | Hidden now: shown_by shows it. Default: false. |
shown_by | Option<String> | name=value: shown only while that checkbox is ticked, its
controls off (not sent) while hidden. |
rest | Attrs | data-* attributes of the page's own. |
Rust
SettingsCard::new("book-saved", "Payments", "Its settings.")
.saved("12 Oct, 09:30")Solid
<SettingsCard name="book-saved" title="Payments" saved="12 Oct, 09:30">Its settings.</SettingsCard>HTML
<section id="card-book-saved" class="settings-card" data-name="book-saved" role="region" aria-labelledby="card-book-saved-title" data-mk="1" data-island="SettingsCard" data-props="{"failed":false,"hidden":false,"name":"book-saved","title":"Payments"}"><header class="card-head"><h3 id="card-book-saved-title">Payments</h3><span class="card-state" data-card-state><!--mk:2--></span><span class="card-spacer"></span><span class="card-meta card-saved" data-card-saved data-mk="4">Saved 12 Oct, 09:30</span><!--mk:5--></header><div class="card-body">Its settings.</div></section>The URL must use https.
Its settings.Rust
SettingsCard::new("book-refused", "Webhooks", "Its settings.")
.failed(true)
.message("The URL must use https.")Solid
<SettingsCard name="book-refused" title="Webhooks" failed message="The URL must use https.">Its settings.</SettingsCard>HTML
<section id="card-book-refused" class="settings-card is-failed" data-name="book-refused" role="region" aria-labelledby="card-book-refused-title" data-mk="1" data-island="SettingsCard" data-props="{"failed":true,"hidden":false,"name":"book-refused","title":"Webhooks"}"><header class="card-head"><h3 id="card-book-refused-title">Webhooks</h3><span class="card-state" data-card-state><!--mk:2--><span class="mk-status-text" data-tone="error" data-card-failed data-mk="3">Not saved</span></span><span class="card-spacer"></span><!--mk:5--></header><div class="card-body"><p class="error" role="alert" data-mk="6">The URL must use https.</p>Its settings.</div></section>Rust
SettingsCard::new("book-readonly", "Server", "Its settings.")
.readonly(true)Solid
<SettingsCard name="book-readonly" title="Server" readonly>Its settings.</SettingsCard>HTML
<section id="card-book-readonly" class="settings-card" data-name="book-readonly" role="region" aria-labelledby="card-book-readonly-title" data-mk="1" data-island="SettingsCard" data-props="{"failed":false,"hidden":false,"name":"book-readonly","title":"Server"}"><header class="card-head"><h3 id="card-book-readonly-title">Server</h3><span class="card-state" data-card-state><!--mk:2--></span><span class="card-spacer"></span><span class="card-meta">Can't be changed here</span></header><div class="card-body">Its settings.</div></section>One setting: its name and what it's for on the left, its control on the right (a phone, or a wide control such as radio cards, a list of choices, a text area or a table, puts the control under them), and "changed" while it differs from what's saved (a control's data-saved after a refused save, its default value otherwise).
| Prop | Type | About |
|---|---|---|
label | String | Required. |
id | String | Required. The id of the control the label is for; for a group, the label's own id, which names the group. |
children | Slot | Required. The control, and anything under it. |
group | bool | Several controls (radio buttons, checkboxes): a group named by its label. Default: false. |
chip | Option<Slot> | Beside the label: where its value comes from. |
help | Option<Slot> | What it's for, under the label. |
help_id | Option<String> | The help's id, for the control to point at. |
described_by | Option<String> | A group's description: its help's id. |
label_id | Option<String> | The label's own id, for a control that names itself by it. |
rest | Attrs | data-* attributes of the page's own, hidden. |
Blocks a payment waits for before it counts as paid.
Rust
Setting::new("Confirmations", "book-setting-confirmations", Input::new("confirmations")
.id("book-setting-confirmations")
.kind(InputType::Number)
.value("10")
.described_by("book-setting-confirmations-help"))
.help("Blocks a payment waits for before it counts as paid.")
.help_id("book-setting-confirmations-help")Solid
<Setting label="Confirmations" id="book-setting-confirmations" help={{
"$slot": [
"Blocks a payment waits for before it counts as paid."
]
}} help_id="book-setting-confirmations-help"><Input name="confirmations" id="book-setting-confirmations" kind="number" value="10" described_by="book-setting-confirmations-help" /></Setting>HTML
<div class="setting-field" data-mk="1" data-island="Setting" data-props="{}"><div class="setting-about"><div class="setting-label-row"><label class="setting-label" for="book-setting-confirmations">Confirmations</label><span class="changed-mark">changed</span></div><p class="field-help" id="book-setting-confirmations-help">Blocks a payment waits for before it counts as paid.</p></div><div class="setting-control"><input class="mk-input" type="number" name="confirmations" id="book-setting-confirmations" value="10" aria-describedby="book-setting-confirmations-help"></div></div>Connect with https. Most nodes on port 18081 don't use it.
Rust
Setting::new("Use TLS", "book-setting-tls", Checkbox::new()
.name("tls")
.id("book-setting-tls")
.described_by("book-setting-tls-help"))
.help("Connect with https. Most nodes on port 18081 don't use it.")
.help_id("book-setting-tls-help")Solid
<Setting label="Use TLS" id="book-setting-tls" help={{
"$slot": [
"Connect with https. Most nodes on port 18081 don't use it."
]
}} help_id="book-setting-tls-help"><Checkbox name="tls" id="book-setting-tls" described_by="book-setting-tls-help" /></Setting>HTML
<div class="setting-field" data-mk="1" data-island="Setting" data-props="{}"><div class="setting-about"><div class="setting-label-row"><label class="setting-label" for="book-setting-tls">Use TLS</label><span class="changed-mark">changed</span></div><p class="field-help" id="book-setting-tls-help">Connect with https. Most nodes on port 18081 don't use it.</p></div><div class="setting-control"><input type="checkbox" class="mk-checkbox" name="tls" id="book-setting-tls" value="on" aria-describedby="book-setting-tls-help"></div></div>What a payment may arrive as.
Rust
Setting::new("Accept", "book-setting-accept", "Its checkboxes.")
.group(true)
.help("What a payment may arrive as.")
.help_id("book-setting-accept-help")
.described_by("book-setting-accept-help")Solid
<Setting label="Accept" id="book-setting-accept" group help={{
"$slot": [
"What a payment may arrive as."
]
}} help_id="book-setting-accept-help" described_by="book-setting-accept-help">Its checkboxes.</Setting>HTML
<div class="setting-field" role="group" aria-labelledby="book-setting-accept" aria-describedby="book-setting-accept-help" data-mk="1" data-island="Setting" data-props="{}"><div class="setting-about"><div class="setting-label-row"><span class="setting-label" id="book-setting-accept">Accept</span><span class="changed-mark">changed</span></div><p class="field-help" id="book-setting-accept-help">What a payment may arrive as.</p></div><div class="setting-control">Its checkboxes.</div></div>The page's one save bar, along the bottom of the window: message, then Discard changes (a link to the page as saved) and Save. Without JavaScript it's always there; with it, only while something changed, a save was refused, or leaving is asked about, and it names the cards changed. Red after a save that refused something.
| Prop | Type | About |
|---|---|---|
message | Slot | Required. What saving does, or why the last save didn't. |
discard_href | String | Required. The page as saved. |
failed | bool | Default: false. |
focus | bool | The message takes focus once fixi swaps it in. Default: false. |
inline | bool | In the page's footer, not along the bottom of the window. Default: false. |
save_label | String | What the Save button says. Default: "Save". |
id | String | Its id: a page has one save bar, save-bar. Default: "save-bar". |
region_label | String | Its region's name. Default: "Save changes". |
Rust
SaveBar::new("Saving writes the changes on this tab.", "#component-save-bar")
.inline(true)
.id("book-save-bar")
.region_label("Example save bar")Solid
<SaveBar message={{
"$slot": [
"Saving writes the changes on this tab."
]
}} discard_href="#component-save-bar" inline id="book-save-bar" region_label="Example save bar" />HTML
<div id="book-save-bar" class="save-bar" data-placement="inline" role="region" aria-label="Example save bar" data-mk="1" data-island="SaveBar" data-props="{"failed":false,"save_label":"Save"}"><div class="save-bar-inner"><p class="save-bar-message" data-save-bar-message tabindex="-1"><span data-mk="2">Saving writes the changes on this tab.</span><!--mk:3--></p><div class="save-bar-actions" data-mk="4"><a class="mk-btn" href="#component-save-bar" data-discard-all data-mk="5">Discard changes</a><button type="submit" class="mk-btn" data-variant="primary" data-save data-mk="6"><!--mk:7-->Save<!--/mk:7--></button></div><!--mk:8--></div></div>Rust
SaveBar::new("Nothing saved. The URL must use https.", "#component-save-bar")
.failed(true)
.inline(true)
.id("book-save-bar-refused")
.region_label("Example refused save bar")Solid
<SaveBar message={{
"$slot": [
"Nothing saved. The URL must use https."
]
}} discard_href="#component-save-bar" failed inline id="book-save-bar-refused" region_label="Example refused save bar" />HTML
<div id="book-save-bar-refused" class="save-bar is-failed" data-placement="inline" role="region" aria-label="Example refused save bar" data-mk="1" data-island="SaveBar" data-props="{"failed":true,"save_label":"Save"}"><div class="save-bar-inner"><p class="save-bar-message" data-save-bar-message tabindex="-1"><span data-mk="2">Nothing saved. The URL must use https.</span><!--mk:3--></p><div class="save-bar-actions" data-mk="4"><a class="mk-btn" href="#component-save-bar" data-discard-all data-mk="5">Discard changes</a><button type="submit" class="mk-btn" data-variant="primary" data-save data-mk="6"><!--mk:7-->Save<!--/mk:7--></button></div><!--mk:8--></div></div>A small message after a save, in the corner of the window. Without JavaScript it fades by itself; with it, one saying something wasn't saved stays until it's closed.
| Prop | Type | About |
|---|---|---|
kind | ToastKind (success | warning | error | neutral) | Required. How a save went. |
title | String | Required. |
lines | Vec<String> | Required. |
show | Option<String> | The card to go to (its name): the one a refusal is about. |
persistent | bool | Stays until it's closed, whatever it says (a review copy). Default: false. |
Rust
Toast::new(ToastKind::Success, "Changes saved and applied", vec![
])
.persistent(true)Solid
<Toast kind="success" title="Changes saved and applied" lines={[]} persistent />HTML
<div class="toast toast-success" role="status" data-toast data-persistent data-mk="1" data-island="Toast" data-props="{"kind":"success"}"><span class="toast-icon" aria-hidden="true"><svg class="mk-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m4 12 5 5L20 6"></path></svg></span><div class="toast-text"><strong>Changes saved and applied</strong></div><!--mk:2--></div>Rust
Toast::new(ToastKind::Warning, "Saved", vec![
"Restart the engine to apply the new node.",
])
.persistent(true)Solid
<Toast kind="warning" title="Saved" lines={[
"Restart the engine to apply the new node."
]} persistent />HTML
<div class="toast toast-warning" role="status" data-toast data-persistent data-mk="1" data-island="Toast" data-props="{"kind":"warning"}"><span class="toast-icon" aria-hidden="true"><svg class="mk-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 3 10 18H2Z M12 9v5M12 17v1"></path></svg></span><div class="toast-text"><strong>Saved</strong><span class="toast-line">Restart the engine to apply the new node.</span></div><!--mk:2--></div>Rust
Toast::new(ToastKind::Error, "Nothing saved", vec![
"The URL must use https.",
])
.show("book-refused")
.persistent(true)Solid
<Toast kind="error" title="Nothing saved" lines={[
"The URL must use https."
]} show="book-refused" persistent />HTML
<div class="toast toast-error" role="alert" data-toast data-persistent data-mk="1" data-island="Toast" data-props="{"kind":"error"}"><span class="toast-icon" aria-hidden="true"><svg class="mk-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m12 3 10 18H2Z M12 9v5M12 17v1"></path></svg></span><div class="toast-text"><strong>Nothing saved</strong><span class="toast-line">The URL must use https.</span><a class="toast-show" href="#card-book-refused" data-show-card="book-refused">Show</a></div><!--mk:2--></div>Rust
Toast::new(ToastKind::Neutral, "Nothing changed", vec![
])
.persistent(true)Solid
<Toast kind="neutral" title="Nothing changed" lines={[]} persistent />HTML
<div class="toast toast-neutral" role="status" data-toast data-persistent data-mk="1" data-island="Toast" data-props="{"kind":"neutral"}"><span class="toast-icon" aria-hidden="true"><svg class="mk-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M12 11v6M12 7v1"></path></svg></span><div class="toast-text"><strong>Nothing changed</strong></div><!--mk:2--></div>The top of every page under the site's bar, always in this order: where the page sits (links to the pages above it, the nearest last, "Home › Logs"; one link alone is a back link, with an arrow), its title, then how far through a flow it is. The title is required, so no page goes without one. Beside the title: a status or a badge (aside), and at its right the page's own actions.
| Prop | Type | About |
|---|---|---|
title | Slot | Required. The page's h1. |
crumbs | Vec<Crumb> | One link of a [PageHeader]'s breadcrumbs. Default: . |
aside | Option<Slot> | Beside the title: a badge, a status, a help tip. |
actions | Option<Slot> | At the title's right: Reload, a network picker. |
progress | Option<Slot> | Under the title: a flow's progress (WizardSteps). |
title_id | Option<String> |
Rust
PageHeader::new("Home")Solid
<PageHeader title={{
"$slot": [
"Home"
]
}} />HTML
<header class="mk-page-header"><div class="mk-page-title"><h1>Home</h1></div></header>Rust
PageHeader::new("Admin settings")
.crumbs(vec![
Crumb { href: "#component-page-header".into(), label: "Home".into() },
])Solid
<PageHeader title={{
"$slot": [
"Admin settings"
]
}} crumbs={[
{
"href": "#component-page-header",
"label": "Home"
}
]} />HTML
<header class="mk-page-header"><nav class="mk-breadcrumbs" aria-label="Breadcrumb"><a href="#component-page-header" title="Home">Home</a></nav><div class="mk-page-title"><h1>Admin settings</h1></div></header>Rust
PageHeader::new("Order 4f2a")
.crumbs(vec![
Crumb { href: "#component-page-header".into(), label: "Corner store with a long name to see it shortened".into() },
Crumb { href: "#component-page-header".into(), label: "Orders".into() },
])
.aside(Tag::new("Paid")
.tone(TagTone::Ok))
.actions(LinkButton::new("Reload", "#component-page-header")
.size(ControlSize::Compact))Solid
<PageHeader title={{
"$slot": [
"Order 4f2a"
]
}} crumbs={[
{
"href": "#component-page-header",
"label": "Corner store with a long name to see it shortened"
},
{
"href": "#component-page-header",
"label": "Orders"
}
]} aside={{
"$slot": [
{
"$component": "Tag",
"props": {
"children": {
"$slot": [
"Paid"
]
},
"tone": "ok"
}
}
]
}} actions={{
"$slot": [
{
"$component": "LinkButton",
"props": {
"block": false,
"children": {
"$slot": [
"Reload"
]
},
"href": "#component-page-header",
"size": "compact"
}
}
]
}} />HTML
<header class="mk-page-header"><nav class="mk-breadcrumbs" aria-label="Breadcrumb"><a href="#component-page-header" title="Corner store with a long name to see it shortened">Corner store with a long name to see it shortened</a><a href="#component-page-header" title="Orders">Orders</a></nav><div class="mk-page-title"><h1>Order 4f2a</h1><span class="tag tag-ok">Paid</span><div class="mk-page-actions"><a class="mk-btn" href="#component-page-header" data-size="compact">Reload</a></div></div></header>Rust
PageHeader::new("Set up a store")
.progress(WizardSteps::new("Setup progress", vec![
WizardStep { label: "Store".into(), detail: "In progress".into(), done: 0, tasks: 1, state: WizardState::Current },
WizardStep { label: "Wallet".into(), detail: "Next".into(), done: 0, tasks: 1, state: WizardState::Next },
WizardStep { label: "Done".into(), detail: "Next".into(), done: 0, tasks: 1, state: WizardState::Next },
]))Solid
<PageHeader title={{
"$slot": [
"Set up a store"
]
}} progress={{
"$slot": [
{
"$component": "WizardSteps",
"props": {
"label": "Setup progress",
"steps": [
{
"detail": "In progress",
"done": 0,
"label": "Store",
"state": "current",
"tasks": 1
},
{
"detail": "Next",
"done": 0,
"label": "Wallet",
"state": "next",
"tasks": 1
},
{
"detail": "Next",
"done": 0,
"label": "Done",
"state": "next",
"tasks": 1
}
]
}
}
]
}} />HTML
<header class="mk-page-header"><div class="mk-page-title"><h1>Set up a store</h1></div><ol class="mk-wizard-steps" data-design="strip" aria-label="Setup progress"><li aria-current="step"><span>Store<small>In progress</small></span><progress class="mk-wizard-progress" value="0" max="1" aria-label="Store: 0 of 1 tasks complete"></progress></li><li><span>Wallet<small>Next</small></span><progress class="mk-wizard-progress" value="0" max="1" aria-label="Wallet: 0 of 1 tasks complete"></progress></li><li><span>Done<small>Next</small></span><progress class="mk-wizard-progress" value="0" max="1" aria-label="Done: 0 of 1 tasks complete"></progress></li></ol></header>The theme switch: System, Light and Dark, each a submit button, so it works without JavaScript; the thumb slides to the one hovered.
| Prop | Type | About |
|---|---|---|
theme | ThemeChoice (system | light | dark) | Required. A colour theme. |
action | String | Required. Where the choice is posted. |
next | String | Required. The page to come back to. |
Rust
ThemeToggle::new(ThemeChoice::System, "#component-theme-toggle", "/")Solid
<ThemeToggle theme="system" action="#component-theme-toggle" next="/" />HTML
<form method="post" action="#component-theme-toggle" class="nav-theme-form"><input type="hidden" name="next" value="/"><div class="theme-toggle theme-toggle-system" role="group" aria-label="Theme"><span class="theme-toggle-track"><button type="submit" name="theme" value="system" class="theme-toggle-option theme-toggle-option-system" aria-label="System theme" aria-pressed="true" title="System theme"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="18" height="13" rx="2"></rect><line x1="12" y1="16" x2="12" y2="21"></line><line x1="8" y1="21" x2="16" y2="21"></line></svg></button><button type="submit" name="theme" value="light" class="theme-toggle-option theme-toggle-option-light" aria-label="Light theme" aria-pressed="false" title="Light theme"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="4"></circle><line x1="12" y1="2" x2="12" y2="4"></line><line x1="12" y1="20" x2="12" y2="22"></line><line x1="4.2" y1="4.2" x2="5.6" y2="5.6"></line><line x1="18.4" y1="18.4" x2="19.8" y2="19.8"></line><line x1="2" y1="12" x2="4" y2="12"></line><line x1="20" y1="12" x2="22" y2="12"></line><line x1="4.2" y1="19.8" x2="5.6" y2="18.4"></line><line x1="18.4" y1="5.6" x2="19.8" y2="4.2"></line></svg></button><button type="submit" name="theme" value="dark" class="theme-toggle-option theme-toggle-option-dark" aria-label="Dark theme" aria-pressed="false" title="Dark theme"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none" aria-hidden="true"><path d="M21 12.5A9 9 0 1 1 11.5 3 7 7 0 0 0 21 12.5Z"></path></svg></button><span class="theme-toggle-thumb"></span></span></div></form>A glowing dot linking to the status page (in the site's bar and the POS's alike), as the instance was when the page was rendered, so it is right without JavaScript. Once its island attaches it polls poll every 30 seconds while the tab is in sight. Its title and accessible name carry the words.
| Prop | Type | About |
|---|---|---|
health | HealthState (ok | slow | error | unknown) | Required. How the instance is, as the status dot shows it. |
href | String | Default: "/status". |
poll | String | Answers {"state": "ok" | "slow" | ...}. Default: "/status/summary". |
id | String | Its id: a page has one, status-indicator. Default: "status-indicator". |
Rust
StatusDot::new(HealthState::Ok)
.href("#component-status-dot")
.poll("")
.id("book-status-ok")Solid
<StatusDot health="ok" href="#component-status-dot" poll="" id="book-status-ok" />HTML
<a href="#component-status-dot" class="nav-status-link" id="book-status-ok" title="all systems healthy" aria-label="Status: all systems healthy" data-mk="1" data-island="StatusDot" data-props="{"health":"ok","poll":""}"><span class="status-dot status-dot-ok" data-mk="2"></span></a>Rust
StatusDot::new(HealthState::Slow)
.href("#component-status-dot")
.poll("")
.id("book-status-slow")Solid
<StatusDot health="slow" href="#component-status-dot" poll="" id="book-status-slow" />HTML
<a href="#component-status-dot" class="nav-status-link" id="book-status-slow" title="scanning is slow - see the status page" aria-label="Status: scanning is slow - see the status page" data-mk="1" data-island="StatusDot" data-props="{"health":"slow","poll":""}"><span class="status-dot status-dot-slow" data-mk="2"></span></a>Rust
StatusDot::new(HealthState::Error)
.href("#component-status-dot")
.poll("")
.id("book-status-error")Solid
<StatusDot health="error" href="#component-status-dot" poll="" id="book-status-error" />HTML
<a href="#component-status-dot" class="nav-status-link" id="book-status-error" title="an issue was detected - see the status page" aria-label="Status: an issue was detected - see the status page" data-mk="1" data-island="StatusDot" data-props="{"health":"error","poll":""}"><span class="status-dot status-dot-error" data-mk="2"></span></a>