1Design tokens
The "Violet & Steel" palette, dark-first. Token names and structure are inherited from TDL; only the values change. Accent is indigo-violet; the reserved semantic colors (warn / danger / PR) are held distinct from it.
steel pagecardtint#6d5ef0 violetamberredgreen · PR onlyhairline2Typography
System sans for body, a strong display family for titles and the big numerals, mono for IDs/weights/code. Weights are the hero — rendered in display weight with tabular figures.
3Buttons
The elevation invariant: 1px border, drop shadow, contrasting fill. One
.btn-primary per context. Danger carries an automatic ⚠ glyph; destructive (filled red) is confirmation-only.
4Links
Every link is underlined by default — the universal text affordance. Accent for primary, muted for secondary. Never blue.
5Status & chips
Status is flat, glyph-prefixed, non-interactive — the glyph carries meaning so it survives colorblindness. Chips are flat metadata pills with no border. PR uses the reserved green.
6Form inputs
1px border, subtle fill, accent focus ring. Labels above the control.
7Cards
Primary content container. Cards never nest. .card-danger for irreversible-action zones.
Push A v4
6 exercises · last run 24 May
Delete routine
Past sessions are kept; only the template is removed.
8Modals
Themed dialogs only — never alert()/confirm(). Two buttons: Cancel left, commit right.
Discard this set?
9Tables
Uppercase-muted headers, muted separators. Numeric columns use mono.
| Date | Avg weight | Volume | Max est. 1RM |
|---|---|---|---|
| 28 May | 91.7 kg | 25 | 122 |
| 24 May | 90.0 kg | 24 | 119 |
| 21 May | 90.0 kg | 22 | 117 |
10Tabs
Active tab gets the accent bottom border. Tabs navigate; toolbars act.
11Sidebar
Selectable rows; active item carries a 3px accent left border (never a full background).
12Topbar
App chrome. Brand link top-left (the one un-underlined link), mode bar centered,
chrome buttons right. .chrome-btn and .brand-link appear only here.
13Feedback
Toasts carry a glyph prefix. Empty states say what is missing and what to do next.
14Progress & code
1RM = weight * 36 / (37 - reps)
15Menus
Short choice lists. Danger item last, separated.
16Gym primitives PFDL
Domain primitives that do not exist in TDL/ADL. A descending-weight sequence of
.set-rows is a drop set (no special type). The .rep-stepper is the hero
control — reps are an output, recorded to failure. .deviation marks plan-vs-actual
(weight/technique/order) with a glyph + the struck-through original. PR uses the reserved green.
PR
17Invariant rules
Inherited from TDL, plus the PFDL-specific reserved-semantics rule. Violations are bugs, not style preferences.
- Category separation. Every interactive/state element is exactly one of
.btn,.status,.chrome-btn,.brand-link; categories never share a location. - Color plus glyph. Any state that matters is carried by both color and shape. ⚠ destructive, ✓ PR/current, ↻ deviation, colored dot for status.
- No false affordances. Non-clickable things must not look clickable. No outlined static labels, no
cursor: pointeron non-buttons. - One primary per context. Exactly one
.btn-primaryper view/form/modal. - Every link underlined. The brand link is the one exception.
- Reserved semantics. Accent is never success. amber = warn, red = danger, green = PR / positive only. The accent (violet) carries interactivity, not state.
- Numerals are display-weight, tabular. Weights and reps render in the display family with
tabular-numsso columns of numbers align and read at a glance. - The gym surface is mobile-first and offline-first. It is the one phone-width, service-worker-backed route; everything else is desktop server-rendered (DESIGN §9.1).
- ISO UTC everywhere in data, logs, configs. Local time is a display conversion only.
- No browser dialogs.
alert()/confirm()/prompt()are banned; every dialog is a themed modal.