| version | beta | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| name | Better Harness Studio | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description | Visual design contract for Studio, interactive reports, and other Better Harness product surfaces. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| colors |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| themes |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| surface-ramp |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| typography |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| weights |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| rounded |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| elevation |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| spacing |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sizing |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| motion |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| components |
|
Better Harness Studio is a technical evidence workbench. It should feel like a calm, precise control room: the current decision and next action are obvious, while traces, checkpoints, costs, and runtime metadata remain available without competing for attention.
This file is the visual source of truth for packages/harness-studio and for
interactive Better Harness reports that do not have a narrower approved design
contract. Product semantics and information architecture remain owned by the
relevant spec and implementation. This contract governs hierarchy, typography,
color, density, component appearance, interaction states, and visual review.
The beta token set replaces the alpha palette, type scale, and shape scale.
The earlier values were structurally correct but rendered as an unfinished
wireframe: near-identical dark grays separated only by solid mid-gray rules,
2px radii on every control, and a single 32px display size above an otherwise
12–14px page. The roles did not change; their values did. packages/harness-studio
implements the beta set. Other surfaces — notably the standalone Harness
Inspector report under scripts/harness-inspector/ui/ — still carry alpha
values and are migration targets, not evidence of alignment.
Use the structure of the classic, docked VS Code workbench as the reference: an application title bar, a primary sidebar, one main editor/workspace, an optional secondary sidebar or bottom panel, and a status bar. These are edge-to-edge regions separated by 1px rules or resize sashes, not cards placed on a page canvas.
This is a structural reference, not a request to copy VS Code branding or every current experiment. VS Code's source now also contains optional floating-panel and shadow treatments. Better Harness deliberately follows the docked, no-shadow branch: fixed work regions stay flat; elevation is reserved for transient UI that actually floats above them.
Primary references:
- VS Code user interface
- VS Code UX Guidelines: containers and items
- VS Code theme color roles
- VS Code workbench layout source
- VS Code accessibility and keyboard navigation
- Prefer restrained, technical, and legible over decorative, playful, or dashboard-like.
- Prefer panes, rows, tabs, toolbars, and editor views over cards. A card is an exception for an independent object that must move, compare, or stand alone; it is never the default content wrapper.
- Let evidence carry the visual interest. Chrome and containers should stay neutral so status, diffs, and comparison lanes remain meaningful.
- Use ordinary product language. Avoid invented scientific language, excessive all-caps labels, and decorative jargon.
- Use Phosphor icons already owned by Studio. Do not use emoji, text glyphs, or improvised SVGs as interface icons.
- The default appearance is the dark technical-control-room theme. A supported light theme maps the same semantic roles and remains available from a labelled title-bar control. Theme choice is local presentation state, not server or Session evidence.
- The visual style is minimal and grid-led: an ordered surface ramp, alpha hairlines, a blue interaction role, and semantic evidence colors. It may borrow the discipline of Swiss minimalism, but it must not turn Studio into a landing page, card dashboard, or decorative terminal pastiche.
- Restraint is not the same as coarseness. Flat, square, and neutral describe the composition, not the craft: a surface step must be visible, a hairline must read as a hairline rather than a drawn box, a control must look deliberately shaped, and the type scale must have a usable middle. A surface that looks unfinished fails this contract as surely as a decorated one.
- Use the system UI stack in both themes. Generated recommendations for Web fonts such as IBM Plex Sans or JetBrains Mono are references only; do not load them unless the files are deliberately bundled and cross-platform tested.
- Do not add glow, gradients, glass surfaces, or scroll-reveal choreography. Hover, pressed, disclosure, pane, and loading transitions must explain state and use the shared motion roles.
- Measure dark and light contrast independently. A token name that passed in one theme is not proof that its mapped value passes in the other.
Every surface must answer one primary question:
- Bench: what is held constant, what changes, and is the comparison ready?
- Live trial: what is happening now, what needs attention, and where is the active evidence?
- Evidence results: what is the verdict, is the evidence sufficient, and what trade-off produced it?
Structure each surface in this order:
- Page context and one sentence describing the decision.
- The primary state or task, with at most one visually dominant action.
- Supporting evidence, controls, and metadata.
Show a surface switcher once per viewport. Do not repeat Bench / Live trial / Evidence results navigation in both the application shell and the page body. Do not repeat the same run status in a banner, row, sidebar, and footer unless each occurrence enables a different action.
Use progressive disclosure for runtime detail. The central work area is primary; execution trees, checkpoint lists, and state inspectors are secondary panes that may collapse or become drawers. An empty or unavailable secondary pane must not occupy more attention than the active task.
- Use the documented system UI stack across macOS, Windows, and Linux.
Interis not part of the stack unless the font files are deliberately bundled and tested on every supported platform. - Use monospace only for code, hashes, identifiers, paths, timestamps where alignment matters, and numeric trace data. Product copy and navigation stay in the UI font.
- Do not render meaningful text below
metadata(12/16). Dense mode reduces spacing before it reduces type size. - The scale has a usable middle.
subhead(15/22) carries view titles and pane headings that are more than a label but less than a section; reach fordisplayonly when a surface genuinely leads with one statement, and never to compensate for a page that is otherwise all 12px. - Headings tighten as they grow, using the documented
letterSpacingper role, so large type reads as one shape rather than spaced letters. Body, label, and metadata roles track at zero;pane-titleis the one role that opens up, because it is set small and semibold. - Weight carries hierarchy before size does. Body copy is regular; a
labelis medium; a selected navigation item, pane title, or section heading is semibold. Reserve bold for the verdict, one primary action, or a genuinely exceptional state; do not make everystrong, button, and navigation item bold, and do not use semibold as the default weight for ordinary rows. - Use sentence case. Uppercase is allowed only for short eyebrows or compact machine-state badges, never for ordinary section titles or paragraphs. An eyebrow must earn its line: do not stack an eyebrow, a title, and a trailing caption on a header that describes a six-row list.
- Do not use a global
!importantrule to force one size onto paragraphs, labels, buttons, code, andstrongelements. Each semantic role owns its documented type token.
- Blue is the interaction color: primary actions, selected navigation, links, and keyboard focus.
- Green, amber, and red are semantic state colors for success, caution/waiting, and failure. Pair every colored state with text or an icon; color is never the only signal.
- Violet identifies the Candidate comparison lane. It is not a second primary action color.
- The
categoricalscale identifies members of a data dimension that carries no judgement, such as tool family or chart lane. It is a fixed ordered scale: a surface maps its taxonomy onto it and does not invent hues. A categorical color must never equal an interaction or state token, must stay perceptually offset from the state hues, and is always redundant with a lane, label, or legend. Do not read success, caution, or failure into a categorical color. - Use neutral borders and surface shifts for structure. Do not assign a new hue merely to distinguish another panel or hierarchy level. Two lanes that carry no verdict — a left and right Session, an A and B column — are positions, not evidence roles: give them the same neutral surface and let the labels and the column split distinguish them. Reaching for the interaction blue and the Candidate violet to mean "left" and "right" states a judgement the data does not support.
- Structure is carried by the surface ramp first and a hairline second. Borders are alpha over their surface, not a fixed gray: one divider token then reads correctly on the canvas, on a panel, and on a selected row. Do not give every nested region its own solid rule — if two regions already differ by a ramp step, they usually do not also need a border.
- Text and controls must meet WCAG 2.2 AA contrast against their actual surface.
Muted text is supporting content, not a way to hide essential information.
Measure
text-subtleagainst the busiest surface it lands on — a hovered or selected row — not against the canvas, and re-measure both themes whenever a neutral moves.
- Use the spacing scale. Related items are separated by
smormd; component padding usesmdorlg; major regions usexlor more. - Docked regions meet edge to edge. Separate the title bar, sidebars, workspace, panels, rows, and sections with a background shift, a 1px divider, or a resize sash; do not place gutters around them to make them look like floating cards.
- Docked panes, tables, list regions, and editor groups use
rounded.none. Controls usexs(5px) orsm(6px).md(10px) andlg(12px) are reserved for floating dialogs, menus, quick picks, notifications, or exceptional standalone objects. The control radius is a deliberate shape, not a hairline chamfer: a 1–2px radius reads as an unstyled default and is below this scale. - Depth is a two-step system and both steps belong to transient surfaces.
elevation.popoverlifts a menu, quick pick, or notification;elevation.overlaylifts a modal dialog above a dimmed workbench.elevation.dockedisnone, and shadows stay forbidden on docked panes, rows, buttons, tabs, tables, empty states, and ordinary content groups. A shadow must disappear with the surface that earned it. - A required first-run workspace chooser may use one centered floating dialog above the dimmed workbench. It has one primary action, keeps the underlying shell inert, cannot be dismissed into an unusable empty application, and replaces itself with stable discovery progress until the workspace opens.
fullradius is limited to a numeric count or circular target. Status text, evidence roles, filters, and navigation do not become pills by default.- Compact desktop text controls are 32px high and toolbar targets are at least 30px square. Pane headers are 36px, dense data rows 30px, navigation rows 46px, and the title bar 44px. At narrow or touch-oriented layouts, targets are at least 44px.
- Focus is drawn outside the control, at
outlineOffset: 1px, so it stays visible on a filled primary button and on a row whose own edge is a hairline. An inset focus ring that disappears into a filled control does not satisfy this contract.
- Wide mode is above 1080px, compact mode is 760–1080px, and narrow mode is below 760px. These modes follow the existing Studio layout boundaries and may be revised only with browser evidence at all three widths.
- Wide workbenches may use three regions, but the central evidence surface must retain at least half of the usable width. Side regions must collapse before central content becomes unreadable.
- Prefer resizable docked panes with independently scrolling content. Keep the active pane title and toolbar visible; do not make the whole page a tall stack of repeated session containers.
- Never allow a paragraph to collapse into one-word or character-wide columns. Define minimum content widths, wrap at phrase boundaries, or make the bounded data region scroll horizontally.
- Use comfortable density for setup, summaries, and empty states. Dense rows are reserved for traces, call trees, diffs, and data tables; they still honor the typography floor and target-size rules.
- Avoid fixed viewport-height layouts when they strand large empty regions or hide the decision below the fold. Prefer local scrolling only for panes whose headers and context remain visible.
- The product rail owns top-level tools. The primary sidebar lists objects in the active tool. Tabs own open views of those objects in the workspace. These levels must not duplicate one another.
- A segmented control is only for a small, mutually exclusive property switch; it is not top-level navigation and should not sit inside a pill-shaped shell.
- Selection uses a filled or soft-blue state plus an
aria-currentor selected semantic. Availability uses a labelled status, not a colored dot alone. - Date scope uses a compact calendar grid with weekday alignment, a visible
month and time zone, and one active date. Follow meeting-calendar conventions:
keep date cells numeric, mark activity with a subtle dot, and show explicit
session and commit counts for the active day below the grid. Do not compress
counts into unexplained abbreviations such as
2sor9c. - In Date mode, keep the calendar at the top of the sidebar and use the remaining sidebar space for flat Session navigator rows from the selected day. A row locates its Session in the workspace; do not duplicate the calendar or turn the workspace into a second schedule view.
- One primary action per task region. Secondary actions use neutral styling; destructive actions use the danger role and require clear copy.
- Put workspace-wide actions in the title bar, view-wide actions in the pane toolbar, and item actions on the row or in its context menu. Do not repeat one command at all three levels.
- Show no more than three view-toolbar actions and two inline row actions. Put less frequent commands in an overflow or context menu and keep their labels and enablement consistent everywhere.
- Disabled controls explain the prerequisite near the control. If an entire control group is unavailable, show the prerequisite once instead of a wide banner plus multiple disabled buttons.
- Icon-only controls require an accessible name and a visible tooltip on hover or focus when the icon is not universally understood.
- A pane has one compact title bar: one view name, optional count or state, and its scoped actions. It does not also need a card title, eyebrow, subtitle, badge, timestamp, and repeated object type.
- A list row has one primary label, at most one short description, and one trailing metadata/state area. Put shared dates or categories in group headers instead of repeating them as a heading inside every row.
- A session list should read as rows in a sidebar or table. Selecting a session reveals its detail in the workspace; the detail view owns the full prompt, activity, and commit panes. Do not expand a miniature three-column dashboard inside every session row.
- In a Session row, put the provider and observed start time before the title; they establish source and chronology before prose. In Session Detail, keep the top bar to product identity, one-line title, view tabs, and Close. Put runtime, model, duration, turns, calls, edits, and token availability in the right-hand facts pane instead of repeating them under the title.
- Inspector panes use definition-list alignment for stable facts and expandable sections for verbose payloads. Long identifiers use copy affordances and middle truncation; prose should wrap normally.
- Harness Inspector does not maintain a global “Selected evidence” state or evidence Drawer. Scope navigation, Open session, local disclosure, and chart inspection own their actions directly; clicking passive labels or rows must not create a second hidden selection model.
- Harness Inspector is a read-only evidence viewer, not a session-resumption surface. Do not generate or expose a continuation packet from Session Detail.
- Empty states name what is missing, why it matters, and the single next action. They should not look like completed results. A missing input reports the flag or command that supplies it; a list of rows that all read "Not supplied" with no remedy is an inventory, not an empty state.
- An item with nothing retained collapses to its title row. Rendering its lanes as three empty sections repeats a full dashboard for every absent scope, buries the items that do carry evidence, and reads as a completed result.
- A boundary claim — what a pause does and does not stop, what the viewer may not assume — is stated once, in the pane that owns it. Do not restate it in a tree footer, a timeline footer, and an inspector note; a claim repeated in three chrome slots is decoration, and it is what forces a sentence into a narrow column where it wraps two words at a time.
- Lead Evidence results with a decision summary: verdict, evidence sufficiency, quality delta, and cost guardrail. Raw aggregate and trial tables are the supporting layer.
- Keep labels left-aligned and numeric columns right-aligned with tabular numerals. Freeze the header in locally scrolling tables when rows exceed the visible region.
- A numeric column is marked, never inferred from its position. A rule such as
"every cell after the first is right-aligned" pushes text columns to the wrong
edge and leaves headers floating away from their values. Mark the cell and its
header with the shared
numeric-cellrole so both align together. - Bound a comparison's value columns. Two
1frcolumns spread a three-digit count across half the viewport and stop reading as a pair. - Reference, Baseline, and Candidate are evidence roles, not generic container colors. Use role labels in addition to blue/violet accents.
- Truncation must preserve the distinguishing suffix or offer the full value on focus/hover. Never let status copy or summary prose break into vertical words.
- Single click selects a row and updates the adjacent preview/detail pane. It must not also expand the row, run a command, or navigate away.
Enteror an explicit Open command opens the selected object as a durable workspace tab. Double-click may be an accelerator for the same command, but it is never the only way to open something.- A disclosure chevron only expands or collapses its own children. Its hit area and accessible name are separate from row selection and from Open.
Escapecloses the topmost transient surface or clears a temporary mode; it must not discard persisted filters, evidence, or edits without confirmation.
- Every command available by pointer is reachable by keyboard. Use natural Tab order between workbench parts; use arrow keys within tab lists, toolbars, trees, and listboxes so each composite contributes one Tab stop.
- In lists and trees, Up/Down moves the active row, Left/Right collapses or
expands hierarchy when present,
Enteropens, and Space toggles a checkbox or explicit selection control. Do not overload Space on ordinary navigation rows. - Every interactive element has a visible
:focus-visibletreatment using the focus token. Focus, selection, hover, active, disabled, and unavailable are distinct states and cannot be expressed by color alone.
- Each user action has one command definition: stable id, verb-first label, handler, visibility condition, enablement condition, and optional shortcut. Toolbars, row actions, context menus, and a future command palette invoke the same command rather than implementing parallel behavior.
- Hide actions that are irrelevant to the current object. Disable an action only when seeing it teaches a useful prerequisite, and explain that prerequisite.
- Hover may reveal secondary row actions only if focus reveals the same actions. Essential state and the primary next action remain visible without hover.
- Resize sashes show a hover/focus affordance and remain keyboard operable. Persist user-adjusted pane sizes only after the layout is stable across wide, compact, and narrow modes.
- Keep layout transitions at 160ms or less. Respect
prefers-reduced-motionby removing non-essential movement and smooth scrolling. - Announce asynchronous run, pause, error, and verdict changes through the appropriate live-region semantics; visual color changes alone are not enough.
- Project tokens must be exposed as shared CSS custom properties before adding new visual variants. Surface-specific aliases may reference the shared roles; they must not fork a second palette or type scale.
- A standalone report may carry a literal copy of the palette so it opens
offline. When that report is embedded in Studio — including inside a shadow
root — the host owns the theme: keep only the declarations that already
resolve through
var()and let the literals inherit. An embedded pane that renders its own light palette inside a dark shell is a defect, not a variant. - Keep component CSS out of
index.htmlas the system is migrated. Split tokens, shell primitives, and feature styles into owned files or modules so visual rules have an inspectable source. - Do not add one-off hex colors, font sizes, weights, radii, or shadows when an existing token expresses the role. Add or revise a token here only when a new semantic role is genuinely required.
- Loading, empty, error, partial, running, paused, completed, and unavailable states must be visually and textually distinct without inventing product semantics absent from runtime evidence.
- Review wide (1440×900), compact (1024×768), and narrow (390×844) layouts. At each width, confirm the primary question and action are visible, the page has no document-level horizontal overflow, and bounded tables/diffs remain usable.
- Check keyboard order, focus visibility, landmark and heading order, accessible control names, state announcements, 200% zoom/reflow, and reduced motion.
- For visual changes, use Playwright against the built preview, inspect browser console and page errors, and save screenshots of every changed surface in meaningful non-loading states. For Studio that means Bench, Live trial, and Evidence results; for an interactive report it means each view a reader can reach without leaving the page.
Do
- Make the verdict, active run, or setup decision the first thing users see.
- Use a restrained neutral canvas and reserve color for action, state, and evidence identity.
- Build the shell from docked regions and the content from rows or editor views.
- Make selection, opening, disclosure, and commands visibly distinct.
- Let users collapse secondary evidence while preserving the current scope.
- Prefer fewer, stronger labels and larger readable type over dense decoration.
- Separate regions with a ramp step first and a hairline second.
- Give an absent input its remedy — the flag, the command, or the control that supplies it — on the same row that reports it missing.
- Let an embedded surface inherit the active theme instead of shipping its own copy of the palette.
Do not
- Duplicate navigation or status merely to fill a header.
- Use a card grid as the default information architecture, or give every object its own rounded title block and embedded dashboard.
- Make the whole row, its chevron, and its Open action perform the same or overlapping behavior.
- Render meaningful text below the
metadatafloor, use broad!importantreadability overrides, or use an unbundled font name. - Give every nested region a border, radius, shadow, badge, and uppercase label.
- Present a plain data dump as a decision screen or a decorative dashboard as evidence.
- Open a workbench with a landing-page hero: a display headline and one button filling a region, above rows set at a third of its size.
- Lead a surface with a slogan.
Observe → Promote,Evidence before defaults, andLocal control planestate a position, not the state of this workspace. - Use the interaction blue or the Candidate violet as a container color for two lanes the data does not rank.
- Leave a docked region's remaining height as bare canvas below its last row.