Corner

Every convention in DESIGN.md, rendered with the real tokens

Corner’s working look borrows Cloudflare’s palette: Tangerine as the one accent, black type, white grounds, hairline borders. If something looks wrong here, it is wrong everywhere, which is the point.

00 — Brand, working

Corner, and Coach Calvin

Corner is the product. Coach Calvin is the voice. The mark is a corner bracket, open to the top right, with a stroke a fifth of its height; the wordmark is Inter 700 at −0.02em, baseline-aligned, a gap of half the mark height.

Lockup on the card ground

Corner

Mark brand-400, wordmark ink-900. Three sizes: 20, 28 and 36px.

On ink-950, and on brand-400

Corner
Corner

On dark it stays brand-400. On brand-400 it becomes ink-950 — orange on orange is nothing.

Coach Calvin

Coach Calvin

Bot display name, sender of every note

A brand-400 disc with the mark in ink-950, at 24 / 32 / 40px.

The mark alone

Holds down to 16px, which is the favicon. Never outline, round or skew it; clearspace is half the mark height on all sides.

01 — Colour

Generated, not picked

The ramps are converted from Cloudflare's anchors in OKLCH by design/generate-palette.mjs, walked down a fixed lightness scale with a chroma bell, and gamut-clipped. The outlined stop is the anchor, exactly. Never hand-edit a ramp.

brand

Tangerine at 400, exactly — an orange anchors light

50

#FFF3EB

100

#FFE4D2

200

#FFCDAD

300

#FFA96F

400

#F6821F

500

#D76C00

600

#B85C00

700

#9A4C00

800

#813F00

900

#6E3400

950

#3C1900

ink

Cloudflare's type black at 900 — carries a whisper (OKLCH C 0.006) of the orange hue

50

#F8F7F6

100

#EEECEB

200

#DEDCDB

300

#C6C4C3

400

#A9A6A5

500

#918E8C

600

#787472

700

#605C5A

800

#4A4745

900

#313131

950

#1E1B19

danger

Raspberry at 600 — a different hue, so orange never has to mean dangerous

50

#FFF2F2

100

#FFE1E3

200

#FFC9CD

300

#FFA2AC

400

#FF6F86

500

#EA4E6C

600

#CE2F55

700

#B81545

800

#9D0037

900

#87002E

950

#4B0015

success

600 clears AA on white (5.04:1)

50

#DFFFE5

100

#BBFDCA

200

#A5EDB7

300

#84D69A

400

#61BC7C

500

#41A562

600

#07803F

700

#00793A

800

#006530

900

#005627

950

#002E12

warning

Lemon's hue, re-lit for text — 600 is 4.59:1 on white

50

#FFF4DD

100

#FFE7B3

200

#F3D695

300

#DDBC73

400

#C4A14F

500

#AC882E

600

#947000

700

#7F5F00

800

#6A4F00

900

#5A4300

950

#302200

info

Blueberry's hue, re-lit for text — 600 is 4.59:1 on white

50

#F1F6FF

100

#E0EAFF

200

#C7D9FF

300

#A1C0FF

400

#78A2FF

500

#5285FF

600

#366BF5

700

#2455DB

800

#1742C2

900

#0E34AD

950

#010D71

The orange rule

ink-950 on brand-400 · the primary button6.63 AA
white on brand-400 · never2.58 FAIL
brand-600 on white · links4.60 AA
brand-700 on brand-50 · highlight5.66 AA

The anchor is a ground and a mark colour, never a type colour. In dark mode the relationship holds unchanged: brand-400 on ink-950 is the same 6.63:1, so there is one orange for both themes.

State never reads as brand

success · Session matched4.70 AA

--success, the flat token, on the card ground

warning · Drinking day5.44 AA

--warning, the flat token, on the card ground

info · Withings synced5.70 AA

--info, the flat token, on the card ground

danger · Vision call failed4.62 AA

--danger, the flat token, on the card ground

Four flat aliases flip with the theme — 600s in light, 400s in dark — each with a tinted `*-surface` ground and a `*-surface-foreground` verified against it. A component never hand-picks a ramp stop per theme.

Contrast, verified rather than assumed

PairingWhereRatioGrade
ink-950 on brand-400the primary button6.63AA
brand-600 on whitelinks4.60AA
brand-700 on brand-50highlight text5.66AA
ink-900 on whitebody13.01AA
ink-600 on whitemuted — the muted token, AA by design4.63AA
ink-400 on ink-950dark muted7.08AA
brand-400 on ink-950dark primary — the same orange6.63AA
white on danger-600the one solid state slab5.05AA
white on brand-400nowhere — the rule that gets broken most often2.58FAIL
ink-600 is the muted token and clears AA by design4.63AA

ink-500 and lighter are for icons, borders, placeholders and disabled states only. The generator prints every one of these on each run, so a regression is visible rather than discovered later.

Chart series, in fixed order

chart-1

brand

chart-2

info

chart-3

success

chart-4

warning

chart-5

violet

02 — Typography

Two faces, no display face

Inter for UI, JetBrains Mono for anything the user compares against yesterday. Tabular numerals are on globally wherever a number can sit beside another number — the single highest-value typographic decision in the system. Pick from this scale; don't invent sizes.

