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

This commit is contained in:
Matt Bruce 2026-01-27 11:23:45 -06:00
parent 90366720c0
commit 1d9100c018
19 changed files with 88 additions and 81 deletions

View File

@ -14,6 +14,15 @@ struct AndromidaApp: App {
@State private var justCompletedWizard = false @State private var justCompletedWizard = false
init() { init() {
// Register app's color theme for Bedrock components
Theme.register(
text: AppTextColors.self,
surface: AppSurface.self,
accent: AppAccent.self,
status: AppStatus.self
)
Theme.register(border: AppBorder.self)
// Include all models in schema - Ritual, RitualArc, and ArcHabit // Include all models in schema - Ritual, RitualArc, and ArcHabit
let schema = Schema([Ritual.self, RitualArc.self, ArcHabit.self]) let schema = Schema([Ritual.self, RitualArc.self, ArcHabit.self])
let configuration = ModelConfiguration( let configuration = ModelConfiguration(

View File

@ -21,9 +21,9 @@ struct EmptyStateCardView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) { VStack(alignment: .leading, spacing: Design.Spacing.medium) {
StyledLabel(title, .title2Bold, emphasis: .custom(AppTextColors.primary)) StyledLabel(title, .title2Bold, emphasis: .primary)
StyledLabel(message, .body, emphasis: .custom(AppTextColors.secondary)) StyledLabel(message, .body, emphasis: .secondary)
Button(action: action) { Button(action: action) {
// Needs .frame() modifiers // Needs .frame() modifiers

View File

@ -12,9 +12,9 @@ struct SectionHeaderView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) {
StyledLabel(title, .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(title, .heading, emphasis: .primary)
if let subtitle { if let subtitle {
StyledLabel(subtitle, .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(subtitle, .subheading, emphasis: .secondary)
} }
} }
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)

View File

@ -22,7 +22,7 @@ struct InsightCardView: View {
private var cardContent: some View { private var cardContent: some View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) { VStack(alignment: .leading, spacing: Design.Spacing.medium) {
HStack { HStack {
StyledLabel(card.title, .subheadingEmphasis, emphasis: .custom(AppTextColors.secondary)) StyledLabel(card.title, .subheadingEmphasis, emphasis: .secondary)
Spacer() Spacer()
@ -32,11 +32,11 @@ struct InsightCardView: View {
} }
// Show value prominently // Show value prominently
StyledLabel(card.value, .titleBold, emphasis: .custom(AppTextColors.primary)) StyledLabel(card.value, .titleBold, emphasis: .primary)
// Show caption if present (non-chart cards) // Show caption if present (non-chart cards)
if !card.caption.isEmpty { if !card.caption.isEmpty {
StyledLabel(card.caption, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(card.caption, .caption, emphasis: .secondary)
} }
// Show full-width mini bar chart at bottom (Athlytic style) // Show full-width mini bar chart at bottom (Athlytic style)

View File

@ -114,7 +114,7 @@ struct InsightDetailSheet: View {
? String(localized: "+\(changePercent)% vs last week") ? String(localized: "+\(changePercent)% vs last week")
: String(localized: "-\(changePercent)% vs last week") : String(localized: "-\(changePercent)% vs last week")
StyledLabel(changeText, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(changeText, .caption, emphasis: .secondary)
} }
Spacer() Spacer()
@ -126,7 +126,7 @@ struct InsightDetailSheet: View {
private var tipsSection: some View { private var tipsSection: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
IconLabel("lightbulb.fill", String(localized: "Tips"), .heading, emphasis: .custom(AppTextColors.primary)) IconLabel("lightbulb.fill", String(localized: "Tips"), .heading, emphasis: .primary)
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
ForEach(tips, id: \.self) { tip in ForEach(tips, id: \.self) { tip in
@ -134,7 +134,7 @@ struct InsightDetailSheet: View {
SymbolIcon("chevron.right", size: .badge, color: AppAccent.primary) SymbolIcon("chevron.right", size: .badge, color: AppAccent.primary)
.padding(.top, Design.Spacing.xSmall) .padding(.top, Design.Spacing.xSmall)
StyledLabel(tip, .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(tip, .subheading, emphasis: .secondary)
} }
} }
} }
@ -149,9 +149,9 @@ struct InsightDetailSheet: View {
private var explanationSection: some View { private var explanationSection: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(String(localized: "What this means"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "What this means"), .heading, emphasis: .primary)
StyledLabel(card.explanation, .body, emphasis: .custom(AppTextColors.secondary)) StyledLabel(card.explanation, .body, emphasis: .secondary)
} }
.padding(Design.Spacing.large) .padding(Design.Spacing.large)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
@ -163,7 +163,7 @@ struct InsightDetailSheet: View {
private func chartSection(_ data: [TrendDataPoint]) -> some View { private func chartSection(_ data: [TrendDataPoint]) -> some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(String(localized: "7-Day Trend"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "7-Day Trend"), .heading, emphasis: .primary)
Chart(data) { point in Chart(data) { point in
BarMark( BarMark(
@ -214,16 +214,16 @@ struct InsightDetailSheet: View {
private func breakdownSection(_ breakdown: [BreakdownItem]) -> some View { private func breakdownSection(_ breakdown: [BreakdownItem]) -> some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(String(localized: "Breakdown"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "Breakdown"), .heading, emphasis: .primary)
VStack(spacing: Design.Spacing.xSmall) { VStack(spacing: Design.Spacing.xSmall) {
ForEach(breakdown) { item in ForEach(breakdown) { item in
HStack { HStack {
StyledLabel(item.label, .subheading, emphasis: .custom(AppTextColors.primary)) StyledLabel(item.label, .subheading, emphasis: .primary)
Spacer() Spacer()
StyledLabel(item.value, .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(item.value, .subheading, emphasis: .secondary)
} }
.padding(.vertical, Design.Spacing.small) .padding(.vertical, Design.Spacing.small)

View File

@ -55,7 +55,7 @@ struct FirstCheckInStepView: View {
private var tutorialView: some View { private var tutorialView: some View {
VStack(spacing: Design.Spacing.xxLarge) { VStack(spacing: Design.Spacing.xxLarge) {
// Header // Header
StyledLabel(String(localized: "Let's try it out!"), .title2Bold, emphasis: .custom(AppTextColors.primary), alignment: .center) StyledLabel(String(localized: "Let's try it out!"), .title2Bold, emphasis: .primary, alignment: .center)
.opacity(animateContent ? 1 : 0) .opacity(animateContent ? 1 : 0)
// Ritual card with habits // Ritual card with habits
@ -65,9 +65,9 @@ struct FirstCheckInStepView: View {
SymbolIcon(ritual.iconName, size: .row, color: AppAccent.primary) SymbolIcon(ritual.iconName, size: .row, color: AppAccent.primary)
VStack(alignment: .leading, spacing: 2) { VStack(alignment: .leading, spacing: 2) {
StyledLabel(String(localized: "Day 1 of \(ritual.durationDays)"), .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(String(localized: "Day 1 of \(ritual.durationDays)"), .caption, emphasis: .secondary)
StyledLabel(ritual.title, .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(ritual.title, .heading, emphasis: .primary)
} }
Spacer() Spacer()
@ -93,14 +93,14 @@ struct FirstCheckInStepView: View {
.offset(y: animateContent ? 0 : 20) .offset(y: animateContent ? 0 : 20)
// Instruction // Instruction
StyledLabel(String(localized: "Tap a habit to check in"), .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(String(localized: "Tap a habit to check in"), .subheading, emphasis: .secondary)
.opacity(animateContent ? 1 : 0) .opacity(animateContent ? 1 : 0)
Spacer() Spacer()
VStack(spacing: Design.Spacing.medium) { VStack(spacing: Design.Spacing.medium) {
Button(action: onComplete) { Button(action: onComplete) {
StyledLabel(String(localized: "Continue"), .heading, emphasis: .custom(AppTextColors.inverse)) StyledLabel(String(localized: "Continue"), .heading, emphasis: .inverse)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight) .frame(height: AppMetrics.Size.buttonHeight)
.background(AppAccent.primary) .background(AppAccent.primary)
@ -108,7 +108,7 @@ struct FirstCheckInStepView: View {
} }
Button(action: onComplete) { Button(action: onComplete) {
StyledLabel(String(localized: "Skip for now"), .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(String(localized: "Skip for now"), .subheading, emphasis: .secondary)
} }
} }
.padding(.horizontal, Design.Spacing.xxLarge) .padding(.horizontal, Design.Spacing.xxLarge)
@ -135,9 +135,9 @@ struct FirstCheckInStepView: View {
.opacity(showCelebration ? 1 : 0) .opacity(showCelebration ? 1 : 0)
VStack(spacing: Design.Spacing.medium) { VStack(spacing: Design.Spacing.medium) {
StyledLabel(String(localized: "Nice work!"), .heroBold, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "Nice work!"), .heroBold, emphasis: .primary)
StyledLabel(String(localized: "You completed your first check-in"), .title3, emphasis: .custom(AppTextColors.secondary), alignment: .center) StyledLabel(String(localized: "You completed your first check-in"), .title3, emphasis: .secondary, alignment: .center)
} }
Spacer() Spacer()
@ -145,7 +145,7 @@ struct FirstCheckInStepView: View {
// Continue button // Continue button
if showContinueButton { if showContinueButton {
Button(action: onComplete) { Button(action: onComplete) {
StyledLabel(String(localized: "Continue to Rituals"), .heading, emphasis: .custom(AppTextColors.inverse)) StyledLabel(String(localized: "Continue to Rituals"), .heading, emphasis: .inverse)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight) .frame(height: AppMetrics.Size.buttonHeight)
.background(AppAccent.primary) .background(AppAccent.primary)
@ -194,7 +194,7 @@ private struct OnboardingHabitRowView: View {
SymbolIcon(symbolName, size: .row, color: isCompleted ? AppStatus.success : AppAccent.primary) SymbolIcon(symbolName, size: .row, color: isCompleted ? AppStatus.success : AppAccent.primary)
.frame(width: AppMetrics.Size.iconLarge) .frame(width: AppMetrics.Size.iconLarge)
StyledLabel(title, .body, emphasis: .custom(AppTextColors.primary)) StyledLabel(title, .body, emphasis: .primary)
Spacer(minLength: Design.Spacing.medium) Spacer(minLength: Design.Spacing.medium)

View File

@ -26,7 +26,7 @@ struct RitualPreviewStepView: View {
.frame(height: Design.Spacing.large) .frame(height: Design.Spacing.large)
// Header // Header
StyledLabel(headerText, .title2Bold, emphasis: .custom(AppTextColors.primary), alignment: .center) StyledLabel(headerText, .title2Bold, emphasis: .primary, alignment: .center)
.opacity(animateContent ? 1 : 0) .opacity(animateContent ? 1 : 0)
// Ritual preview card // Ritual preview card
@ -36,9 +36,9 @@ struct RitualPreviewStepView: View {
SymbolIcon(preset.iconName, size: .card, color: AppAccent.primary) SymbolIcon(preset.iconName, size: .card, color: AppAccent.primary)
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
StyledLabel(preset.title, .title3Bold, emphasis: .custom(AppTextColors.primary)) StyledLabel(preset.title, .title3Bold, emphasis: .primary)
StyledLabel(preset.theme, .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(preset.theme, .subheading, emphasis: .secondary)
} }
} }
@ -52,7 +52,7 @@ struct RitualPreviewStepView: View {
SymbolIcon(habit.symbolName, size: .inline, color: AppTextColors.secondary) SymbolIcon(habit.symbolName, size: .inline, color: AppTextColors.secondary)
.frame(width: 24) .frame(width: 24)
StyledLabel(habit.title, .body, emphasis: .custom(AppTextColors.primary)) StyledLabel(habit.title, .body, emphasis: .primary)
} }
} }
} }
@ -62,9 +62,9 @@ struct RitualPreviewStepView: View {
// Duration and time // Duration and time
HStack(spacing: Design.Spacing.large) { HStack(spacing: Design.Spacing.large) {
IconLabel("calendar", String(localized: "\(preset.durationDays) days"), .caption, emphasis: .custom(AppTextColors.secondary)) IconLabel("calendar", String(localized: "\(preset.durationDays) days"), .caption, emphasis: .secondary)
IconLabel(preset.timeOfDay.symbolName, preset.timeOfDay.displayName, .caption, emphasis: .custom(AppTextColors.secondary)) IconLabel(preset.timeOfDay.symbolName, preset.timeOfDay.displayName, .caption, emphasis: .secondary)
} }
} }
.padding(Design.Spacing.large) .padding(Design.Spacing.large)
@ -80,7 +80,7 @@ struct RitualPreviewStepView: View {
VStack(spacing: Design.Spacing.medium) { VStack(spacing: Design.Spacing.medium) {
// Primary CTA // Primary CTA
Button(action: onStartRitual) { Button(action: onStartRitual) {
StyledLabel(String(localized: "Start This Ritual"), .heading, emphasis: .custom(AppTextColors.inverse)) StyledLabel(String(localized: "Start This Ritual"), .heading, emphasis: .inverse)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight) .frame(height: AppMetrics.Size.buttonHeight)
.background(AppAccent.primary) .background(AppAccent.primary)
@ -89,7 +89,7 @@ struct RitualPreviewStepView: View {
// Skip option // Skip option
Button(action: onSkip) { Button(action: onSkip) {
StyledLabel(String(localized: "Skip for now"), .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(String(localized: "Skip for now"), .subheading, emphasis: .secondary)
} }
} }
.padding(.horizontal, Design.Spacing.xxLarge) .padding(.horizontal, Design.Spacing.xxLarge)

View File

@ -62,7 +62,7 @@ struct WhatsNextStepView: View {
// CTA button // CTA button
Button(action: onComplete) { Button(action: onComplete) {
StyledLabel(String(localized: "Let's Go"), .heading, emphasis: .custom(AppTextColors.inverse)) StyledLabel(String(localized: "Let's Go"), .heading, emphasis: .inverse)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: AppMetrics.Size.buttonHeight) .frame(height: AppMetrics.Size.buttonHeight)
.background(AppAccent.primary) .background(AppAccent.primary)
@ -99,9 +99,9 @@ private struct FeatureHelpCard: View {
// Text // Text
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
StyledLabel(title, .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(title, .heading, emphasis: .primary)
StyledLabel(description, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(description, .caption, emphasis: .secondary)
} }
Spacer() Spacer()

View File

@ -29,7 +29,7 @@ struct HabitPerformanceView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(String(localized: "Habit Performance"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "Habit Performance"), .heading, emphasis: .primary)
LazyVGrid(columns: habitColumns, alignment: .leading, spacing: Design.Spacing.xSmall) { LazyVGrid(columns: habitColumns, alignment: .leading, spacing: Design.Spacing.xSmall) {
ForEach(sortedByRate, id: \.habit.id) { item in ForEach(sortedByRate, id: \.habit.id) { item in
@ -50,7 +50,7 @@ struct HabitPerformanceView: View {
SymbolIcon(habit.symbolName, size: .inline, color: colorForRate(rate)) SymbolIcon(habit.symbolName, size: .inline, color: colorForRate(rate))
.frame(width: AppMetrics.Size.iconMedium) .frame(width: AppMetrics.Size.iconMedium)
StyledLabel(habit.title, .subheading, emphasis: .custom(AppTextColors.primary)) StyledLabel(habit.title, .subheading, emphasis: .primary)
Spacer() Spacer()

View File

@ -58,7 +58,7 @@ struct RitualCardView: View {
.typography(.subheading) .typography(.subheading)
.foregroundStyle(hasActiveArc ? AppTextColors.secondary : AppTextColors.tertiary) .foregroundStyle(hasActiveArc ? AppTextColors.secondary : AppTextColors.tertiary)
StyledLabel(completionSummary, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(completionSummary, .caption, emphasis: .secondary)
} }
.padding(Design.Spacing.large) .padding(Design.Spacing.large)
.background(AppSurface.card) .background(AppSurface.card)

View File

@ -14,7 +14,7 @@ struct RitualMilestonesView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(String(localized: "Milestones"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "Milestones"), .heading, emphasis: .primary)
HStack(spacing: Design.Spacing.small) { HStack(spacing: Design.Spacing.small) {
ForEach(milestones) { milestone in ForEach(milestones) { milestone in

View File

@ -40,7 +40,7 @@ struct RitualProgressStatsView: View {
ProgressView(value: progress) ProgressView(value: progress)
.tint(AppAccent.primary) .tint(AppAccent.primary)
StyledLabel("\(Int(progress * 100))% complete", .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel("\(Int(progress * 100))% complete", .caption, emphasis: .secondary)
} }
} }
.padding(Design.Spacing.large) .padding(Design.Spacing.large)
@ -51,12 +51,12 @@ struct RitualProgressStatsView: View {
private func statColumn(value: String, secondary: String?, label: String) -> some View { private func statColumn(value: String, secondary: String?, label: String) -> some View {
VStack(spacing: Design.Spacing.xSmall) { VStack(spacing: Design.Spacing.xSmall) {
StyledLabel(value, .titleBold, emphasis: .custom(AppTextColors.primary)) StyledLabel(value, .titleBold, emphasis: .primary)
// Always show secondary row for consistent height // Always show secondary row for consistent height
StyledLabel(secondary ?? " ", .caption, emphasis: .custom(secondary != nil ? AppTextColors.tertiary : .clear)) StyledLabel(secondary ?? " ", .caption, emphasis: .custom(secondary != nil ? AppTextColors.tertiary : .clear))
StyledLabel(label, .caption2, emphasis: .custom(AppTextColors.secondary)) StyledLabel(label, .caption2, emphasis: .secondary)
} }
} }
} }

View File

@ -108,7 +108,7 @@ struct RitualDetailView: View {
if !ritual.notes.isEmpty { if !ritual.notes.isEmpty {
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
SectionHeaderView(title: String(localized: "Notes")) SectionHeaderView(title: String(localized: "Notes"))
StyledLabel(ritual.notes, .body, emphasis: .custom(AppTextColors.secondary)) StyledLabel(ritual.notes, .body, emphasis: .secondary)
} }
} }
@ -257,7 +257,7 @@ struct RitualDetailView: View {
VStack(alignment: .leading, spacing: Design.Spacing.medium) { VStack(alignment: .leading, spacing: Design.Spacing.medium) {
HStack { HStack {
SymbolIcon("clock.arrow.circlepath", size: .inline, color: AppTextColors.secondary) SymbolIcon("clock.arrow.circlepath", size: .inline, color: AppTextColors.secondary)
StyledLabel(String(localized: "This ritual is not currently active"), .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(String(localized: "This ritual is not currently active"), .subheading, emphasis: .secondary)
} }
if let lastArc = ritual.latestArc { if let lastArc = ritual.latestArc {
@ -266,7 +266,7 @@ struct RitualDetailView: View {
let possibleCheckIns = habits.count * lastArc.durationDays let possibleCheckIns = habits.count * lastArc.durationDays
let completionRate = possibleCheckIns > 0 ? Int(Double(totalCheckIns) / Double(possibleCheckIns) * 100) : 0 let completionRate = possibleCheckIns > 0 ? Int(Double(totalCheckIns) / Double(possibleCheckIns) * 100) : 0
StyledLabel(String(localized: "Last arc completed with \(completionRate)% habit completion over \(lastArc.durationDays) days."), .caption, emphasis: .custom(AppTextColors.tertiary)) StyledLabel(String(localized: "Last arc completed with \(completionRate)% habit completion over \(lastArc.durationDays) days."), .caption, emphasis: .tertiary)
} }
Button { Button {
@ -318,7 +318,7 @@ struct RitualDetailView: View {
.background(AppAccent.primary.opacity(0.2)) .background(AppAccent.primary.opacity(0.2))
.clipShape(.capsule) .clipShape(.capsule)
} else { } else {
StyledLabel(String(localized: "No active arc"), .caption, emphasis: .custom(AppTextColors.tertiary)) StyledLabel(String(localized: "No active arc"), .caption, emphasis: .tertiary)
.padding(.horizontal, Design.Spacing.small) .padding(.horizontal, Design.Spacing.small)
.padding(.vertical, Design.Spacing.xSmall) .padding(.vertical, Design.Spacing.xSmall)
.background(AppTextColors.tertiary.opacity(0.2)) .background(AppTextColors.tertiary.opacity(0.2))
@ -381,7 +381,7 @@ struct RitualDetailView: View {
) )
if completedArcs.isEmpty { if completedArcs.isEmpty {
StyledLabel(String(localized: "No completed arcs yet."), .caption, emphasis: .custom(AppTextColors.tertiary)) StyledLabel(String(localized: "No completed arcs yet."), .caption, emphasis: .tertiary)
} else { } else {
LazyVGrid(columns: arcColumns, alignment: .leading, spacing: Design.Spacing.small) { LazyVGrid(columns: arcColumns, alignment: .leading, spacing: Design.Spacing.small) {
ForEach(completedArcs) { arc in ForEach(completedArcs) { arc in
@ -407,9 +407,9 @@ struct RitualDetailView: View {
} label: { } label: {
HStack { HStack {
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
StyledLabel(String(localized: "Arc \(arc.arcNumber)"), .subheadingEmphasis, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "Arc \(arc.arcNumber)"), .subheadingEmphasis, emphasis: .primary)
StyledLabel("\(dateFormatter.string(from: arc.startDate)) \(dateFormatter.string(from: arc.endDate))", .caption, emphasis: .custom(AppTextColors.tertiary)) StyledLabel("\(dateFormatter.string(from: arc.startDate)) \(dateFormatter.string(from: arc.endDate))", .caption, emphasis: .tertiary)
} }
Spacer() Spacer()
@ -417,7 +417,7 @@ struct RitualDetailView: View {
VStack(alignment: .trailing, spacing: Design.Spacing.xSmall) { VStack(alignment: .trailing, spacing: Design.Spacing.xSmall) {
StyledLabel("\(completionRate)%", .subheadingEmphasis, emphasis: .custom(completionRate >= 70 ? AppStatus.success : AppTextColors.secondary)) StyledLabel("\(completionRate)%", .subheadingEmphasis, emphasis: .custom(completionRate >= 70 ? AppStatus.success : AppTextColors.secondary))
StyledLabel(String(localized: "\(arc.durationDays) days"), .caption, emphasis: .custom(AppTextColors.tertiary)) StyledLabel(String(localized: "\(arc.durationDays) days"), .caption, emphasis: .tertiary)
} }
SymbolIcon.chevron(color: AppTextColors.tertiary) SymbolIcon.chevron(color: AppTextColors.tertiary)
@ -436,7 +436,7 @@ struct RitualDetailView: View {
HStack(spacing: Design.Spacing.medium) { HStack(spacing: Design.Spacing.medium) {
// Days remaining // Days remaining
if daysRemaining > 0 { if daysRemaining > 0 {
IconLabel("hourglass", String(localized: "\(daysRemaining) days remaining"), .caption, emphasis: .custom(AppTextColors.secondary)) IconLabel("hourglass", String(localized: "\(daysRemaining) days remaining"), .caption, emphasis: .secondary)
.padding(.horizontal, Design.Spacing.medium) .padding(.horizontal, Design.Spacing.medium)
.padding(.vertical, Design.Spacing.small) .padding(.vertical, Design.Spacing.small)
.background(AppSurface.card) .background(AppSurface.card)

View File

@ -141,7 +141,7 @@ struct RitualsView: View {
// Time of day header // Time of day header
HStack(spacing: Design.Spacing.small) { HStack(spacing: Design.Spacing.small) {
SymbolIcon(group.timeOfDay.symbolName, size: .inline, color: AppAccent.primary) SymbolIcon(group.timeOfDay.symbolName, size: .inline, color: AppAccent.primary)
StyledLabel(group.timeOfDay.displayName, .subheadingEmphasis, emphasis: .custom(AppTextColors.secondary)) StyledLabel(group.timeOfDay.displayName, .subheadingEmphasis, emphasis: .secondary)
} }
.padding(.top, Design.Spacing.small) .padding(.top, Design.Spacing.small)
@ -178,7 +178,7 @@ struct RitualsView: View {
VStack(spacing: Design.Spacing.large) { VStack(spacing: Design.Spacing.large) {
SymbolIcon("sparkles", size: .hero, color: AppAccent.primary) SymbolIcon("sparkles", size: .hero, color: AppAccent.primary)
StyledLabel(String(localized: "No Active Rituals"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "No Active Rituals"), .heading, emphasis: .primary)
Text(String(localized: "Create a custom ritual or browse our preset library to get started.")) Text(String(localized: "Create a custom ritual or browse our preset library to get started."))
.typography(.subheading) .typography(.subheading)
@ -217,7 +217,7 @@ struct RitualsView: View {
VStack(spacing: Design.Spacing.large) { VStack(spacing: Design.Spacing.large) {
SymbolIcon("clock.arrow.circlepath", size: .hero, color: AppTextColors.tertiary) SymbolIcon("clock.arrow.circlepath", size: .hero, color: AppTextColors.tertiary)
StyledLabel(String(localized: "No Past Rituals"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "No Past Rituals"), .heading, emphasis: .primary)
Text(String(localized: "Rituals that have ended will appear here. You can restart them anytime.")) Text(String(localized: "Rituals that have ended will appear here. You can restart them anytime."))
.typography(.subheading) .typography(.subheading)
@ -324,12 +324,12 @@ struct RitualsView: View {
.clipShape(.rect(cornerRadius: Design.CornerRadius.small)) .clipShape(.rect(cornerRadius: Design.CornerRadius.small))
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
StyledLabel(ritual.title, .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(ritual.title, .heading, emphasis: .primary)
StyledLabel(ritual.theme, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(ritual.theme, .caption, emphasis: .secondary)
if let lastArc = ritual.latestArc { if let lastArc = ritual.latestArc {
IconLabel("calendar", formattedEndDate(lastArc.endDate), .caption2, emphasis: .custom(AppTextColors.tertiary)) IconLabel("calendar", formattedEndDate(lastArc.endDate), .caption2, emphasis: .tertiary)
} }
} }
@ -337,7 +337,7 @@ struct RitualsView: View {
// Arc count badge // Arc count badge
if ritual.completedArcCount > 0 { if ritual.completedArcCount > 0 {
StyledLabel("\(ritual.completedArcCount) arc\(ritual.completedArcCount == 1 ? "" : "s")", .caption2, emphasis: .custom(AppTextColors.secondary)) StyledLabel("\(ritual.completedArcCount) arc\(ritual.completedArcCount == 1 ? "" : "s")", .caption2, emphasis: .secondary)
.padding(.horizontal, Design.Spacing.small) .padding(.horizontal, Design.Spacing.small)
.padding(.vertical, Design.Spacing.xSmall) .padding(.vertical, Design.Spacing.xSmall)
.background(AppSurface.secondary) .background(AppSurface.secondary)

View File

@ -92,14 +92,14 @@ struct PresetLibrarySheet: View {
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
HStack { HStack {
StyledLabel(preset.title, .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(preset.title, .heading, emphasis: .primary)
if isAdded { if isAdded {
SymbolIcon("checkmark.circle.fill", size: .badge, color: AppStatus.success) SymbolIcon("checkmark.circle.fill", size: .badge, color: AppStatus.success)
} }
} }
StyledLabel(preset.theme, .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(preset.theme, .subheading, emphasis: .secondary)
} }
Spacer() Spacer()
@ -107,7 +107,7 @@ struct PresetLibrarySheet: View {
VStack(alignment: .trailing, spacing: Design.Spacing.xSmall) { VStack(alignment: .trailing, spacing: Design.Spacing.xSmall) {
SymbolIcon(preset.timeOfDay.symbolName, size: .badge, color: AppTextColors.tertiary) SymbolIcon(preset.timeOfDay.symbolName, size: .badge, color: AppTextColors.tertiary)
StyledLabel(String(localized: "\(preset.habits.count) habits"), .caption, emphasis: .custom(AppTextColors.tertiary)) StyledLabel(String(localized: "\(preset.habits.count) habits"), .caption, emphasis: .tertiary)
} }
} }
@ -118,9 +118,7 @@ struct PresetLibrarySheet: View {
} }
if preset.habits.count > 4 { if preset.habits.count > 4 {
Text("+\(preset.habits.count - 4)") StyledLabel("+\(preset.habits.count - 4)", .caption2, emphasis: .tertiary)
.font(.caption2)
.foregroundStyle(AppTextColors.tertiary)
} }
} }
} }
@ -199,9 +197,9 @@ struct PresetDetailSheet: View {
private var descriptionSection: some View { private var descriptionSection: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(String(localized: "About"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "About"), .heading, emphasis: .primary)
StyledLabel(preset.notes, .body, emphasis: .custom(AppTextColors.secondary)) StyledLabel(preset.notes, .body, emphasis: .secondary)
} }
.padding(Design.Spacing.large) .padding(Design.Spacing.large)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
@ -211,7 +209,7 @@ struct PresetDetailSheet: View {
private var habitsSection: some View { private var habitsSection: some View {
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(String(localized: "Habits"), .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "Habits"), .heading, emphasis: .primary)
VStack(spacing: Design.Spacing.xSmall) { VStack(spacing: Design.Spacing.xSmall) {
ForEach(preset.habits) { habit in ForEach(preset.habits) { habit in
@ -219,7 +217,7 @@ struct PresetDetailSheet: View {
SymbolIcon(habit.symbolName, size: .inline, color: AppAccent.primary) SymbolIcon(habit.symbolName, size: .inline, color: AppAccent.primary)
.frame(width: 24) .frame(width: 24)
StyledLabel(habit.title, .subheading, emphasis: .custom(AppTextColors.primary)) StyledLabel(habit.title, .subheading, emphasis: .primary)
Spacer() Spacer()
} }

View File

@ -122,7 +122,7 @@ struct RitualEditSheet: View {
// Category selection - simple picker from CategoryStore // Category selection - simple picker from CategoryStore
VStack(alignment: .leading, spacing: Design.Spacing.medium) { VStack(alignment: .leading, spacing: Design.Spacing.medium) {
StyledLabel(String(localized: "Category"), .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(String(localized: "Category"), .subheading, emphasis: .secondary)
// Horizontal scrollable category chips // Horizontal scrollable category chips
ScrollView(.horizontal, showsIndicators: false) { ScrollView(.horizontal, showsIndicators: false) {
@ -169,7 +169,7 @@ struct RitualEditSheet: View {
} }
// Show the time range for the selected time of day // Show the time range for the selected time of day
IconLabel(timeOfDay.symbolName, timeOfDay.timeRange, .caption, emphasis: .custom(AppTextColors.tertiary)) IconLabel(timeOfDay.symbolName, timeOfDay.timeRange, .caption, emphasis: .tertiary)
} }
.listRowBackground(AppSurface.card) .listRowBackground(AppSurface.card)
@ -314,7 +314,7 @@ struct RitualEditSheet: View {
HStack { HStack {
Text(String(localized: "Habits")) Text(String(localized: "Habits"))
Spacer() Spacer()
StyledLabel(String(localized: "\(habits.count) habits"), .caption, emphasis: .custom(AppTextColors.tertiary)) StyledLabel(String(localized: "\(habits.count) habits"), .caption, emphasis: .tertiary)
} }
} footer: { } footer: {
if habits.count > 1 { if habits.count > 1 {
@ -493,7 +493,7 @@ struct IconPickerSheet: View {
LazyVStack(alignment: .leading, spacing: Design.Spacing.large) { LazyVStack(alignment: .leading, spacing: Design.Spacing.large) {
ForEach(iconGroups, id: \.name) { group in ForEach(iconGroups, id: \.name) { group in
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(group.name, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(group.name, .caption, emphasis: .secondary)
.padding(.horizontal, Design.Spacing.small) .padding(.horizontal, Design.Spacing.small)
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 6), spacing: Design.Spacing.small) { LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 6), spacing: Design.Spacing.small) {
@ -597,7 +597,7 @@ struct HabitIconPickerSheet: View {
LazyVStack(alignment: .leading, spacing: Design.Spacing.large) { LazyVStack(alignment: .leading, spacing: Design.Spacing.large) {
ForEach(iconGroups, id: \.name) { group in ForEach(iconGroups, id: \.name) { group in
VStack(alignment: .leading, spacing: Design.Spacing.small) { VStack(alignment: .leading, spacing: Design.Spacing.small) {
StyledLabel(group.name, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(group.name, .caption, emphasis: .secondary)
.padding(.horizontal, Design.Spacing.small) .padding(.horizontal, Design.Spacing.small)
LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 6), spacing: Design.Spacing.small) { LazyVGrid(columns: Array(repeating: GridItem(.flexible()), count: 6), spacing: Design.Spacing.small) {

View File

@ -33,7 +33,7 @@ struct RitualFocusCardView: View {
.accessibilityHidden(true) .accessibilityHidden(true)
// Title // Title
StyledLabel(title, .heading, emphasis: .custom(AppTextColors.primary)) StyledLabel(title, .heading, emphasis: .primary)
Spacer(minLength: Design.Spacing.medium) Spacer(minLength: Design.Spacing.medium)
@ -48,12 +48,12 @@ struct RitualFocusCardView: View {
.accessibilityLabel(Text(dayLabel)) .accessibilityLabel(Text(dayLabel))
} }
StyledLabel(theme, .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(theme, .subheading, emphasis: .secondary)
VStack(alignment: .leading, spacing: Design.Spacing.xSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xSmall) {
ProgressView(value: progress) ProgressView(value: progress)
.tint(AppAccent.primary) .tint(AppAccent.primary)
StyledLabel(completionSummary, .caption, emphasis: .custom(AppTextColors.secondary)) StyledLabel(completionSummary, .caption, emphasis: .secondary)
} }
} }
.padding(Design.Spacing.large) .padding(Design.Spacing.large)

View File

@ -29,7 +29,7 @@ struct TodayHabitRowView: View {
.frame(width: AppMetrics.Size.iconLarge) .frame(width: AppMetrics.Size.iconLarge)
.accessibilityHidden(true) .accessibilityHidden(true)
StyledLabel(title, .subheading, emphasis: .custom(AppTextColors.primary)) StyledLabel(title, .subheading, emphasis: .primary)
Spacer(minLength: Design.Spacing.medium) Spacer(minLength: Design.Spacing.medium)

View File

@ -10,8 +10,8 @@ struct TodayHeaderView: View {
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) { VStack(alignment: .leading, spacing: Design.Spacing.xxxSmall) {
StyledLabel(String(localized: "Today"), .heroBold, emphasis: .custom(AppTextColors.primary)) StyledLabel(String(localized: "Today"), .heroBold, emphasis: .primary)
StyledLabel(dateText, .subheading, emphasis: .custom(AppTextColors.secondary)) StyledLabel(dateText, .subheading, emphasis: .secondary)
} }
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.accessibilityElement(children: .combine) .accessibilityElement(children: .combine)