Navigation rail

Nav rail — Default (active + hover rendered)
Active item gets the 3px signal left-edge bar plus a lightened icon. Third item shows the hover treatment rendered inline (lightened icon on rgba white 6% background).
Nav rail — Collapsed tooltip state
Sources
The rail is icon-only; tooltips carry the label. No skeleton variant: the rail is app chrome and renders instantly — it never waits on data.

Page bar & breadcrumbs

Page bar — Default
churn-model Sources raw_orders
raw_orders
Breadcrumb reads project ▸ area ▸ object. The object identifier is always in data mono — if it's data, it's mono.
Page bar — Long object name edge case
churn-model Derived relations customer_lifetime_value_rolling_90d_by_region_v3
customer_lifetime_value_rolling_90d_by_region_v3
Long identifiers truncate with an ellipsis; the full name is available via title tooltip. The action button never wraps or shrinks.
Page bar — With tab row
churn-model Sources raw_orders
raw_orders
Healthy
Overview
Schema
Freshness
Runs
Settings
Page bar — Title band skeleton skeleton
churn area object
title
btn
Skeleton mirrors the loaded layout exactly — same heights, same slots — so nothing shifts when data lands.

Buttons

Buttons — Variants
Buttons say what they do — verbs, sentence case. Danger red is reserved for destructive actions, never for emphasis.
Buttons — Hover state
Buttons — Focus ring state
Visible 2px signal focus ring with 2px offset, on every interactive element — keyboard reach is part of the accessibility floor.
Buttons — Disabled state
Buttons — Busy state
Label switches to present progressive while the request is in flight.
Buttons — Long label edge case
Buttons never wrap (white-space: nowrap). A label this long is a smell — prefer moving detail into help text — but if it happens, the button grows and the layout must accommodate it.

Inputs & fields

Text field — Default
Shown in the source list and in run logs.
Text field — Focus state
Text field — Invalid state
Enter a valid folder path.
Invalid = red border + error text below. Never color alone; the message says what to fix.
Text field — Disabled state
Assigned by the system and cannot be changed.
Mono input — Long path overflow edge case
The field scrolls horizontally; the caret stays visible while typing.
Select
Rows become available to training at this timestamp.
Textarea

Search & filters

Search box — Default
🔍
/
The slash keycap hints the keyboard shortcut; search focuses on "/" anywhere in the app.
Search box — Typed state
🔍
3 matches — raw_orders, raw_order_items, raw_orders_dlq
Search box — No results state
🔍
No datasets match "raw_ordersz". Check the spelling or clear the search.
Filter pills — Row
Source: postgres Health: Failed ✕ + Add filter
Active filter pills use badge-blue; each removes with its own ✕. "Add filter" is the neutral gray pill at the end.
Filter pills — Overflowing set edge case
Source: postgres Health: Failed ✕ Owner: dhenneberger Updated: last 7 days ✕ +2 more
When pills exceed one row, the row clips with a fade and a "+N more" pill; "Clear all filters" appears whenever two or more filters are active.

Tabs

Tabs — Default set
Overview
Schema
Freshness
Runs
Settings
Tabs — Hover + active state
Overview
Schema
Freshness
Runs
Settings
Active = signal text + 2px signal underline, weight 500. Hover (second tab, rendered) only darkens the text — the underline is reserved for active.
Tabs — 9-tab overflow edge case
Overview
Schema
Freshness
Runs
Lineage
Quality
Access
Costs
Settings
Tabs never shrink or wrap. Past ~6 tabs the row scrolls horizontally: clipped edge gets a surface-colored fade plus a chevron button that scrolls the row.

Badges & chips

Badges — Semantic colors
Active v2 Draft Deprecated Critical Temporal
Color follows semantics, not taste: green = healthy/active only, amber = warning, red = failure, violet = temporal, blue = brand/selection, gray = neutral. Never use state colors decoratively.
Version chip
customer_features v3 v2 — current version leads, prior versions gray
Chip — Long label truncation edge case
anchored_on_ordered_at_minus_48h_grace Active
Badges cap at ~180px and truncate with an ellipsis; the full value is exposed via title tooltip. Badges never wrap to two lines.

Health indicators

Health — Fixed vocabulary
Healthy Lagging Failed
The vocabulary is fixed everywhere: Healthy · Lagging · Failed. Dots are always paired with words — never color alone.
Health — In-table usage
SourceHealthLast sync
raw_ordersHealthy14:02
raw_eventsLagging09:47
crm_accountsFailed06:15
Health lives in its own column as dot + word. Row backgrounds are never tinted by health.
Health — With detail timestamp
Failed · 3 failed runs since 09:12
Lagging · last row landed 2026-07-28 06:40, expected hourly
Detail follows the word after a middot, in muted body text with data values in mono. The health word itself never changes.

Table

