Signed-off-by: Matt Bruce <mbrucedogs@gmail.com>

This commit is contained in:
Matt Bruce 2026-01-27 09:50:42 -06:00
parent 23b9c90871
commit 76dda638cc
20 changed files with 107 additions and 176 deletions

View File

@ -21,16 +21,17 @@ struct EmptyStateCardView: View {
var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
Text(title)
.font(.title3)
.foregroundStyle(AppTextColors.primary)
.bold()
TitleLabel(title, style: .displaySmall, color: AppTextColors.primary)
// Exception: Uses .bodyLarge which isn't a standard BodyLabel weight
Text(message)
.font(.body)
.font(Design.Typography.bodyLarge)
.foregroundStyle(AppTextColors.secondary)
Button(action: action) {
// Exception: Needs .frame() modifiers
Text(actionTitle)
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
.frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight)

View File

@ -12,13 +12,9 @@ struct SectionHeaderView: View {
var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) {
Text(title)
.font(.headline)
.foregroundStyle(AppTextColors.primary)
TitleLabel(title, style: .headline, color: AppTextColors.primary)
if let subtitle {
Text(subtitle)
.font(.subheadline)
.foregroundStyle(AppTextColors.secondary)
BodyLabel(subtitle, emphasis: .secondary, color: AppTextColors.secondary)
}
}
.frame(maxWidth: .infinity, alignment: .leading)

View File

@ -22,31 +22,21 @@ struct InsightCardView: View {
private var cardContent: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
HStack {
Text(card.title)
.font(.subheadline)
.bold()
.foregroundStyle(AppTextColors.secondary)
BodyLabel(card.title, weight: .bold, color: AppTextColors.secondary)
Spacer()
// Subtle tap affordance
Image(systemName: "chevron.right")
.font(.caption2)
.foregroundStyle(AppTextColors.tertiary)
SymbolIcon.chevron(color: AppTextColors.tertiary)
.accessibilityHidden(true)
}
// Show value prominently
Text(card.value)
.font(.title)
.foregroundStyle(AppTextColors.primary)
.bold()
TitleLabel(card.value, style: .displayMedium, color: AppTextColors.primary)
// Show caption if present (non-chart cards)
if !card.caption.isEmpty {
Text(card.caption)
.font(.caption)
.foregroundStyle(AppTextColors.secondary)
CaptionLabel(card.caption, color: AppTextColors.secondary)
}
// Show full-width mini bar chart at bottom (Athlytic style)

View File

@ -81,17 +81,15 @@ struct InsightDetailSheet: View {
private var headerSection: some View {
VStack(spacing: Design.Spacing.medium) {
Image(systemName: card.symbolName)
.font(.system(size: Design.IconSize.hero))
.foregroundStyle(AppAccent.primary)
SymbolIcon(card.symbolName, size: .hero, color: AppAccent.primary)
.accessibilityHidden(true)
Text(card.value)
.font(.system(size: Design.IconSize.hero, weight: .bold))
.font(.system(size: Design.SymbolSize.hero, weight: .bold))
.foregroundStyle(AppTextColors.primary)
Text(card.caption)
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
}
.frame(maxWidth: .infinity)

View File

@ -146,9 +146,7 @@ struct FirstCheckInStepView: View {
Spacer()
// Success icon
Image(systemName: "checkmark.circle.fill")
.font(.system(size: Design.IconSize.hero))
.foregroundStyle(AppStatus.success)
SymbolIcon("checkmark.circle.fill", size: .hero, color: AppStatus.success)
.scaleEffect(showCelebration ? 1 : 0.5)
.opacity(showCelebration ? 1 : 0)

View File

@ -21,8 +21,7 @@ struct GoalSelectionStepView: View {
// Header
VStack(spacing: Design.Spacing.small) {
Text(String(localized: "What would you like to focus on?"))
.font(.title2)
.fontWeight(.bold)
.font(Design.Typography.displaySmall)
.foregroundStyle(AppTextColors.primary)
.multilineTextAlignment(.center)
}
@ -56,7 +55,7 @@ struct GoalSelectionStepView: View {
if !selectedGoals.isEmpty {
Button(action: onContinue) {
Text(String(localized: "Continue"))
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.inverse)
.frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight)
@ -95,18 +94,16 @@ private struct GoalCardView: View {
Button(action: onTap) {
VStack(spacing: Design.Spacing.medium) {
// Icon
Image(systemName: goal.symbolName)
.font(.system(size: Design.IconSize.xxLarge))
.foregroundStyle(isSelected ? AppAccent.primary : AppTextColors.secondary)
SymbolIcon(goal.symbolName, size: .card, color: isSelected ? AppAccent.primary : AppTextColors.secondary)
// Text
VStack(spacing: Design.Spacing.xSmall) {
Text(goal.displayName)
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
Text(goal.subtitle)
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.secondary)
.multilineTextAlignment(.center)
.lineLimit(2)

View File

@ -16,8 +16,7 @@ struct TimeSelectionStepView: View {
// Header
VStack(spacing: Design.Spacing.small) {
Text(String(localized: "When do you want to build habits?"))
.font(.title2)
.fontWeight(.bold)
.font(Design.Typography.displaySmall)
.foregroundStyle(AppTextColors.primary)
.multilineTextAlignment(.center)
}
@ -51,7 +50,7 @@ struct TimeSelectionStepView: View {
if selectedTime != nil {
Button(action: onContinue) {
Text(String(localized: "Continue"))
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.inverse)
.frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight)
@ -82,19 +81,17 @@ private struct TimeCardView: View {
Button(action: onTap) {
HStack(spacing: Design.Spacing.medium) {
// Icon
Image(systemName: time.symbolName)
.font(.system(size: Design.IconSize.xLarge))
.foregroundStyle(isSelected ? AppAccent.primary : AppTextColors.secondary)
.frame(width: 44)
SymbolIcon(time.symbolName, size: .rowContainer, color: isSelected ? AppAccent.primary : AppTextColors.secondary)
.frame(width: Design.Size.actionRowMinHeight)
// Text
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
Text(time.displayName)
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
Text(time.subtitle)
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
}
@ -102,9 +99,7 @@ private struct TimeCardView: View {
// Selection indicator
if isSelected {
Image(systemName: "checkmark.circle.fill")
.font(.title2)
.foregroundStyle(AppAccent.primary)
SymbolIcon("checkmark.circle.fill", size: .rowContainer, color: AppAccent.primary)
}
}
.padding(Design.Spacing.large)

View File

@ -21,13 +21,12 @@ struct WelcomeStepView: View {
VStack(spacing: Design.Spacing.medium) {
Text(String(localized: "Welcome to Rituals"))
.font(.largeTitle)
.fontWeight(.bold)
.font(Design.Typography.displayLarge)
.foregroundStyle(AppTextColors.primary)
.multilineTextAlignment(.center)
Text(String(localized: "Build lasting habits through focused, time-bound journeys"))
.font(.title3)
.font(Design.Typography.bodyLarge)
.foregroundStyle(AppTextColors.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, Design.Spacing.xxLarge)
@ -40,7 +39,7 @@ struct WelcomeStepView: View {
// Get Started button
Button(action: onContinue) {
Text(String(localized: "Get Started"))
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.inverse)
.frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight)
@ -105,9 +104,7 @@ struct WelcomeStepView: View {
)
// Center icon
Image(systemName: "sparkles")
.font(.system(size: Design.IconSize.xxLarge))
.foregroundStyle(AppAccent.primary)
SymbolIcon("sparkles", size: .card, color: AppAccent.primary)
.scaleEffect(animateRings ? 1.1 : 1.0)
.animation(
.easeInOut(duration: 1.5).repeatForever(autoreverses: true),

View File

@ -13,17 +13,14 @@ struct WhatsNextStepView: View {
// Header
VStack(spacing: Design.Spacing.medium) {
Image(systemName: "checkmark.circle.fill")
.font(.system(size: Design.IconSize.display))
.foregroundStyle(AppStatus.success)
SymbolIcon("checkmark.circle.fill", size: .section, color: AppStatus.success)
Text(String(localized: "You're all set!"))
.font(.largeTitle)
.fontWeight(.bold)
.font(Design.Typography.displayLarge)
.foregroundStyle(AppTextColors.primary)
Text(String(localized: "Here's how to get the most from Rituals"))
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
.multilineTextAlignment(.center)
}

View File

@ -32,20 +32,19 @@ struct RitualCardView: View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
HStack(spacing: Design.Spacing.small) {
// Icon
Image(systemName: iconName)
.foregroundStyle(hasActiveArc ? AppAccent.primary : AppTextColors.tertiary)
SymbolIcon(iconName, size: .row, color: hasActiveArc ? AppAccent.primary : AppTextColors.tertiary)
.accessibilityHidden(true)
// Title
Text(title)
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(hasActiveArc ? AppTextColors.primary : AppTextColors.tertiary)
Spacer(minLength: Design.Spacing.medium)
// Day label
Text(dayLabel)
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.secondary)
.padding(.horizontal, Design.Spacing.small)
.padding(.vertical, Design.Spacing.xxxSmall)
@ -55,11 +54,11 @@ struct RitualCardView: View {
}
Text(theme)
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(hasActiveArc ? AppTextColors.secondary : AppTextColors.tertiary)
Text(completionSummary)
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.secondary)
}
.padding(Design.Spacing.large)

View File

@ -199,20 +199,17 @@ struct RitualDetailView: View {
private var headerSection: some View {
HStack(spacing: Design.Spacing.medium) {
Image(systemName: ritual.iconName)
.font(.system(size: Design.IconSize.xLarge))
.foregroundStyle(ritual.hasActiveArc ? AppAccent.primary : AppTextColors.secondary)
.frame(width: 56, height: 56)
SymbolIcon(ritual.iconName, size: .rowContainer, color: ritual.hasActiveArc ? AppAccent.primary : AppTextColors.secondary)
.frame(width: Design.Size.iconContainerLarge, height: Design.Size.iconContainerLarge)
.background((ritual.hasActiveArc ? AppAccent.primary : AppTextColors.secondary).opacity(0.1))
.clipShape(.rect(cornerRadius: Design.CornerRadius.large))
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
Text(ritual.title)
.font(.title2)
.font(Design.Typography.displaySmall)
.foregroundStyle(AppTextColors.primary)
.bold()
Text(ritual.theme)
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
}

View File

@ -180,16 +180,14 @@ struct RitualsView: View {
private var currentEmptyState: some View {
VStack(spacing: Design.Spacing.large) {
Image(systemName: "sparkles")
.font(.system(size: Design.IconSize.hero))
.foregroundStyle(AppAccent.primary)
SymbolIcon("sparkles", size: .hero, color: AppAccent.primary)
Text(String(localized: "No Active Rituals"))
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
Text(String(localized: "Create a custom ritual or browse our preset library to get started."))
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
.multilineTextAlignment(.center)
@ -212,7 +210,7 @@ struct RitualsView: View {
if !store.pastRituals.isEmpty {
Text(String(localized: "Or restart a past ritual from the Past tab."))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
.padding(.top, Design.Spacing.small)
}
@ -223,16 +221,14 @@ struct RitualsView: View {
private var pastEmptyState: some View {
VStack(spacing: Design.Spacing.large) {
Image(systemName: "clock.arrow.circlepath")
.font(.system(size: Design.IconSize.hero))
.foregroundStyle(AppTextColors.tertiary)
SymbolIcon("clock.arrow.circlepath", size: .hero, color: AppTextColors.tertiary)
Text(String(localized: "No Past Rituals"))
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
Text(String(localized: "Rituals that have ended will appear here. You can restart them anytime."))
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
.multilineTextAlignment(.center)
}

View File

@ -63,17 +63,15 @@ struct ArcRenewalSheet: View {
private var celebrationHeader: some View {
VStack(spacing: Design.Spacing.medium) {
Image(systemName: "checkmark.seal.fill")
.font(.system(size: Design.IconSize.display))
.foregroundStyle(AppStatus.success)
SymbolIcon("checkmark.seal.fill", size: .section, color: AppStatus.success)
Text(ritual.title)
.font(.title2.bold())
.font(Design.Typography.displaySmall)
.foregroundStyle(AppTextColors.primary)
if let arc = completedArc {
Text(String(localized: "Arc \(arc.arcNumber) Complete"))
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
}
}
@ -84,26 +82,24 @@ struct ArcRenewalSheet: View {
private var summaryCard: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
Text(String(localized: "Your Journey"))
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
HStack {
Image(systemName: "chart.line.uptrend.xyaxis")
.foregroundStyle(AppAccent.primary)
SymbolIcon("chart.line.uptrend.xyaxis", size: .row, color: AppAccent.primary)
Text(arcSummary)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
}
.font(.subheadline)
if let arc = completedArc {
let habitCount = (arc.habits ?? []).count
HStack {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(AppAccent.primary)
SymbolIcon("checkmark.circle.fill", size: .row, color: AppAccent.primary)
Text(String(localized: "\(habitCount) habits tracked"))
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
}
.font(.subheadline)
}
}
.padding(Design.Spacing.large)
@ -115,13 +111,13 @@ struct ArcRenewalSheet: View {
private var durationSection: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
Text(String(localized: "Next Arc Duration"))
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
VStack(spacing: Design.Spacing.small) {
HStack {
Text(String(localized: "\(Int(durationDays)) days"))
.font(.title3.bold())
.font(Design.Typography.displaySmall)
.foregroundStyle(AppAccent.primary)
Spacer()
}
@ -131,11 +127,11 @@ struct ArcRenewalSheet: View {
HStack {
Text(String(localized: "1 week"))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
Spacer()
Text(String(localized: "1 year"))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
}
}
@ -147,7 +143,7 @@ struct ArcRenewalSheet: View {
durationDays = Double(days)
} label: {
Text("\(days)")
.font(.caption.bold())
.font(Design.Typography.captionBold)
.foregroundStyle(Int(durationDays) == days ? AppTextColors.primary : AppTextColors.secondary)
.padding(.horizontal, Design.Spacing.small)
.padding(.vertical, Design.Spacing.xSmall)
@ -171,10 +167,10 @@ struct ArcRenewalSheet: View {
dismiss()
} label: {
HStack {
Image(systemName: "arrow.clockwise")
SymbolIcon("arrow.clockwise", size: .row, color: AppTextColors.primary)
Text(String(localized: "Continue with Same Habits"))
}
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppTextColors.primary)
.frame(maxWidth: .infinity)
.padding(Design.Spacing.medium)
@ -189,10 +185,10 @@ struct ArcRenewalSheet: View {
showingEditSheet = true
} label: {
HStack {
Image(systemName: "pencil")
SymbolIcon("pencil", size: .row, color: AppAccent.primary)
Text(String(localized: "Continue with Changes"))
}
.font(.headline)
.font(Design.Typography.headline)
.foregroundStyle(AppAccent.primary)
.frame(maxWidth: .infinity)
.padding(Design.Spacing.medium)
@ -211,10 +207,10 @@ struct ArcRenewalSheet: View {
dismiss()
} label: {
HStack {
Image(systemName: "checkmark.circle")
SymbolIcon("checkmark.circle", size: .row, color: AppTextColors.secondary)
Text(String(localized: "End This Ritual"))
}
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
.frame(maxWidth: .infinity)
.padding(Design.Spacing.medium)

View File

@ -200,19 +200,17 @@ struct PresetDetailSheet: View {
private var headerSection: some View {
VStack(spacing: Design.Spacing.medium) {
Image(systemName: preset.iconName)
.font(.system(size: Design.IconSize.hero))
.foregroundStyle(AppAccent.primary)
SymbolIcon(preset.iconName, size: .hero, color: AppAccent.primary)
Text(preset.theme)
.font(.title3)
.font(Design.Typography.bodyLarge)
.foregroundStyle(AppTextColors.secondary)
HStack(spacing: Design.Spacing.large) {
Label(preset.timeOfDay.displayName, systemImage: preset.timeOfDay.symbolName)
Label(String(localized: "\(preset.durationDays) days"), systemImage: "calendar")
}
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
}
.frame(maxWidth: .infinity)

View File

@ -29,20 +29,17 @@ struct RitualFocusCardView: View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
HStack(spacing: Design.Spacing.small) {
// Icon
Image(systemName: iconName)
.foregroundStyle(AppAccent.primary)
SymbolIcon(iconName, size: .row, color: AppAccent.primary)
.accessibilityHidden(true)
// Title
Text(title)
.font(.headline)
.foregroundStyle(AppTextColors.primary)
TitleLabel(title, style: .headline, color: AppTextColors.primary)
Spacer(minLength: Design.Spacing.medium)
// Day label
// Day label - needs additional modifiers (padding, background)
Text(dayLabel)
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.secondary)
.padding(.horizontal, Design.Spacing.small)
.padding(.vertical, Design.Spacing.xxxSmall)
@ -51,16 +48,12 @@ struct RitualFocusCardView: View {
.accessibilityLabel(Text(dayLabel))
}
Text(theme)
.font(.subheadline)
.foregroundStyle(AppTextColors.secondary)
BodyLabel(theme, color: AppTextColors.secondary)
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
ProgressView(value: progress)
.tint(AppAccent.primary)
Text(completionSummary)
.font(.caption)
.foregroundStyle(AppTextColors.secondary)
CaptionLabel(completionSummary, color: AppTextColors.secondary)
}
}
.padding(Design.Spacing.large)

View File

@ -16,13 +16,11 @@ struct TodayEmptyStateView: View {
VStack(spacing: Design.Spacing.large) {
// Icon
Image(systemName: "sparkles")
.font(.system(size: Design.IconSize.hero))
.foregroundStyle(AppAccent.primary)
SymbolIcon("sparkles", size: .hero, color: AppAccent.primary)
.padding(.top, Design.Spacing.large)
Text(String(localized: "Rituals help you build consistent habits through focused, time-bound journeys."))
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
.multilineTextAlignment(.center)
.padding(.horizontal, Design.Spacing.large)
@ -36,7 +34,7 @@ struct TodayEmptyStateView: View {
.fill(AppBorder.subtle)
.frame(height: 1)
Text(String(localized: "or"))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
Rectangle()
.fill(AppBorder.subtle)
@ -73,7 +71,7 @@ struct TodayEmptyStateView: View {
// Past rituals hint
if !store.pastRituals.isEmpty {
Text(String(localized: "You can also restart a past ritual from the Rituals tab."))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
.multilineTextAlignment(.center)
.padding(.top, Design.Spacing.small)
@ -97,8 +95,7 @@ struct TodayEmptyStateView: View {
private var quickStartSection: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) {
Text(String(localized: "Quick Start"))
.font(.caption)
.fontWeight(.medium)
.font(Design.Typography.captionMedium)
.foregroundStyle(AppTextColors.tertiary)
.padding(.horizontal, Design.Spacing.medium)
@ -135,12 +132,10 @@ private struct QuickStartButton: View {
var body: some View {
Button(action: action) {
HStack(spacing: Design.Spacing.small) {
Image(systemName: goal.symbolName)
.font(.body)
.foregroundStyle(AppAccent.primary)
SymbolIcon(goal.symbolName, size: .row, color: AppAccent.primary)
Text(goal.displayName)
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.primary)
Spacer()

View File

@ -25,22 +25,20 @@ struct TodayHabitRowView: View {
var body: some View {
Button(action: action) {
HStack(spacing: Design.Spacing.medium) {
Image(systemName: symbolName)
.font(.body)
.foregroundStyle(isCompleted ? AppStatus.success : AppAccent.primary)
SymbolIcon(symbolName, size: .row, color: isCompleted ? AppStatus.success : AppAccent.primary)
.frame(width: AppMetrics.Size.iconLarge)
.accessibilityHidden(true)
Text(title)
.font(.subheadline)
.foregroundStyle(AppTextColors.primary)
BodyLabel(title, color: AppTextColors.primary)
Spacer(minLength: Design.Spacing.medium)
Image(systemName: isCompleted ? "checkmark.circle.fill" : "circle")
.font(.body)
.foregroundStyle(isCompleted ? AppStatus.success : AppBorder.subtle)
.accessibilityHidden(true)
SymbolIcon(
isCompleted ? "checkmark.circle.fill" : "circle",
size: .row,
color: isCompleted ? AppStatus.success : AppBorder.subtle
)
.accessibilityHidden(true)
}
.padding(.horizontal, horizontalPadding)
.padding(.vertical, Design.Spacing.medium)

View File

@ -10,13 +10,8 @@ struct TodayHeaderView: View {
var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) {
Text(String(localized: "Today"))
.font(.largeTitle)
.foregroundStyle(AppTextColors.primary)
.bold()
Text(dateText)
.font(.subheadline)
.foregroundStyle(AppTextColors.secondary)
TitleLabel(String(localized: "Today"), style: .displayLarge, color: AppTextColors.primary)
BodyLabel(dateText, color: AppTextColors.secondary)
}
.frame(maxWidth: .infinity, alignment: .leading)
.accessibilityElement(children: .combine)

View File

@ -48,19 +48,17 @@ struct TodayNoRitualsForTimeView: View {
VStack(spacing: Design.Spacing.large) {
// Icon
Image(systemName: currentTimePeriod.symbolName)
.font(.system(size: Design.IconSize.hero))
.foregroundStyle(AppAccent.primary.opacity(0.6))
SymbolIcon(currentTimePeriod.symbolName, size: .hero, color: AppAccent.primary.opacity(0.6))
.padding(.top, Design.Spacing.large)
VStack(spacing: Design.Spacing.xSmall) {
Text(String(localized: "No rituals scheduled for \(currentTimePeriod.displayName.lowercased())."))
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.secondary)
.multilineTextAlignment(.center)
Text(currentTimePeriod.timeRange)
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
}
@ -68,23 +66,21 @@ struct TodayNoRitualsForTimeView: View {
if !nextRituals.isEmpty {
VStack(alignment: .leading, spacing: Design.Spacing.medium) {
Text(String(localized: "Coming up later:"))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
ForEach(nextRituals) { ritual in
HStack(spacing: Design.Spacing.small) {
Image(systemName: ritual.timeOfDay.symbolName)
.font(.caption)
.foregroundStyle(AppTextColors.secondary)
SymbolIcon(ritual.timeOfDay.symbolName, size: .chevron, color: AppTextColors.secondary)
Text(ritual.title)
.font(.subheadline)
.font(Design.Typography.body)
.foregroundStyle(AppTextColors.primary)
Spacer()
Text(ritual.timeOfDay.displayName)
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
}
.padding(Design.Spacing.small)
@ -100,7 +96,7 @@ struct TodayNoRitualsForTimeView: View {
tomorrowRitual.timeOfDay.displayName,
tomorrowRitual.timeOfDay.timeRange
))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
.multilineTextAlignment(.center)
.padding(.top, Design.Spacing.small)
@ -108,7 +104,7 @@ struct TodayNoRitualsForTimeView: View {
// Motivational message
Text(String(localized: "Enjoy this moment. Your next ritual will appear when it's time."))
.font(.caption)
.font(Design.Typography.caption)
.foregroundStyle(AppTextColors.tertiary)
.multilineTextAlignment(.center)
.padding(.top, Design.Spacing.small)

View File

@ -33,8 +33,7 @@ struct TodayRitualSectionView: View {
Spacer()
// Time of day indicator
Image(systemName: timeOfDay.symbolName)
.foregroundStyle(AppTextColors.tertiary)
SymbolIcon(timeOfDay.symbolName, size: .row, color: AppTextColors.tertiary)
.accessibilityLabel(timeOfDay.displayName)
}