Fonts

Pick one font for English and one for Arabic, and every component uses the right one for each character.

dir="ltr"

Invoice INV-2041 فاتورة

Paid by Northwind Traders, ودفعت الفاتورة كاملة في 14 October.

dir="rtl"

فاتورة INV-2041 Invoice

دفعت الفاتورة كاملة من Northwind Traders في 14 تشرين الأول.

import { Card } from "@hel/ui/card"
import { Flex } from "@hel/ui/flex"
import { Heading } from "@hel/ui/heading"

How it works

Hel UI is built for apps that mix English and Arabic, on the same screen and sometimes in the same sentence. So a font setting has two parts: one font for Latin text, and one for Arabic text. The two are listed together in one stack, and the browser picks the font per character. An English name inside an Arabic sentence gets the Latin font, and an Arabic word inside an English page gets the Arabic one.

Only the order changes with the direction. In a left-to-right layout the Latin font is tried first. In a right-to-left layout, in lang="ar" content, and inside any element with dir="rtl", the Arabic font is tried first. A left-to-right island inside right-to-left content goes back to Latin first. The two cards in the preview above are the same markup with different dir values.

The defaults are Inter for Latin and IBM Plex Sans Arabic for Arabic, with the system font as the last resort.

Choose your fonts

Set them in the config. Each language has a body font and an optional heading font, which falls back to the body font.

src/hel.config.ts
import { defineConfig } from "@hel/ui/config"
 
export const helConfig = defineConfig({
  fonts: {
    latin: { body: "Geist", heading: "Geist" },
    arabic: { body: "Cairo" },
    fallback: ["system-ui", "sans-serif"],
  },
})

The config only names a font. It does not load it, so the name has to match the font-family of the font you load. fallback replaces the whole list, because arrays are not merged. Names with spaces or digits are quoted for you.

Load the font files

@hel/ui/fonts.css loads the default fonts, Inter in six weights and IBM Plex Sans Arabic in four, from Fontsource. Each file is split by unicode range, so the browser downloads only the parts a page uses.

src/styles/index.css
@import "tailwindcss";
@import "@hel/ui/styles.css";
@import "@hel/ui/fonts.css";

If you choose other fonts, leave fonts.css out and load your own, so no font that you do not use is downloaded. Fontsource is the quickest way:

pnpm add @fontsource/geist @fontsource/cairo
src/styles/fonts.css
@import "@fontsource/geist/400.css";
@import "@fontsource/geist/500.css";
@import "@fontsource/geist/600.css";
@import "@fontsource/cairo/arabic-400.css";
@import "@fontsource/cairo/arabic-600.css";

Load the weights that the components use. Text is regular (400) and medium (500), and headings and some labels are semibold (600).

Regular 400

Order shipped تم الشحن

Medium 500

Order shipped تم الشحن

Semibold 600

Order shipped تم الشحن

Bold 700

Order shipped تم الشحن

import { Flex } from "@hel/ui/flex"
import { Stack } from "@hel/ui/stack"
import { Text } from "@hel/ui/text"

Set the variables yourself

The config writes five CSS variables on :root, and you can write them in CSS instead. This is the way to use a font loaded by a framework such as next/font, which gives you a variable that holds the family name.

:root {
  --hel-font-latin-body: var(--font-geist);
  --hel-font-latin-heading: var(--hel-font-latin-body);
  --hel-font-arabic-body: "Cairo";
  --hel-font-arabic-heading: var(--hel-font-arabic-body);
  --hel-font-fallback: system-ui, sans-serif;
}

The stacks that components read are built from these. --brand-font-family-body and --brand-font-family-headings hold the final lists, in the right order for the direction.

A custom property is inherited after it has been resolved. So if you change --hel-font-* on an element in the middle of the page, the text inside does not follow. Set the variables on :root, or set --brand-font-family-body on the element.

In your own code

Components already use the right stack. For your own elements, use the font utilities of Tailwind, or inherit.

<p className="font-body">Follows the config and the direction</p>

Config reference

KeyTypeDefault
fonts.latin.bodystring"Inter"
fonts.latin.headingstringthe body font
fonts.arabic.bodystring"IBM Plex Sans Arabic"
fonts.arabic.headingstringthe body font
fonts.fallbackstring[]system-ui, -apple-system, Segoe UI, sans-serif
VariableMeaning
--hel-font-latin-body, --hel-font-latin-headingThe Latin fonts.
--hel-font-arabic-body, --hel-font-arabic-headingThe Arabic fonts.
--hel-font-fallbackThe list tried last.
--brand-font-family-body, --brand-font-family-headingsThe final stacks, in the order of the direction.