inlay v0.1
00 — inlay / design system for eyos projects

One calm look for everything eyos makes.

inlay turns Relay's look into a shared system: its day and night themes, Inter with small mono labels, and a single pink accent for anything that needs attention. The structure follows Interfere's design system, so every color, size and timing is a named token. It is built for phones first, and Phosphor supplies every icon.

2 typefaces 1 accent 4 data colors 4px grid 2 themes
TaskAgent
Investigating the root cause 1m 34s
Reading logs on eyos-vps · step 3 of 5
Workspace⌘ K
Text / labels
Aa Aa
Inter · IBM Plex Mono
02TrafficLast hour
ReadWrite
View
01CPULive / 2s
38%
Online4 coresDisk 91%
01 — Principles

Five rules, in order of importance.

01

Quiet by default.

Monochrome first. Ink does the work, and color only appears when it means something: pink for attention, data colors for charts and categories. Healthy states stay quiet; only exceptions get color.

02

Every element earns its place.

If you can't say what something does, remove it. Labels name things, numbers carry their units, and decoration is never the point.

03

Thumb first.

Designed at 390 wide in one hand, then scaled up. 48px controls on touch, primary actions in the bottom third, bottom sheets instead of popovers, nothing that needs hover.

04

Instant feedback.

Every press shows a pressed state within 100 ms. Motion confirms what happened and never makes anyone wait. On phones, selection changes get a light haptic.

05

Clear structure.

Numbered sections, small mono labels, units beside numbers. State is always a word plus a dot, never color alone.

02 — Color

Ink, paper, one accent.

Relay's two themes, made into tokens. Day is graphite on pale gray and white; night is charcoal and silver. Components only read semantic names, so every screen switches theme for free.

02.1Themes

DayRelay light
NightRelay night

02.2Gray ramp

Open Color's grays, which Relay already uses, extended with three deep darks for night surfaces and terminals.

02.3Accent and data colors

Signal · the one accent
Disk 91%

Focus rings, alerts and destructive actions, and nothing else. It is never decoration. Text on a pink fill is dark for contrast. When it shows up, something needs a person.

Data colors · charts and categories
Data 1 Data 2 Data 3 Data 4

A fixed order for chart series and category tags. Data 1 is ink; 2 to 4 are blue, green and violet, tested for color-blind separation in both themes (closest neighbors ΔE ≥ 23). Never used for status.

Depth by tone

bg → paper → raised gets lighter in day and in night, so layering reads without shadows. Dark (action cards, toasts) and terminal stay dark in both themes.

03 — Typography

Two typefaces.

Relay's pairing. Inter for everything people read. IBM Plex Mono, small and uppercase, for labels, and in normal case for numbers with units and for code.

TextInter · cv11 ss01
Aa
A home for your servers.

Titles, body, buttons, everything people read. The single-storey a and open digits come from the replay site.

Labels & dataIBM Plex Mono
Aa
CPU / Load avg · Live / 2s · 01

Labels sit above or beside what they name and never wrap. Values with units, IDs, paths and code also use mono, in normal case.

03.1Scale

Mobile first

Body is 15px. Inputs go to 16px on touch so iOS never zooms. Headlines track tighter as they grow.

Numbers

Live numbers use the reading style with tabular figures, and the unit sits beside them in mono: 38 %, 2.4 GB.

Small labels

9px labels are for metadata only. Anything someone needs to act on is 10px or larger.

04 — Space & layout

Everything on a 4px grid.

Spacing, sizes and radii step in 4px. Layouts change at three window classes, and touch screens get bigger controls automatically.

04.1Window classes

Compact< 600
Columns
4
Gutter
20 (16 under 380)
Nav
Bottom tab bar, 70 + safe area
Overlays
Bottom sheets
Medium600–999
Columns
8
Gutter
24
Nav
Bottom tab bar
Overlays
Sheets, or dialogs at 480 max
Expanded≥ 1000
Columns
12
Gutter
36
Nav
214px numbered sidebar
Overlays
Menus, popovers, dialogs

04.2Reach

SAFE AREA STRETCH READ, DON'T TAP OK EASY PRIMARY ACTIONS TAB BAR · 70
  1. 01Primary actions live in the bottom third. Send, save, done. Top bars carry titles and secondary icons.
  2. 0244px minimum hit area, 48px controls. Controls grow automatically on touch screens (pointer: coarse); the visible shape can stay smaller than the hit area.
  3. 03Respect the safe areas. Bars pad with env(safe-area-inset-*). Nothing tappable under the home indicator.
  4. 04No hover-only meaning. Shortcuts (⌘K) hide on touch. Long-press opens the same actions as a right-click.
  5. 05One column, then more. Compact screens stack panels full width. Grids appear at medium, the sidebar at expanded.
