Replace custom AppTypography with native iOS font sizes

Removed the custom AppTypography system and migrated all font references
to use native SwiftUI Dynamic Type sizes. This enables automatic text
scaling based on user preferences and ensures consistency with iOS
design standards.

Changes:
- Removed AppTypography struct from DesignSystem.swift
- Updated PrimaryButtonStyle and SecondaryButtonStyle to use .headline
- Replaced all typography references throughout the app with native fonts:
  - displayLarge → .largeTitle.weight(.bold)
  - headlineSmall → .title3.weight(.semibold)
  - titleMedium → .title3.weight(.semibold)
  - bodyMedium → .body
  - labelMedium → .footnote.weight(.medium)
  - And many more across 18 files

Benefits:
- Supports Dynamic Type for accessibility
- Reduces custom code maintenance
- Aligns with iOS design guidelines

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Trey t
2025-11-12 21:26:56 -06:00
parent b1a24014ab
commit 4d05292daa
18 changed files with 80 additions and 110 deletions

View File

@@ -23,7 +23,7 @@ struct ContractorCard: View {
// Name with favorite star
HStack(spacing: AppSpacing.xxs) {
Text(contractor.name)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary)
.lineLimit(1)
@@ -37,7 +37,7 @@ struct ContractorCard: View {
// Company
if let company = contractor.company {
Text(company)
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
.lineLimit(1)
}
@@ -47,21 +47,21 @@ struct ContractorCard: View {
// Specialty
if let specialty = contractor.specialty {
Label(specialty, systemImage: "wrench.and.screwdriver")
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
// Rating
if let rating = contractor.averageRating, rating.doubleValue > 0 {
Label(String(format: "%.1f", rating.doubleValue), systemImage: "star.fill")
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.warning)
}
// Task count
if contractor.taskCount > 0 {
Label("\(contractor.taskCount) tasks", systemImage: "checkmark.circle")
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.success)
}
}

View File

@@ -39,13 +39,13 @@ struct ContractorDetailView: View {
// Name
Text(contractor.name)
.font(AppTypography.headlineSmall)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary)
// Company
if let company = contractor.company {
Text(company)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textSecondary)
}
@@ -55,7 +55,7 @@ struct ContractorDetailView: View {
Image(systemName: "wrench.and.screwdriver")
.font(.caption)
Text(specialty)
.font(AppTypography.bodyMedium)
.font(.body)
}
.padding(.horizontal, AppSpacing.sm)
.padding(.vertical, AppSpacing.xxs)
@@ -73,13 +73,13 @@ struct ContractorDetailView: View {
.font(.caption)
}
Text(String(format: "%.1f", rating.doubleValue))
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary)
}
if contractor.taskCount > 0 {
Text("\(contractor.taskCount) completed tasks")
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textSecondary)
}
}
@@ -140,7 +140,7 @@ struct ContractorDetailView: View {
if let notes = contractor.notes, !notes.isEmpty {
DetailSection(title: "Notes") {
Text(notes)
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
.padding(AppSpacing.md)
}
@@ -153,7 +153,7 @@ struct ContractorDetailView: View {
.foregroundColor(AppColors.success)
Spacer()
Text("\(contractor.taskCount) completed tasks")
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
}
.padding(AppSpacing.md)
@@ -235,7 +235,7 @@ struct DetailSection<Content: View>: View {
var body: some View {
VStack(alignment: .leading, spacing: AppSpacing.sm) {
Text(title)
.font(AppTypography.titleSmall)
.font(.headline)
.foregroundColor(AppColors.textPrimary)
.padding(.horizontal, AppSpacing.md)
@@ -264,11 +264,11 @@ struct DetailRow: View {
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
Text(label)
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary)
Text(value)
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textPrimary)
}

View File

@@ -190,7 +190,7 @@ struct ContractorFormSheet: View {
.frame(width: 20)
Text("Private Notes")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
@@ -208,7 +208,7 @@ struct ContractorFormSheet: View {
Image(systemName: "star.fill")
.foregroundColor(isFavorite ? AppColors.warning : AppColors.textSecondary)
Text("Mark as Favorite")
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textPrimary)
}
}
@@ -219,7 +219,7 @@ struct ContractorFormSheet: View {
// Error Message
if let error = viewModel.errorMessage {
Text(error)
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.error)
.padding(AppSpacing.sm)
.frame(maxWidth: .infinity)
@@ -360,7 +360,7 @@ struct SectionHeader: View {
var body: some View {
HStack {
Text(title)
.font(AppTypography.titleSmall)
.font(.headline)
.foregroundColor(AppColors.textPrimary)
Spacer()
}
@@ -385,12 +385,12 @@ struct FormTextField: View {
.frame(width: 20)
Text(title)
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
} else {
Text(title)
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}

View File

@@ -204,7 +204,7 @@ struct SearchBar: View {
.foregroundColor(AppColors.textSecondary)
TextField(placeholder, text: $text)
.font(AppTypography.bodyMedium)
.font(.body)
if !text.isEmpty {
Button(action: { text = "" }) {
@@ -233,7 +233,7 @@ struct FilterChip: View {
.font(.caption)
}
Text(title)
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
Button(action: onRemove) {
Image(systemName: "xmark")
@@ -259,12 +259,12 @@ struct EmptyContractorsView: View {
.foregroundColor(AppColors.textTertiary)
Text(hasFilters ? "No contractors found" : "No contractors yet")
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textSecondary)
if !hasFilters {
Text("Add your first contractor to get started")
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textTertiary)
}
}

View File

@@ -52,36 +52,6 @@ struct AppColors {
)
}
struct AppTypography {
// Display - For hero sections
static let displayLarge = Font.system(size: 57, weight: .bold, design: .rounded)
static let displayMedium = Font.system(size: 45, weight: .bold, design: .rounded)
static let displaySmall = Font.system(size: 36, weight: .bold, design: .rounded)
// Headline - For section headers
static let headlineLarge = Font.system(size: 32, weight: .bold, design: .rounded)
static let headlineMedium = Font.system(size: 28, weight: .semibold, design: .rounded)
static let headlineSmall = Font.system(size: 24, weight: .semibold, design: .rounded)
// Title - For card titles
static let titleLarge = Font.system(size: 22, weight: .semibold, design: .default)
static let titleMedium = Font.system(size: 18, weight: .semibold, design: .default)
static let titleSmall = Font.system(size: 16, weight: .semibold, design: .default)
// Body - For main content
static let bodyLarge = Font.system(size: 17, weight: .regular, design: .default)
static let bodyMedium = Font.system(size: 15, weight: .regular, design: .default)
static let bodySmall = Font.system(size: 13, weight: .regular, design: .default)
// Label - For labels and captions
static let labelLarge = Font.system(size: 14, weight: .medium, design: .default)
static let labelMedium = Font.system(size: 12, weight: .medium, design: .default)
static let labelSmall = Font.system(size: 11, weight: .medium, design: .default)
// Caption
static let caption = Font.system(size: 12, weight: .regular, design: .default)
}
struct AppSpacing {
static let xxs: CGFloat = 4
static let xs: CGFloat = 8
@@ -143,7 +113,7 @@ struct PrimaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(AppTypography.titleSmall)
.font(.headline)
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.frame(height: 56)
@@ -159,7 +129,7 @@ struct PrimaryButtonStyle: ButtonStyle {
struct SecondaryButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.label
.font(AppTypography.titleSmall)
.font(.headline)
.foregroundColor(AppColors.primary)
.frame(maxWidth: .infinity)
.frame(height: 56)

View File

@@ -39,21 +39,21 @@ struct DocumentCard: View {
VStack(alignment: .leading, spacing: 4) {
Text(document.title)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.fontWeight(.bold)
.foregroundColor(AppColors.textPrimary)
.lineLimit(1)
if let description = document.description_, !description.isEmpty {
Text(description)
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textSecondary)
.lineLimit(2)
}
HStack(spacing: 8) {
Text(getDocTypeDisplayName(document.documentType))
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(typeColor)
.padding(.horizontal, 6)
.padding(.vertical, 2)
@@ -62,7 +62,7 @@ struct DocumentCard: View {
if let fileSize = document.fileSize {
Text(formatFileSize(Int(fileSize)))
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
}

View File

@@ -12,11 +12,11 @@ struct EmptyStateView: View {
.foregroundColor(AppColors.textSecondary)
Text(title)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textSecondary)
Text(message)
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textTertiary)
.multilineTextAlignment(.center)
}

View File

@@ -29,12 +29,12 @@ struct WarrantyCard: View {
HStack(alignment: .top) {
VStack(alignment: .leading, spacing: 4) {
Text(document.title)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.fontWeight(.bold)
.foregroundColor(AppColors.textPrimary)
Text(document.itemName ?? "")
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
}
@@ -42,7 +42,7 @@ struct WarrantyCard: View {
// Status Badge
Text(statusText)
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.fontWeight(.bold)
.foregroundColor(statusColor)
.padding(.horizontal, 8)
@@ -57,10 +57,10 @@ struct WarrantyCard: View {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text("Provider")
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary)
Text(document.provider ?? "N/A")
.font(AppTypography.bodyMedium)
.font(.body)
.fontWeight(.medium)
.foregroundColor(AppColors.textPrimary)
}
@@ -69,10 +69,10 @@ struct WarrantyCard: View {
VStack(alignment: .trailing, spacing: 2) {
Text("Expires")
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary)
Text(document.endDate ?? "N/A")
.font(AppTypography.bodyMedium)
.font(.body)
.fontWeight(.medium)
.foregroundColor(AppColors.textPrimary)
}
@@ -80,14 +80,14 @@ struct WarrantyCard: View {
if document.isActive && daysUntilExpiration >= 0 {
Text("\(daysUntilExpiration) days remaining")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
.foregroundColor(statusColor)
}
// Category Badge
if let category = document.category {
Text(getCategoryDisplayName(category))
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(Color(hex: "374151"))
.padding(.horizontal, 8)
.padding(.vertical, 4)

View File

@@ -16,7 +16,7 @@ struct HomeScreenView: View {
ProgressView()
.scaleEffect(1.2)
Text("Loading...")
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
}
} else {
@@ -25,12 +25,12 @@ struct HomeScreenView: View {
// Greeting Header
VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text("Hello!")
.font(AppTypography.headlineLarge)
.font(.title.weight(.bold))
.fontWeight(.bold)
.foregroundColor(AppColors.textPrimary)
Text("Welcome to MyCrib")
.font(AppTypography.bodyLarge)
.font(.body)
.foregroundColor(AppColors.textSecondary)
}
.frame(maxWidth: .infinity, alignment: .leading)

View File

@@ -62,11 +62,11 @@ struct LoginView: View {
VStack(spacing: AppSpacing.xs) {
Text("Welcome Back")
.font(AppTypography.displaySmall)
.font(.title2.weight(.bold))
.foregroundColor(AppColors.textPrimary)
Text("Sign in to manage your properties")
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
}
}
@@ -76,7 +76,7 @@ struct LoginView: View {
// Username Field
VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text("Email or Username")
.font(AppTypography.labelLarge)
.font(.subheadline.weight(.medium))
.foregroundColor(AppColors.textSecondary)
HStack(spacing: AppSpacing.sm) {
@@ -111,7 +111,7 @@ struct LoginView: View {
// Password Field
VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text("Password")
.font(AppTypography.labelLarge)
.font(.subheadline.weight(.medium))
.foregroundColor(AppColors.textSecondary)
HStack(spacing: AppSpacing.sm) {
@@ -167,7 +167,7 @@ struct LoginView: View {
Button("Forgot Password?") {
showPasswordReset = true
}
.font(AppTypography.labelLarge)
.font(.subheadline.weight(.medium))
.foregroundColor(AppColors.primary)
}
@@ -177,7 +177,7 @@ struct LoginView: View {
Image(systemName: "exclamationmark.circle.fill")
.foregroundColor(AppColors.error)
Text(errorMessage)
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.error)
Spacer()
}
@@ -195,13 +195,13 @@ struct LoginView: View {
// Sign Up Link
HStack(spacing: AppSpacing.xs) {
Text("Don't have an account?")
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
Button("Sign Up") {
showingRegister = true
}
.font(AppTypography.bodyMedium)
.font(.body)
.fontWeight(.semibold)
.foregroundColor(AppColors.primary)
}
@@ -276,7 +276,7 @@ struct LoginView: View {
.progressViewStyle(CircularProgressViewStyle(tint: .white))
}
Text(viewModel.isLoading ? "Signing In..." : "Sign In")
.font(AppTypography.titleSmall)
.font(.headline)
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)

View File

@@ -16,7 +16,7 @@ struct ResidencesListView: View {
ProgressView()
.scaleEffect(1.2)
Text("Loading properties...")
.font(AppTypography.bodyMedium)
.font(.body)
.foregroundColor(AppColors.textSecondary)
}
} else if let error = viewModel.errorMessage {
@@ -38,10 +38,10 @@ struct ResidencesListView: View {
HStack {
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
Text("Your Properties")
.font(AppTypography.headlineSmall)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary)
Text("\(response.residences.count) \(response.residences.count == 1 ? "property" : "properties")")
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textSecondary)
}
Spacer()

View File

@@ -22,12 +22,12 @@ struct HomeNavigationCard: View {
// Text Content
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
Text(title)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.fontWeight(.semibold)
.foregroundColor(AppColors.textPrimary)
Text(subtitle)
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textSecondary)
}

View File

@@ -20,7 +20,7 @@ struct OverviewCard: View {
}
Text("Overview")
.font(AppTypography.headlineSmall)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary)
}
Spacer()

View File

@@ -19,12 +19,12 @@ struct StatView: View {
}
Text(value)
.font(AppTypography.headlineMedium)
.font(.title2.weight(.semibold))
.fontWeight(.bold)
.foregroundColor(AppColors.textPrimary)
Text(label)
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
.foregroundColor(AppColors.textSecondary)
.multilineTextAlignment(.center)
}

View File

@@ -21,13 +21,13 @@ struct ResidenceCard: View {
VStack(alignment: .leading, spacing: AppSpacing.xxs) {
Text(residence.name)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.fontWeight(.bold)
.foregroundColor(AppColors.textPrimary)
.lineLimit(1)
Text(residence.propertyType)
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textTertiary)
.textCase(.uppercase)
.tracking(0.5)
@@ -54,7 +54,7 @@ struct ResidenceCard: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary)
Text(residence.streetAddress)
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textSecondary)
}
@@ -63,7 +63,7 @@ struct ResidenceCard: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary)
Text("\(residence.city), \(residence.stateProvince)")
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textSecondary)
}
}

View File

@@ -9,7 +9,7 @@ struct PriorityBadge: View {
.font(.system(size: 10, weight: .bold))
Text(priority.capitalized)
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.fontWeight(.semibold)
}
.padding(.horizontal, AppSpacing.sm)

View File

@@ -5,7 +5,7 @@ struct StatusBadge: View {
var body: some View {
Text(formatStatus(status))
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.fontWeight(.semibold)
.padding(.horizontal, AppSpacing.sm)
.padding(.vertical, AppSpacing.xxs)

View File

@@ -17,7 +17,7 @@ struct TaskCard: View {
HStack(alignment: .top, spacing: AppSpacing.sm) {
VStack(alignment: .leading, spacing: AppSpacing.xs) {
Text(task.title)
.font(AppTypography.titleMedium)
.font(.title3.weight(.semibold))
.foregroundColor(AppColors.textPrimary)
.lineLimit(2)
@@ -34,7 +34,7 @@ struct TaskCard: View {
// Description
if let description = task.description_, !description.isEmpty {
Text(description)
.font(AppTypography.bodySmall)
.font(.callout)
.foregroundColor(AppColors.textSecondary)
.lineLimit(3)
}
@@ -46,7 +46,7 @@ struct TaskCard: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary)
Text(task.frequency.displayName)
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
.padding(.horizontal, AppSpacing.sm)
@@ -62,7 +62,7 @@ struct TaskCard: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(AppColors.textTertiary)
Text(formatDate(dueDate))
.font(AppTypography.labelSmall)
.font(.caption.weight(.medium))
.foregroundColor(AppColors.textSecondary)
}
.padding(.horizontal, AppSpacing.sm)
@@ -88,7 +88,7 @@ struct TaskCard: View {
.foregroundColor(AppColors.success)
}
Text("Completions (\(task.completions.count))")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
.fontWeight(.semibold)
.foregroundColor(AppColors.textPrimary)
}
@@ -108,7 +108,7 @@ struct TaskCard: View {
Image(systemName: "play.circle.fill")
.font(.system(size: 16, weight: .semibold))
Text("In Progress")
.font(AppTypography.labelLarge)
.font(.subheadline.weight(.medium))
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
@@ -125,7 +125,7 @@ struct TaskCard: View {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: 16, weight: .semibold))
Text("Complete")
.font(AppTypography.labelLarge)
.font(.subheadline.weight(.medium))
.fontWeight(.semibold)
}
.frame(maxWidth: .infinity)
@@ -146,7 +146,7 @@ struct TaskCard: View {
Image(systemName: "pencil")
.font(.system(size: 14, weight: .medium))
Text("Edit")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
}
.frame(maxWidth: .infinity)
.frame(height: 36)
@@ -161,7 +161,7 @@ struct TaskCard: View {
Image(systemName: "xmark.circle")
.font(.system(size: 14, weight: .medium))
Text("Cancel")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
}
.frame(maxWidth: .infinity)
.frame(height: 36)
@@ -175,7 +175,7 @@ struct TaskCard: View {
Image(systemName: "arrow.uturn.backward")
.font(.system(size: 14, weight: .medium))
Text("Restore")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
}
.frame(maxWidth: .infinity)
.frame(height: 36)
@@ -193,7 +193,7 @@ struct TaskCard: View {
Image(systemName: "tray.and.arrow.up")
.font(.system(size: 14, weight: .medium))
Text("Unarchive")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
}
.frame(maxWidth: .infinity)
.frame(height: 36)
@@ -209,7 +209,7 @@ struct TaskCard: View {
Image(systemName: "archivebox")
.font(.system(size: 14, weight: .medium))
Text("Archive")
.font(AppTypography.labelMedium)
.font(.footnote.weight(.medium))
}
.frame(maxWidth: .infinity)
.frame(height: 36)