Skip to content

Jump to…

↑ ↓ to move · Enter to open · Esc to close

Components / Foundations

UI foundations

Actions, identity and feedback using existing theme tokens. Try the states, then expand the markup to reuse each pattern.

Design tokens

A compact reference for type, colour, geometry and interface icons.

Type scale

text-3xl

Page heading

text-xl

Section heading

text-sm

Operational copy stays compact and readable.

font-mono

RUN-081 · 08:42 UTC

Semantic colour

  • --dd-canvasPage background
  • --dd-surfaceWorking surface
  • --dd-inkPrimary text and action
  • --dd-accentFocus and emphasis

Spacing & geometry

Use the Tailwind spacing scale, a 44px practical control target and --dd-radius for restrained rounding. Borders separate routine regions; reserve shadows for overlays.

Icon contract

Use 20px outline icons with a consistent stroke. Decorative icons stay hidden from assistive technology; icon-only controls require an accessible name and 44px target.

Buttons

One primary action, secondary controls and explicit disabled or busy states.

Ready. Saving simulates a one-second request.

Deploy is unavailable until the project has an environment.

Markup & reuse: buttons

dd-button provides the 44px target; add dd-primary for emphasis. Keep icon-only controls named. A toggle retains its label and changes aria-pressed. Set disabled and aria-busy during a request, announce the result outside the button. The opt-in createButtonLoading helper runs an async task with an abort signal, blocks repeat clicks and restores the button after success or failure. Reset cancels this simulated request; navigation and toggle buttons never enter loading automatically.

<button type="button" class="dd-button dd-primary">Save</button>
<button type="button" class="dd-button" disabled>Deploy</button>
<button type="button" class="dd-button"
  aria-label="Pin example" aria-pressed="false">…</button>
<p role="status"></p>
<button id="save" type="button" class="dd-button dd-primary"
  data-loading-label="Saving…">
  <span data-button-spinner hidden aria-hidden="true"
    class="h-4 w-4 shrink-0 rounded-full border-2 border-current border-r-transparent motion-safe:animate-spin"></span>
  <span data-button-label>Save</span>
</button>
<p id="save-status" role="status"></p>

import { createButtonLoading } from './src/theme/ts/button-loading';
const button = document.querySelector('#save');
const status = document.querySelector('#save-status');
const loading = createButtonLoading(button);
button.addEventListener('click', async () => {
  if (loading.busy) return;
  status.textContent = 'Saving…';
  try {
    // Replace persistChanges with your own async operation.
    const completed = await loading.run(signal => persistChanges(signal));
    if (completed) status.textContent = 'Saved.';
  } catch {
    status.textContent = 'Could not save. Please try again.';
  }
});
// loading.reset() aborts the signal and restores the button.
// Respect that signal in your task; cancellation cannot undo saved work.

Avatars & status

A name remains readable when the image or color is unavailable.

  • Jamie Davis

    Workspace owner · Online

    Active
  • Alex Chen

    Invitation sent

    Pending
ConnectedNeeds reviewNot configured
Markup & reuse: identity

Use an empty image alternative when the adjacent name identifies the person. Initials are a manually supplied fallback, not an automatic error handler. dd-status adds a decorative dot; the text conveys the state. The photo presence indicator sits in the bottom-right corner with a surface-colored separating ring. Always pair it with readable presence text; presence and account access are separate states.

<span class="relative shrink-0">
  <img src="./images/jamie-davis.jpg" alt=""
    width="44" height="44" class="h-11 w-11 rounded-md object-cover">
  <span aria-hidden="true" class="absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 rounded-full border-2 border-surface bg-success"></span>
</span>
<span>Jamie Davis</span>
<span>Online</span>
<!-- For away or offline use bg-warning or bg-muted AND update the text. -->

Feedback

Inline feedback stays next to the action. Critical changes should never disappear on a timer.

Review required

A maintainer must approve this change before it can run.

Markup & reuse: feedback

Use a polite status for completion and an alert for urgent errors. Keep live regions in the DOM before updating their text. Static notices need no live-region role. This example has no toast queue or notification service.

<p role="status" class="text-sm text-success"></p>
<p role="alert" class="text-sm text-warning"></p>

Data states

Known progress, indeterminate loading and an empty result with a recovery action.

60%

Fixed specimen; no files are created.

Progress specimen selected.

Markup & reuse: data states

Label native progress with completed and total work. Set aria-busy on the loading region, hide decorative skeletons from assistive technology and place announcements outside that busy region. Skeletons are static and need no animation.

<label for="export-progress">Preparing export: 3 of 5 files</label>
<progress id="export-progress" value="3" max="5">60%</progress>
<section aria-busy="true" aria-label="Recent runs">
  <p>Loading recent runs…</p>
</section>
<p role="status"></p>