Foundations
Typography
Lexend Light carries everything Kaptio says. Lexend Bold carries what matters. JetBrains Mono carries what a machine produced. Three jobs, two families, and no weight in between.
Two voices
A sans that speaks, and a mono that reports.
Flux 1.x had one family and two weights, which kept output consistent but left no way to distinguish a booking reference from a sentence. Everything looked like prose, including the things that were not.
2.0 promotes JetBrains Mono Variable from a code font to a documented second voice. It carries identifiers, metadata, structural labels and values — anything that is data rather than language. Look at the eyebrow above this page's title, or the column headers in any Flux table: that is the second voice doing its job.
Lexend
Two weights. Nothing between them, and nothing outside them.
Lexend Light 300 — the default voice
Lexend Bold 700 — headings and emphasis
<p style="font-family: var(--flux-font-sans); font-weight: 300; font-size: var(--flux-text-4xl); letter-spacing: var(--flux-tracking-tight); margin: 0">
Lexend Light 300 — the default voice
</p>
<p style="font-family: var(--flux-font-sans); font-weight: 700; font-size: var(--flux-text-4xl); letter-spacing: var(--flux-tracking-tight); margin: var(--flux-space-4) 0 0">
Lexend Bold 700 — headings and emphasis
</p>Light is the default for everything, headings included. Bold is for headings that need to anchor a page, for emphasis inside a sentence, for labels, and for table headers.
There is no 400, 500 or 600. A request for a weight Flux does not load gets synthesised by the browser into something that is not Lexend, which is why the base layer forces every bold element back to 700.
/* From flux.css. This is enforced, not suggested. */
b, strong, h1, h2, h3, h4, h5, h6, th {
font-weight: var(--flux-weight-bold);
}JetBrains Mono
Three weights, because the second voice has its own hierarchy.
400 Regular — code, values, raw output
500 Medium — inline metadata, badges, keyboard hints
700 Bold — structural labels, small and letter-spaced
<p style="font-family: var(--flux-font-mono); font-weight: 400; font-size: var(--flux-text-lg); margin: 0">
400 Regular — code, values, raw output
</p>
<p style="font-family: var(--flux-font-mono); font-weight: 500; font-size: var(--flux-text-lg); margin: var(--flux-space-2) 0 0">
500 Medium — inline metadata, badges, keyboard hints
</p>
<p style="font-family: var(--flux-font-mono); font-weight: 700; font-size: var(--flux-text-xs); letter-spacing: var(--flux-tracking-widest); text-transform: uppercase; margin: var(--flux-space-4) 0 0">
700 Bold — structural labels, small and letter-spaced
</p>| Weight | Token | Use |
|---|---|---|
| 400 Regular | --flux-weight-mono-regular | Code blocks, raw values, identifiers in body copy. |
| 500 Medium | --flux-weight-mono-medium | Inline metadata, version numbers, keyboard hints, table figures. |
| 700 Bold | --flux-weight-mono-bold | Eyebrows, column headers, section labels. Always small, uppercase and letter-spaced. |
text-lg or larger reads as a warning label. Keep the bold, letter-spaced form attext-xs.Prototypes
Product prototypes set in Google Sans Flex and Manrope. Decks and this site stay on Lexend and JetBrains Mono.
<link rel="stylesheet" href="https://flux.kaptio.com/tokens/v2/flux.css" />
<link rel="stylesheet" href="https://flux.kaptio.com/tokens/v2/flux-components.css" />
<link rel="stylesheet" href="https://flux.kaptio.com/tokens/v2/flux-prototype.css" />Sold-out departures
Review sold-out departures before they need a stop-sell.
79.7%
+3.2 pts vs last week
<div style="display: grid; gap: var(--flux-space-5)">
<h2 style="margin: 0; font-size: var(--flux-text-2xl)">Sold-out departures</h2>
<p class="flux-description" style="margin: 0">Review sold-out departures before they need a stop-sell.</p>
<div>
<p class="flux-meta" style="margin: 0">Berths sold against capacity</p>
<p class="flux-kpi" style="margin: var(--flux-space-2) 0">79.7%</p>
<p class="flux-kpi-change" style="margin: 0; color: var(--flux-text-success)">+3.2 pts vs last week</p>
</div>
<div class="flux-field" style="max-width: 20rem">
<label class="flux-field__label" for="type-search">Search</label>
<input class="flux-input" id="type-search" type="text" placeholder="Voyage, ship or order" />
</div>
<div>
<button class="flux-button" type="button">Ask Runa for a brief</button>
</div>
</div>| Role | Font | Size | Weight | Apply with |
|---|---|---|---|---|
| Headings | Google Sans Flex | From the scale, as in Lexend | Medium | h1–h6 |
| Card heading | Google Sans Flex | text-base · 16px | Medium | .flux-card__title |
| KPI value | Google Sans Flex | text-4xl · 36px | Bold, text-brand | .flux-metric__value, .flux-kpi |
| Body and description | Manrope | text-base · 16px | Regular | body, .flux-description |
| Tables | Manrope | text-sm · 14px | Regular | .flux-table |
| Input and dropdown | Manrope | text-sm · 14px | Regular | .flux-input, .flux-select |
| Labels | Manrope | text-sm · 14px | Medium | .flux-field__label |
| Buttons | Manrope | text-sm · 14px | Semi Bold | .flux-button |
| KPI change | Manrope | text-sm · 14px | Semi Bold | .flux-metric__change, .flux-kpi-change |
| Small and meta | Manrope | text-xs · 12px | Regular | .flux-meta |
flux-prototype.css remaps the font and weight roles every component reads, so buttons, fields, tables and badges retype without new markup. Prototypes have no mono voice: identifiers, badges and column headers set in Manrope. Sizes do not change: a prototype uses the same scale and component sizes as the Lexend product. The px values are the equivalents at the 16px root; the root stays fluid.
Set weights with --flux-prototype-weight-regular, -medium,-semibold and -bold, never a literal number. A KPI change carries a sign or arrow as well as its status colour, so the direction does not rely on colour alone.
This site shows prototype regions inside Lexend pages: its root setsdata-flux-type="lexend", and each component specimen setsdata-flux-type="prototype". A prototype needs neither attribute.
flux-prototype.css in a deck. Decks keep Lexend and JetBrains Mono.The scale
| Token | Size | Typical use |
|---|---|---|
--flux-text-xs | 0.75rem | Short metadata, badges, help text |
--flux-text-sm | 0.875rem | Dense UI, table cells, secondary copy |
--flux-text-base | 1rem | Body |
--flux-text-lg | 1.125rem | Lede, card titles |
--flux-text-xl | 1.25rem | Subsection headings |
--flux-text-2xl | 1.5rem | Section headings |
--flux-text-3xl | 1.875rem | Page headings, dense layouts |
--flux-text-4xl | 2.25rem | Page headings |
--flux-text-5xl | 3rem | Marketing display |
--flux-text-6xl | 3.75rem | Slide display |
Sizes are in rem, and the root is fluid — see below. Atext-base paragraph is 16px on a laptop and 19px on a large display, without a media query.
text-xs is not a compact body size. Reserve it for badges, help text and short metadata. Controls, table cells and compact secondary copy start at text-sm; sentences intended to be read use text-base. If content does not fit, reflow or scroll it rather than shrinking the type.The fluid root
Every rem in Flux scales. This is the one place the system reaches past tokens into a base rule.
html {
font-size: clamp(16px, 0.875rem + 0.4vw, 19px);
}Type, spacing and component heights all move together, because they are all expressed inrem. A dashboard on a 27-inch display gets a proportionally larger interface rather than the same 16px interface with more empty space around it.
The consequence to remember: never mix a px value into a layout built from Flux spacing. It will not scale with everything around it, and the gap will widen as the viewport does. The one sanctioned exception is--flux-hairline, which is 1px on purpose.
Measure and leading
| Token | Value | Use |
|---|---|---|
--flux-measure-prose | 38rem | Reading column. Roughly 70 characters. |
--flux-measure-content | 62rem | Mixed content: tables, cards, specimens. |
--flux-measure-wide | 78rem | Full-bleed layouts and dashboards. |
--flux-leading-tight | 1.15 | Display headings. |
--flux-leading-snug | 1.3 | Headings and dense labels. |
--flux-leading-normal | 1.55 | Interface default. |
--flux-leading-relaxed | 1.7 | Body copy meant to be read. |
leading-relaxed inside measure-prose is the combination the rest of the system is tuned around.Do and don’t
Do
- Use Lexend 300 for everything, and 700 when something has to anchor.
- Reach for the mono voice when the content is data rather than language.
- Keep body copy inside measure-prose at leading-relaxed.
- Use at least text-sm for controls and table cells, and text-base for readable sentences.
- Use tracking-tight on headings at text-3xl and above.
- Use tracking-tighter only on metric figures, through --flux-metric-value-tracking. It is too tight for words.
Don’t
- Do not request a Lexend weight other than 300 or 700.
- Do not set mono uppercase above text-xs.
- Do not use text-xs for paragraphs, table cells, controls or primary values.
- Do not shrink type to fit more content above the fold.
- Do not mix px into a layout built from rem tokens.
- Do not use another sans-serif family. Lexend everywhere, except prototypes, which load exactly two through flux-prototype.css.