A dashboard is a saved document

Canvases

A canvas is a named collection of visuals over one or more datasets, laid out on a grid and saved as a document. Opening /a/:id re-renders every card against the files as they are today, so a canvas is a question rather than a snapshot.

What a card can be

Anything a chart page can draw can be a card: a table, a bar, pie, line, scatter, heatmap, stacked, combo, treemap or histogram, a map, or a word cloud. A card added to a filtered or parameterised canvas is drawn through that same state, so it lands on screen already narrowed like its neighbours. A card can also be your own words: a title, an explanation or a label.

Layout

A 12-column grid

Every card has an x, y, w and h on a grid twelve columns wide. A card spans at most six rows, and x + w - 1 cannot exceed twelve. Positions are stored as given — a gap is a gap — and two cards may not share a cell.

Layout

Drag, or use the keyboard

Drag a card by its header to any free cell, or focus the header and use the arrow keys, with Shift going to the edge. Each move is announced: the column and row the card landed in, and any card the move pushed down. The position is saved as you go.

Layout

Sizes without a drag

A card's Size menu sets full, half, a third or a quarter of the width, and one, two, three or six rows high. The size the card currently is sits on the card itself, beside the menu, so a width left by the corner grip reads without opening anything.

Adding

Add current view

The toolbar appends another table, chart or map without leaving the canvas, and a status region announces the card and the new count. When a card's script has drawn, the region says so — and if D3 or Leaflet could not be loaded, it names the missing library instead of failing silently.

Titles and explanations

Add text in the toolbar puts a card of plain text on the canvas. It reads no data, so a filter or a time range leaves its words alone, and it moves and resizes like any other card.

Text

How it looks

Each card has its own font (the page's, Arial, Georgia, Times New Roman, Verdana, Trebuchet MS or Courier New), a size from 8 to 96 points, an alignment, bold and italic, a text colour and a background. The fonts are ones browsers already have, so a canvas loads nothing extra for them.

Text

A heading, not a box

A card can hide its frame and header until it is pointed at or has keyboard focus, so a title reads as a heading. On a printed or shared canvas the header stays hidden.

Text

Rows that fit

A new card starts full width. A row holding only one-row text cards is as tall as its text, rather than the usual 15rem. Beside a chart, the card is as tall as the chart, and a text card two rows high or more keeps full rows, so the Size menu is how to ask for a taller one.

Text

Editing and removing

Double-click the text, or use Edit text in its header. The card shows each change as it is made, and Cancel or Escape puts it back. Remove card is in the same panel. Undo and redo cover the canvas's filters, not its cards, so removing one cannot be undone.

Text is plain text: HTML tags and Markdown show as the characters typed. A colour left unset follows the light or dark theme. A chosen colour stays the same in both, so give it a background it can be read against. Text cards have no chart or data to save, so they carry no download buttons; they appear in the printed canvas. They are also the only cards that can be removed.

A card picks its own measure

A card that shows an aggregated value chooses the function in its own panel, from the vocabulary the pivot uses and with the same names: Sum, Average, Min, Max, Count distinct, Median, Std dev, First, Last, Range, or a percentile with its level. That is what lets one card show a median where the chart page it came from could only sum. A card's derived title says which function it used — median(amount) by region — while a plain sum keeps the title it has always had.

Cross-filtering

Cards share the page. Clicking a mark cross-filters the canvas to the value behind it, and the card that was clicked is marked as the source rather than narrowed by its own click. The remaining cards re-render against the narrowed rows, each through its own filters, the page's cross-filter and its own options — which is why a word cloud card narrowed to one region shows the words of that region.

The source is drawn as the source, not as a card that failed to narrow, so a reader can always see which mark the page is answering. A page-wide relative time range narrows every card, the source included, because it is the page's range rather than a card's click.

Drill-through

A canvas can carry drill hierarchies: ?dd=<column>&dl=<level> opens a card at a deeper level of the hierarchy, with the cards around it still showing the level the reader is looking at. A visit that filters the data — a cross-filter or a drill path — leaves the arrangement alone: the card you put in row four stays in row four.

Parameters

A visual's fields can be bound to named parameters, overridden per visit with ?p.<name>=<value>. A bookmark that carries p.region=North is one URL, and the same canvas answers the same question for a different reader by changing that one value.

Per-card cross-row plans

The page's cross-row plan applies to every card unless a card brings one of its own. That is what lets one card show a running total while the cards beside it show raw rows, and a card with an empty plan ({ "cs": [] }) runs no cross-row pass at all — which is how a card opts out of the page's.

The plan is checked against the canvas's columns on every render, not only when it is saved, so a stored plan that no longer names a column is that card's error, while a bad page-level plan is the page's 400.

Bookmarks and undo

Bookmarks record the canvas's state, including parameters, and undo and redo step through them. Because the state lives in the URL as well as in the document, a bookmark is a link that can be sent; because the range inside it is relative, the link still means the same thing when it is opened next month.

Sharing and embedding

/a/:id
The canvas itself, with cross-filter, drill, parameter and range parameters.
/share/:id
A share link, signed with an HMAC-SHA256 token, for handing a canvas to someone who should not need an account. The canvas toolbar's Share… panel makes one, valid for 30 days.
/embed/:id
A page with no navigation, for putting the canvas inside another page; the Share panel gives its <iframe> code. It is sent with X-Frame-Options: SAMEORIGIN, so only pages served from the same site can frame it. Each card is drawn through its own failure: one card that cannot be drawn says so and leaves the others. It stays in the light colours, because it sits on a page whose colours it cannot know.
/catalog
Every saved canvas in one place, as cards that open.

Exporting from a canvas

Each card can save itself whole — SVG, PNG or a one-page PDF — including the parts that hang outside the viewBox, such as rotated axis labels. A pie's HTML legend is drawn into the saved copy, and a map or table card keeps the image buttons hidden because there is no chart to save. A card's data can also be downloaded as CSV, from the canvas's own rows rather than from any other source. The whole canvas prints to paper or PDF. Saved cards and printed canvases use the light colours, whichever theme the reader has chosen.

The canvas itself can be kept as a file: the Share panel's Download as JSON saves it, and Import canvas on the Canvases page (/canvases, the main navigation's Canvases link) makes a new canvas from that file, on this server or another.

Canvases live in analyses.json under the model directory, not in a database. The JSON API can list, create, patch, import and export them, and can append a visual without a browser — see the visuals endpoint.

Where the numbers come from

A canvas stores datasets, joins, calculated fields and visuals, not rows. Each card runs the same pipeline as the page it was saved from (Data describes the stages), which is why a canvas over a file that changed overnight shows this morning's figures, and why the same canvas can be narrowed by a range without storing the dates it resolved to.