05 — Shape & depth

Small radii, flat tone.

Hairline edges and tonal layers do most of the work. Shadows are only for things that float above the page.

Nesting

Inner radius = outer radius − padding. A segmented control is 8 outside, 3 padding, 5 inside.

Squares

Status dots are 6px squares, as in Relay. Radios, switches and avatars are round.

05.1Depth

0 · FlatPanels and lists. A hairline, no shadow.
1 · RaisedThe selected segment, switch thumbs.
2 · FloatingMenus, popovers, toasts.
3 · SheetBottom sheets over a scrim.
06 — Motion & haptics

Quick and quiet.

Motion is short and purposeful. It confirms what happened and never holds anything up. Keyboard users and reduced-motion settings get none at all.

Easing out = cubic-bezier(.23, 1, .32, 1); readings use in-out.

06.1Haptics

On phones, haptics mark the moments a person would want to feel: a selection changing, a long-press, a send. Never on ordinary taps.

TokenWhenFlutterWeb (Android)
selectionA segmented control, picker row or slider step changesselectionClick()vibrate(8)
pressA long-press opens a menu, or a drag picks something uplightImpact()vibrate(12)
confirmA committed action: send, save, deletemediumImpact()vibrate(20)
warningSomething failed or needs attentionheavyImpact() ×2vibrate([30,60,30])

While content streams, haptics fire at most every 320 ms. They follow the system setting and always accompany a visible change.

07 — Icons

Phosphor icons.

Every icon is from Phosphor, the set Relay and replay already use. There are no custom illustrations: heroes and empty states put a Phosphor icon on a tile.

07.1Weights and sizes

RegularDefault, everywhere
FillOnly the selected tab or an on state
Sizes16 inline · 20 UI · 24 tabs and rows · 32 on a hero tile
Color

Icons take the text color: ink, or muted when inactive. Pink only inside a signal row or button, data colors only in a chart legend.

Labels

On phones, icons in navigation always have a word under them. Icon-only buttons need an aria-label (and a tooltip with a pointer).

Install

Web: @phosphor-icons/web or @phosphor-icons/react. Flutter: phosphor_flutter, re-exported by the inlay package.

08 — Components

Parts.

Every part is plain HTML plus a class, with variants as data attributes and state as ARIA. Each has a touch size and a pointer size, and says how it behaves on a phone.

08.01Button
.buttonTouch 48
Variants

Primary is ink, at most one per view. Secondary has a hairline. Ghost is for toolbars. Signal is only for destructive actions.

Sizes

sm 32 · md 40 · lg 48 with a pointer; 40 · 48 · 56 on touch, from the same markup.

Phones

Moves down 1px while pressed. Full-width (data-block) primary buttons sit at the bottom of the screen.

08.02Segmented control
.segmentedHaptic: selection
Use

2 to 4 options that switch a view in place. With 5 or more, use a sheet with a list.

Phones

Full width (data-block). Shortcut hints disappear on touch screens.

08.03Switch, check, radio
.switch .check .radio
Switch

Applies immediately. Ink when on.

Check

Part of a form that gets submitted. The tick is drawn in the on-ink color for each theme.

Phones

The row is the target: the whole label is tappable, and it is at least 44 tall.

08.04Text field
.field .input16px on touch
Anatomy

Label above, field below, hint underneath. Units are a mono suffix inside the field; search gets a leading Phosphor icon.

States

Focus turns the edge to ink. Errors turn it pink and replace the hint with what went wrong and what to do.

Phones

Set inputmode and autocomplete. The keyboard must never cover the focused field or the primary action.

08.05Slider & stepper
.slider .stepper
Volume 64
3
Slider

For a feel, not a number: volume, brightness, zoom. Show the value beside the label.

Stepper

For exact values in small ranges: retries, copies, columns. Hold to repeat.

08.06Meter & progress
.meter .progress .spinner
38% CPU
91% DISK
Meter

Relay's segmented meter, for levels: CPU, memory, disk, quota. Eases between readings over 500 ms. Turns pink past a threshold and says so in words.

Progress

A plain bar for tasks. Use a determinate bar whenever you know the size; the spinner is for short waits inside a control.

08.07Dot, pill & chip
.dot .pill .chip
RunningPausedFailedLive
Dot

A 6px square. Gray is off, ink is on, pink needs attention, data colors only in legends.

Pill

Status. Always a word plus a dot. Healthy is ink, not green, because normal shouldn't stand out.

Chip

A filter you toggle. Selected chips go to paper with an ink edge.

08.08Panels & cards
.panel .tile .action-card
Online
All quiet.
UP 14D 03H · 3 SERVICES · 0 ALERTS
02Memory3s interval
5.6of 8 GB
A second pair of hands.Claude, on your server.
Panel

