Skip to the page
A Rough Idea Design system
Get started

Over the page

What floats over the page: popovers, menus, tooltips, dialogs, drawers and toasts. Each sits in the browser's top layer, so opening it moves nothing beneath, and each has the one shadow v2 allows.

Each section is built from the component's spec in mcp/components.mjs, the same spec the design system MCP gives agents.

Popover

A small panel tied to a button: about this, a filter, a picker. It opens over the page, so nothing moves.

Each bar is one possible next token. Its length and its number are the model's probability for it.

<button class="ds-button" type="button" popovertarget="ex-popover">About this view</button>
<div class="ds-popover" id="ex-popover" popover>
  <p>Each bar is one possible next token. Its length and its number are the model's probability for it.</p>
</div>

Rules

  • The native popover attribute, opened by a button with popovertarget: a click outside or Escape closes it, and the browser tells screen readers that the button's popover is open.
  • It sits in the top layer, so opening it moves nothing.
  • Where anchor positioning works it opens under its button; elsewhere, in the middle of the window.
  • Short: a few lines, a short list or a small form. A long explanation or a decision is a dialog.
  • It carries the one shadow, for things that float; panels have none.

Variants and states

States: closed and open.

Accessibility

Element<div class="ds-popover" popover> and the <button popovertarget> that opens it
KeysEnter Space Escape
NotesThe browser gives the button its expanded state; focus stays on the button until you move into the popover.

Anatomy

The whole<div class="ds-popover" id="…" popover>
Opener<button popovertarget="…">, just before it
Contenta few lines, a short list or a small form

Right after the button that opens it.

Roles it uses

Background--ds-surface
Border--ds-rule
Radius--ds-radius-panel
Shadow--ds-shadow-float
Padding--ds-space-4

Related: Dialog, Button and Field. Used in Top bar, forms and Figure.

Tooltip

A short name or value, on hover and on keyboard focus: an icon button's name, a point on a chart.

Full screen Back to temperature 1 and the whole vocabulary.
<span class="ds-tooltip-wrap">
  <button class="ds-icon-button" type="button" aria-labelledby="ex-tip-full"><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">…</svg></button>
  <span class="ds-tooltip" id="ex-tip-full" role="tooltip" popover="manual">Full screen</span>
</span>
<span class="ds-tooltip-wrap">
  <button class="ds-button" type="button" aria-describedby="ex-tip-reset">Reset</button>
  <span class="ds-tooltip" id="ex-tip-reset" role="tooltip" popover="manual">Back to temperature 1 and the whole vocabulary.</span>
</span>

Rules

  • Load /v2/tooltip.js: it shows on hover and on keyboard focus, stays while the pointer moves onto it, and hides on Escape (WCAG 1.4.13).
  • Its text is always in the page, named by the control's aria-labelledby (the tooltip is its name) or aria-describedby (it adds to the name), so a screen reader has it whether it shows or not.
  • Never the title attribute, which a keyboard never sees.
  • A few words: a name or a value. Anything longer, or anything to act on, is a popover.
  • Nothing hovers on a phone: an icon button there still needs an icon people know.
  • It opens in the top layer, above its control and inside the window, so it moves nothing.
  • Corners signal function: this is something you read, so it stays square (--ds-radius-read).

Variants and states

Namearia-labelledby on the control: the tooltip is its name, for an icon button.
Descriptionaria-describedby on the control: the tooltip adds to the name it already has.

States: hidden and shown.

Accessibility

Element<span class="ds-tooltip" role="tooltip" popover="manual">, after its control in a .ds-tooltip-wrap
Needsaria-labelledby or aria-describedby on the control, naming the tooltip
KeysTab Escape

Anatomy

The whole<span class="ds-tooltip-wrap">
Controlthe button or element it is about, first
Tooltip<span class="ds-tooltip" id="…" role="tooltip" popover="manual">

On icon buttons in the top bar's tools; on a chart's points.

Roles it uses

Background--ds-surface
Border--ds-rule
Shadow--ds-shadow-float
Font size--ds-type-label
Padding--ds-space-1

Related: Popover and Icon button. Used in Top bar and Figure.

Dialog

A window over the page: information, an editor, or a confirmation before something that cannot be undone.

Clear this run?

Its 12 answers and their probabilities are deleted. This cannot be undone.

<button class="ds-button" type="button" data-ds-opens="ex-dialog">Clear the run</button>
<dialog class="ds-dialog" id="ex-dialog" role="alertdialog" aria-labelledby="ex-dialog-title" aria-describedby="ex-dialog-text">
  <h2 id="ex-dialog-title">Clear this run?</h2>
  <p id="ex-dialog-text">Its 12 answers and their probabilities are deleted. This cannot be undone.</p>
  <form class="ds-dialog-actions" method="dialog">
    <button class="ds-button ds-button--primary" value="clear">Clear the run</button>
    <button class="ds-button" value="keep" autofocus>Keep it</button>
  </form>
</dialog>

Rules

  • The native <dialog> opened with showModal(): focus moves in and stays, Escape closes it, and focus returns to what opened it. Load /v2/dialog.js and give the opening button data-ds-opens="<the dialog's id>", or call showModal() yourself.
  • A <form method="dialog"> closes it: the value of the button pressed says which, with no script.
  • A confirmation is role="alertdialog": its title asks the question and its text names what will happen ("Its 12 answers are deleted").
  • The primary action first, named for what it does (Clear the run, not OK); focus starts on the safe choice (autofocus).
  • Never the browser's own alert() or confirm().
  • It sits in the top layer, so opening it moves nothing.

Variants and states

Default<dialog class="ds-dialog" aria-labelledby="…">: information or an editor.
Confirmrole="alertdialog" with aria-describedby: a confirmation that names what will happen; focus starts on the safe choice.

