FSAI Design System
Foundations

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 / 48px

Every 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

The quick brown fox jumps over the lazy dog

text-sm

--text-sm

12px / 16px

The quick brown fox jumps over the lazy dog

text-md

--text-md

13px / 18px

The quick brown fox jumps over the lazy dog

text-base

--text-base

14px / 20px

The quick brown fox jumps over the lazy dog

text-lg

--text-lg

16px / 24px

The quick brown fox jumps over the lazy dog

text-xl

--text-xl

20px / 28px

The quick brown fox jumps over the lazy dog

text-2xl

--text-2xl

24px / 32px

The quick brown fox jumps over the lazy dog

text-3xl

--text-3xl

32px / 40px

The quick brown fox jumps over the lazy dog

text-4xl

--text-4xl

40px / 48px

The quick brown fox jumps over the lazy dog

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

The quick brown fox jumps over the lazy dog

text-portal-h2

--text-portal-h2

20px / 28px

The quick brown fox jumps over the lazy dog

text-portal-h3

--text-portal-h3

18px / 24px

The quick brown fox jumps over the lazy dog

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

The quick brown fox jumps over the lazy dog

font-semibold

600 · Semibold · headings and emphasis

The quick brown fox jumps over the lazy dog

font-normal

400 · Normal · body prose

The quick brown fox jumps over the lazy dog

font-bold

700 · Bold · display and strong emphasis

The quick brown fox jumps over the lazy dog

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-base from body.
  • Do default to font-medium for UI text, font-semibold for headings, and font-normal for body prose.
  • Don't use text-xs for 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, or font-light — they are not part of the system.
  • Don't assume text-md must be paired with sm: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.

On this page