The building blocks every Artelier dashboard is assembled from, plus the layout, colour and engineering rules that decide how they go together. Monochrome soft UI: nothing has a border, shape comes from two shadows on a surface the same colour as its background, and hierarchy is elevation and value rather than hue.
01
The chrome is grey so the data can be coloured
Interface colour and data colour compete for the same channel. We give the whole channel to the data.
02
Depth is the only decoration
A raised surface is available; a surface pressed in is holding something. No ornament beyond the shadow pair.
03
Numbers are monospaced
Counts, codes and durations line up column to column, so a column is scanned downward instead of read across.
04
Every component names its tokens
Change a token and everything listing it moves together. That is the difference between a system and a style guide.
How this differs from a general-purpose system. Carbon, Material and Fluent list components alphabetically and leave the decisions to you — they have to, because they do not know what you are building. ADS is narrow on purpose, for data-rich screens only, so it can take positions those systems cannot: contents ordered by job rather than by spelling, one validated data palette rather than a themeable one, a chart-selection table rather than a chart gallery, and an engineering section — because at ten thousand rows the rendering strategy is a design decision.
01 — Foundations
The variables everything else reads.
Six surfaces, four depths, four radii, five type roles and one easing curve. Every component below is a composition of these and nothing more.
Value scale
Six steps in a narrow band. Neumorphism needs a mid-tone so a shadow has room to go lighter and darker — pure black leaves nowhere to fall.
--canvasbehind everything
--railthe sidebar
--surfacethe frame, and every well
--raisedcards
--elevatedchips, bars, overlays
--hoverrow hover
Elevation
A tight contact shadow reads as thickness; a wide soft fall seats the shape on the page. One blur gives you one or the other and looks printed on.
sm--soft-raised-smcontrols, rows
md--soft-raisedcards
lg--soft-raised-lgthe frame, overlays
inset--soft-insetwells, fields
Radius
Large on purpose. Soft UI reads as moulded rather than drawn, and a tight 8pt corner looks like a stock dashboard again.
--radius-lgthe frame
--radiuscards
--radius-smcontrols
--pillchips, meters
Motion
One easing curve everywhere. Three durations, chosen by how far the thing has to travel. All of it collapses under reduced motion.
Four families, each with a job. The display face carries personality at size; the UI face stays legible at 13px in a dense grid, which the display face would not.
DisplayDashboardsBricolage Grotesque · opsz
HeadingResponse codesBricolage Grotesque
BodyEvery number has a threshold.Inter Tight
LabelSEVERITYMartian Mono · wdth
Data1,284 · 301 · 74msJetBrains Mono · tnum
Iconography
A 24px grid, a 1.6 stroke, round caps. Two tones: the frame or track recedes, the thing the glyph is about sits at full strength. Frames take the interface's rounded corners, and a solid dot marks the value.
dashboard
signal
growth
gauge
discovery
blueprint
system
prototype
access
handoff
engine
theme
audit
table
02 — Colour
Grey chrome, coloured data.
A dashboard has one colour channel and two things competing for it. ADS settles the argument by giving the whole channel to the data and leaving the interface in greyscale.
c-thesis
The thesis
Interface colour and data colour draw on the same perceptual budget. Spend it on toolbars and the one red bar that matters stops being visible.
Interface — greyscale
Surfaces, controls, type and state live in six greys plus white. Hierarchy comes from elevation and value. The accent — pure white in dark mode, near-black in light — is reserved for the one action per screen.
Data — the full channel
Charts get hue, and they get it exclusively. A coloured mark on a grey page is unmissable, which is the entire argument: colour means something here because nothing else is using it.
Use it
Hue inside the plot area, greyscale everywhere outside it.
Text in text tokens, never in a series colour — the mark beside it carries identity.
Avoid
Brand colour on chrome. It competes with the data for the only channel that encodes meaning.
Colouring nominal bars by their own value — that re-encodes what bar length already shows and spends the identity channel for nothing.
c-brand
Brand & semantic
Five colours Artelier keeps. Two carry the identity, three carry state — and the two sets never borrow from each other, because a brand colour that also means 'error' means nothing.
Brand — identity only, never a chrome fill
Tyrian--brand-primarythe mark, identity moments11.2 ΔE from the nearest known brand
Verdigris--brand-secondarysupporting identity, links in marketing8.7 ΔE from the nearest known brand
Semantic — locked meanings, always with a glyph
Celadon--okcompleted, healthy, within target
Orpiment--warnapproaching a threshold, stale, pending
Vermilion--badfailed, destructive, over threshold
The semantic three sit close to the conventional green/amber/red on purpose — Celadon is 6.1 ΔE from Supabase's green, Vermilion 3.0 from Tailwind's red. That is not a miss. A success colour nobody recognises is a failed success colour. The uniqueness lives in the brand pair, where it costs nothing to be unfamiliar.
States
healthystalefailed
Use it
Brand on the mark and on identity moments; semantic on state. Never the reverse.
Inside dense data — a severity column, a heat grid — keep the luminance grades instead. Three hues repeated down four hundred rows is noise, not meaning.
Avoid
Brand colour as a surface or a toolbar fill. The chrome is grey so that these five mean something when they appear.
A sixth. If something needs its own colour, it is probably a state that already has one.
c-categorical
Categorical palette
Identity — which series is which. Eight hues in a fixed order, assigned in sequence and never cycled. The order is the colour-blindness safety mechanism, so it does not change.
Dark surface
1 · blue
2 · orange
3 · aqua
4 · yellow
5 · magenta
6 · green
7 · violet
8 · red
Light surface — restepped, not flipped
#2a78d6
#eb6834
#1baf7a
#eda100
#e87ba4
#008300
#6250d6
#e34948
States
validated, both modesworst adjacent ΔE 8.4 dark · 9.1 light
Use it
Assign slot 1, then 2, then 3 — in order, by entity, so a filter that drops a series never repaints the survivors.
Three series maximum in scatter, bubble or small multiples, where any two marks can sit side by side. Only the first three slots clear the all-pairs test.
A ninth series folds into "Other" or becomes small multiples. It is never a generated hue.
Avoid
Picking hues by eye. Every slot here was computed and re-run against both ADS surfaces.
Using a status colour as "series 4". Status meanings are reserved.
c-sequential
Sequential & ordinal
Magnitude — how much. One hue, light to dark, never a rainbow. Where the quantity has no natural hue, the monochrome ramp keeps the plot in the system's own voice.
Single hue — choropleth, heat, magnitude
Monochrome — density inside a grey interface
Use it
Order the steps by lightness, so magnitude survives a greyscale print.
Use the ordinal form — one hue, monotone steps — whenever reordering the categories would change the meaning: funnel stages, size tiers, age bands.
Avoid
A rainbow ramp. It invents boundaries the data does not have.
A pale lightest step that drops below 2:1 against the surface and stops reading as a mark at all.
c-diverging
Diverging
Polarity — which side of a baseline. Two hues meeting at a neutral grey midpoint, with equal steps per arm so the two directions stay comparable.
Below target · at target · above target
Use it
Only where a meaningful zero or target exists — variance to budget, week-on-week change.
A neutral grey midpoint, so "no change" reads as no signal.
Avoid
A hue at the midpoint. It makes zero look like a value.
Unequal arms. If one side spans twice the range, the chart is lying about symmetry.
c-status
Status & severity
State — good through critical. Reserved meanings that never double as series colours, and inside the monochrome interface they are luminance steps rather than hues.
criticalhighmediumlow
12.4%3.1%0.0%
Use it
Always an icon or a label beside the colour. Status is never carried by colour alone.
Inside the grey interface, severity is brightness: the worse it is, the brighter it is.
Avoid
Reusing a status colour for a data series — it makes every genuine alert ambiguous.
c-vision
Colour vision & contrast
Roughly one man in twelve has a colour vision deficiency. A palette that ignores that is not a palette, it is a guess — so this one was computed rather than chosen.
Method
Machado-Oliveira-Fernandes 2009, severity 1.0
CVD ΔE
8.4 dark · 9.1 light (target ≥ 8, OKLab ×100)
Normal ΔE
19.3 dark · 19.6 light (floor ≥ 15)
Marks
≥ 3:1 against the chart surface
Body text
≥ 4.5:1 — WCAG AA
On the light surface, slots 2, 3, 4 and 5 fall below 3:1 as bare marks. That is not dismissable: in light mode those series carry visible direct labels or ship with a table view. The obligation is part of the palette, not a caveat about it.
Use it
Pair hue with position, a glyph or a direct label — so the chart still works in greyscale.
Keep a table view behind every chart. It is the accessible fallback and the thing analysts actually want.
Avoid
Red/green as the only difference between two states. It is the single most common failure.
Judging a palette by eye. The maths is cheap and the eye is unreliable.
03 — Layout
Where things go.
A dashboard's information architecture is decided before any component is chosen. These three rules settle most of it.
l-grid
12-column grid
Twelve divides by 2, 3, 4 and 6, which covers every panel arrangement a dashboard needs without a custom track.
123456789101112
33338 — primary4 — detail
Use it
One gutter token for the page margin, the gap between cards and the padding inside them.
A KPI row of four at span 3; a chart-plus-detail split at 8 / 4.
Avoid
Panels that align to nothing but the window. Full-width bands are why most admin screens have no vertical structure.
l-scan
Scan order
Readers sweep the top edge, then down the left — the F-shape. Put the answer where the eye lands and the granularity where it has to travel.
1Top-level answer — KPIs, alerts, the state of things
2Trend & comparison
3Detail — tables, logs, the long tail
Use it
One sentence of answer above the fold, not twelve charts of evidence.
Anything time-critical in the first band; anything exploratory below it.
Avoid
Burying the primary action past the point anyone is still reading.
Giving every panel equal visual weight — then the layout has ranked nothing.
l-density
Density
Arguments about row height are arguments about two different readers. Make it a setting instead of picking a side.
34px rows · 24px gutter → 28px rows · 16px gutter
Use it
Express row height, gutter and font scale as tokens a density mode can swap.
Remember the choice per user, not per device.
Avoid
Scaling the comfortable layout down. In compact mode some elements should disappear, not shrink.
04 — Surfaces
Containers.
Three shapes do all the containing: something raised out of the page, something pressed into it, and the frame everything sits on.
ads-card
Card
A block of content standing proud of the page. Cards, not strips — alignment comes from one shared gutter, not from rules drawn between full-width bands.
Crawl health98of 100 · unchanged
States
Rest
Pressed
Use it
Any group of related content, at one radius and one padding.
Hover states that press in rather than lift — the inversion is the gesture this style is built on.
Avoid
Nesting a card inside a card. Use a well for the inner level.
A second radius unless it means a second kind of thing.
ads-well
Well
The same shape pressed into the page. Holds things rather than presenting them: inputs, specimens, tracks, grids.
A well holds content
Use it
Every input, so a field never reads as a button.
The background behind a table or a chart inside a card.
Avoid
Putting a well directly on the canvas — it needs a raised parent to be pressed into.
ads-tab-seam
Frame tab
The selected rail item, cut from the frame's own surface and reaching into the rail with concave fillets. It covers the seam, so rail and content read as one piece of material.
ProjectsResultsIssues
The frame
Use it
The single selected destination in a persistent rail.
Avoid
More than one at a time, and any use where the tab does not actually touch the frame — the fillets then curve into nothing.
05 — Actions
Things that do something.
One accent, spent once per screen. Everything else earns attention through weight and elevation, which is what keeps the accent legible when it appears.
ads-button
Button
Three weights. Accent for the one action the screen exists to produce, default for everything else, quiet for actions that should not compete.
States
Use it
Exactly one accent button per screen, on the primary action.
A verb and its object: Export CSV, not OK.
Avoid
Two accent buttons side by side. If both matter, neither does.
Accent on a destructive action — use default and confirm instead.
ads-icon-button
Icon button
A tile-shaped target for an action whose glyph is unambiguous. Always carries an accessible name, because a glyph is not a label.
Use it
Toolbar actions where space is tight and the glyph is conventional.
Avoid
Any action a reader would have to hover to understand.
ads-segmented
Segmented control
A small set of mutually exclusive views, all visible at once. Use it where a select would hide the options and a tab row would imply navigation.
Use it
Two to five options whose labels are short and comparable.
Avoid
More than five, or labels that wrap. That is a select.
ads-menu
Menu
Row actions and overflow. Everything a reader can do to one record, in one place, so the table does not need a column of buttons.
Use it
Group destructive items below a rule, never first.
Show the keyboard shortcut where one exists — it is how a menu teaches.
Avoid
Hiding the primary row action in here. If every reader needs it, it belongs in the row.
ads-link
Link
Navigation inside prose. Underlined, because in a system with no spare colour the underline is the only signal available.
Underline always. Colour alone is not a signal here, and would not be an accessible one anywhere.
Avoid
Links styled as buttons inside body copy — it breaks the reading line.
06 — Inputs
Things that take a value.
Every field is a well. A raised input reads as something to press, which is the single most common mistake made with this style.
ads-field
Text field
One line of text. Label above, hint below, error replacing the hint rather than appearing beside it.
The crawl begins here and follows internal links.
Must be a positive whole number.
States
Use it
A visible label always — placeholder text disappears exactly when it is needed.
Error text that says what to do, not that something is wrong.
Avoid
Placeholder-as-label. It fails for everyone and fails hardest for screen readers.
ads-number
Number input
A figure with steppers, set right-aligned in the data face so it lines up with the values it will be compared against.
Use it
Right-aligned, monospaced, with the unit in the label rather than the field.
Avoid
A range where a reader needs an exact figure. Give them both or give them this.
ads-select
Select, search, textarea
The remaining text inputs. The chevron is painted into the select's background so the control stays a single element.
Use it
A search field whenever a list exceeds roughly twenty rows.
Avoid
A select for a boolean. That is a switch.
ads-daterange
Date range
The most-used control on any dashboard, and the one most often bolted on last. It belongs in the filter row with the dimension filters, not in a corner.
2026-09-25→2026-10-25
Use it
Presets beside the custom range — most readers want "last 30 days", not a calendar.
ISO dates in the data face, so ranges sort and compare visually.
Avoid
A range that resets on navigation. It belongs in the URL with the rest of the view state.
ads-choice
Checkbox, radio, switch
Three shapes for three meanings: many-of, one-of, and a setting that takes effect the moment it is flipped.
Use it
A switch only where the change applies immediately and needs no save.
Avoid
A switch inside a form with a submit button. Use a checkbox.
ads-range
Range
A continuous value where the approximate position matters more than the exact figure — but the figure is shown anyway, because somebody always needs it.
Use it
Alongside a numeric readout, always.
Avoid
Precise entry. If a reader needs exactly 47, give them a number field.
ads-tag
Tag
A filter the reader applied and can take off again. Distinct from a status chip, which describes the data and cannot be removed — the dismiss affordance is the whole difference.
status: 4xx depth ≤ 3 indexable
Use it
One tag per applied filter, so the reader can see and undo the state they are in.
Avoid
Tags for data attributes. Those are status chips and must not look removable.
07 — Data display
The reason the screen exists.
Every chart here is hand-drawn SVG — no charting library. A chart should match the design system rather than the system bending around a library's defaults.
ads-stat
Stat tile & delta
A figure, its direction of travel, and its standing. A number without a comparison is trivia — the reader has no way to know whether it is good.
URLs crawled1,284+37 this run
Median latency74ms−12% vs last
12.4%3.1%0.0%
States
599+draft
Use it
Pair every headline figure with a target, a previous period or a peer.
Avoid
A tile whose only content is a number. That is a label with extra steps.
ads-meter
Meter & progress
Two shapes often confused. A meter is a quantity against a threshold; a progress bar is a task moving toward a known end. The threshold mark is what separates them.
Budget used68%
Error rate91%
Coverage34%
Crawling46%
Use it
A meter wherever a known maximum exists: quota, budget, coverage.
The threshold mark always. Without it the fill has no meaning.
Avoid
A meter for an unbounded value. It implies a ceiling that does not exist.
ads-chart-choice
Choosing a form
The decision before the component. Most bad charts are the wrong shape rather than the wrong colour, and a gallery of chart types does not help you pick.
The data's job
Form
What decides it
Change over time
Line, or a sparkline beside the figure
Minimal grid; label the first and last value, never every point
Comparing categories
Horizontal bars, ordered by value
Never a pie — angle is the hardest encoding to compare
One headline number
Stat tile with a delta
A figure with no comparison is trivia; pair it with a target or a prior period
Part of a whole
Stacked bar, or just the percentage
Two or three parts at most, or it becomes a pie lying down
Distribution
Histogram or box plot
An average hides the shape; show the spread where the spread is the point
Density over two axes
Heatmap
Opacity for magnitude — the channel the eye reads fastest
Progress to a target
Meter with a threshold mark
The mark is the component; without it the fill means nothing
Relationship
Scatter
Three series maximum — any two marks can sit side by side
Use it
Pick the form from the job, then the colour. Choosing colour first is how most charts go wrong.
Consider that the answer may not be a chart — often it is a stat tile or a sentence.
Avoid
A dual-axis chart. Two measures at different scales means two charts, or index both to a common base.
Decorative non-data ink: 3D, heavy fills, extra gridlines. Every pixel that is not data competes with the data.
ads-charts
Charts
Four shapes cover nearly everything a dashboard has to say. Position and length first, because the eye reads them faster than digits.
68%coverage
Trend
Adoption
Overview92
Reports61
Exports24
Density
Use it
A sparkline beside every headline figure that has a history.
Thin marks, recessive axes, direct labels on the values that matter rather than all of them.
Avoid
Pie charts. Angle is the hardest encoding to compare and there is always a better shape.
A chart per metric, forever. That is scrolling, not knowing.
ads-grid
Data grid
The workhorse. Sortable headers, a keyboard cursor, rows separated by a whisper rather than a rule, and every figure monospaced so columns can be scanned downward.
Crawl results
200
/pricing
1286
low
301
/blog/old-post
330
high
404
/careers/2019
0
critical
200
/docs/getting-started
912
medium
States
virtualise beyond ~200 rows
Use it
Right-align numbers, left-align text, never centre either.
Keep units in the header so the digits stay in register.
Avoid
Row striping. If rows need stripes, they are too tall.
Rendering every row. Past a few hundred, window it — see Engineering.
ads-dl
Structured list
Key-value detail, which is what a selected row almost always needs. A grid rather than a table, so long values wrap instead of forcing a scroll.
Status
200 · indexable
Title
New York Landlord-Tenant Attorneys
Words
1,286
Inlinks
3,762
Last crawled
2026-10-24 14:22
Use it
Labels in the label face, values in the data face — so values stay comparable down the column.
Avoid
A two-column table for this. A table implies rows you can sort; this is one record.
08 — Disclosure
Summary first, detail on request.
The central move in a data-rich interface. Show the actionable summary by default and give the reader a way down to full context — without losing their place.
ads-accordion
Accordion
The simplest disclosure. Built on native details/summary, so it works before JavaScript arrives and is announced correctly without a single ARIA attribute.
Why this page ranks eighth
371 pages share near-duplicate content, but they are 278 separate pieces of work — so the job sinks below fixes that one template change would resolve.
What a fix would involve
Two template changes and a canonical rule. Roughly a day.
Pages affected
371 URLs, listed in the detail drawer.
Use it
Reasoning behind a ranked result, where most readers accept the ranking and some want the argument.
Avoid
Hiding something every reader needs. An accordion is for the minority who ask.
ads-drawer
Detail drawer
A right-hand panel where the selected row explains itself. The table never leaves the screen, so the reader keeps their place in it.
Results
The grid stays where it is; selection drives the panel.
/pricing
Code
200
Words
1,286
Inlinks
3,762
Use it
Any table where readers compare a record against its neighbours.
Avoid
A modal for row detail. It hides the context the reader is working in.
ads-popover
Popover
A small amount of extra context, anchored to what prompted it. More than a tooltip, less than a drawer, and it can contain interactive elements where a tooltip cannot.
Link scoreA 0–100 measure of how much of the site's internal link value reaches this page.How it is calculated
Use it
Definitions for a column header a reader may not know.
Avoid
Putting essential information only in here. It is supplementary by definition.
ads-tree
Tree view
Real nesting — site structure, a category hierarchy, a file system. Built on nested lists so the structure lives in the markup and not only in the indentation.
/ 1,284
blog 842
docs 311
audits 45
pricing 1
Use it
Hierarchies readers already picture as a tree — URLs, folders, org charts.
Avoid
Flat data with a made-up parent level. That is a grouped table.
ads-steps
Progress indicator
Where you are in a sequence with a known end. Distinct from a progress bar: this counts discrete stages, not percentage.
Configure
Crawl
3Audit
4Plan
5Export
Use it
Multi-stage jobs where a reader may leave and return.
Avoid
Sequences with an unknown number of steps — use an indeterminate loader.
09 — Navigation
Getting between screens.
One persistent rail for destinations, tabs for views of the same thing, crumbs for depth. Mixing the three is how dashboards become mazes.
ads-tabs
Tabs
Views of one dataset. The underline is the accent's only appearance in a nav context, so a tab row never competes with the primary action.
Use it
Filtered views of the same table, where the columns stay put.
Avoid
Tabs that navigate to different screens. That is the rail's job.
ads-crumbs
Breadcrumb & pagination
Where you are, and which slice you are looking at. Both are monospaced, because both are mostly numbers and paths.
Use it
Crumbs once a screen is three levels deep or more.
Avoid
Infinite scroll in a data table — it loses the reader's place and makes "row 4,000" unreachable.
10 — Feedback
When there is nothing to show yet.
Latency is a design material and emptiness is a state, not an accident. These components exist because most dashboards treat both as edge cases.
ads-empty
Empty state
The first screen every new user sees, and usually the last one designed. It should teach: what appears here, why it is empty, and the one action that fixes it.
No crawls yet
Results, issues and the ranked plan all appear here once a crawl has finished. The first one takes about two minutes.
Use it
Distinguish never-had-data from no-results-for-this-filter — different copy, different action.
Put the action inside the empty state, not in a toolbar nobody has looked at.
Avoid
"No data" in grey 12px. That is a dead end at the exact moment a new user is deciding whether this is worth the effort.
ads-skeleton
Skeleton
Holds the layout at the real content's dimensions while the data is in flight, so nothing shifts when it lands.
Use it
Match the real content's dimensions, or the shift you avoided arrives anyway.
Avoid
A spinner over the whole page — it throws away a layout the reader had already begun reading.
ads-loading
Loading
For the cases a skeleton cannot cover: an action in flight, or a panel whose shape is not known until the data arrives.
Recrawling 311 URLs…
Use it
Inline, next to the thing that is loading, so the rest of the screen stays usable.
Avoid
A spinner where the shape is known. That is a skeleton.
ads-banner
Banner, toast, tooltip
Three levels of interruption: a banner stays until resolved, a toast confirms and leaves, a tooltip answers a question only if asked.
This crawl is 14 days old. Figures below describe the site as it was on 11 October.
Export finished · 733 rows
Internal links pointing here
Use it
A banner for a condition that persists; a toast for something that already happened.
Avoid
A toast for an error the reader must act on. It will be gone before they read it.
ads-modal
Modal
Blocks everything until answered, so it is reserved for the few moments that genuinely cannot proceed without a decision.
Confirm
Delete this project?
Its crawls, history and comparisons go with it. This cannot be undone.
Use it
Destructive confirmations, where the verb goes on the button rather than "OK".
Avoid
Accent on the destructive action. The accent means "the thing to do", and this is not it.
11 — Patterns
The compositions.
Components are only half a system. These are the arrangements that recur in every dashboard we build, assembled from the parts above and nothing else.
ads-kpi-row
KPI row
The top of nearly every dashboard. Four figures at most — a fifth is a sign the screen has not decided what it is for.
URLs1,284+37
Health98of 100
Median74ms−12%
Issues234 critical
Use it
Rank them left to right by how often they are the reason someone opened the screen.
Avoid
Giving every figure the same weight. Then the row has answered nothing.
ads-filter-bar
Filter bar & saved views
One row, above the data, holding every control that changes what is shown. Saved views turn a repeated set of filters into a named thing a team can share.
30d→now
SavedExec summaryBroken links
Use it
Role-based defaults — an executive and an operator should not land on the same configuration.
Serialise the whole bar into the URL, so a view can be sent, bookmarked and reopened.
Avoid
Filters scattered across the screen. Readers cannot see the state they are in.
A saved view that silently stops matching. Say when it last returned nothing.
ads-table-screen
Table screen
Header, tabs, filter bar, grid, pagination. The arrangement that carries most of the working hours in any data product.
Results
733 rows
Results
200
/pricing
1286
low
301
/blog/old-post
330
high
404
/careers/2019
0
critical
200
/docs/getting-started
912
medium
1–4 of 733
Use it
Inline actions in the row — a status toggle, a batch select — so a decision does not need a round trip to another screen.
Avoid
A full-bleed table. Inside a card it can have a header that stays put.
ads-disclosure-pattern
Summary → detail
The pattern the whole system is arranged around: a ranked answer at the top, the reasoning one level down, the raw rows two levels down — and the reader never loses their place.
1
Answer. One ranked list. "Fix the alt text on the blog template — 823 pages, two changes."
2
Reasoning. An accordion or drawer: why this ranks first, what the score is made of.
3
Evidence. The grid, filtered to the 823 rows, with the filter visible as a tag.
Use it
Each level reachable in one click and leaveable in one — a drawer, not a new screen.
Avoid
Starting at level three. Ten thousand rows is not a to-do list.
12 — Engineering
At ten thousand rows, rendering is a design decision.
This section is the clearest reason ADS is narrow. A general-purpose system cannot take positions on data volume, because it does not know whether you have twelve rows or a million. We do.
e-virtual
Virtualisation
Render the rows in the viewport and a small buffer, not the dataset. Past a few hundred rows this is the difference between a grid and a frozen tab.
// A fixed --row-h is what makes windowing cheap: the scroll height is// arithmetic rather than measurement, so no row has to be laid out// just to know where it sits.
const rowH = 34
const first = Math.floor(scrollTop / rowH)
const count = Math.ceil(viewportH / rowH) + 8 // overscan
const offset = first * rowH
Use it
Fixed row heights, so the scroll position is arithmetic and never a measurement pass.
Keep real table semantics — a virtualised grid still needs rows a screen reader can walk.
Avoid
Virtualising a list of twelve. The complexity is only worth it past a few hundred.
e-loading
Loading strategy
Above-the-fold figures immediately, everything else streamed. A screen that waits for its slowest query is as slow as its slowest query.
0msKPI row — blocking, cached
+120msCharts — streamed
+400msGrid page 1 — streamed
idleHistory, exports — prefetched
Use it
Skeleton each panel at its real dimensions so nothing shifts as the waves land.
Mark stale or partial data explicitly, with the time it was last refreshed.
Avoid
One request that blocks the whole screen. Partial answers beat a blocked page — as long as a partial answer never looks complete.
e-state
State & caching
View state belongs in the URL; fetched data belongs in a cache keyed by it. A view that cannot be sent to a colleague gets rebuilt by hand, and people take screenshots instead.
/results?tab=internal&code=4xx&sort=-inlinks&from=2026-09-25
// The query string is the cache key. Back, forward, refresh and// "send me that link" all work for free.
Use it
Keep the parameters legible — people do edit them by hand.
Cache by the serialised view, so returning to a tab is instant.
Avoid
A base64 blob. Shareable, but nobody can fix one by hand.
e-realtime
Real-time
Push only what is genuinely time-critical. A dashboard where every figure flickers is harder to read than one that updates on a timer and says when it last did.
WebSocket
bidirectional, high-frequency — live crawl progress
SSE
one-way server push — alert feeds, job status
Polling
everything else — background metrics, 30–60s
Never
a figure that moves while someone is reading it aloud
Use it
Show the last-updated time beside anything live. "When was this true?" is the first question.
Let the reader pause the stream — the live updates switch exists for a reason.
Avoid
Animating a figure on every tick. The movement reads as change even when the value has not changed.
Before you call it finished
01Name the personas and the three questions the screen answers, before any layout.
02Put the top-level answer where the eye lands first; push granularity down the page.
03Pair every headline figure with a threshold, a target or a prior period.
04Give the reader a density control rather than picking a row height for them.
05Serialise filters and sort into the URL so a view can be sent to a colleague.
06Virtualise any table that can exceed a few hundred rows.
07Stream the cheap panels first; skeleton the rest at their real dimensions.
08Test the screen desaturated, at 200% zoom, and with the keyboard only.
09Load it with a realistic payload before calling it finished.