05 · Brand Layer
Foundations
Colour, type, shape and elevation — the tokens everything is built from.
01 · Overview
Overview
GALTON Brands is a dark-first, editorial, high-contrast brand system. The page floor is a near-black canvas (#1A1A1A — #1A1A1A), text is white (#FFFFFF), and a single signature primary yellow (#FFD700 — #FFD700) carries every accent — CTAs, highlights, focus rings, the hero word. Yellow is never a large fill; on a yellow surface text is always black (#1A1A1A).
Everything is set in one typeface — GALTON — at Light (300). Weight steps up to Medium (500) / Bold (600) only for emphasis. H1/display headlines and section titles (H2) are UPPERCASE; H3+ and body are Sentence case; uppercase elsewhere only for eyebrow, button and badge. Where GALTON can't be embedded (email, Google Docs/Slides, system contexts), Montserrat is the official substitute — it is last in every font stack. Hierarchy is created by size and a leading ratio (Headline ×1.1, Subhead ×1.2, Body ×1.5), not by heavy weights.
The component language is pill-and-flat: buttons are full pills (100px), uppercase, GALTON Bold; inputs are flat (0px), transparent, bottom-border only, with a primary-yellow focus. The palette is fixed — no invented shades; raised surfaces are built from white at low opacity (rgba(255,255,255,0.04)), never a new hex.
If this system doesn't define something, do not invent it. Derive it from the primitives in the Fallbacks & unknown elements section (the
fallbackblock): monochrome surfaces and text, hairline lines, and a single yellow accent — never a red/blue/green you reached for by default. This one rule prevents most off-brand output.
02 · Colors
Colors
Primary palette
Secondary palette
Semantic tokens
How the palette is applied across the UI. Reference these as {colors.name} — they resolve to the brand colours above (or a grey derived from black/white at opacity).
Brand palette
The brand has three colours, in two groups:
- Primary palette — White (#FFFFFF) and Black (#1A1A1A). The base of the system: black is the dark-first canvas, white is the ink.
- Secondary palette — Yellow (#FFD700). The single accent: CTAs, highlights, focus rings, the hero word. Used sparingly, never a large fill; on a yellow surface, foreground is always black (#1A1A1A).
Text is always solid: white (#FFFFFF) on dark, near-black (#1A1A1A) on light. Only non-text structure (rgba(255,255,255,0.12) dividers, rgba(255,255,255,0.04) panels, chart marks) is derived from white at low opacity. The canonical statement of this, and the one agents receive with every colour payload, is the colorRules block in the token frontmatter.
Surface
- Canvas (
#1A1A1A— #1A1A1A): The default page floor on every dark surface. The brand is dark-first. - Surface Raised (
rgba(255,255,255,0.04)— white @4%): Cards, modals, raised panels. Built from white at low opacity over the canvas — never a new hex. Pair with argba(255,255,255,0.12)border. - Hairline (
rgba(255,255,255,0.12)— white @12%): 1px dividers, table rows, card outlines, the top/bottom bars. - Light Canvas (
#FFFFFF— #FFFFFF): The floor for light surfaces (cards on light, print, email).
The full light rendering is the lightSurface block in the token frontmatter: which roles change, which do not, and the rules that go with it. brand.css emits it as a .bl-light scope, so a light area uses the SAME .bl-{component} classes and nobody derives an inversion by hand.
Text
Four roles, one value. #FFFFFF (headlines, primary text), #FFFFFF (running text), #FFFFFF (secondary paragraphs, leads) and #FFFFFF (captions, metadata, placeholders, disabled) are all #FFFFFF on the dark canvas. The names mark editorial role, not a different colour. On a light surface all four become #1A1A1A (#1A1A1A).
The .bl-text-* classes in brand.css bind each role to its type token, so .bl-text-caption is the caption size AND the caption colour in one class. Dividers and borders use rgba(255,255,255,0.12), never a grey text colour.
Usage Rules
- Default page =
#1A1A1Abackground,#FFFFFFtext. #FFD700is for emphasis only — never a large fill. On a primary surface, text is always black.- Need a raised surface? Build it from white/grey at low opacity (
rgba(255,255,255,0.04)) — do not introduce a new hex. - Selection highlight is primary-on-black.
- Text is never translucent. See
colorRules.text: solid values only, hierarchy from size, weight and case. On a primary surface text is solid#1A1A1A. - Text contrast meets WCAG AA at all sizes; the yellow accent is never used for text on white.
03 · Combinations
Combinations
04 · Typography
Typography
- Family
- GALTON
- Default
- 300 · Sentence case
- Weights
- 100 Thin · 300 Light (default) · 400 Regular · 500 Medium · 600 Bold · 700 Extrabold · 800 Black
- Fallback
- Montserrat
- · 100–900· the single web font — variable, covers every weight
@font-face {
font-family: "GALTON";
src: url("https://galton-ai-assets.vercel.app/brand/fonts/GaltonVariable.woff2") format("woff2-variations");
font-weight: 100 900;
font-style: normal;
font-display: swap;
/* Vertical-metric correction — KEEP these three lines. The variable woff2
sets USE_TYPO_METRICS with an asymmetric 750/-250 typo ascent/descent,
which floats UPPERCASE labels ~0.1em high in tight (line-height:1) boxes
like buttons and badges. These overrides pin the webfont to the desktop
masters' metrics (ascent−descent ≈ the cap height) so text centres
correctly everywhere — no per-button padding hacks needed. */
ascent-override: 96%;
descent-override: 24%;
line-gap-override: 0%;
}Type scale & hierarchy
Size, scale and position all play a factor in how information is read. Always ensure there is a purposeful difference between type sizes. Type sizes are examples only.
Confident headlines, set in GALTON Light.
Leading = size × 1.1 → 80/88pt
Subheads carry a clear, secondary voice beneath the headline.
Leading = size × 1.2 → 30/36pt
Body copy holds the detail at a comfortable measure, with a leading ratio that keeps long passages easy to read across the page.
Leading = size × 1.5 → 18/27pt
All type styles
The quick brown fox
The quick brown fox
The quick brown fox
The quick brown fox
The quick brown fox
The quick brown fox
The quick brown fox
Section label
Button label
The quick brown fox
Section label
Font Family
The system runs one typeface — GALTON — for both headings and body, from a single variable woff2 that covers every weight 100–900 (the only web font). The default is Light (300), set in Sentence case at normal tracking.
Montserrat is the official substitute: use it wherever GALTON can't be embedded (email, Google Docs/Slides, system contexts). It is last in every font stack: GALTON, Montserrat, ui-sans-serif, system-ui, sans-serif. There is no second brand face — GALTON does everything.
Weights — the GALTON named cuts: 100 Thin · 300 Light (default) · 400 Regular · 500 Medium · 600 Bold · 700 Extrabold · 800 Black. The single variable woff2 covers 100–900 continuously, so any in-between weight is available. Buttons use Bold (600) — the same Galton-Bold weight the live site renders its .btn at.
Font source & embedding
The GALTON typeface ships with this package — don't substitute a lookalike. Load the variable woff2 (one file, weights 100–900):
- File:
https://galton-ai-assets.vercel.app/brand/fonts/GaltonVariable.woff2— the single web font (variable, weights 100–900). Embed this and nothing else. - Base URL:
https://galton-ai-assets.vercel.app/brand/(resolved to wherever this is deployed) - Hotlink these absolute URLs exactly as given — the files are served with permissive CORS (
Access-Control-Allow-Origin: *), so a cross-origin@font-faceworks. Do not relativise them to/brand/fonts/…(that resolves against your host and 404s, silently falling back to Montserrat) and don't copy the file locally. One exception: in the unzipped offline package (https://galton-ai-assets.vercel.app/bundle.zip),./brand/fonts/GaltonVariable.woff2is this file, so an offline build pointssrcthere.
@font-face {
font-family: "GALTON";
src: url("https://galton-ai-assets.vercel.app/brand/fonts/GaltonVariable.woff2") format("woff2-variations");
font-weight: 100 900;
font-style: normal;
font-display: swap;
/* Vertical-metric correction — KEEP these. The variable woff2 ships with
USE_TYPO_METRICS and an asymmetric 750/-250 typo ascent/descent, so the glyphs
sit ~0.1em high in tight (line-height:1) boxes — UPPERCASE buttons and badges
look floated up and off-centre. These overrides pin the webfont to the desktop
masters' metrics (ascent−descent ≈ cap height) so labels centre correctly. */
ascent-override: 96%;
descent-override: 24%;
line-gap-override: 0%;
}Why the three
*-overridelines matter. Without them, GALTON's UPPERCASE labels float ~0.1em above the optical centre of any pill/badge (the font reserves empty descender space it never uses). The overrides re-declare the metrics the browser lays text out with — so you center buttons and badges with plain symmetric padding, never an asymmetric "push the text down" hack. Embed the@font-faceexactly as given, descriptors included.
Then use the stack GALTON, Montserrat, ui-sans-serif, system-ui, sans-serif. Montserrat is the substitute and loads from Google Fonts (https://fonts.google.com/specimen/Montserrat) — it is last in the stack and only kicks in where GALTON can't be embedded. The full fonts block in the frontmatter lists every file, format and the ready-to-paste @font-face.
Hierarchy
All GALTON Light (300). H1/display headlines and section titles (H2) are UPPERCASE; H3+ and body are Sentence case; uppercase elsewhere only for eyebrow, button and badge (matching the slide system). Leading (line-height) is a ratio of the type size. Display sizes are responsive: the token value is the desktop step and the full ladder is the token's own responsive map, keyed by the breakpoints block. brand.css emits the media queries, so the .bl-text-* classes resize on their own.
| Token | Size (responsive) | Weight | Line Height | Letter Spacing | Use |
|---|---|---|---|---|---|
{typography.display-lg} | 56 → 88 → 136 → 180px | 300 | 1.05 | 0 | Jumbotron / extreme display |
{typography.display} | 48 → 72 → 96 → 120px | 300 | 1.1 | 0 | Large hero headline |
{typography.headline} | 40 → 52 → 64 → 80px | 300 | 1.1 | 0 | Headline (size ×1.1) |
{typography.subhead} | 22 → 24 → 30px | 300 | 1.2 | 0 | Subhead (size ×1.2) |
{typography.body} | 18px | 300 | 1.5 | 0 | Body — the web/body minimum (never below 18px) |
{typography.body-strong} | 18px | 500 | 1.4 | 0 | Emphasis inside body |
{typography.eyebrow} | 15px | 500 | 1.4 | 0.22em | Section label — uppercase overline above headlines, primary color |
{typography.button} | 18px | 600 | 1.0 | 0 | Uppercase button labels (GALTON Bold = 600, the live .btn weight) |
{typography.label} | 18 → 24px | 300 | 1.4 | 0 | Form labels |
{typography.badge} | 15px | 400 | 1.0 | 0.08em | Uppercase pill chip (the global 15px minimum) |
Principles
Emphasis comes from size and leading, then a small step in weight (300 → 500/600) — never from uppercase body copy. The rule of thumb for any new size: Headline ×1.1, Subhead ×1.2, Body ×1.5 leading, with a purposeful jump between levels.
H1/display headlines and section titles (H2) are UPPERCASE; H3+ and body are Sentence case; uppercase elsewhere only for eyebrow, button and badge. Never uppercase body copy.
Note on Font Substitutes
If GALTON is unavailable, fall back to Montserrat (Google Fonts) — the official substitute, already last in every stack. Montserrat is a geometric sans close to GALTON in proportion; keep the Light (300) default and Sentence case. Do not substitute a serif or a condensed face.
05 · Data visualization
Data visualization
12345Read chart via get_chart_palette. One highlight per chart, never a library-default rainbow, and red/green only for genuine good-vs-bad deltas.
Charts are dark-first and monochrome-led, exactly like the rest of the system: a near-black plot floor, hairline gridlines, muted labels, and one yellow accent carrying the series you want read first. The full palette is the chart block in the frontmatter (read it via get_chart_palette or list_tokens → chart).
Colour rules
- Categorical series use the ramp in order: the signature yellow first (the highlight), then white, then white at 62% / 40% / 24%. Five distinguishable categories is the practical ceiling — beyond that, prefer small multiples over more colours.
- Never reach for a chart library's default palette (the rainbow), and never use red / blue / green as a decorative series colour. This is the most common way a generated chart goes off-brand.
- The plot background is
#1A1A1A, gridlines arergba(255,255,255,0.12), axis ticks and labels are#FFFFFF, data labels#FFFFFF. - Semantic good/bad colouring (
#5FB87A/#E0664E) is used only when the chart literally encodes positive vs negative — a delta, a target met/missed. Otherwise stay in the yellow-and-neutral ramp. - One highlight per chart. Put the series that matters in yellow and keep the rest neutral; that single accent does the work a rainbow only pretends to.
Sequential / intensity
For a heat ramp or single-measure intensity, go white @10% → white @40% → yellow (low → high). Keep the steps few and legible.
06 · Status & feedback colours
Status & feedback colours
#5FB87AConfirmation, valid input
#FFD700Caution (the brand yellow)
#E0664EError, failed validation
#7FB2D4Neutral, informational
Functional, not brand, colours — semantic feedback only. They are never the accent (that's always yellow) and never a large fill: a 3px rule, an icon, a 1px error border.
The brand palette is fixed — black, white, yellow. But real products must signal success, warning, error and information, and an agent with no sanctioned status colour will invent a garish one (the red sliders and green box that prompted this work). So the system defines four functional status hues, muted for the dark canvas:
| Role | Token | Hex | Use |
|---|---|---|---|
| Success | #5FB87A | #5FB87A | Confirmation, valid input, positive delta |
| Warning | #FFD700 | #FFD700 | Caution / attention (this is the brand yellow) |
| Error | #E0664E | #E0664E | Error, destructive action, failed validation |
| Info | #7FB2D4 | #7FB2D4 | Neutral, informational |
These are functional, not brand, colours. Hard rules:
- They signal status only — form validation, alerts/notifications, semantic chart deltas. Never decoration, never a heading colour, never a button fill.
- They are never the accent. The accent is always
#FFD700(yellow). An active tab, a selected chip, a slider fill, a focus ring — all yellow, neversuccess/error. - They appear minimally: a 3px rule on an alert, an icon, a 1px error border on an input — never a large coloured panel.
07 · Elevation & Depth
Elevation & Depth
| Level | Treatment | Use |
|---|---|---|
| Flat | No shadow, no border | Body, top nav, footer, most sections |
| Hairline | 1px rgba(255,255,255,0.12) border | Section dividers, table rows, card outlines |
| Raised surface | rgba(255,255,255,0.04) (white @4%) + hairline border | Cards, modals, panels — no drop shadow |
| Accent glow | Radial primary tint at very low opacity | Optional hover halo on a feature panel only |
The system avoids drop shadows and gradients. Depth comes from the contrast between the near-black canvas and a barely-raised white-at-opacity surface, plus the hairline. Do not add material-style elevation shadows.
08 · Shapes
Shapes
Border Radius Scale
| Token | Value | Use |
|---|---|---|
0px | 0px | Inputs, textareas, selects, checkboxes — always flat |
10px | 10px | Small UI elements |
20px | 20px | Standard card / media container — the signature radius (galton.sk uses 20px on nearly every card, image and media block) |
28px | 28px | Large panel / floating select dropdown (galton.sk .nice-select-dropdown) |
100px | 100px | Buttons, tags, chips, the badge |
50% (not a token) | 50% | True circles only — avatars, status dots, the custom cursor (galton.sk) |
The radius language is intentional and matches the live site: form controls are flat (0px); interactive pills are fully round (100px); content containers and media are softly rounded (20px), with large floating panels at 28px; true circles use 50%. Avoid arbitrary in-between radii — galton.sk's one-off 5/12/15/25/30px values are page-specific and not part of the system.
