Skip to content
relay_
design guidelines ]

The rules behindevery screen.

Relay looks like a terminal on purpose. This is the system that keeps it consistent: what each token is for, how it moves, what it does for accessibility and where it still falls short. Everything below is rendered with the real components and tokens.

01

Principles

  1. 01

    Data is typography.

    Numbers, ids, timestamps and error text are set in mono. Prose and headings stay in sans. The two faces do different jobs, so a reader can tell data from explanation at a glance. see it

  2. 02

    Two signals for every state.

    Healthy, syncing, failed and never-synced each have an icon and a word as well as a colour. Nothing depends on telling red from green. see it

  3. 03

    One accent, kept for the next action.

    Terracotta marks what to press or where you are: the primary button, the active tab, the caret. It is never decoration, so it stays rare enough to mean something. see it

  4. 04

    Detail on demand.

    Tooltips, drawers and modals hold what most people don't need most of the time. The page stays quiet, and the long error is one click away instead of truncated in a row. see it

  5. 05

    Motion explains.

    Things move to show where they came from or where they went. A drawer slides from the edge it lives on. Nothing bounces to fill silence. see it

02

Colour

Warm neutrals and a single terracotta accent, dark first. Dark is the designed identity. Light is a functional fallback for a light system preference. Elevation is a lighter surface plus a hairline border, never a shadow or a blur.

Colour tokens in both themes
LiveTokenRoleDarkLight
backgroundPage ground#141210#faf7f2
surfaceCards, windows, header. One step lighter than the ground#1c1916#ffffff
foregroundBody text and strong UI#f2ede7#1c1712
mutedSecondary text, labels, icons#948b81#7a6f63
accentThe next action, and where you are#d97757#c2410c
accent-foregroundText on accent fills#141210#faf7f2
successHealthy, answered#4ade80#15803d
dangerError, refused, destructive#f87171#b91c1c

border is the foreground colour at 10% opacity in both themes, so hairlines follow the theme instead of being a fixed grey.

Contrast, computed from the values above (WCAG 2.x)
PairDarkLight
foreground on background16.06:1 AA16.64:1 AA
muted on background5.58:1 AA4.59:1 AA
muted on surface5.22:1 AA4.90:1 AA
accent on background5.99:1 AA4.85:1 AA
accent-foreground on accent5.99:1 AA4.85:1 AA
success on background10.72:1 AA4.69:1 AA
danger on background6.76:1 AA6.05:1 AA

03

Type

Two families and no more. Geist Sans for prose, headings and UI labels. JetBrains Mono for data and meta labels. Display sizes get tight tracking. Uppercase labels get loose tracking.

Aa

Geist Sans · 400 500 600

Aa

JetBrains Mono · 400 500 600

  • Display

    clamp 2.5–8.5rem · 600 · −0.04em · sans

    Cited answers

  • Heading

    1.9–3.5rem · 600 · −0.035em · sans

    Staying up to date

  • Body

    16px · 400 · 1.625 leading · 62–66ch · sans

    Relay reads your Drive, Gmail, Calendar and Sheets, then answers with a link to the exact passage.

  • UI label

    12–14px · 500 · sans

    Sync Gmail now

  • Data

    12–14px · 400–600 · mono · tabular figures

    6,267ms 0.412 9/21/2026, 8:50 AM

  • Meta label

    11–12px · uppercase · +0.08–0.12em · mono

    Per-surface sync lag

04

Shape & space

Everything has a 4px radius, with 2px for very small parts like checkboxes. Circles are reserved for status dots. Spacing runs on a 4px grid: tight inside an app screen, generous on the public site, with vertical gaps that vary by how related two sections are instead of repeating one number.

4px · rounded

Cards, buttons, inputs, windows

2px · rounded-sm

Checkboxes, chart parts, tiny controls

full · rounded-full

Status dots and window dots only

elevation without shadows

background

surface

hover / selected · foreground at 5%

05

Components