States: closed and open.

Accessibility

Element<dialog class="ds-dialog"> opened with showModal()
Needsaria-labelledby naming its title; role="alertdialog" and aria-describedby naming its text, for a confirmation
KeysTab Escape
NotesshowModal() makes the rest of the page inert while it is open, and returns focus when it closes.

Anatomy

The whole<dialog class="ds-dialog" id="…" aria-labelledby="…">
Title<h2 id="…">: a question, for a confirmation
Text<p id="…">: what will happen
Actions<form class="ds-dialog-actions" method="dialog"> holding the buttons, the primary first

Opened by a button with data-ds-opens; the dialog itself may sit anywhere in the page.

Roles it uses

Background--ds-surface
Border--ds-rule
Radius--ds-radius-panel
Shadow--ds-shadow-float
Scrim--ds-scrim
Width--ds-measure
Padding--ds-space-6
Title--ds-type-h3

Related: Popover, Button, Field and Notice. Used in pages.

Drawer

A panel at the window's edge, over the page: a phone's navigation, settings, an inspector.

Settings

1
40
<button class="ds-button" type="button" data-ds-opens="ex-drawer">Settings</button>
<dialog class="ds-drawer" id="ex-drawer" aria-labelledby="ex-drawer-title" closedby="any">
  <div class="ds-drawer-head">
    <h2 id="ex-drawer-title">Settings</h2>
    <form method="dialog"><button class="ds-button ds-button--ghost">Close</button></form>
  </div>
  <div class="ds-slider">
    <label class="ds-label" for="ex-drawer-temp">Temperature</label>
    <output for="ex-drawer-temp">1</output>
    <input id="ex-drawer-temp" type="range" min="0" max="2" step="0.1" value="1">
  </div>
  <div class="ds-slider">
    <label class="ds-label" for="ex-drawer-topk">Top K</label>
    <output for="ex-drawer-topk">40</output>
    <input id="ex-drawer-topk" type="range" min="1" max="100" step="1" value="40" aria-valuetext="40 tokens">
  </div>
</dialog>

Rules

  • A native <dialog> opened with showModal(), as a dialog is: focus moves in and stays, Escape closes it and focus returns. Load /v2/dialog.js and open it from a button with data-ds-opens.
  • closedby="any" closes it on a click outside, where the browser knows the attribute; Escape and its Close button work everywhere.
  • Its head holds its title and a Close button (a word; v2 has no close icon) in a <form method="dialog">.
  • An inspector that stays open on a wide screen is a column of the page's layout, not a drawer; the drawer is for when there is no room for one.
  • It sits in the top layer, so opening it moves nothing.
  • Corners signal function: this is something you read, so it stays square (--ds-radius-read).

Variants and states

Endclass="ds-drawer": at the right edge; settings, an inspector.
Startclass="ds-drawer ds-drawer--start": at the left edge; a phone's navigation.

States: closed and open.

Accessibility

Element<dialog class="ds-drawer"> opened with showModal()
Needsaria-labelledby naming its title
KeysTab Escape
NotesshowModal() makes the page behind it inert and returns focus when it closes.

Anatomy

The whole<dialog class="ds-drawer" id="…" aria-labelledby="…" closedby="any">
Head<div class="ds-drawer-head">: the <h2> title and a <form method="dialog"> with Close
Bodyfields, sliders, a list of links

Opened by a button with data-ds-opens, often in the top bar's tools.

Roles it uses

Background--ds-surface
Border--ds-rule
Shadow--ds-shadow-float
Scrim--ds-scrim
Padding--ds-space-5
Title--ds-type-h3

Related: Dialog, Popover, Slider, Field and Button. Used in pages.

Toast

Word that something finished elsewhere on the page: saved, added, failed. A notice that floats at the window's foot.

<button class="ds-button" type="button" data-ds-toast="Your prompt is kept for this workshop." data-ds-toast-kind="ok" data-ds-toast-word="Saved">Save the prompt</button>

Rules

  • Load /v2/toast.js: it makes the page's .ds-toasts live region when the page loads, so the first toast is read; dsToast(text, { kind, word }) adds one, and so does a button with data-ds-toast.
  • A toast is a notice: its first word says the kind (Saved, Added, Error), and the color only repeats it.
  • It stays six seconds, and longer while the pointer or focus is on it; an error stays until it is dismissed. Each has a Dismiss button.
  • Only for word from elsewhere on the page. What happens here, where someone is looking, is a notice in its own place.
  • Never the only record of an error: say it where it happened too, in a field's note or a notice.
  • Fixed at the window's foot, so it moves nothing.

Variants and states

NotedsToast(text): news, neither good nor bad.
OkdsToast(text, { kind: 'ok', word: 'Saved' }): done.
WarndsToast(text, { kind: 'warn' }): done, with a catch.
BaddsToast(text, { kind: 'bad' }): it failed; role="alert", and it stays until dismissed.

States: shown, paused and gone.

Accessibility

Element<div class="ds-toasts" aria-live="polite"> holding notices; an error is role="alert"
Needsthe live region in the page before the first toast (toast.js makes it)
KeysTab Enter
NotesIt pauses while the pointer or focus is on it (WCAG 2.2.1).

Anatomy

The whole<div class="ds-toasts" aria-live="polite">, made by toast.js
Toast<div class="ds-notice ds-notice--ok">, its kind word first
Dismiss<button class="ds-button ds-button--ghost">Dismiss</button>

At the window's foot; newer toasts below older ones.

Roles it uses

Background--ds-surface
Border--ds-rule
Shadow--ds-shadow-float
Layer--ds-layer-float

Related: Notice and Copy button. Used in pages.