Files
zeekay e738240139 IBM Plex Sans Arabic beside the Noto companions; 1.9.6
The Arabic table now holds a third companion, and every companion must draw
every letter the Arabic-script catalogs in @hanzo/i18n write ("needs":
Arabic, Persian, Dari, Urdu, Pashto, Sindhi, Sorani Kurdish, Uyghur,
Shahmukhi Punjabi). arabic.py refuses a face that does not. Measured
against those catalogs, Cairo, Readex Pro and Rubik each miss letters
Pashto, Sindhi, Kurdish or Uyghur write; Noto Kufi, Noto Sans and IBM Plex
Sans Arabic miss none.

Plex ships as IBM published it, one woff2 per weight, byte for byte: the
OFL reserves "Plex", and a subset is a Modified Version that may not carry
it. So a face is either cut (the Noto pair, as before) or whole, and a
face lists its files. The default must still be one variable file over
Zen's 100-900, since a static face under 'Zen' wins the weight match for
Latin; Plex is offered under its own name only.

The publish check reads the files to expect from the table.

Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
2026-10-29 23:34:46 -07:00

7.0 KiB
Raw Permalink Blame History

Zen, Zen Mono & Zen Pixel

Zen is the Hanzo type family, under the SIL Open Font License. Every copyright the licence requires is in LICENSE.txt, which ships beside the binaries.

Zen is a sans-serif typeface designed for legibility and simplicity. It is modern, geometric, and based on the principles of classic Swiss typography. It is designed to be used in body copy, headlines, logos, posters, and other large display sizes.

Zen Mono is a monospaced typeface, crafted to be the perfect partner to Zen. It is designed to be used in code editors, diagrams, terminals, and other text-based interfaces where code is rendered.

Zen Pixel is a display typeface family featuring five unique pixel-based variants, each with a distinct visual style. It is designed for decorative use in headlines, logos, and other display contexts where a pixelated aesthetic is desired.

Installation

pnpm add @hanzo/font

Using with Next.js

Zen is exported from @hanzo/font/sans, ZenMono can be found in @hanzo/font/mono, and Zen Pixel variants are available from @hanzo/font/pixel. All are NextFontWithVariable instances. You can learn more by reading the next/font docs.

Zen Pixel Variants

Zen Pixel includes five distinct variants, each exported separately:

Export CSS Variable Description
ZenPixelSquare --font-zen-pixel-square Square pixel shapes
ZenPixelGrid --font-zen-pixel-grid Grid-based pixel pattern
ZenPixelCircle --font-zen-pixel-circle Circular pixel shapes
ZenPixelTriangle --font-zen-pixel-triangle Triangular pixel shapes
ZenPixelLine --font-zen-pixel-line Line-based pixel pattern
import {
  ZenPixelSquare,
  ZenPixelGrid,
  ZenPixelCircle,
  ZenPixelTriangle,
  ZenPixelLine,
} from "@hanzo/font/pixel";

App Router

In app/layout.js:

import { Zen } from "@hanzo/font/sans";

export default function RootLayout({ children }) {
  return (
    <html lang="en" className={Zen.className}>
      <body>{children}</body>
    </html>
  );
}

Pages Router

In pages/_app.js:

import { Zen } from "@hanzo/font/sans";

export default function MyApp({ Component, pageProps }) {
  return (
    <main className={Zen.className}>
      <Component {...pageProps} />
    </main>
  );
}

If you're using a version of Next.js that's older than 15, then in next.config.js or next.config.mjs add:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: true,
+  transpilePackages: ["@hanzo/font"],
};

export default nextConfig;

This is required to fix errors like:

  • TypeError: next_font_local__WEBPACK_IMPORTED_MODULE_0___default(...) is not a function
  • SyntaxError: Cannot use import statement outside a module

With Tailwind CSS

All Zen fonts can be used through CSS variables.

  • Zen: --font-zen-sans
  • ZenMono: --font-zen-mono
  • ZenPixelSquare: --font-zen-pixel-square
  • ZenPixelGrid: --font-zen-pixel-grid
  • ZenPixelCircle: --font-zen-pixel-circle
  • ZenPixelTriangle: --font-zen-pixel-triangle
  • ZenPixelLine: --font-zen-pixel-line

