mirror of
https://github.com/hanzoai/gui.git
synced 2026-10-25 02:31:41 +00:00
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]>
160 lines
3.2 KiB
TypeScript
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()
|
|
})
|
|
})
|