24 / 700 / −0.02emToday
32 / 600 / mono / −0.01em1,420kcal to go
16 / 600This week’s plan
15 / 400 / 1.5Trend 100.8 kg, down 0.4 this week. Fat mass 28.1 kg, down 0.6. Lean mass holding, which counts as a win.
13 / 400 / ink-600Scale read 101.3 kg this morning. The trend is the number that matters.
12 / 700 / caps / 0.12emYesterday

Why tabular

Tabular — a column you can scan

101.3 kg100.6 kg2,450 kcal1,111 kcal

Proportional — the same four numbers

101.3 kg100.6 kg2,450 kcal1,111 kcal

03 — The phone

Designed at 390px and grown

Bottom tab bar, flat cards, one orange bar marking the active tab, and Calvin's note where the evening's decisions get made. Content is capped at 640px for the phone-shaped views and 1024px for the ones that carry charts.

Today

Fri 12 Sep

Still to go

1,420kcal

95 g protein to go · target 2,450 kcal

Coach Calvin18:00

1,420 kcal left, 95 g protein to go. Room for dinner and the usual bowl.

This week · 6 of 11 done

Lifting3 / 4
Zone 22 / 4
Run1 / 2
HIIT1 / 1
Huel Black (2 scoops)400 kcal
Oat milk cappuccino ×2240 kcal
Chicken, rice, greensphoto390 kcal

Morning note, 06:45

Coach Calvin06:45

Trend 100.8 kg, down 0.4 this week. Fat mass 28.1 kg, down 0.6. Today: 2,450 kcal, 190 g protein. Slept 5h40 — zone 2 today, don't fight it. Front-load your protein. Yesterday: 2,390 in, 12 days on track.

Four lines. Report the number, move on.

Photo confirmation

Coach Calvin13:12

Looks like chicken, rice and greens — about 390 kcal, 42 g protein. Medium confidence.

At most four buttons, the confirming action first, verbs only, 44px tall.

04 — Components

The numbers that must not drift

Buttons 32 / 36 / 44. Badges 20px pills with a 6px dot. Inputs 40px, 44 under a finger. Table rows 44px, list rows 48px. Radius 6px; pills are the only fully-round thing.

Buttons — variants

Primary is brand-400 with ink-950 text at 600. Destructive renders soft on danger-surface, carries a warning icon, sits behind confirmation and is never the dominant button in a view.

Badges

MatchedDrinking daySyncedFailedStaplePending

20px, 12px/600. State badges carry the 6px dot because colour is never the sole carrier of meaning; the two that label rather than report pass `dot=`.

Alerts

A `*-surface` ground, a 25%-alpha state border, a solid state icon. Never a raw vendor error.

Form

Needs a number between 3 and 60.

40px controls, 44 under a finger, labels 13/500. Errors arrive through `aria-invalid`, which draws the danger line and announces the state from one attribute. A ticked box fills brand-400 with an ink-950 tick — never a white one.

Stat cards

Trend weight

100.8kg

0.4 kg this week

Scale read 101.3 today

Fat mass

28.1kg

0.6 kg this week

Lean 72.7 kg, holding

TDEE estimate

3,050kcal

Medium confidence · 16 days

Protein hit rate

79%

22 of 28 days

13px muted label, 32/600 mono value, a delta with an arrow glyph in a state colour, the raw reading as the caption. Trend before scale, always — and there is no “bad” tone to reach for.

Plan slots

Lifting3 / 4
Zone 22 / 4
Run0 / 2
HIIT1 / 1
Lifting1 / 4

A 40px ring, ink-200 track, brand-400 progress, the count in mono beneath. Complete fills solid brand-400 with an ink-950 tick. Never a red ring for missed — a slot that is behind is a slot with an unfilled ring, not an accusation.

Empty state, skeleton, toast

Nothing logged yet

Send a photo of your first meal and Calvin will take it from there.

An empty state says what to do next, never “No entries”. Toasts sit bottom-centre on phones and bottom-right on desktop; they are one of the three things allowed a shadow.

05 — Calvin's voice

Report the number, move on

Most of what the user reads arrives as a message from Calvin, so the copy rules carry more weight here than in a data tool. Never punish. Sentence case. Verbs on buttons. Units on numbers. Trend before scale.

SayNever
1,420 still to goOnly 1,420 left
2,610 in. Trend still down.You went over by 160
Zone 2 today, you slept 5h40Poor sleep detected
Log itSubmit
Send a photo of your first mealNo entries

The word “still” is fine. The word “only” is not. No red numbers for going over, no streak-breaking guilt, no raw vendor errors.

06 — Charts

The trend is the headline

Trend line in chart-1 at 2px; scale readings as faint dots behind it; the week's target dashed; drinking days as warning bands, so the correlation with a stalled week shows itself rather than being explained.

Weight, last 30 days

100.0100.5101.0101.5102.0102.5103.0103.5week target 100.4 kg101.1 kg14 Aug21 Aug28 Aug4 Sep12 Sep
Trend (EWMA, α 0.15)Scale readingDrinking dayWeek target

Weigh-ins

DateSourceScaleTrendFat %
Fri 12 SepWithings101.3100.827.9
Thu 11 SepWithings100.6100.728.1
Wed 10 SepManual100.9100.7
Tue 9 SepBoditrax100.4100.727.6
7-day change−0.9−0.4−0.5

44px rows, 13px muted headers, right-aligned mono numerals, a sticky aggregate footer.