In app/layout.js:

import { Zen } from "@hanzo/font/sans";
import { ZenMono } from "@hanzo/font/mono";
import { ZenPixelSquare } from "@hanzo/font/pixel";

export default function RootLayout({ children }) {
  return (
    <html
      lang="en"
      className={`${Zen.variable} ${ZenMono.variable} ${ZenPixelSquare.variable}`}
    >
      <body>{children}</body>
    </html>
  );
}
Tailwind CSS V4

Then in tailwind.css:

@theme {
  /* rest of your theme config */

  --font-sans: var(--font-zen-sans);
  --font-mono: var(--font-zen-mono);
  --font-pixel-square: var(--font-zen-pixel-square);
  --font-pixel-grid: var(--font-zen-pixel-grid);
  --font-pixel-circle: var(--font-zen-pixel-circle);
  --font-pixel-triangle: var(--font-zen-pixel-triangle);
  --font-pixel-line: var(--font-zen-pixel-line);

  /* rest of your theme config */
}
Tailwind CSS V3

Then in tailwind.config.js:

module.exports = {
  theme: {
    extend: {
      fontFamily: {
        sans: ["var(--font-zen-sans)"],
        mono: ["var(--font-zen-mono)"],
        "pixel-square": ["var(--font-zen-pixel-square)"],
        "pixel-grid": ["var(--font-zen-pixel-grid)"],
        "pixel-circle": ["var(--font-zen-pixel-circle)"],
        "pixel-triangle": ["var(--font-zen-pixel-triangle)"],
        "pixel-line": ["var(--font-zen-pixel-line)"],
      },
    },
  },
};

Arabic

Zen draws Latin and Cyrillic and no Arabic. @hanzo/font/css carries three Arabic companions, each declared with an Arabic unicode-range (U+0600–06FF, U+0750–077F, U+FB50–FDFF, U+FE70–FEFC), so a page with no Arabic on it never requests one:

Family Files woff2
Noto Kufi Arabic fonts/arabic/NotoKufiArabic-Variable.woff2, wght 100–900 94.7 KB
Noto Sans Arabic fonts/arabic/NotoSansArabic-Variable.woff2, wght 100–900 132.5 KB
IBM Plex Sans Arabic fonts/arabic/IBMPlexSansArabic-<Weight>.woff2, 100–700 70–75 KB a weight

The default (Noto Kufi Arabic, also var(--font-zen-arabic)) is declared under Zen and Zen Mono too, so font-family: Zen sets Arabic with no other change. To set Arabic in another companion, name it ahead of Zen: font-family: 'IBM Plex Sans Arabic', Zen, sans-serif.

Every companion draws every letter the Arabic-script languages Hanzo ships write (Arabic, Persian, Dari, Urdu, Pashto, Sindhi, Sorani Kurdish, Uyghur, Shahmukhi Punjabi); scripts/arabic.py refuses a face that does not. Cairo, Readex Pro and Rubik were measured and are not here: each misses letters Pashto, Sindhi, Kurdish or Uyghur write.

The Noto faces are cut to the Arabic blocks, variable over Zen's weight axis. IBM Plex Sans Arabic is IBM's own woff2, one file per weight, byte for byte: the OFL reserves the name "Plex", and a subset is a Modified Version that may not carry it. The default is a single variable file over 100–900 because faces in one family composite only when every descriptor but the range is equal, so Plex is offered under its own name only. None of them is ours: each keeps its own name and notice under the SIL Open Font License (fonts/arabic/OFL.txt for Noto, fonts/arabic/OFL-Plex.txt for Plex), and scripts/arabic.py rebuilds them from upstream pinned by commit and checksum. The next/font bindings carry Zen only.

License

The Zen font family is free and open sourced under the SIL Open Font License. The Arabic companions are the Noto Project's (dist/fonts/arabic/OFL.txt) and IBM's (dist/fonts/arabic/OFL-Plex.txt), under the same license.