UI

Pages and interface

Build a page around the work, choose its presentation, and keep its controls reachable by keyboard, pointer and touch.

Start with the page's content and widgets, then choose a theme and Layout. Extending explains the contracts for reusable packages; How it works explains the record behind the interface.

In this topic

Parts of a page

The agent writes an HTML document containing the page's title, description, content, and layout. When the server delivers it, Leaf adds the page identity, visual theme, browser JavaScript, and canonical URL. The browser runtime supplies the commenting and collaboration interface. Authors build the content with these tools, starting with ordinary HTML:

  1. Ordinary HTML. Leaf styles headings, paragraphs, tables, code, and details without requiring CSS classes. These elements provide most of a page's content.
  2. Theme classes. Leaf supplies classes for common presentation patterns, including .eyebrow, .lede, .callout, aside.sidenote for a margin note, and ol.steps for a numbered sequence like this list.
  3. Widgets. A widget is a complete page feature, authored as a custom HTML element, such as <lf-options> for choices and <lf-board> for cards in columns. Other lf-* elements provide structure, such as <lf-pane> for a pane. The registry declares each element’s attributes and behavior using JSON Schema. The browser, validator, and agent instructions read those same declarations.
  4. Page-specific presentation. A local <style> and inline SVG can define arrangements and drawings specific to the page. They use the theme's shared color and type tokens to stay consistent with the rest of Leaf. The Layout tab's drawings use this approach.

Leaf elements load their JavaScript modules when the page uses them. Behavior specific to one page can use inline module scripts or local modules under page/. Reusable behavior belongs in a package, a directory containing element declarations and their supporting files. The page-behavior reference defines module and dependency rules.

Widgets

These examples are interactive parts of this page. Their choices, card moves, and comments share its event log, Undo, and On me. Each activity feed shows the same page history, including actions in other examples.

Example page: a release review

Choose a rollout audience, then one follow-up job. Open Show activity to inspect both selections. Choose Undo in the page’s leaf menu to withdraw the job choice, then press Activity changed in the feed. The rollout audience remains selected.

Start with canary clients; expand after the error rate settles.

recommendedCanary clients Start with a small group and compare its error rate before expanding. All clients Enable the release for everyone once the checks pass. Hold the release Gather more evidence before enabling it for any clients.

Which job should start first?

Prepare the rollback flag Add a concurrency test Set up the error-rate comparison

Recorded actions

Options with explanations render as cards; options containing only labels render as rows. The content determines the presentation. A row can link to a section that explains the choice. Clicking an option immediately shows the selection and records it for the agent. A later revision preserves that choice. The group also offers an empty option for an answer the author did not list.

Move “Flaky auth test” to “This week” by dragging its grip. With a keyboard, focus the grip, press Enter to grab, Right to move, and Enter to drop. On a narrow touch screen, use the card’s Move to buttons. Open Show activity to inspect the recorded move. Choose Undo in the page’s leaf menu to return the card to Incoming; press Activity changed in the feed to see the move marked undone.

Flaky auth test Fails one run in twenty on CI only. Dark-mode contrast pass Rate-limit the export Tokenize the palette

Recorded actions

In a live Leaf, cards drag between columns by a grip, and each drop reaches the agent as a structured action.

The widget catalog also includes diagrams, charts, diffs with collapsible files, annotated code, editable drafts, playgrounds, and recorded browser traces. The examples gallery opens complete pages using them.

Styles and overrides

The user chooses the visual direction. A theme supplies shared colors, type and reusable styles; widget packages draw their own controls; the author arranges the page and makes local adjustments.

flowchart TD
  accTitle: A page captures its selected theme
  accDescr: The author selects Editorial from the user's themes. Leaf captures that theme in the release-review page, and the export carries the same theme. Changing the user's default later changes neither existing copy.
  Themes["User's themes<br/>Editorial · Dense"] -->|select Editorial| Page["Release review<br/>Captured: Editorial"]
  Page -->|export| Export["Exported review<br/>Same Editorial theme"]