Paper, hairline, radius 12, padding 20 (16 on small phones). Starts with a numbered section title.

Hero

A pale panel with a Phosphor icon on a 64 tile. One per screen, at the top.

Action card

The dark card leads somewhere. Title in Inter, subtitle in mono, an arrow at the end.

08.09List rows
.list .row56 min
Anatomy

A 24 icon or a 40 tile, title, mono meta, then a trailing value, switch or chevron. Dividers inset to the text.

Phones

The whole row is the target. Swipe left reveals at most two actions; destructive is pink and last.

08.10Navigation
.tabbar .sidebar .topbar
Compact & medium

Bottom tab bar with 3 to 5 destinations: a 24 icon over a 10px label. The current tab uses the Fill icon, ink and bold, with a 2px bar. Hidden while the keyboard is up.

Expanded

The 214px sidebar takes over, with numbered items.

Top bar

Title or breadcrumbs, with up to two icon buttons. Primary actions do not go here on phones.

08.11Sheet & menu
.sheet .menu
Move 3 files
Phones

Menus, pickers and confirmations become bottom sheets: handle, title, close, then content, then the primary action at the bottom. Drag down or tap outside to dismiss.

Pointer

The same actions as a menu, with shortcuts. Destructive items are last, in pink, below a separator.

08.12Toast & banner
.toast .banner
3 files moved to backups.
Toast

Dark, floating above the tab bar, 4 s, one action at most. Swipe to dismiss. Never the only record of an error.

Banner

Stays until the problem is gone. Pink for trouble, neutral for information. Says what's happening and what happens next.

08.13Sparkline
.sparkline
01CPU activityLive / 2s
CPU38%
ReadWrite
Rules

Three hairline gridlines, a 1.8px line, a faint area and a marker on the latest value. Readings ease; they never count up from zero on load.

Series

One series is ink. Several take data colors 2, 3, 4 in order, with a legend of dots and words.

08.14Empty state
.empty-state
No alerts yet

Alerts show up here when CPU, memory or disk cross a limit you set.

Anatomy

A Phosphor icon on a 64 tile, a title that says what's missing, one sentence on how it gets filled, and at most one action.

Tone

Plain and useful. No jokes and no illustrations.

09 — Mobile patterns

Put together, in a hand.

Four screens built only from the parts above, at 360 wide: a Relay-style overview in both themes, a file browser, and settings with a sheet open.

A · Overview · Day
B · Files · Night
C · Settings + sheet · Day
D · Overview · Night
10 — Use it

One source, every platform.

tokens/inlay.tokens.json is the source of truth. node scripts/build.mjs writes the CSS, the Flutter constants and a flat JSON from it.

10.1Web

<!-- one file: fonts, tokens, base, components -->
<link rel="stylesheet" href="inlay/dist/inlay.css">
<html data-theme="dark">  <!-- or leave it off to follow the system -->

<!-- icons: Phosphor, regular weight (npm i @phosphor-icons/web) -->
<button class="button" data-variant="primary"><i class="ph ph-plus"></i> Add server</button>
<div class="meter" style="--value: 38"></div>
<span class="pill" data-tone="signal">Disk 91%</span>

.my-thing { background: var(--paper); color: var(--muted); border-radius: var(--radius-panel); }

10.2Flutter

# pubspec.yaml
dependencies:
  inlay: { path: ../inlay/flutter }

MaterialApp(theme: Inlay.theme(Brightness.light), darkTheme: Inlay.theme(Brightness.dark));

final p = Inlay.of(context);       // p.paper, p.ink, p.signal, p.data2 …
Text('CPU', style: Inlay.label(context));
InlayMeter(38);  InlayDot(color: p.signal);  InlaySectionTitle('01', 'CPU', trailing: 'Live / 2s');
InlayTile(PhosphorIconsRegular.hardDrives, large: true);   // phosphor_flutter is re-exported
InlayHaptics.selection();          // selection · press · confirm · warning

10.3Naming

GroupCSSFlutterExamples
Color--paperInlay.of(c).paperbg paper raised pale well dark terminal · ink muted faint · line line-strong · signal signal-ink signal-pale · data-1…4
Type.text-label, var(--text-body)InlayType.labeldisplay title-1…3 headline body-lg body body-sm caption reading label label-sm mono mono-sm code
Space--space-4InlaySpace.s40 0_5 1 1_5 2 3 4 5 6 8 10 12 16 20 (× 4px)
Radius--radius-panelInlayRadius.panelsegment xs tag control panel overlay sheet full
Motion--dur-quick, --ease-outInlayMotion.quickpress 90 · quick 160 · surface 200 · page 250 · reading 500
Icons<i class="ph ph-house">PhosphorIconsRegular.houseRegular by default, Fill for the selected tab