Links and primary buttons
Brand Guidelines · v1.0
The colour system, type scale and application rules — derived from the live product and measured against it, not sketched alongside it.
Every colour is tied to a role in the interface rather than a decorative mood. That is what stops the palette drifting as the product grows — change the value, and everything playing that role changes with it.
Ocean is a light identity. Surfaces do the work; Primary is punctuation, not a field colour. Aim for roughly this split on any given screen.
Links and primary buttons
Headings, body text, outlines
Cards and reversed text
Accents fill blocks and badges and always carry Ink type on top — all five clear AAA that way. Surfaces are the tinted section grounds that alternate down a page.
Slogan chip, feature blocks
Button hover fill
Badges and the stat circle
Secondary buttons
Review stars
Hero and section background
Alternating sections
Every colour pair the product actually renders, checked against WCAG 2.1. Thirteen pass. Two need attention, and they are called out rather than buried.
Bars are drawn against 7:1 — the AAA threshold for normal text. "Large text" means 24px, or 18.66px bold and above.
At #F5A623 on white the review stars measure 2.03:1. Graphical elements need 3:1. Darkening by 13% lightness clears it and is near-impossible to tell apart at 22px.
Primary on Canvas measures 4.41:1 — fine for the 128px hero word, just under the 4.5:1 needed at body size. Either keep Primary off 18px links on a Canvas section, or darken it fractionally and stop thinking about it.
The identity runs on Metropolis at every level. There is no display face and no secondary face — hierarchy comes from weight, size and tracking. Metropolis is a geometric sans: circular bowls, even stroke, a warmer and more constructed feel than a neutral grotesk, and it needs a little more air to read at size.
Section tags, dropdown items, accordion labels, blockquotes, counters and form fields. At 400 it carries paragraphs without drawing attention; the same family at 700 does the headings, so nothing on the page is competing.
| Hero heading | 128 / 0.98 | 700 · −0.035em |
| Section heading | 96 / 0.98 | 700 · −0.03em |
| Heading 2 | 60 / 1.2 | 700 · −0.025em |
| Card heading | 32 / 1.13 | 600 · −0.02em |
| Body | 18 / 1.55 | 400 · −0.01em |
| Button | 18 / 600 | 600 · −0.01em |
| Eyebrow / label | 11 / 1.4 | 500 · +0.14em caps |
| Tracking tightens as size grows and loosens as it shrinks — but less aggressively than a grotesk would want. Metropolis has circular bowls, and over-tightening closes them up. | ||
| Headings sit in Ink. One word may take Primary — that is the only colour a heading gets. | ||
| Body copy is #555753, never pure Ink, so hierarchy holds without extra weight. | ||
| Sentence case everywhere except buttons and eyebrow labels. | ||
| Never more than three weights on one screen. If something needs to stand out further, change its size, not its weight. |
| Metropolis | Unlicense | Public domain. Self-hosted in assets/fonts — what ships. Weights 300, 400, 500, 600, 700. |
| Inter | SIL OFL 1.1 | Free. First fallback, and the closest substitute if Metropolis is ever dropped. |
| Helvetica Neue | Licensed | Fallback only — present on macOS, not licensed to embed. |
| Suisse Int’l | Commercial | Swiss Typefaces. Per-domain web licence. [NEEDS: budget approval before specifying] |
| Neue Montreal | Commercial | Pangram Pangram. Per-domain web licence. [NEEDS: budget approval before specifying] |
Metropolis is self-hosted, so this document and the site carry their own typography — no CDN, no Google Fonts request, nothing to 404. The family is one token, --font-sans, so a later change is a single line. If that change is to a commercial face, confirm the web licence specifically: a desktop licence does not cover a website.
Live, not screenshots — hover anything.
| Pill buttons | 80px | 18px / 600 |
| Cards & blocks | 20–30px | |
| Chips | 5px |
The default section ground. Ink headings, grey body.
Alternate with Canvas to separate sections without drawing rules.
One feature block per screen. Any more and it stops highlighting.
Beyond the ten named colours the interface uses a long tail of soft washes — card fills, hover states, section grounds. None are chosen by hand: each is remapped onto the hue of the nearest named colour, which is what stops a rebrand leaving stray colours behind.
Generated, not curated. Don't pick from this grid by hand — add a colour to the named palette and let the tints follow.
Worth knowing before someone files them as bugs. Both are inherent to recolouring a product that was drawn in another palette.
Illustrations are bitmaps, so they can't read a colour variable. They're rotated with a filter instead — -28.9° for Ocean, saturation pulled to 0.788 so secondary hues fade rather than clash. One rotation can only be right about one hue: the primary. Photographs and the wordmark are deliberately excluded, since hue-rotating skin tones looks broken.
Four section and testimonial backgrounds are JPEG textures with the colour baked into the pixels — reachable by neither the tokens nor the artwork filter, so they keep their original tint. You can see it below: the lilac testimonial card on an otherwise all-blue page. Fixing it means re-exporting those four files.
The form-validation red is excluded from the palette by design. Rotating it with the brand turned the error message cyan on a blue identity — off-brand is survivable, an error message that doesn't read as an error is not. It stays #FF4D44 whatever the palette does.
Drop this on :root. Every rule in the stylesheet already reads these tokens, so nothing else needs editing. The 53 supporting tints derive themselves from these ten values.
/* Ocean — Remote Doers */ :root { --bk-primary: #1D6FE0; --bk-ink: #08172A; --bk-paper: #FFFFFF; --bk-mint: #8FE9FF; --bk-pink: #A9C8FF; --bk-yellow: #FFD98A; --bk-lime: #CFF2FF; --bk-star: #F5A623; --bk-cream: #EFF7FF; --bk-lilac: #E4EEFF; }
Edit the ten named values and the tints re-derive themselves. Don't hand-edit the tint list — it's generated by tools/gen_brand_kit.py and will be overwritten.
Let Canvas and Paper hold the page. Ocean reads as calm and light.
Flood a section with Primary. It stops being an accent and the hierarchy collapses.
Put Ink type on every accent block. All five clear AAA with Ink on top.
Put white type on an accent. They're light colours; white disappears.
Give one word of a heading the Primary colour, at heading size.
Use Primary for body-size text on a Canvas section — it measures 4.41:1.
Alternate Canvas and Alt canvas to separate sections without drawing rules.
Introduce a colour that isn't in the palette. Add it as a named token instead.
Keep Ink as a deep navy — it's what stops Ocean feeling like default blue-on-black.
Swap Ink for pure black. The whole palette is tuned against that navy.