A page keeps the theme it was made with. leaf theme list shows the user’s named themes. Changing the default later leaves this page and its export intact.
Choosing a theme and honoring browser preferences

leaf theme list exposes the user's named themes and their author instructions. page init --theme NAME selects one; a new page otherwise uses the user's default. Re-initializing keeps the captured choice unless explicitly changed. The author follows the user's request first, then the task and the theme's instructions.

The default theme supports light and dark schemes. Authors preserve supported platform preferences when composing a page; an explicit visual comparison may need a fixed appearance, declared with color-scheme. CSS precedence determines which rule wins, not who has authority over the work.

Make a local adjustment: colors, width or controls
To change Write
Leaf's colors :root { --accent: teal; }
A Layout's width :root { --col: 46rem; }
A widget lf-card { padding: 8px; }
Controls Leaf or a widget builds <style data-controls>lf-board .lf-grip { color: teal; }</style>

A Layout reads tokens such as --col, --sidebar and --page-pad-start, and any other rule changes it too, such as main { padding-top: 28px; }.

A bare button or input rule styles the page’s own controls. To reach controls Leaf or a widget builds, a page marks its stylesheet data-controls; a package puts those rules in controls.css. Widget packages already style their own controls through widgets.css. Leaf’s panel and thread cards also inherit theme inputs such as --accent and --sans from :root.

In this topic

Layouts

Leaf supplies page layouts and a layout for blocks within a page. A column, sidebar, or workspace sets the page’s shape; tiles arrange a block’s children and can be used within any of them. The table and drawings below show the same classes in the same order.

Class Arrangement
layout-columnPage layout Text in a centred 720px column; figures, boards and tables may extend past it. Suits plans, reviews and explanations, including this page.
layout-sidebarPage layout A body beside a narrower side column, with the header across both, as wide as the window up to 1600px. A board and its status, a release dashboard, or a review with its verdict beside the diff.
layout-workspacePage layout Task regions kept together: a queue beside its selected item, or controls beside a preview. In a large window, the workspace fills the height and its panes scroll separately.
layout-tilesBlock layout Equal cells, as many to a row as fit. A row of metrics or status tiles.
Prose in the reading column, with a wide block growing past it wide block
A column pagelayout-column on main. Text keeps the 720px column, and a block with a declared width, such as a diagram, extends past it.
A sidebar page: a body beside its side column, each block starting at its region's left edge main.layout-sidebar board aside
A sidebar pagelayout-sidebar on main widens the page and sets a body beside its side column. Every block starts at its region's left edge, and text retains its reading width.
A workspace fills the window, and each pane scrolls separately queue detail
A workspacelayout-workspace keeps task regions together. In a large window it fills the height, and its panes scroll separately.
A tile block within a page: equal cells wrap into rows Ready · 24 Review · 8 Blocked · 6 Done · 3
A tile blocklayout-tiles arranges children in equal cells, wrapping as the available width changes. The surrounding page keeps its own layout.

Leaf's Layout classes are ordinary CSS, applied beneath the page's own stylesheet in the cascade. A page can adjust them with CSS rules, for example to change a column's share of the width or a gap. It can also use its own grid when the supplied layouts do not fit.

The reading column

On a layout-column page, headings, paragraphs, lists, and code occupy a centred column up to 720px wide. On a sidebar page too, the theme limits paragraphs, list items, and headings to that reading width. A workspace applies neither the reading width nor the limits below: every block fills its pane.

A diagram, board, or wide table can extend into the margins beyond the reading column. The text above and below it keeps its width and position. These width settings control the block:

  • column is the text measure. In a wider region, such as a sidebar page's body, a column block starts at the same left edge as the text.
  • wide starts at the edge the text starts from (the left, in a left-to-right page) and extends toward its end, up to about 910px, enough for 100 columns of code. Wide blocks share this width so they align with one another.
  • available uses the window's width after subtracting the outer page margins and space for margin notes and controls.

