Typography
The FSAI type scale and the two font families we use.
Overview
The type scale lives in fsai/packages/config-tailwind/theme.css. Every size is a CSS custom property that Tailwind exposes as a text-* utility, and each size ships a matching --text-*--line-height — so leading-* rarely needs to be set by hand.
The scale is intentionally compact. Nine general-purpose sizes cover everything from microcopy to display headings, plus three applicant-portal headings that tenants can re-brand.
--text-xs: 10px / 13px
--text-sm: 12px / 16px
--text-md: 13px / 18px
--text-base: 14px / 20px ← inherited body default
--text-lg: 16px / 24px
--text-xl: 20px / 28px
--text-2xl: 24px / 32px
--text-3xl: 32px / 40px
--text-4xl: 40px / 48pxEvery value is overridable per-tenant via unprefixed CSS variables (for example, --sm-font-size, --sm-line-height, --base-font-size), which is how the portal theme editor customizes sizes at runtime.
The Scale, Rendered
Ordered small to large. Each specimen renders at the exact font-size / line-height defined in the theme, independent of this site's own typography.
text-xs
--text-xs
10px / 13px
text-sm
--text-sm
12px / 16px
text-md
--text-md
13px / 18px
text-base
--text-base
14px / 20px
text-lg
--text-lg
16px / 24px
text-xl
--text-xl
20px / 28px
text-2xl
--text-2xl
24px / 32px
text-3xl
--text-3xl
32px / 40px
text-4xl
--text-4xl
40px / 48px
When To Use Each Size
text-xs — 10 / 13
Use sparingly. This is for tight metadata that sits next to something else on the line — counter badges, tabular footnotes, trailing metadata in a dense row. If a user needs to read it as content, it's too small.
Do not reach for text-xs for body copy, paragraphs, helper text, field labels, or validation errors. Those should be text-sm or larger.
text-sm — 12 / 16
Compact UI text. Small form controls, tooltips, compact table rows, dropdown rows, menu items, and any chrome that needs to pack tightly without becoming unreadable.
This is the default size for "small" variants of most input-style components.
text-md — 13 / 18
The half-step between compact chrome and body. Useful when content sits just below body copy — row titles and subtitles in list or grid views, labels in dense toolbars, metadata text that should read comfortably but stay quieter than body.
text-md is sometimes combined with a responsive step-up (sm:text-base text-md) when a control should bump up on tablet and above. That is a specific responsive pattern, not a rule. Use plain text-md when you want 13px at every breakpoint.
text-base — 14 / 20
Default body text. Both brand-dashboard and applicant-portal apply @apply text-base; to body, so every element inherits 14/20 unless it opts into a different size.
Do not write text-base on every paragraph. Let it inherit. The class is only needed when an element sits inside a parent that overrode the size and you want to restore body, or when a responsive control explicitly steps up to it.
text-lg — 16 / 24
Inline section headings — titles that live inside a larger surface (a panel, a card, a sidebar group) and name a region without claiming page-level presence.
Typically paired with font-semibold or font-bold and a strong text color.
text-xl — 20 / 28
Dialog, modal, and card titles. The heading owns a region but is not the title of the page.
text-2xl — 24 / 32
Page-level and panel titles. This is the size used for the main title of a detail panel, an applicant-portal page header, or a large KPI readout.
text-3xl — 32 / 40
Hero headings for focused, standalone screens — auth screens, article headers, wizard and unauthenticated layouts. Reserved for surfaces without surrounding chrome.
text-4xl — 40 / 48
Display-scale. Use sparingly — ideally once per view, for landing heroes and marketing moments.
If a design calls for anything bigger than text-4xl, it almost always wants a hand-tuned, one-off style — not a new token.
Portal Headings
The applicant portal exposes three dedicated heading sizes so tenants can override size, line-height, and font family without touching shared UI. Use them only inside applicant-portal surfaces, always paired with the matching font-heading-01 / font-heading-02 / font-heading-03 utility.
text-portal-h1
--text-portal-h1
28px / 36px
text-portal-h2
--text-portal-h2
20px / 28px
text-portal-h3
--text-portal-h3
18px / 24px
All three inherit from tenant-controlled CSS variables (--portal-h1-font-size, --heading-01-font-family, etc.), so they can end up noticeably larger or smaller than their default in branded environments. Do not swap them for general-purpose text-* utilities — that breaks tenant theming.
Font Families
FSAI uses one typeface for everything: Geist Variable. It is loaded globally and inherited by default, so you rarely need to apply a font utility.
font-sans
--font-sans
Geist Variable
stack: 'Geist Variable', Inter, ui-sans-serif, system-ui, sans-serif
The single product typeface. Applied to body and inherited everywhere; rarely set explicitly.
The one exception is Roboto Mono, used for content that needs a fixed-width glyph — code samples, ID fields, CSS editors, and tabular numerics.
font-mono
--font-mono
Roboto Mono
stack: Roboto Mono, monospace
Monospace. Use only when fixed-width glyphs matter.
Font Weights
Geist Variable is a variable font — weight is a continuous axis, but we lock product UI to four Tailwind utilities. Ordered from most common to least.
font-medium
500 · Medium · default UI weight
font-semibold
600 · Semibold · headings and emphasis
font-normal
400 · Normal · body prose
font-bold
700 · Bold · display and strong emphasis
font-medium — 500
The default UI weight. Use for almost all interactive and chrome text: buttons, menu items, table cells, labels, badges, tabs, row titles that aren't section headings. If you aren't sure what weight to pick for a piece of UI text, it is font-medium.
font-semibold — 600
Headings and moderate emphasis. Use for section and card titles (text-lg / text-xl / text-2xl panels and modals), primary row titles in lists, and anywhere a piece of text needs to out-rank the font-medium around it without jumping all the way to bold.
This is the default weight for all heading-level sizes.
font-normal — 400
Body prose — paragraphs of long-form reading text. Form helper copy, descriptive blocks inside modals, article body, quiz stems.
Use font-normal when the content is meant to be read as sentences, not scanned as UI. For the rest of product UI, font-medium reads as the "regular" weight.
font-bold — 700
Display-scale headings (text-3xl / text-4xl), sidebar / filter-group headings that need to stamp a region, and very occasional inline emphasis. Reserve for moments that need to punch past font-semibold.
Weights We Don't Use
font-thin, font-extralight, font-extrabold, and font-black are not part of the design system. Avoid them. font-light exists in Tailwind but is used only in a handful of legacy locations — prefer font-normal for low-emphasis prose.
Guidelines
- Do rely on the built-in line-height for each size. Override
leading-*only when a design truly requires it. - Do let body copy inherit
text-basefrombody. - Do default to
font-mediumfor UI text,font-semiboldfor headings, andfont-normalfor body prose. - Don't use
text-xsfor content a user must read. It is for badges and tight metadata, not for helper text or errors. - Don't reach for
font-thin,font-extralight,font-extrabold,font-black, orfont-light— they are not part of the system. - Don't assume
text-mdmust be paired withsm:text-base. That pairing is a specific responsive pattern, not a rule. - Don't use
text-portal-*outside portal surfaces — they are intentionally tenant-overridable. - Don't introduce one-off values like
text-[13px]. If a design needs something outside the scale, propose a new token.