Skip to the page
TACTILE UI

Kit part · Buttons · Loaders

Commit key

A key for work that takes time and must not run twice. It latches below the plate while the work runs, an LED matrix in its face scanning amber, and says how it ended.

Element
<tui-commit> + <button>
Needs
kit.css + kit.mjs
Rules
commit.rules.md · 6
Finishes
Aluminium · Graphite · Porcelain · Black
Volumes
Tactile · Quiet
On this page
  1. Variants
  2. Every state, at once
  3. API
  4. Markup
  5. Accessibility
  6. How it’s built
  7. Rules
  8. Related parts
01

Variants

Contrast · the surface’s main action
Plain · 36px
Small · in a table row
Guarded · a cover to lift
Busy · latched, scanning
Failed · says what, and why
02

Every state, at once

Both finishes, side by side. Hover, pressed and focus are forced with .is-hover, .is-down and .is-focus; checked and disabled are real.

Aluminium

PartIdleHoverPressedBusyDoneFailedFocusCover lifted
Contrast
—
Plain
—
Guarded
—

Graphite

PartIdleHoverPressedBusyDoneFailedFocusCover lifted
Contrast
—
Plain
—
Guarded
—
03

API

NameKindValuesWhat it does
<tui-commit>elementwraps one kit keyBuilds the cut, the matrix, the legends and a polite live region around the <button> it is given.
<button class="tui-key">elementany size, --primary, --dangerThe key, kept real. Its text is the idle legend.
data-busy · data-done · data-failedattribute"Working…" · "Done" · "Failed"The legend for each state. All four share one cell, so the key never changes width.
data-stateattributeidle · busy · done · failedWhere it is. Written by the part; set it in markup only to show a state.
data-restattribute2400 (ms)How long done stays before the key goes idle; failed stays twice as long.
data-holdattribute—Done and failed stay until the next press or reset().
data-guardattributems, 6000A hinged cover over the cut: the first press lifts it, the second commits; it drops after this long unused.
data-whyattributetextThe reason a run failed, printed under the key. Written from the rejection’s message.
data-coverattribute"closed" · "open"The guard’s cover. Written by the part.
commiteventdetail.wait(promise)Fired on a press. Hand wait() the work’s promise and the key runs on it; hand it nothing and it was an ordinary press.
.run(work)methodpromise or functionRun on work directly (skips the guard). Resolves with its value; rejects with its error once the key has said what failed. A second call while busy returns the same promise.
.reset() · .state · .keyproperty—Back to idle now; read the state; reach the button.
Enter · Spacekeyboard—Native: press the key. While busy nothing happens and focus stays.
EscapekeyboardguardedDrops a lifted cover.
.is-hover · .is-down · .is-focusclasskey · key · hostForced states, for documentation and specimens only.
04

Markup

<tui-commit data-busy="Deploying…" data-done="Deployed" data-failed="Deploy failed">
  <button class="tui-key tui-key--primary tui-key--lg" type="button">Deploy to production</button>
</tui-commit>

<tui-commit data-guard data-busy="Deleting…" data-done="Deleted" data-failed="Delete failed">
  <button class="tui-key tui-key--lg tui-key--danger" type="button">Delete workspace</button>
</tui-commit>
import '/kit/kit.mjs';   // defines <tui-commit>

const deploy = document.querySelector('tui-commit');

// Hand it the real work: busy lasts exactly as long as the promise.
deploy.addEventListener('commit', (e) => e.detail.wait(api.deploy('main')));

// Or run it yourself. A rejection's message is what the key prints.
try {
  await deploy.run(() => api.deploy('main'));   // "Deployed"
} catch (err) {
  // The key already says "Deploy failed — " + err.message
}

Needs kit.css and kit.mjs. How to install the kit.

05

Accessibility

  • It stays a native <button>. While busy it takes aria-busy="true" and aria-disabled="true" (not disabled) so focus stays on it and a screen reader hears that it is working.
  • A polite live region says each state in words: “Deploying…”, “Deployed”, “Payment failed — card declined”. The button’s own name is always the legend showing.
  • The matrix is decoration (aria-hidden); the words carry the state, and each state has its own lamp colour as well as its own pattern: scan, tick, cross.
  • The guard is two ordinary presses, so it works with a keyboard, a switch and a screen reader; the key describes the cover, the live region says when it has lifted, Escape drops it.
  • Reduced motion stops the scan, the spring and the cover’s swing: the matrix lights steady amber while busy, and every state still shows.
06

How it’s built

The key is the kit key, unchanged, standing in its own cut: a recess 3px bigger than the cap whose lower lip sits where the skirt meets the plate. Busy moves the cap its skirt plus 2px (past a normal press, under the lip) and deepens the shading from the cut’s upper wall. The matrix is sixteen dots behind a small dark window in the cap, each with a lit layer whose opacity alone animates, delayed by its diagonal, so the amber wave runs corner to corner; done and failed light fixed patterns in green and red. The four legends share one grid cell, so the widest sets the key and a swap only fades. Done springs the cap back up 1px past rest; failed pops it straight back. The guard is a smoked cover on a hinge barrel, turned about its top edge with a 3D rotateX.

07

Rules

commit.rules.mdIn the brain

Busy lasts exactly as long as the promise you hand it. Deletions get a cover to lift first.

6 rules in this file, with the foundations they rest on and the anti-patterns that break them. Full rules ship in the brain with the Pass.

08

Key

Every button, and every link that acts like one: a cap on a skirt that drops below the plate when pressed.

Lamp

The only colour in the kit: a lamp that is on. Green is on, amber is waiting, red is recording or failing, blue is linked, white is standing by. Always next to words.

Progress

Progress through a task: ink filling a groove, never a coloured bar.

Docs
Finish
Volume