The registry, which declares each element's behavior, also gives it a default width. For example, lf-options uses column, lf-diff uses wide, and lf-board uses available. A data-width attribute asks for wide or available on any block, overriding the default. If the margins have unequal space, an available block extends farther into the wider margin. A table given one of these widths uses its columns' natural width, up to that limit, rather than stretching across the extra space.

data-width on a figure extends it past the reading column main figure data-width
data-width on a blockThe block extends past the column, and the paragraphs around it keep their width.

A log, feed, or long listing with data-bound scrolls within a limited height and opens at its newest entry. Its growing content therefore does not make the whole page longer.

Margins

A sufficiently wide window places these controls and notes beside the reading column:

  • The rail, on the right, where Leaf places a marker beside each commented or decided passage. It needs a window about 960px wide, or about 1010px on a touch screen.
  • A sidebar, the page's first aside.sidebar, on the left. It holds a table of contents, a legend or reference facts, and stays in view at the window's top as the page scrolls. It needs about 1130px.
  • Sidenotes, each an aside.sidenote, on the right, level with the block they annotate. They need about 1150px.

The column shifts sideways to make room for a sidebar or sidenotes. In a narrow window, those notes appear within the column at their original positions in the HTML.

Workspace

layout-workspace fits the page to the window above Leaf's panel. A single-row header holds the title and status tags. The body takes the remaining height and contains an lf-pane, a widget that arranges its own panes, or a grid of panes defined by the page's CSS. Each pane scrolls its content beneath its own header. A pane's footer stays visible, keeping its actions within reach.

An lf-tabs list="side" in the workspace body displays a list of tabs as a queue, such as tickets or findings. The selected item's content appears beside that list.

The workspace fills the window's height when the window is at least 720px wide and 480px tall. In a smaller window, panes expand to fit their content and the whole page scrolls.

A workspace fills the window and its panes scroll queue detail footer
A large windowThe workspace fills the window. Each pane scrolls under its header, and a footer's actions stay in view.
In a small window, a workspace's panes take their natural height and the page scrolls queue detail
A small windowBelow 720px wide or 480px tall, the panes take their natural height and the page scrolls.
<main class="layout-workspace density-working">
  <header><h1>Payments alerts</h1></header>
  <div id="regions">
    <lf-pane label="Queue">…</lf-pane>
    <lf-pane label="Alert">…</lf-pane>
  </div>
</main>

