Interface kit

Every control in the application layer, in every state, on one page. If two screens have drifted apart, it shows up here first.

Colour

Read live from config/brand.php, so a swatch can never claim a value the site is not using.

Brand

--void

#0E0A0B

The default surface. Roughly four screens in five.

--oxblood

#5C1F1B

A surface, not an accent. Whole panels, rarely.

--signal

#FF4A2E

Alert only, brand layer. Three per viewport, maximum.

--bone

#E8E2D6

Warm off-white. One or two sections at most.

--ash

#8A7F7C

Muted text on dark.

--hairline

#2E2224

Rules and dividers.

Interactive

--interactive

#FF4A2E

Primary actions, links, focus rings. No budget.

--interactive-hover

#FF6347

Hover on a primary action.

--interactive-press

#E03A20

Active — the moment of the press.

State

--success

#4A9D5F

Confirmed, attended, active.

--warning

#D9A441

Expiring, nearly full, pending.

--danger

#D14545

Cancel, delete, expired, no-show.

Surfaces

--surface-raised

#1A1315

Cards, panels, table headers.

--surface-input

#150F11

Form fields.

--border

#3A2C2E

Default control border.

--border-strong

#5A4548

Emphasis, secondary buttons, hover.

--disabled-bg

#1A1315

A switched-off control.

--disabled-fg

#5A4F4C

Text on a switched-off control.

The three-per-viewport budget applies to --signal used as an alert in the brand layer. --interactive has no budget: every actionable thing has to look actionable.

Type roles

One row per role. Big Shoulders appears in the first two rows and nowhere else on this page — it is a marketing display face, and the application layer never uses it.

Role Face Case and size Specimen
Marketing display Big Shoulders 800 UPPER · 48–200px Strength is a practice
Section eyebrow Big Shoulders / mono UPPER · 11px, 0.18em What we run
Page title (app) Instrument Sans 600 Sentence · 24–32px Your bookings
Section heading (app) Instrument Sans 600 Sentence · 18–20px Credit history
Body Instrument Sans 400 Sentence · 16–18px Small groups, qualified coaches, programming that progresses.
Button Instrument Sans 600 Sentence · 15px Book this class
Form label Instrument Sans 500 Sentence · 14px Email address
Helper / error Instrument Sans 400 Sentence · 13px Confirmations are sent here.
Table header Instrument Sans 500 Sentence · 13px Balance
Numbers and codes Martian Mono 13–15px 06:00 · 24/24 · AED 1,200
Board time Martian Mono 20–28px 18:30

Buttons

Every label carries a verb and an object. Every variant clears a 44px target. Tab through this section to see the focus ring — 2px, --interactive, 2px offset.

Primary

The one decision on a screen. Solid --interactive.

Secondary

Everything else that is still a real action.

Tertiary

In-flow, low weight. The underline is permanent.

Danger

Destructive. Outlined until it is the confirm.

Danger, confirming

The second press, inside a dialog.

Ghost

Chrome only — sign out, close, dismiss.

States

Default

At rest.

Loading

Set on submit, so a slow request cannot be double-pressed.

Disabled

Hover or focus it — the reason is in the title. The component cannot render disabled without one.

Full width

Forms on narrow screens.

Labels

Not this

  • Book
  • Cancel
  • Submit
  • OK

This

  • Book this class
  • Cancel booking
  • Request my free session
  • Save changes

Form fields

Visible border on --surface-input, label above the field, helper text where the label cannot carry it. No field is ever labelled by its placeholder.

Booking confirmations go here. Nothing else does.

Anything the coach should know before your first session.

That address is missing an @ sign. Type it as name@example.com and try again.

Plans are changed at the studio desk, not here.

Checkbox and radio

Status badges

One component for every status in the product. A coloured dot and a word, always — colour alone fails for roughly one man in twelve and for anyone reading over a shoulder.

Booked Attended Cancelled No-show Waitlisted Active Expired Paused Full Places left
Success tone Warning tone Danger tone Info tone Neutral tone

Tables

Distinct header row, hover on every row, numeric columns right-aligned in mono, and an explicit action per row — a clickable row is never the only way in.

A specimen credit ledger
Date Reason Status Change Balance Action
12.07.2026 Booked Olympic Lifting, Thursday 18:00 Booked -1 7
10.07.2026 Cancelled Conditioning, Tuesday 06:00 Cancelled +1 8
08.07.2026 Booked Strength, Sunday 07:00 Attended -1 7

Alerts and explanations

An alert is something that happened. A note explains a decision the system made that the user did not — and it sits in the flow, because a tooltip is not a label.

Booked

Olympic Lifting, Thursday 3 August 18:00. One credit used, seven left.

You do not need to sign in

This is a demonstration build — the sign-in page puts you into any role without a password.

Waitlist 2 — you move up when someone cancels, and a credit is only taken at that point.

All 24 places are taken. Joining the waitlist costs nothing.

Empty states

Never a blank area. A rule, a sentence saying why it is empty, and the button that fills it.

No upcoming bookings

You have nothing in the diary. Pick a class from the week and it will appear here straight away.

No credits have moved yet

Book a class and it will appear here, with the balance it left you on.

Confirmations

Every destructive action states its consequence before the confirm button, in the dialog rather than on the page behind it. Press one — they are live.

Pagination

The first and last pages disable the control they cannot use, and say why on hover.

In place

The same parts assembled the way the portal uses them, so a change here is visible in context rather than only in isolation.

Book a class

The whole week, with the places left in each room. Booking takes one credit; joining a waitlist takes none.

Mon 3 Aug — Sun 9 Aug

  • 18:00
    Olympic Lifting
    2 left
  • 19:00
    Conditioning
    Full

    All 24 places are taken. Joining the waitlist costs nothing — you are only charged a credit if a place comes free.

Keyboard

Tab from here. Every control on this page takes focus in document order and shows the same ring. The skip link at the very top is the first stop.

Back to the top of this page