axiom-typography-ref について
Appleプラットフォームのデザイン・開発者向けのタイポグラフィリファレンススキルです。システムフォント、テキストスタイル、Dynamic Type、文字間隔、国際化をSwiftUIとUIKitの例とともに扱います。
主な機能
- 01Appleのシステムフォント系列
- 02テキストスタイルとDynamic Type
- 03トラッキング、行送り、行高
- 04AttributedStringの組版ガイド
- 05多言語テキストのレイアウト
活用シーン
- 01画面に適したテキストスタイルの選択
- 02Dynamic Typeに合わせたカスタムフォントの拡大
- 03段落の行送りとトラッキングの調整
- 04リッチテキストスタイルが上書きされる問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-typography-refインストール後、Claude Code で/axiom-typography-refを入力して読み込まれたか確認します。
詳細
タイポグラフィのリファレンス
iOS 26 に対応した、San Francisco フォントシステム、テキストスタイル、Dynamic Type、トラッキング、行送り、国際化を含む、Apple プラットフォームのタイポグラフィに関する完全なリファレンスです。
San Francisco フォントシステム
フォントファミリー
SF Pro および SF Pro Rounded(iOS、iPadOS、macOS、tvOS)
- ほとんどの UI 要素で使用される主要なシステムフォントです
- 親しみやすく、近づきやすいインターフェース向けの角丸バリエーションです(例:リマインダーアプリ)
SF Compact および SF Compact Rounded(watchOS、幅の狭いカラム)
- 制約のあるスペースや小さなサイズに最適化されています
- watchOS のデフォルトのシステムフォントです
SF Mono(コード環境、等幅テキスト)
- コードエディターや技術的なコンテンツ向けの等幅フォントです
- 文字の幅が一定のため、位置を揃えられます
New York(セリフ系システムフォント)
- エディトリアルコンテンツ向けのセリフ体フォントです
- SF Pro と同じようにテキストスタイルに対応しています
可変フォントの軸
ウェイト軸(9 段階)
- Ultralight、Thin、Light、Regular、Medium、Semibold、Bold、Heavy、Black
- 可変フォントによる連続的なウェイトの範囲
- 小さいサイズでは細いウェイトを避けてください(読みやすさに影響します)
幅軸(WWDC 2022)
- Condensed — 最も狭い幅
- Compressed — 狭い幅
- Regular — 標準の幅(デフォルト)
- Expanded — 広い幅
次の方法でアクセスできます:
// iOS/macOS
let descriptor = UIFontDescriptor(fontAttributes: [
.family: "SF Pro",
kCTFontWidthTrait: 1.0 // 1.0 = Expanded
])
SF Arabic(WWDC 2022)
- アラビア語テキストに SF Pro のデザイン言語を適用します
- 適切な右から左への表示に対応しています
オプティカルサイズ
可変フォントは、ポイントサイズに応じてオプティカルサイズを自動調整します:
- Text バリアント(< 20pt)— 文字間隔が広く、線が太めです
- Display バリアント(≥ 20pt)— 文字間隔が狭く、細部が洗練されています
- 可変 SF Pro では 滑らかに切り替わります(17-28pt)
WWDC 2020 より:
「TextKit 2 はグリフの処理を抽象化し、国際化されたテキストで一貫したエクスペリエンスを実現します。」
テキストスタイルと Dynamic Type
システムテキストスタイル
| テキストスタイル | デフォルトサイズ(iOS) | 用途 |
|---|---|---|
.largeTitle | 34pt | ページの主要な見出し |
.title | 28pt | 2 番目のレベルの見出し |
.title2 | 22pt | 3 番目のレベルの見出し |
.title3 | 20pt | 4 番目のレベルの見出し |
.headline | 17pt(Semibold) | 強調する本文テキスト |
.body | 17pt | 主要な本文テキスト |
.callout | 16pt | 2 番目のレベルの本文テキスト |
.subheadline | 15pt | 3 番目のレベルの本文テキスト |
.footnote | 13pt | 脚注、キャプション |
.caption | 12pt | 小さな注釈 |
.caption2 | 11pt | 最も小さな注釈 |
フォントサイズの指針
- 読みやすさが必要なコンテンツに
.caption2を使わないでください — 11pt はタイムスタンプやメタデータの注釈には適していますが、本文やユーザーが読む必要のあるラベルには小さすぎます。読みやすさが必要なコンテンツには、.captionまたは.footnoteを最小サイズとして使用してください。
強調テキストスタイル
強調バリエーションを適用するには、.bold のシンボリック特性を指定します:
// 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())
テキストスタイルごとの実際のウェイト:
- 一部のスタイルは medium に対応します
- それ以外は semibold、bold、または heavy に対応します
- セマンティックな階層によって異なります
行送りのバリエーション
詰めた行送り(iOS では行の高さを 2pt、watchOS では 1pt 減らします):
// UIKit
let descriptor = UIFontDescriptor.preferredFontDescriptor(withTextStyle: .body)
let tightDescriptor = descriptor.withSymbolicTraits(.traitTightLeading)!
// SwiftUI
Text("Compact text")
.font(.body.leading(.tight))
広げた行送り(iOS では行の高さを 2pt、watchOS では 1pt 増やします):
// SwiftUI
Text("Spacious paragraph")
.font(.body.leading(.loose))
Dynamic Type
自動スケーリング(iOS): テキストスタイルは、「設定」→「画面表示と明るさ」→「テキストサイズ」で指定したユーザー設定に基づいて自動的に拡大縮小されます。
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
- AppKit は Dynamic Type に対応していません
- テキストスタイルのサイズは macOS のコントロールサイズに合わせて最適化されています
- Catalyst アプリは iOS のサイズの 77%(従来方式)または macOS に最適化されたサイズ(「Mac 用にインターフェイスを最適化」)を使用します
watchOS
- 文字盤に合わせて最適化された、小さめのテキストスタイルです
- コンパクトなディスプレイ向けに、デフォルトで詰めた行送りを使用します
visionOS
- システムフォントは iOS と同じように動作します
- Dynamic Type に対応しています
トラッキングと行送り
トラッキング(文字間隔)
トラッキングは文字間の間隔を調整します。オプティカルサイズの動作には欠かせません。
サイズ別トラッキングテーブル:
SF Pro には、最適な文字間隔を保つため、ポイントサイズに応じて変化するトラッキング値が含まれています。
- 大きいサイズ:トラッキングを詰めます
- 小さいサイズ:トラッキングを広げます
Apple Design Resources の例:
- 34pt(largeTitle):トラッキング +0.016
- 17pt(body):トラッキング +0.008
- 11pt(caption2):トラッキング +0.06
タイトトラッキング API(テキストを収める場合):
// UIKit
textView.allowsDefaultTightening(for: .byTruncatingTail)
// SwiftUI
Text("Long text that needs to fit")
.lineLimit(1)
.minimumScaleFactor(0.5) // Allows tight 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
重要: 意味上の正確さを保つため、.kerning() API ではなく .tracking() を使用してください。トラッキングは必要に応じて合字を無効にしますが、カーニングは無効にしません。
行送り(行間)
デフォルトの行の高さ: フォントに組み込まれたメトリクス(アセンダー+ディセンダー+行間隔)から計算されます。
言語に応じた調整: iOS 17 以降では、アセンダーやディセンダーが高い文字体系に対して、行の高さが自動的に大きくなります。
- アラビア語
- タイ語、ラオス語
- ヒンディー語、ベンガル語、テルグ語
WWDC 2023 より:
「高さが変化する文字体系に対する行の高さの自動調整」
手動での行送り:
// UIKit
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 8.0 // 8pt additional space
// SwiftUI (iOS 13+)
Text("Custom spacing")
.lineSpacing(8.0)
行の高さ(iOS 26 以降):
.lineHeight() はベースライン間の距離を直接設定します。下から上までを測定する .lineSpacing() よりも直感的です。
// Presets
Text("Open layout").lineHeight(.loose)
Text("Compact layout").lineHeight(.tight)
// Precise control
Text("Scaled").lineHeight(.multiple(factor: 1.5))
Text("Fixed").lineHeight(.exact(points: 30)) // Does NOT scale with Dynamic Type
スタイル付き文字列では AttributedString.lineHeight も使用できます。API の詳細については axiom-swiftui-26-ref を参照してください。
サードパーティ製フォントのトラッキング
iOS 18 の新機能: フォントベンダーは、STAT テーブルと CTFont のオプティカルサイズ属性を使用して、カスタムフォントにトラッキングテーブルを埋め込めます。
let attributes: [String: Any] = [
kCTFontOpticalSizeAttribute as String: pointSize
]
let descriptor = CTFontDescriptorCreateWithAttributes(attributes as CFDictionary)
let font = CTFontCreateWithFontDescriptor(descriptor, pointSize, nil)
SwiftUI の AttributedString によるタイポグラフィ
フォント環境との相互作用
重要なパターン: SwiftUI の Text で AttributedString を使用する場合、フォントが属性付きコンテンツではなく環境から取得されると、lineHeightMultiple などの段落スタイルが失われることがあります。
WWDC 2025-280 より:
「TextEditor は、値が nil の AttributedStringKeys に対して、環境から計算されたデフォルト値を代わりに使用します。」
この同じ原則は Text にも当てはまります。AttributedString でフォントを指定しないと、SwiftUI は環境フォントを適用します。その結果、テキストランが再構築され、段落スタイルの詳細が削除されたり標準化されたりすることがあります。
問題点
// ❌ WRONG - .font() modifier can override and drop paragraph styles
var s = AttributedString(longString)
// Set paragraph style
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
// ⚠️ No font set in AttributedString
Text(s)
.font(.body) // ⚠️ May rebuild runs, lose lineHeightMultiple
これが失敗する理由:
AttributedStringにフォント属性が設定されていません(値はnil)- SwiftUI の
.font(.body)修飾子は、「テキストラン全体にこのフォントを使う」よう指示します - SwiftUI は環境フォントを使ってテキストランを再構築します
- 再構築中に段落スタイルが削除されたり標準化されたりします
解決策
細かな制御が必要な場合は、タイポグラフィを AttributedString 内に保持してください。
// ✅ CORRECT - Font in AttributedString, no environment override
var s = AttributedString(longString)
// Set font INSIDE the attributed content
s.font = .system(.body) // ✅ Typography inside AttributedString
// Set paragraph style
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
Text(s) // ✅ No .font() modifier
これが機能する理由:
- フォントは属性付きコンテンツの一部です(
nilではありません) .font()修飾子による環境からの上書きがありません- SwiftUI はフォントと段落スタイルの両方を保持します
- すべての属性を含むテキストランが維持されます
各アプローチの使い分け
AttributedString でフォントを指定する(細かな制御)
var s = AttributedString("Carefully styled text")
s.font = .system(.body)
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
p.alignment = .leading
s.paragraphStyle = p
Text(s) // No modifier
使用する場面:
- 段落のスタイル(行の高さ、配置)を正確に指定する必要がある
- 1 つの文字列で複数のフォントを混在させる
- コンテンツを
TextとTextEditorの両方に表示する - リッチテキストエディタの書式を正確に保持する
.font() 修飾子を使用する(広範囲の上書き)
Text("Simple text")
.font(.body)
.lineSpacing(4.0) // SwiftUI-level spacing
使用する場面:
- 段落スタイルを使わないシンプルなテキスト
- Dynamic Type による自動スケーリングを使用する
- SwiftUI のセマンティックフォントの動作(ダークモード、アクセシビリティ)が必要
- AttributedString のフォントを意図的に上書きする
1 つの文字列で複数のフォントを使用する
var s = AttributedString("Title")
s.font = .system(.title).bold()
var body = AttributedString(" and body text")
body.font = .system(.body)
s.append(body)
Text(s) // ✅ No .font() modifier preserves both fonts
よくある誤解:順序は関係ありません
// ❌ WRONG mental model: "Create AttributedString first"
var s = AttributedString(text)
var p = AttributedString.ParagraphStyle()
p.lineHeightMultiple = 0.92
s.paragraphStyle = p
s.font = .system(.body) // ⚠️ Setting font last doesn't help if you use .font() modifier
Text(s).font(.body) // Still breaks!
問題は、AttributedString でフォントを設定するタイミングではありません。属性付きコンテンツが独自のフォント属性を持つか、それとも SwiftUI の .font(...) 環境に依存するかが問題です。
検証チェックリスト
段落スタイルで AttributedString を使用する場合:
-
AttributedString内でフォントを設定する(nilではないこと) -
Textビューに.font()修飾子を指定しない(意図的に上書きする場合を除く) - フォントの設定前または設定後に段落スタイルを設定する(順序は問いません)
- 実際のコンテンツでテストし、行の高さと配置が保たれることを確認する
国際化
双方向テキスト
複雑な文字スクリプトの例(WWDC 2021より):
カンナダ語の「October」:
- 文字インデックス4の分割母音は、2つのグリフになります
- 合字の適用前にグリフの順序が入れ替わります
- グリフインデックスと文字インデックスは一致しません
これが、TextKit 2で整数インデックスの代わりに NSTextLocation を使用する理由です。
ヘブライ語/アラビア語の選択範囲:
右から左へのレイアウトでは、視覚上1つの選択範囲が、AttributedString 内では複数の NSRange になることがあります。
改行
言語に応じた改行(iOS 17以降):
- 中国語、日本語、韓国語:意味上の境界で改行します
- ドイツ語:複合語の途中での改行を避けます
- 英語:ハイフン位置での改行を優先します
均等な行分割(TextKit 2): 両端揃えの段落では、改良された行分割アルゴリズムが使用されます:
- 間延びした行を減らします
- 単語間の間隔をより均等にします
- TextKit 2で自動的に適用されます
テキストのクリッピングを防ぐ
ベストプラクティス:
- Dynamic Typeを使用する(自動調整されます)
- SwiftUIで
.lineLimit(nil)または.lineLimit(2...5)を設定する - 1行に制限されたテキストには
.minimumScaleFactor()を使用する - 大きなアクセシビリティサイズでテストする
CSSとウェブタイポグラフィ
システムUIフォントファミリー:
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 */
レガシー:
font-family: -apple-system; /* deprecated, use system-ui */
コード例
強調された大きなタイトル(SwiftUI)
Text("Recipe Editor")
.font(.largeTitle.bold()) // Emphasized variant
カスタムフォント+Dynamic Type(UIKit)
let customFont = UIFont(name: "Avenir-Medium", size: 17)!
let metrics = UIFontMetrics(forTextStyle: .body)
label.font = metrics.scaledFont(for: customFont)
label.adjustsFontForContentSizeCategory = true
角丸デザイン(UIKit)
let descriptor = UIFontDescriptor
.preferredFontDescriptor(withTextStyle: .largeTitle)
.withDesign(.rounded)!
let font = UIFont(descriptor: descriptor, size: 0)
角丸デザイン(SwiftUI)
Text("Today")
.font(.largeTitle.bold())
.fontDesign(.rounded)
ScaledMetric(SwiftUI)
struct RecipeView: View {
@ScaledMetric(relativeTo: .body) var padding: CGFloat = 20
var body: some View {
Text("Recipe")
.padding(padding) // Scales with Dynamic Type
}
}
リソース
WWDC:2020-10175、2022-110381、2023-10058
ドキュメント:/uikit/uifontdescriptor、/uikit/uifontmetrics、/swiftui/font