Console components and styles
Start with the closest page in Console Storybook. The
controller serves native HTML, CSS, and browser modules.
Use the shared DOM helpers for behavior
and console.css for appearance.
Keep resource names, permissions, requests, and recovery in the page that owns them.
Write new browser modules in strict, erasable TypeScript.
For example, edit dom.mts and import it as ./dom.mjs. The controller serves
JavaScript at that URL. Run pnpm typecheck to check the browser code.
Compose a page
- Structure. Let the shell
supply the page title, Namespace, and navigation. Use a
.sectionwith anh2to group related content. Add a.cardwhen the group needs its own surface. Desktop page content scrolls as one document. The sidebar stays in the viewport, with the account control at its bottom. Diagnostics scroll within the sidebar; short viewports can scroll the sidebar itself. The phone drawer retains its own viewport. - Collections. Agents and Secrets put search and the primary action in a
.collection-toolbar. Give the search input an accessible name. It comes first in markup and in tab order, even when the action wraps below it. Use.resource-idfor an identifier people may need to read or copy. - Actions. Use
button(label, action, { tone: "primary" })for the main action, a plain button for a secondary action, and a link for a destination. - Forms and dialogs.
field({ label, control, help })connects a label and help to the input. In a native modaldialog, use.dialogand put dismissal first in.dialog-actions. Thebutton()helper defaults totype="button". To submit, use atype="submit"button associated with the form and handle the form’ssubmitevent so Enter and clicking take the same path. - Feedback.
statusBlock(tone, label, body)usesinfo,success,warning, ordanger. Say what happened and what the person can do next. Before reporting success or granting access that depends on a write, verify that the response identifies the intended resource and confirms the expected saved state. If the write may have committed but you cannot confirm it, say the outcome is unknown and read that same resource again. Do not repeat the write to find out. - Disabled controls. Show the reason next to a disabled control. For a button,
checkbox, select or text field, call
setUnavailable(control, unavailable, reason)wherever its state or reason changes. While the reason shows, the control stays in the Tab order (aria-disabled), is described by the reason, and ignores clicks, Enter, Space and edits. With no reason it is natively disabled. For other controls, link the reason withdescribeDisabled(control, reason).
Extend the shared styles
Use the named roles in :root: --color-link for links, --color-accent for
the primary action, and --focus-ring for keyboard focus. Inherit the body
font, pair type sizes with their line heights (for example, --text-md with
--text-md--line-height), and use the spacing and radius scales.
The token checks
cover these scales and the supported breakpoints.
Navigation pairs the wordmark with a mascot for dark surfaces. The white contour is part of that artwork and dims with the header under a modal. On a phone the wordmark scales from 14 to 18 px, leaving room for both 44 px controls. Navigation logos fit within 48 × 48 px and keep their aspect ratio, including operator-supplied logos and forced-colour artwork. Sign-in uses the original artwork at 40 × 40 px.
Keep a one-page layout with that page. When another page needs the same pattern, move the shared rule next to the component it extends and use it in both places. Register new assets in the controller's allowlist. Storybook prepares its preview with the same list and asset reader, then simulates API responses for each story.
Review the result
Rebuild the affected stories, then try the task at desktop, tablet, and phone widths. Follow the keyboard through the action and back to its result. Include the state where the next action changes, such as a refused save or a search with no matches. Use the Console browser tests to load the page through the controller. Check that new assets load and the interaction works with the actual API. Follow the visual review instructions for screenshots and video. Record the interaction with pr-walkthrough so reviewers can read the starting state, follow the action, and see the result.
Use responseTo to wait for a response
to the exact request method and URL. Start listening before triggering the action,
then assert the status separately. An unexpected 500 fails the assertion immediately
instead of making the test time out while waiting for a 403 or 503.
