94 lines
4.3 KiB
Swift
94 lines
4.3 KiB
Swift
import SwiftUI
|
|
|
|
/// The web app's three-font type system (see `DESIGN (1).md` → Typography,
|
|
/// and `tailwind.config.ts`'s `fontFamily`), ported with the SAME font
|
|
/// files: `Resources/Fonts/*.ttf` are the real Hanken Grotesk / Inter /
|
|
/// JetBrains Mono variable fonts (downloaded from the canonical
|
|
/// google/fonts repository — the same open-source files the web app loads
|
|
/// from Google Fonts' CDN), registered via `UIAppFonts` in `project.yml`.
|
|
///
|
|
/// PostScript names below were read directly out of each font file's `name`
|
|
/// table (not guessed) — see the font files themselves if these ever need
|
|
/// re-verifying after a font update.
|
|
///
|
|
/// - Hanken Grotesk → headlines, set tight (negative tracking) at large
|
|
/// sizes for the "locked, architectural" feel the design system asks for.
|
|
/// - Inter → body copy.
|
|
/// - JetBrains Mono → labels, captions, technical/numeric data. `labelCaps`
|
|
/// is additionally uppercased with wide tracking — apply
|
|
/// `.zenithLabelCapsStyle()`, not just the bare font, or the case/tracking
|
|
/// won't happen (SwiftUI doesn't derive that from the font itself).
|
|
enum ZenithFont {
|
|
static let display = "HankenGrotesk-Regular"
|
|
static let body = "Inter-Regular"
|
|
static let mono = "JetBrainsMono-Regular"
|
|
}
|
|
|
|
extension Font {
|
|
/// Rarely needed on iPhone (72pt) — kept for completeness / a future
|
|
/// iPad or marketing surface.
|
|
static var zenithDisplayLg: Font { .custom(ZenithFont.display, size: 72).weight(.bold) }
|
|
|
|
/// Screen/section titles. Sized at the web system's "headline-lg-mobile"
|
|
/// (32/40) rather than the 48pt desktop size, since this app is
|
|
/// iPhone-only (see project.yml's TARGETED_DEVICE_FAMILY).
|
|
static var zenithHeadlineLg: Font { .custom(ZenithFont.display, size: 32).weight(.semibold) }
|
|
|
|
/// Card/sub-section titles.
|
|
static var zenithHeadlineMd: Font { .custom(ZenithFont.display, size: 24).weight(.medium) }
|
|
|
|
static var zenithBodyLg: Font { .custom(ZenithFont.body, size: 18) }
|
|
/// Default body/UI text size.
|
|
static var zenithBodyMd: Font { .custom(ZenithFont.body, size: 16) }
|
|
static var zenithBodySm: Font { .custom(ZenithFont.body, size: 14) }
|
|
|
|
/// Field labels, eyebrows, section headers — use with
|
|
/// `.zenithLabelCapsStyle()` for the uppercase + tracking, not bare.
|
|
static var zenithLabelCaps: Font { .custom(ZenithFont.mono, size: 12).weight(.medium) }
|
|
/// Chips, technical/tabular data (amounts, dates, IDs).
|
|
static var zenithLabelMd: Font { .custom(ZenithFont.mono, size: 14) }
|
|
static var zenithLabelSm: Font { .custom(ZenithFont.mono, size: 12) }
|
|
}
|
|
|
|
/// Complete text styles (font + tracking + default color) for each type
|
|
/// role. Tracking values are the design system's em values
|
|
/// (`DESIGN (1).md` → Typography) converted to points AT EACH ROLE'S OWN
|
|
/// size — `SwiftUI.tracking(_:)` takes absolute points, not em, so these
|
|
/// can't be a single generic modifier parameterized only by a ratio.
|
|
/// Prefer these over composing `.font(.zenith...)` by hand.
|
|
extension View {
|
|
/// Screen/section titles (32pt, -0.02em ⇒ -0.64pt tracking).
|
|
func zenithHeadlineLgStyle(color: Color = .zenithText) -> some View {
|
|
font(.zenithHeadlineLg).tracking(-0.64).foregroundStyle(color)
|
|
}
|
|
|
|
/// Card/sub-section titles (24pt, -0.01em ⇒ -0.24pt tracking).
|
|
func zenithHeadlineMdStyle(color: Color = .zenithText) -> some View {
|
|
font(.zenithHeadlineMd).tracking(-0.24).foregroundStyle(color)
|
|
}
|
|
|
|
/// Default body text (16pt, 0 tracking).
|
|
func zenithBodyStyle(color: Color = .zenithText) -> some View {
|
|
font(.zenithBodyMd).foregroundStyle(color)
|
|
}
|
|
|
|
func zenithBodySmStyle(color: Color = .zenithMuted) -> some View {
|
|
font(.zenithBodySm).foregroundStyle(color)
|
|
}
|
|
|
|
/// `label-caps` role: JetBrains Mono, uppercase, wide tracking
|
|
/// (12pt, 0.1em ⇒ 1.2pt). This is the style used above every
|
|
/// `ZenithTextField` and for chip/badge/eyebrow text.
|
|
func zenithLabelCapsStyle(color: Color = .zenithSubtle) -> some View {
|
|
font(.zenithLabelCaps)
|
|
.textCase(.uppercase)
|
|
.tracking(1.2)
|
|
.foregroundStyle(color)
|
|
}
|
|
|
|
/// Technical/tabular data — amounts, dates, IDs (14pt, 0 tracking).
|
|
func zenithLabelMdStyle(color: Color = .zenithText) -> some View {
|
|
font(.zenithLabelMd).foregroundStyle(color)
|
|
}
|
|
}
|