Files
nick-doc/05 - Design System/Typography.md
2026-05-23 20:35:34 +03:30

5.3 KiB
Raw Blame History

title, tags, created
title tags created
Typography
design-system
typography
fonts
2026-05-23

Typography

The system uses Public Sans Variable as the primary face with Barlow as a secondary (display) face, plus locale-specific Persian/Arabic faces loaded when the active language requires them.


1. Font stack

Loaded via @fontsource-variable (variable fonts streamed at build) plus @fontsource/barlow. Confirm in frontend/package.json:

"@fontsource-variable/public-sans":  "^5.2.5",  // Primary
"@fontsource-variable/dm-sans":      "^5.2.5",  // Optional preset
"@fontsource-variable/inter":        "^5.2.5",  // Optional preset
"@fontsource-variable/nunito-sans":  "^5.2.5",  // Optional preset
"@fontsource/barlow":                "^5.2.5",  // Secondary (display)

Imported in frontend/src/app/layout.tsx (or a fonts module) so Next can fingerprint and preload them.

Default font-family stack in the theme:

font-family: "Public Sans Variable", "Helvetica", "Arial", sans-serif;

Display-only headings (banners, hero) may override with Barlow via the sx prop:

<Typography variant="h1" sx={{ fontFamily: '"Barlow", serif' }}>Welcome</Typography>

2. Type scale

Variant Size (px) Line height Weight Use
h1 64 80 800 Hero titles only
h2 48 64 800 Page titles
h3 32 48 700 Section titles
h4 24 36 700 Card titles, dialog headers
h5 20 30 700 Sub-section titles
h6 18 28 700 Item titles, sidebar headers
subtitle1 16 24 600 Form section labels
subtitle2 14 22 600 List item subtitles
body1 16 24 400 Body copy default
body2 14 22 400 Secondary copy, table cells
caption 12 18 400 Helper text, timestamps
overline 12 18 700 Tags, all-caps category labels
button 14 24 700 Button text — NOT uppercase

Tip

Use responsiveFontSizes(theme) once at theme creation to scale h1/h2 on small screens automatically.


3. Weights

The variable Public Sans face supports 100900. Convention:

Weight Token Use
400 fontWeightRegular Body
500 fontWeightMedium Mild emphasis
600 fontWeightSemiBold Subtitles, table headers
700 fontWeightBold Headings
800 (raw) Display

Never use font-weight: bolder — always pick from the table.


4. Letter-spacing

  • Headings (h1h2): -1 (tight, modern)
  • Overline: +1.1 (open, all-caps)
  • Body, subtitle: 0

5. Numeric variants

Tabular figures matter for financial UI (payments, prices). Enable per-instance:

<Typography variant="body1" sx={{ fontVariantNumeric: 'tabular-nums' }}>
  {amount}
</Typography>

6. Multi-script support

The system officially supports Latin, Persian (Farsi), and Arabic rendering; French and Vietnamese use Latin glyphs already covered by Public Sans; Chinese (Simplified) falls back to the OS-default CJK font (the current fontsource set does not ship CJK).

Persian/Arabic font swap is layered at theme build:

fontFamily:
  locale === 'fa' || locale === 'ar'
    ? '"Vazirmatn", "Public Sans Variable", "Tahoma", sans-serif'
    : '"Public Sans Variable", "Helvetica", sans-serif'

Note

"Vazirmatn" is the recommended Persian face for new installations — it has full Unicode coverage and a variable axis. If not installed via @fontsource, ship it as a self-hosted woff2 under public/fonts/.

For Chinese (cn), consider adding "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC" to the family at the front.

For Vietnamese (vi) — Public Sans already covers all needed glyphs.


7. Line-length

Aim for 4575 characters per line on body copy. Constrain with maxWidth:

<Typography variant="body1" sx={{ maxWidth: 720 }}>{longBody}</Typography>

8. Text truncation

Single-line:

<Typography noWrap>{text}</Typography>

Multi-line (clamp at 2 lines):

<Typography
  sx={{
    display: '-webkit-box',
    WebkitLineClamp: 2,
    WebkitBoxOrient: 'vertical',
    overflow: 'hidden',
  }}
>{text}</Typography>

9. Bidi & RTL considerations

  • Don't hard-code text-align: left/right — use text-align: start/end so it flips with direction.
  • For inline mixed-script (Persian + English), prefer the auto direction inside <bdi> or wrap with the Unicode (RLE) / (PDF) markers.
  • Numeric formatting per locale handled by Intl.NumberFormat; do NOT translate digits manually.

10. Loading & FOUC prevention

  • All fonts loaded at the route segment that needs them (Next.js best practice).
  • font-display: swap is the default in fontsource — visible text uses fallback first, then swaps.
  • For the FIRST PAINT on auth pages, preload the primary Latin face by adding <link rel="preload" as="font" /> to the root layout.

11. Customising

Add a font:

  1. yarn add @fontsource-variable/<name>
  2. import '@fontsource-variable/<name>' in src/app/layout.tsx.
  3. Add to fontFamily stack in theme/options/typography.ts.
  4. Register as a settings option if user-selectable.