Apple platform typography reference (San Francisco fonts, text styles, Dynamic Type, tracking, leading, internationalization) through iOS 26
Inherits all available tools
Additional assets for this skill
This skill inherits all available tools. When active, it can use any tool Claude has access to.
Complete reference for typography on Apple platforms including San Francisco font system, text styles, Dynamic Type, tracking, leading, and internationalization through iOS 26.
SF Pro and SF Pro Rounded (iOS, iPadOS, macOS, tvOS)
SF Compact and SF Compact Rounded (watchOS, narrow columns)
SF Mono (Code environments, monospaced text)
New York (Serif system font)
Access via:
// iOS/macOS
let descriptor = UIFontDescriptor(fontAttributes: [
.family: "SF Pro",
kCTFontWidthTrait: 1.0 // 1.0 = Expanded
])
SF Arabic (WWDC 2022)
Variable fonts automatically adjust optical size based on point size:
From WWDC 2020:
"TextKit 2 abstracts away glyph handling to provide a consistent experience for international text."
| Text Style | Default Size (iOS) | Use Case |
|---|---|---|
.largeTitle | 34pt | Primary page headings |
.title | 28pt | Secondary headings |
.title2 | 22pt | Tertiary headings |
.title3 | 20pt | Quaternary headings |
.headline | 17pt (Semibold) | Emphasized body text |
.body | 17pt | Primary body text |
.callout | 16pt | Secondary body text |
.subheadline | 15pt | Tertiary body text |
.footnote | 13pt | Footnotes, captions |
.caption | 12pt | Small annotations |
.caption2 | 11pt | Smallest annotations |
Apply .bold symbolic trait to get emphasized variants:
// UIKit
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .title1)
let boldDescriptor = descriptor.withSymbolicTraits(.traitBold)!
let font = UIFont(descriptor: boldDescriptor, size: 0)
// SwiftUI
Text("Bold Title")
.font(.title.bold())
Actual weights by text style:
Tight Leading (reduces line height by 2pt on iOS, 1pt on watchOS):
// UIKit
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
let tightDescriptor = descriptor.withSymbolicTraits(.traitTightLeading)!
// SwiftUI
Text("Compact text")
.font(.body.leading(.tight))
Loose Leading (increases line height by 2pt on iOS, 1pt on watchOS):
// SwiftUI
Text("Spacious paragraph")
.font(.body.leading(.loose))
Automatic Scaling (iOS): Text styles scale automatically based on user preferences from Settings → Display & Brightness → Text Size.
Custom Fonts with Dynamic Type:
// UIKit - UIFontMetrics
let customFont = UIFont(name: "Avenir-Medium", size: 34)!
let bodyMetrics = UIFontMetrics(forTextStyle: .body)
let scaledFont = bodyMetrics.scaledFont(for: customFont)
// Also scale constants
let spacing = bodyMetrics.scaledValue(for: 20.0)
// SwiftUI - .font(.custom(_:relativeTo:))
Text("Custom scaled text")
.font(.custom("Avenir-Medium", size: 34, relativeTo: .body))
// @ScaledMetric for values
@ScaledMetric(relativeTo: .body) var padding: CGFloat = 20
macOS
watchOS
visionOS
Tracking adjusts space between letters. Essential for optical size behavior.
Size-Specific Tracking Tables:
SF Pro includes tracking values that vary by point size to maintain optimal spacing:
Example from Apple Design Resources:
Tight Tracking API (for fitting text):
// UIKit
textView.allowsDefaultTightening(for: .byTruncatingTail)
// SwiftUI
Text("Long text that needs to fit")
.lineLimit(1)
.minimumScaleFactor(0.5) // Allows tight tracking
Manual Tracking:
// UIKit
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.preferredFont(forTextStyle: .body),
.kern: 2.0 // 2pt tracking
]
// SwiftUI
Text("Tracked text")
.tracking(2.0)
.kerning(2.0) // Alternative API
Important: Use .tracking() not .kerning() API for semantic correctness. Tracking disables ligatures when necessary; kerning does not.
Default Line Height: Calculated from font's built-in metrics (ascender + descender + line gap).
Language-Aware Adjustments: iOS 17+ automatically increases line height for scripts with tall ascenders/descenders:
From WWDC 2023:
"Automatic line height adjustment for scripts with variable heights"
Manual Leading:
// UIKit
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 8.0 // 8pt additional space
// SwiftUI
Text("Custom spacing")
.lineSpacing(8.0)
New in iOS 18: Font vendors can embed tracking tables in custom fonts using STAT table + CTFont optical size attribute.
let attributes: [String: Any] = [
kCTFontOpticalSizeAttribute as String: pointSize
]
let descriptor = CTFontDescriptorCreateWithAttributes(attributes as CFDictionary)
let font = CTFontCreateWithFontDescriptor(descriptor, pointSize, nil)
Complex Script Example (from WWDC 2021):
Kannada word "October":
This is why TextKit 2 uses NSTextLocation instead of integer indices.
Hebrew/Arabic Selection: Single visual selection = multiple NSRanges in AttributedString due to right-to-left layout.
Language-Aware (iOS 17+):
Even Line Breaking (TextKit 2): Justified paragraphs use improved line breaking algorithm:
Best Practices:
.lineLimit(nil) or .lineLimit(2...5) in SwiftUI.minimumScaleFactor() for constrained single-line textSystem UI Font Families:
font-family: system-ui; /* SF Pro */
font-family: ui-rounded; /* SF Pro Rounded */
font-family: ui-serif; /* New York */
font-family: ui-monospace; /* SF Mono */
Legacy:
font-family: -apple-system; /* deprecated, use system-ui */
Text("Recipe Editor")
.font(.largeTitle.bold()) // Emphasized variant
let customFont = UIFont(name: "Avenir-Medium", size: 17)!
let metrics = UIFontMetrics(forTextStyle: .body)
label.font = metrics.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true
let descriptor = UIFontDescriptor
.preferredFontDescriptor(withTextStyle: .largeTitle)
.withDesign(.rounded)!
let font = UIFont(descriptor: descriptor, size: 0)
Text("Today")
.font(.largeTitle.bold())
.fontDesign(.rounded)
struct RecipeView: View {
@ScaledMetric(relativeTo: .body) var padding: CGFloat = 20
var body: some View {
Text("Recipe")
.padding(padding) // Scales with Dynamic Type
}
}