<style>
  #regions { display: grid; grid-template-columns: 1fr 2fr; }
  @media (width < 720px) { #regions { grid-template-columns: 1fr; } }
</style>

density-working reduces type size and spacing for a compact working surface. It is independent of the Layout: a workspace can use ordinary reading sizes, and a pane or table on another page can use working density.

Examples: Notification playground, Merge a worktree, Alert review.

Tiles

layout-tiles arranges an element's children in equal cells at least 9rem wide, placing as many cells in each row as fit. Cells in the same row have equal heights. A page can set its own columns when it needs a particular grouping, such as four tiles in two rows of two. A row of headline numbers can use a layout-tiles block of native dl elements with labels in dt and values in dd.

Tabs and parts

An lf-tabs at the top of a page creates tabs like the ones on this page. Their tab strip stays at the window's top while the page scrolls, and one tab's content is visible at a time. All tabs share the page's width, Layout, threads, and version history.

When different parts of the work need different layouts, lf-parts holds an lf-part for each. Each part contains its own layout container; main itself carries no Layout. For example, one part can hold an interactive workspace and another a proposal read in a column. Parts share the page’s history, threads, and On me list. The page-layout reference gives the markup.

Panel, rail and thread cards

Leaf adds controls around the authored HTML. This interface is called chrome: the Leaf panel floating at the window's foot, comment and decision markers, and thread cards. At rest the panel is one row: the leaf, coloured by the agent's state; the comment field, which shows the agent's status while it is empty; and the On me mark, counting what waits on the user, with a dot while a thread holds a reply they haven't read. The leaf opens a small menu of the page's other controls. Pressing the On me mark opens its view above the row. A ✕ replaces the mark while the view is open and closes it when pressed. A folded panel shows only the leaf in the window's corner.

The On view shows one list at a time, under a tab named for its actor, with completed items under Done, and a Threads tab listing the open threads, then the resolved ones under Resolved. On me and the agent’s list defines their membership. Opening a view preserves the page’s text wrapping and reading position.

The page reserves space at the bottom for the panel's row, so the last lines can scroll above it. A view opens above the row and overlays the page without moving its content. Escape or a press on the page closes the view. Phones and touch screens describes the panel on a touch screen.

The rail is the strip to the right of main where Leaf places comment and decision markers. It appears when the window has enough space: about 960px on a column page or about 1920px on a sidebar page. data-rail="right" keeps it visible above 640px, except on a workspace, whose panes scroll on their own. Where no rail fits, and inside a scrolling pane, a small pin appears beside the target on the page. Leaf places it clear of text and controls when space permits. The o shortcut hides pins and passage highlights.

A thread card opens beside its marker in the margin. If the margin is narrower than the card, it overlaps the text's edge. Threads, a thread opened from it, the On view and All pages open as views above the panel's row, one at a time.

Leaf's panel floats at the window's foot, and the rail sits in the margin beside the column Rail Comment field
Panel and railThe panel's row floats over the foot of the window, where the page leaves room for it. On a column page, the rail sits in the margin beside the column and takes no width from it.
Threads, the On view and All pages open as a view above the panel's row Threads, On or All pages Comment field
A view above the rowThreads, the On view and All pages open one at a time above the panel's row. The page keeps its width underneath.
A thread card opens in the rail beside its marker Thread card Comment field
A thread cardIt opens in the margin and overlaps the edge of the text only where the margin is too narrow. Nothing below it moves.

Narrow windows

In a narrow window, page regions stack vertically in HTML order. Margin notes move into the reading column, a side column appears above or below its body, and workspace panes expand to fit their content. Tiles can still fit two per row on a 390px phone.

A sidebar page's regions, numbered in source order, stack into one column on a phone 1 2 3 4 1 2 3 4
A side column on a phoneThe side column stacks under the body, so region 3 follows region 2.

Phones and touch screens

Touch users can read, comment and operate widgets. Leaf’s page commands have direct gestures or visible controls; authored widgets supply their own routes. Narrow windows describes how page regions stack.

The panel and its menu

The panel fits a narrow window with space at its edges. Its row holds the leaf, a comment field showing agent status, and the On me count beside its icon. A temporary notice replaces the status words.

The leaf’s menu includes Search page and Page Map under Navigate, Undo, and Comment on an element, Draw mode, Design mode and Hide annotations under Annotations. Decisions appear in On me and in their widgets. An active mode or search names itself in the panel and provides a ✕ exit. Search arrows appear beside the match count; tapping the marked match selects it.

Comments, threads and widgets

Select words to quote them in the panel’s field; tap the field to open a comment below the block. For a whole table, figure or other element, use Comment on an element.

Where the margin cannot hold a rail, markers appear as pins on their blocks. Tapping a pin highlights its passage and opens its thread card. Threads opens above the panel’s row. Hide annotations hides the pins.

On a touch screen, a board at most 640px wide shows a Move to row on each card, reaching offscreen columns without dragging.

Touch coverage and testing

  • Whole elements. Open the leaf’s menu → Annotations → Comment on an element, then tap the target. This opens its comment composer without activating the target’s controls.
  • Commands. Menu commands and direct controls provide touch routes; the panel and reply fields suppress keyboard hints for a coarse pointer.
  • Exits. The panel’s ✕ leaves an active mode, search or target picker. Within the picker it first removes a typed hint prefix.
  • Hover detail. Authored hover explanations need a touch-accessible alternative; keyboard and hover behavior alone do not establish a touch route.
  • Layout. Page-authored controls and diagrams still need review at narrow widths for target size, legibility and reachable content.
  • Device coverage. Automated touch journeys use browser emulation; they do not establish real-device behavior.