These are the real components from the app, not screenshots. Toggle, hover and open them.

buttons
use
One primary per view. Name the action: Sync Gmail now, not Submit.
avoid
Two accent buttons side by side. A destructive action styled as primary.
toggle
keeps sources fresh
use
Immediate state changes, like auto-sync. It states its own value in text.
avoid
A pill switch with only colour and position to say on or off.
sync status
synced 4m agosyncing…Quota exceeded, retry in a minutenever synced
use
Every state gets an icon and a word as well as a colour.
avoid
A bare red or green dot that means nothing to a colour-blind reader.
tooltip and chip
Auto-syncRuns — Sheet1 (row 12)
use
Tooltips for secondary detail only. Chips for things you can open.
avoid
Putting the one thing people must read inside a tooltip.
modal and drawer
use
Modal to confirm or warn. Drawer for one item's detail or a small workflow.
avoid
A modal that hides information people need to compare.
prompt input
use
A visible border and a prompt glyph, so it reads as a place to type.
avoid
A borderless field that relies on placeholder text alone.
metric card · sample data

Query volume

18%

167

15 24h83 7d
The arrow shows direction. The colour says whether that direction is good for this metric: more queries is fine, more refusals or more latency is not, so the same upward arrow is green on one card and red on another. A prior period with no data shows “new” instead of an invented percentage.

06

Motion

Animate transform and opacity only, since those run on the compositor and never trigger layout. Enter slower than you exit. Never linear, except a blinking caret and a spinner. Everything obeys prefers-reduced-motion.

easing · linear vs the site curve
linear
site curve

Same distance, same 1.2 seconds. Linear starts and stops like a machine. cubic-bezier(0.16, 1, 0.3, 1) leaves fast and settles slowly, which is how things with weight move.

stagger · 60ms per item

Lists arrive one after another so the eye can follow. Past about 80ms per item it starts to feel slow. Under 30ms it reads as a single block.

spring · nav and drawers

click the track

Springs for things you move yourself: the nav underline (stiffness 500, damping 40) and the drawer (380, 38). Critically damped, so nothing wobbles.

Motion durations and what they animate
WhatTimingAnimatesWhy
Tooltip in120msopacity + 4pxFast: it answers a hover
Hover lift150–200mstranslateY(−1px)A hint of pressability, not a jump
Message enter180msopacity + 6pxEase-out, so the reply arrives and stays
Drawerspring 380/38translateX from the edgeSlides from where it lives
Nav indicatorspring 500/40layout, shared elementFollows you between tabs
Section reveal600–750msopacity + 14px, or a masked lineOnly on the public site
List stagger30–60ms/itemsame as the itemLong enough to follow, short enough to finish

07

Accessibility

What is in place, and, listed just as plainly, what isn't yet.

in place

  • Focus is always visible: the browser outline by default, an accent ring or border on custom controls
  • Icon-only buttons carry an aria-label
  • Switch, checkbox and dialog roles, with Esc closing overlays
  • Nav uses aria-current for the active page
  • Status is never colour alone: icon plus word
  • prefers-reduced-motion respected in JavaScript and CSS
  • On the public site: landmarks, a skip link, and captioned tables with row and column headers
  • Text contrast measured above, both themes

known gaps

  • Modal and Drawer close on Esc and backdrop click but do not trap focus or return it to the trigger
  • Some dense controls are under 44px: the send button is 40px, citation chips about 28px
  • Charts have hover tooltips but no text alternative such as a data table
  • Native date inputs keep the browser's own styling and picker
  • 11px mono labels pass contrast but are small

08

Voice

Say what happened and what to do next. Name the action on the button. Sentence case for prose, lowercase mono for data labels. Skip the words that could be on any product page.

Error 500

Gmail is rate-limited. Wait a minute and sync again.

Submit

Sync Gmail now

Unlock seamless insights

Ask a question, get a cited answer

Oops! Something went wrong

Nothing in your sources supports an answer, so Relay won't guess.