How to generate css custom properties for typography
- Step 1Type your family name — The **Family name** field (default `Inter`) becomes the first entry in `--font-family-base`. The output always appends a full system fallback chain: `"<Family>", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif`. A separate `--font-family-mono` token (ui-monospace stack) is always emitted too — you don't configure it.
- Step 2Set the base size — **Base size** accepts 10–32px (default 16). This is the px value that maps to `--font-size-base: 1.000rem`. It is also the divisor for every other step, so changing it rescales the whole rem ladder. The value 16 keeps `1rem = base` aligned with the browser default.
- Step 3Choose a ratio — The **Ratio** dropdown offers eight presets: Minor Second (1.067), Major Second (1.125), Minor Third (1.2), Major Third (1.25, default), Perfect Fourth (1.333), Augmented Fourth (1.414), Perfect Fifth (1.5), Golden Ratio (1.618). Larger ratios spread the scale faster — Golden Ratio jumps from base to `2.618rem` at `--font-size-xl` where Major Third is only `1.563rem`.
- Step 4Pick the step count — The **Steps** slider runs 3–12 (default 7). The scale is centred on the base: with 7 steps you get `xs, sm, base, lg, xl, 2xl, 3xl`. Raising steps adds larger labels (`4xl` … `8xl`); the base always stays at index 2, so the two smallest steps (`xs`, `sm`) sit below it and the rest above.
- Step 5Read the generated :root block — The output panel shows the full `:root { ... }` text plus a metrics summary (Family, Base size, Steps, Ratio). The block is read-only here — there is no in-tool rename or reorder. To change a token name (e.g. `--font-size-base` → `--text-base`), paste into your editor and rename there.
- Step 6Copy or download — Use **Copy** to put the block on your clipboard, or **Download** to save it as `typography-tokens.css`. Drop it into your global stylesheet (or a `tokens.css` imported first) and reference tokens with `font-size: var(--font-size-lg)` throughout your components.
The four controls and what each changes in the output
Every option this tool exposes, its real range from the schema, and exactly which part of the :root block it affects. Anything not in this table is emitted as a fixed block you cannot configure here.
| Control | Type / range | Default | What it changes |
|---|---|---|---|
| Family name | Free text | Inter | First entry in --font-family-base; the system fallback chain after it is fixed |
| Base size | Number, 10–32 px, step 1 | 16 | The px value mapped to 1.000rem; divisor for every size token |
| Ratio | Select, 8 presets (1.067–1.618) | Major Third (1.25) | The multiplier between adjacent --font-size-* steps |
| Steps | Range, 3–12, step 1 | 7 | How many --font-size-* tokens are emitted (base always at index 2) |
Tokens always emitted (not configurable here)
The weight, line-height, letter-spacing, and mono-family tokens are hard-coded in the generator. They appear in every output regardless of your option choices.
| Token group | Tokens | Values |
|---|---|---|
| Mono family | --font-family-mono | ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace |
| Weights | --font-weight-light/regular/medium/semibold/bold/extrabold | 300 / 400 / 500 / 600 / 700 / 800 |
| Line heights | --line-height-tight/snug/normal/relaxed/loose | 1.2 / 1.375 / 1.5 / 1.625 / 2 |
| Letter spacings | --letter-spacing-tighter/tight/normal/wide/wider | -0.05em / -0.025em / 0 / 0.025em / 0.05em |
Default scale at 16px base, Major Third ratio
Exact output for the default settings (base 16, ratio 1.25, 7 steps). px column shown for reference; the tool emits only the rem values.
| Token | rem (emitted) | ≈ px |
|---|---|---|
--font-size-xs | 0.640rem | 10.24 |
--font-size-sm | 0.800rem | 12.8 |
--font-size-base | 1.000rem | 16 |
--font-size-lg | 1.250rem | 20 |
--font-size-xl | 1.563rem | 25 |
--font-size-2xl | 1.953rem | 31.25 |
--font-size-3xl | 2.441rem | 39.06 |
Cookbook
Real generator output for different option combinations. Sizes are computed live; the fixed token groups are trimmed here for brevity but always ship in full.
Default Inter scale
ExampleFamily Inter, base 16px, Major Third (1.25), 7 steps. This is what you get out of the box. Note --font-size-base is exactly 1.000rem because the base lands on scale index 2.
:root {
--font-family-base: "Inter", system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--font-family-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--font-weight-light: 300;
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
--font-weight-extrabold: 800;
--line-height-tight: 1.2;
--line-height-snug: 1.375;
--line-height-normal: 1.5;
--line-height-relaxed: 1.625;
--line-height-loose: 2;
--letter-spacing-tighter: -0.05em;
--letter-spacing-tight: -0.025em;
--letter-spacing-normal: 0;
--letter-spacing-wide: 0.025em;
--letter-spacing-wider: 0.05em;
--font-size-xs: 0.640rem;
--font-size-sm: 0.800rem;
--font-size-base: 1.000rem;
--font-size-lg: 1.250rem;
--font-size-xl: 1.563rem;
--font-size-2xl: 1.953rem;
--font-size-3xl: 2.441rem;
}Bigger body, tighter scale
ExampleFamily Roboto, base 18px, Perfect Fourth (1.333), 5 steps — a punchier scale for a reading-heavy app. Only the size tokens shown; the fixed groups still ship above them.
/* base 18px · ratio 1.333 · 5 steps */ --font-size-xs: 0.563rem; /* ≈10.13px */ --font-size-sm: 0.750rem; /* ≈13.5px */ --font-size-base: 1.000rem; /* =18px */ --font-size-lg: 1.333rem; /* ≈23.99px */ --font-size-xl: 1.777rem; /* ≈31.98px */
Referencing the tokens
ExampleHow the emitted tokens are meant to be consumed. The tool does not generate selectors — it only ships the :root block. You write the usage CSS yourself.
body {
font-family: var(--font-family-base);
font-size: var(--font-size-base);
line-height: var(--line-height-normal);
}
h1 {
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold);
line-height: var(--line-height-tight);
letter-spacing: var(--letter-spacing-tight);
}
code {
font-family: var(--font-family-mono);
font-size: var(--font-size-sm);
}Max steps overflow check
ExampleGolden Ratio (1.618) at 12 steps — the maximum. Twelve labels exist (xs…8xl), so the full ladder gets named. The top step is huge (≈1216px); large display sizes like this are usually clamped per-component, not used raw.
/* base 16px · ratio 1.618 · 12 steps */ --font-size-xs: 0.382rem; --font-size-sm: 0.618rem; --font-size-base: 1.000rem; --font-size-lg: 1.618rem; --font-size-xl: 2.618rem; --font-size-2xl: 4.236rem; --font-size-3xl: 6.854rem; --font-size-4xl: 11.089rem; --font-size-5xl: 17.942rem; --font-size-6xl: 29.030rem; --font-size-7xl: 46.971rem; --font-size-8xl: 75.999rem;
Renaming tokens after copy
ExampleThere is no rename UI in the tool. To switch from Tailwind-style size names to your own convention, paste into your editor and find-replace. Example: map to a --text-* prefix.
Generated: After editor find-replace (--font-size- → --text-):
--font-size-xs --text-xs
--font-size-base --text-base
--font-size-lg --text-lg
/* then reference: */
h2 { font-size: var(--text-lg); }Edge cases and what actually happens
Every row below was probed against the live API. Some documented requirements (alphabetical axis order, numerical tuple order) are not actually enforced in practice — useful to know if you've been blaming the wrong thing for a 400.
You expected a clamp()/fluid scale
By design — wrong toolThis generator emits a static rem scale only. There is no fluid toggle in its four controls and the handler contains no clamp() logic. If you need viewport-interpolated sizes, the typography-scale-builder emits both a static and a fluid clamp() variant, and clamp-font-size-generator builds a single fluid token.
Base size below 10 or above 32
Clamped by inputThe Base size field is a number input bounded to 10–32 with step 1. Typing 8 or 40 is rejected by the control's min/max; the generator's own fallback is 16 if the value is missing. Pick the in-range value closest to your design and scale visually with the ratio rather than pushing the base out of bounds.
Steps set below 3 or above 12
Clamped to 3–12The slider only allows 3–12, and the handler additionally clamps with Math.max(3, Math.min(12, steps)). Because exactly 12 labels exist (xs through 8xl), the step-N fallback name in the code never actually fires — at the maximum of 12 steps the last token is cleanly named --font-size-8xl.
Family name with quotes or special characters
Wrapped verbatimWhatever you type is trimmed and wrapped in double quotes inside --font-family-base: "<Family>", .... If your family name itself contains a double quote it will produce invalid CSS — use the literal family name as registered (e.g. IBM Plex Sans, no surrounding quotes needed in the field). Empty input falls back to Inter.
You wanted JSON / W3C design tokens output
Not supportedThe output type is css only — a :root block, MIME text/css, downloaded as typography-tokens.css. There is no JSON, Sass, or W3C Design Tokens export. To feed a token pipeline, copy the CSS and translate the values yourself, or read the dedicated guide on wiring this output into a build.
Mono family not what you wanted
Fixed value--font-family-mono ships the same ui-monospace, SFMono-Regular, ... stack every time and is not configurable here. To build a custom monospace stack (or a serif/OS-targeted one), use system-font-stack-generator and paste its output alongside this block.
Rounding makes two steps look almost equal
ExpectedSizes are rounded to two decimal places in px, then divided by base and printed to three decimal rem places. At a small ratio like Minor Second (1.067), adjacent rem values are very close (e.g. 0.937rem vs 1.000rem). That is correct modular-scale behaviour — small ratios produce subtle scales. Pick a larger ratio for more contrast.
Output references a font you haven't loaded
Fallback rendersSetting Family name to Inter does not load Inter — it only names it in the variable. If the font isn't loaded via @font-face or a <link>, the browser falls through to the system fallback chain (system-ui etc.). Pair this with font-face-generator or google-fonts-css-generator to actually load the family.
Frequently asked questions
Are the sizes in px or rem?
rem. Each --font-size-* is (stepPx / baseSize) printed to three decimal places, so --font-size-base is always 1.000rem and --font-size-lg at the default ratio is 1.250rem. Using rem means the whole scale respects a user's browser default font-size for accessibility.
Can I generate a fluid clamp() scale here?
No — this tool emits a static rem scale only and has no fluid option. Use typography-scale-builder, which outputs both a static rem scale and a fluid clamp() variant in one go, or clamp-font-size-generator for a single fluid token.
What naming convention does the output use?
Tailwind-style size names: xs, sm, base, lg, xl, 2xl, 3xl (and up to 8xl at higher step counts). There is no rename control in the tool — paste the output into your editor and find-replace if you prefer a different prefix or convention.
Why is the base always in the middle of the scale?
The scale is built as base × ratio^(i − 2), so index 2 (--font-size-base) is exactly the base size. With the default 7 steps, two tokens (xs, sm) fall below the base and four (lg through 3xl) above it.
What weights and line-heights does it include?
A fixed set every time: weights 300/400/500/600/700/800 (light through extrabold) and line-heights 1.2/1.375/1.5/1.625/2 (tight through loose). These are not configurable in this tool — they ship as a complete vocabulary you reference by intent.
Does it include letter-spacing tokens?
Yes — a fixed group: --letter-spacing-tighter (-0.05em), tight (-0.025em), normal (0), wide (0.025em), wider (0.05em). They're emitted unconditionally; use them on headlines and small caps where tracking matters.
How many size steps can I generate?
Between 3 and 12. The slider and the handler both enforce that range. Twelve steps fills the full xs→8xl label set.
Does it load the font I name?
No. The Family name only populates the --font-family-base value. To actually load the font, generate an @font-face block with font-face-generator or a Google Fonts link with google-fonts-css-generator.
Can I edit the output before copying?
Not inside the tool — the output panel is read-only. Copy or download it, then edit in your code editor: rename tokens, drop groups you don't need, or merge with other generators' output.
Is anything uploaded?
No. This is a generative tool — it takes no font file. The :root block is built in your browser from your four option values and copied to your clipboard. Nothing leaves your machine.
Why custom properties instead of Sass variables?
Runtime mutation and inheritable scoping. CSS custom properties exist in the browser, so you can re-bind them in a media query (dark mode) or a [data-theme] scope and the whole tree updates — no recompile. Sass variables compile away to static values.
Can I theme on top of these tokens?
Yes — that's the point of custom properties. Generate the base :root block here, then add your own overrides like [data-theme="dense"] :root { --font-size-base: 0.875rem; } in a separate file. The Download button saves the block as typography-tokens.css (MIME text/css); import it first so the variables are defined before any rule references them. For dark-mode-specific size/weight nudges, see dark-mode-font-adjuster.
Privacy first
Every JAD Font tool runs entirely in your browser using opentype.js and the wawoff2 WASM Brotli encoder. Your fonts never leave your device — verified by zero outbound network requests during processing.