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) } }