Files
gui/pkgs/core/core-test/createFont.web.test.tsx
zeekayandHanzo Dev 56e004e4b2 feat(fonts): Zen only — delete the nine faces that were not ours
The kit shipped ten typefaces. One is ours.

Deleted: font-inter, font-silkscreen, font-cherry-bomb, font-nohemi,
font-dm-sans, font-dm-serif-display, font-fira-mono, font-glusp, font-munro.
Six of those had no dependent in the workspace at all. font-zen stays, and so do
font-size, get-font-sized and font, which size and register rather than name a
face. Published versions remain on the registry, so anything pinned to one keeps
resolving.

@hanzogui/config's DEFAULT entry built heading and body from Inter — v4 was
already system fonts and v5 already Zen, so an app on the default got a face
nobody chose. It builds from Zen now. dev-config, which drives gui.hanzo.ai, was
worse: Inter for text, Berkeley Mono for code, plus Cherry Bomb and Silkscreen.
Every size, weight, letter-spacing and transform value is unchanged — only the
faces moved.

The pixel ROLE was worth keeping and only its face was borrowed, so font-zen
gained Zen Pixel Square (createZenPixelFont, --font-zen-pixel-*) and the token is
`$pixel`. `$silkscreen` and `$cherryBomb` are gone across ~35 call sites. Cherry
Bomb had no role of its own and no Zen counterpart, so it is deleted rather than
substituted.

gui.hanzo.ai now self-hosts three Zen files in place of four foreign ones — one
variable file per family instead of a weight per request — and its OG-image
route renders cards in the same faces as the pages they link to. The Inter,
Berkeley, Silkscreen and glusp binaries are gone from public/fonts, including an
otf directory of 20 Inter cuts.

Native is honestly short: @hanzo/font ships woff2 and react-native wants
otf/ttf, so the expo starter and kitchen-sink's native entry load no custom face
and say why, rather than keeping Inter because it happened to be there.

bun install 0 · build 161/161 · typecheck passed.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-24 11:44:08 -07:00

160 lines
3.2 KiB
TypeScript

import { describe, expect, test } from 'vitest'
import { createFont } from '../core/src'
import type { GenericFont } from '../core/src'
import { getFontSized } from '../get-font-sized/src'
const font: GenericFont = {
family: 'Zen',
size: {
1: 11,
2: 12,
3: 14,
4: 16,
5: 20,
6: 24,
},
lineHeight: {
1: 14,
2: 16,
3: 18,
4: 20,
5: 24,
6: 28,
},
transform: {
3: 'uppercase',
4: 'none',
},
weight: {
1: '400',
3: '700',
},
color: {
1: '$colorFocus',
6: '$color',
},
letterSpacing: {
5: 2,
6: 1,
7: 0,
8: 0,
9: -1,
10: -1.5,
12: -2,
14: -3,
15: -4,
},
face: {
700: { normal: 'InterBold' },
800: { normal: 'InterBold' },
},
}
const expectedProcessedFont: GenericFont = {
family: 'Zen',
size: {
1: 11,
2: 12,
3: 14,
4: 16,
5: 20,
6: 24,
},
lineHeight: {
1: 14,
2: 16,
3: 18,
4: 20,
5: 24,
6: 28,
},
transform: {
1: 'uppercase',
2: 'uppercase',
3: 'uppercase',
4: 'none',
5: 'none',
6: 'none',
},
weight: {
1: '400',
2: '400',
3: '700',
4: '700',
5: '700',
6: '700',
},
color: {
1: '$colorFocus',
2: '$colorFocus',
3: '$colorFocus',
4: '$colorFocus',
5: '$colorFocus',
6: '$color',
},
letterSpacing: {
1: 2,
2: 2,
3: 2,
4: 2,
5: 2,
6: 1,
7: 0,
8: 0,
9: -1,
10: -1.5,
12: -2,
14: -3,
15: -4,
},
face: {
100: { normal: 'Zen' },
200: { normal: 'Zen' },
300: { normal: 'Zen' },
400: { normal: 'Zen' },
500: { normal: 'Zen' },
600: { normal: 'Zen' },
700: { normal: 'InterBold' },
800: { normal: 'InterBold' },
900: { normal: 'InterBold' },
},
}
describe('createFont', () => {
test('should return a font object with normalised property hashes', () => {
const processedFont = createFont(font)
expect(processedFont).toStrictEqual(expectedProcessedFont)
})
})
describe('getFontSized', () => {
// tokens are keyed by strings like "$true" / "$9", so a numeric size has no
// entry in the size/lineHeight maps. Build a font with both so we can compare
// the token path against a raw-number override.
const sizedFont = {
family: 'Zen',
size: { $true: 16, $9: 32 },
lineHeight: { $true: 24, $9: 38 },
} as unknown as GenericFont
const extras = { font: sizedFont, fontFamily: '$body', props: {} } as any
test('a size token still pairs its fontSize with the token lineHeight', () => {
const style = getFontSized('$9' as any, extras) as any
expect(style.fontSize).toBe(32)
expect(style.lineHeight).toBe(38)
})
// regression for #4028: a numeric fontSize used to fall through every token
// lookup and return nothing, leaving the default "$true" lineHeight in place
// and clipping glyph tops on iOS. It must now apply the number as a literal
// fontSize and carry no mismatched token lineHeight.
test('a numeric size applies as a literal fontSize and drops the token lineHeight', () => {
const style = getFontSized(32 as any, extras) as any
expect(style.fontSize).toBe(32)
expect(style.lineHeight).toBeUndefined()
})
})