Table — Default
Dataset Health Rows Last sync ▾
raw_orders Healthy 12,842,113 2026-07-28 14:02
raw_order_items Healthy 48,110,976 2026-07-28 14:01
raw_events Lagging 402,338,514 2026-07-28 06:40
crm_accounts Failed 88,412 2026-07-28 06:15
36px rows, hairline rules, sticky header. Numerics right-aligned in mono with tabular figures so columns of numbers align. "▾" on Last sync marks the sorted column (descending).
Table — Hover + selected rows state
DatasetHealthRowsLast sync ▾
raw_orders Healthy 12,842,113 2026-07-28 14:02
raw_order_items Healthy 48,110,976 2026-07-28 14:01
raw_events Lagging 402,338,514 2026-07-28 06:40
Row 1 renders hover (paper-blue wash); row 2 renders selected (signal-soft). Selection is the only blue row tint — health never tints rows.
Table — Long cell truncation edge case
DatasetHealthRowsLast sync ▾
customer_lifetime_value_rolling_90d_by_region_v3 Healthy 1,204,551 2026-07-28 13:58
raw_orders Healthy 12,842,113 2026-07-28 14:02
Long identifiers truncate with an ellipsis inside a fixed-layout column; the cell carries a title attribute so the native tooltip shows the full name on hover.
Table — Loading skeleton
DatasetHealthRowsLast sync
x x x x
x x x x
x x x x
Real header, skeleton body — three rows regardless of expected count. Line widths vary so the shimmer doesn't read as a stripe pattern.

Stat card

Stat card — Default
Rows synced today
12,842,113
Value in Display face with tabular figures; label is an 11px uppercase eyebrow.
Stat card — With delta sub-line
Rows synced today
12,842,113
4.2% vs yesterday · as of 14:02
Green in the delta is allowed only because it encodes a healthy direction; a cost increase would use amber/red, a neutral delta stays ink-soft.
Stat card — Huge number edge case
Total events ingested
1,284,113,902
since 2024-01-01
When the value would overflow the card, step the size down (26 → 20px) rather than truncating or abbreviating — engineers need exact counts.
Stat card — Loading skeleton
Rows synced today
value
sub
The label renders immediately (it is chrome); only the data slots shimmer.

Time spine

Time spine — Full anatomy
2026-06-01
2026-06-15
2026-07-01
2026-07-15
training window
unavailable
anchor 06-18 09:00
anchor 07-05 09:00
now
2026-07-28 14:02
axis (2px) now — solid caret anchor time — hollow ring solid region hatched — data you may not use yet
Violet means time — the color is reserved for temporal semantics everywhere in the product, so this spine reads without a legend. It looks identical on every page: freshness, label horizons, training splits, run timelines.
Time spine — Compact in-card variant
raw_orders Healthy
07-26
07-27
07-28
now
Last row 2026-07-28 14:02 · syncs hourly
The compact variant keeps the same marks at the same weights — only ticks and labels thin out. Never restyle the spine per context.

Banners & toasts

Banner — Error
Errors say what happened and what to do next, in the interface's voice. The primary fix is a real button, not a link buried in prose.
Banner — Warning
Banner — Info
Toast — Success
Saved changes to customer_features.
Toasts confirm and leave (4s). Success is a toast; errors are banners that stay until resolved or dismissed.
Banner — Long multi-line error edge case
Prose wraps; the raw error line stays mono in its own scrolling well so it can be copied verbatim. Icon and layout hold at any length.

Empty states

Empty state — Generic pattern
No sources connected yet
Connect a database or file drop to start syncing data into this project.
Empty states are invitations: one sentence of what belongs here plus one primary action. No illustrations, no mascots.
Empty state — Search empty
No datasets match "raw_ordersz"
Check the spelling, or clear the search to see all 24 datasets.
Search-empty echoes the query in mono and offers escape, not creation — the data exists, the filter just missed it. The action is secondary, not primary.

Modals & drawers

Confirm modal — Danger action
Delete derived relation?
customer_features_v3 and its 14 materialized partitions will be removed. Two downstream tasks reference this relation and will fail on their next run.
Destructive confirms name the object and the consequences. The danger button repeats the verb + noun ("Delete relation") — never just "OK". Cancel sits to its left.
Side drawer — Shell (header + footer)
Edit source
raw_orders
Drawers slide from the right (150ms ease-out) over a scrim. Header names the object in mono; the footer is sticky so Save is always reachable.
Side drawer — Loading body skeleton
Edit source
name
label
field
label
field
Drawer chrome (title eyebrow, close, borders) renders immediately; only data-bound slots shimmer. Footer buttons are withheld until the form is real — no dead Save.

Steppers & progress

Wizard rail — 5 steps (done / current / todo)
Location
File handling
3 Parsing
4 Schema
5 Review
Done = filled signal disc with check; current = ring + soft fill, bold signal label; todo = hairline ring, muted. The connector fills with signal only up to the current step.
Inline progress bar — Determinate
Backfilling raw_orders 62% · 8,110,432 / 12,842,113 rows
Inline progress bar — Indeterminate state
Connecting to Postgres…
Indeterminate (a sliding segment) is only for waits under ~10s where no count exists. The moment row counts are known, switch to determinate with mono numbers — never fake a percentage.

Form controls

Toggle — On / off / disabled
Auto-sync on schema change
Notify on failed runs
Allow backdated writes
On = signal track, knob right. Toggles apply immediately — anything that needs a Save button is a checkbox in a form instead.
Checkbox — States
Radio group
Segmented control — Graph | Table
Graph Table
switches the same data's view, never the page
Segmented controls are for two or three mutually exclusive views of the same content. More than three options, or options that navigate, become tabs.
RelQL Studio · Components 100% scroll to pan · pinch to zoom · ⌘0 reset Collections · Pages