fix: UI 设计系统优化 - 无障碍、深色模式、对比度

- DesignSystem: 深色模式阴影适配,textMuted 对比度修复
- DesignSystem: SoftIconButton/SoftSlider/SoftProgressRing 添加 accessibilityLabel
- EditorView: AspectRatioButton 添加无障碍支持,清理硬编码颜色
- WallpaperGuideView: 清理硬编码颜色 (Color.secondary → Color.softElevated)
- Localizable: 修复 home.worksCount 插值 key 格式

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
empty
2026-01-03 23:15:41 +08:00
parent 143c471714
commit e041cacd7d
6 changed files with 199 additions and 47 deletions

View File

@@ -41,6 +41,18 @@ enum DesignTokens {
static let xxxl: CGFloat = 32 static let xxxl: CGFloat = 32
static let display: CGFloat = 40 static let display: CGFloat = 40
} }
// MARK:
enum Animation {
///
static let spring = SwiftUI.Animation.spring(response: 0.3, dampingFraction: 0.6)
///
static let quick = SwiftUI.Animation.easeInOut(duration: 0.15)
///
static let standard = SwiftUI.Animation.easeInOut(duration: 0.25)
///
static let smooth = SwiftUI.Animation.easeInOut(duration: 0.5)
}
} }
// MARK: - // MARK: -
@@ -59,7 +71,8 @@ extension Color {
// MARK: // MARK:
static let textPrimary = Color(light: Color(hex: "#2D2D3A"), dark: Color(hex: "#E4E4EB")) static let textPrimary = Color(light: Color(hex: "#2D2D3A"), dark: Color(hex: "#E4E4EB"))
static let textSecondary = Color(light: Color(hex: "#6B6B7B"), dark: Color(hex: "#A0A0B2")) static let textSecondary = Color(light: Color(hex: "#6B6B7B"), dark: Color(hex: "#A0A0B2"))
static let textMuted = Color(light: Color(hex: "#9999A9"), dark: Color(hex: "#6B6B7B")) // textMuted WCAG AA (4.5:1)
static let textMuted = Color(light: Color(hex: "#777788"), dark: Color(hex: "#8888A0"))
// MARK: // MARK:
static let accentPurple = Color(hex: "#6366F1") static let accentPurple = Color(hex: "#6366F1")
@@ -137,42 +150,71 @@ extension Color {
// MARK: - Soft UI // MARK: - Soft UI
struct SoftShadow: ViewModifier { struct SoftShadow: ViewModifier {
let isPressed: Bool let isPressed: Bool
@Environment(\.colorScheme) private var colorScheme
func body(content: Content) -> some View { func body(content: Content) -> some View {
content content
.shadow( .shadow(
color: Color.black.opacity(isPressed ? 0.15 : 0.08), color: shadowDark,
radius: isPressed ? 4 : 8, radius: isPressed ? 4 : 8,
x: isPressed ? 2 : 4, x: isPressed ? 2 : 4,
y: isPressed ? 2 : 4 y: isPressed ? 2 : 4
) )
.shadow( .shadow(
color: Color.white.opacity(isPressed ? 0.5 : 0.7), color: shadowLight,
radius: isPressed ? 4 : 8, radius: isPressed ? 4 : 8,
x: isPressed ? -2 : -4, x: isPressed ? -2 : -4,
y: isPressed ? -2 : -4 y: isPressed ? -2 : -4
) )
} }
// 使
private var shadowDark: Color {
colorScheme == .dark
? Color.black.opacity(isPressed ? 0.4 : 0.3)
: Color.black.opacity(isPressed ? 0.15 : 0.08)
}
// 使
private var shadowLight: Color {
colorScheme == .dark
? Color.white.opacity(isPressed ? 0.03 : 0.05)
: Color.white.opacity(isPressed ? 0.5 : 0.7)
}
} }
struct SoftInnerShadow: ViewModifier { struct SoftInnerShadow: ViewModifier {
@Environment(\.colorScheme) private var colorScheme
func body(content: Content) -> some View { func body(content: Content) -> some View {
content content
.overlay( .overlay(
RoundedRectangle(cornerRadius: DesignTokens.Radius.lg) RoundedRectangle(cornerRadius: DesignTokens.Radius.lg)
.stroke(Color.black.opacity(0.06), lineWidth: 1) .stroke(innerShadowDark, lineWidth: 1)
.blur(radius: 2) .blur(radius: 2)
.offset(x: 1, y: 1) .offset(x: 1, y: 1)
.mask(RoundedRectangle(cornerRadius: DesignTokens.Radius.lg).fill(LinearGradient(colors: [.black, .clear], startPoint: .topLeading, endPoint: .bottomTrailing))) .mask(RoundedRectangle(cornerRadius: DesignTokens.Radius.lg).fill(LinearGradient(colors: [.black, .clear], startPoint: .topLeading, endPoint: .bottomTrailing)))
) )
.overlay( .overlay(
RoundedRectangle(cornerRadius: DesignTokens.Radius.lg) RoundedRectangle(cornerRadius: DesignTokens.Radius.lg)
.stroke(Color.white.opacity(0.5), lineWidth: 1) .stroke(innerShadowLight, lineWidth: 1)
.blur(radius: 2) .blur(radius: 2)
.offset(x: -1, y: -1) .offset(x: -1, y: -1)
.mask(RoundedRectangle(cornerRadius: DesignTokens.Radius.lg).fill(LinearGradient(colors: [.clear, .black], startPoint: .topLeading, endPoint: .bottomTrailing))) .mask(RoundedRectangle(cornerRadius: DesignTokens.Radius.lg).fill(LinearGradient(colors: [.clear, .black], startPoint: .topLeading, endPoint: .bottomTrailing)))
) )
} }
private var innerShadowDark: Color {
colorScheme == .dark
? Color.black.opacity(0.3)
: Color.black.opacity(0.06)
}
private var innerShadowLight: Color {
colorScheme == .dark
? Color.white.opacity(0.08)
: Color.white.opacity(0.5)
}
} }
extension View { extension View {
@@ -315,11 +357,13 @@ struct SoftSecondaryButton: View {
struct SoftIconButton: View { struct SoftIconButton: View {
let icon: String let icon: String
let isActive: Bool let isActive: Bool
let accessibilityLabel: String?
let action: () -> Void let action: () -> Void
init(_ icon: String, isActive: Bool = false, action: @escaping () -> Void) { init(_ icon: String, isActive: Bool = false, accessibilityLabel: String? = nil, action: @escaping () -> Void) {
self.icon = icon self.icon = icon
self.isActive = isActive self.isActive = isActive
self.accessibilityLabel = accessibilityLabel
self.action = action self.action = action
} }
@@ -347,6 +391,9 @@ struct SoftIconButton: View {
) )
} }
.buttonStyle(ScaleButtonStyle()) .buttonStyle(ScaleButtonStyle())
.accessibilityLabel(accessibilityLabel ?? icon)
.accessibilityAddTraits(.isButton)
.accessibilityAddTraits(isActive ? .isSelected : [])
} }
} }
@@ -356,17 +403,20 @@ struct SoftProgressRing: View {
let size: CGFloat let size: CGFloat
let lineWidth: CGFloat let lineWidth: CGFloat
let gradient: LinearGradient let gradient: LinearGradient
let accessibilityLabel: String?
init( init(
progress: Double, progress: Double,
size: CGFloat = 120, size: CGFloat = 120,
lineWidth: CGFloat = 8, lineWidth: CGFloat = 8,
gradient: LinearGradient = Color.gradientPrimary gradient: LinearGradient = Color.gradientPrimary,
accessibilityLabel: String? = nil
) { ) {
self.progress = progress self.progress = progress
self.size = size self.size = size
self.lineWidth = lineWidth self.lineWidth = lineWidth
self.gradient = gradient self.gradient = gradient
self.accessibilityLabel = accessibilityLabel
} }
var body: some View { var body: some View {
@@ -394,8 +444,11 @@ struct SoftProgressRing: View {
.stroke(gradient, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round)) .stroke(gradient, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
.frame(width: size - 16, height: size - 16) .frame(width: size - 16, height: size - 16)
.rotationEffect(.degrees(-90)) .rotationEffect(.degrees(-90))
.animation(.easeInOut(duration: 0.5), value: progress) .animation(DesignTokens.Animation.smooth, value: progress)
} }
.accessibilityElement(children: .ignore)
.accessibilityLabel(accessibilityLabel ?? String(localized: "进度"))
.accessibilityValue(Text("\(Int(progress * 100))%"))
} }
} }
@@ -475,15 +528,21 @@ struct SoftSlider: View {
@Binding var value: Double @Binding var value: Double
let range: ClosedRange<Double> let range: ClosedRange<Double>
let gradient: LinearGradient let gradient: LinearGradient
let accessibilityLabel: String
let step: Double
init( init(
value: Binding<Double>, value: Binding<Double>,
in range: ClosedRange<Double>, in range: ClosedRange<Double>,
gradient: LinearGradient = Color.gradientPrimary step: Double = 0.1,
gradient: LinearGradient = Color.gradientPrimary,
accessibilityLabel: String = ""
) { ) {
self._value = value self._value = value
self.range = range self.range = range
self.step = step
self.gradient = gradient self.gradient = gradient
self.accessibilityLabel = accessibilityLabel
} }
var body: some View { var body: some View {
@@ -521,6 +580,19 @@ struct SoftSlider: View {
} }
} }
.frame(height: 28) .frame(height: 28)
.accessibilityElement(children: .ignore)
.accessibilityLabel(accessibilityLabel.isEmpty ? String(localized: "滑块") : accessibilityLabel)
.accessibilityValue(Text(String(format: "%.1f", value)))
.accessibilityAdjustableAction { direction in
switch direction {
case .increment:
value = min(range.upperBound, value + step)
case .decrement:
value = max(range.lowerBound, value - step)
@unknown default:
break
}
}
} }
} }

View File

@@ -254,7 +254,7 @@
} }
} }
}, },
"home.worksCount" : { "home.worksCount %lld" : {
"extractionState" : "manual", "extractionState" : "manual",
"localizations" : { "localizations" : {
"en" : { "en" : {
@@ -1874,6 +1874,78 @@
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "确定要清空最近作品记录吗?这不会删除相册中的 Live Photo。" } }, "zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "确定要清空最近作品记录吗?这不会删除相册中的 Live Photo。" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "確定要清除最近作品記錄嗎?這不會刪除相簿中的 Live Photo。" } } "zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "確定要清除最近作品記錄嗎?這不會刪除相簿中的 Live Photo。" } }
} }
},
"已选中" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Selected" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "已选中" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "已選中" } }
}
},
"进度" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Progress" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "进度" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "進度" } }
}
},
"滑块" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Slider" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "滑块" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "滑桿" } }
}
},
"accessibility.settings" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Settings" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "设置" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "設定" } }
}
},
"accessibility.play" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Play" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "播放" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "播放" } }
}
},
"accessibility.pause" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Pause" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "暂停" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "暫停" } }
}
},
"accessibility.livePhoto" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Live Photo" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "Live Photo 作品" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "Live Photo 作品" } }
}
},
"accessibility.aspectRatio" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Aspect ratio %@" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "画面比例 %@" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "畫面比例 %@" } }
}
},
"accessibility.duration" : {
"extractionState" : "manual",
"localizations" : {
"en" : { "stringUnit" : { "state" : "translated", "value" : "Duration" } },
"zh-Hans" : { "stringUnit" : { "state" : "translated", "value" : "时长" } },
"zh-Hant" : { "stringUnit" : { "state" : "translated", "value" : "時長" } }
}
} }
}, },
"version" : "1.0" "version" : "1.0"

View File

@@ -86,8 +86,8 @@ struct EditorView: View {
compatibilitySection compatibilitySection
generateButton generateButton
} }
.padding(.horizontal, 20) .padding(.horizontal, DesignTokens.Spacing.xl)
.padding(.vertical, 16) .padding(.vertical, DesignTokens.Spacing.lg)
} }
} }
@@ -117,11 +117,11 @@ struct EditorView: View {
compatibilitySection compatibilitySection
generateButton generateButton
} }
.padding(.vertical, 16) .padding(.vertical, DesignTokens.Spacing.lg)
} }
.frame(maxWidth: 360) .frame(maxWidth: 360)
} }
.padding(24) .padding(DesignTokens.Spacing.xxl)
} }
// MARK: - iPad // MARK: - iPad
@@ -239,10 +239,10 @@ struct EditorView: View {
Text("选择适合壁纸的比例,锁屏推荐使用「锁屏」或「全屏」") Text("选择适合壁纸的比例,锁屏推荐使用「锁屏」或「全屏」")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
.padding(16) .padding(16)
.background(Color.secondary.opacity(0.1)) .background(Color.softElevated)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
} }
@@ -271,26 +271,26 @@ struct EditorView: View {
.clipShape(RoundedRectangle(cornerRadius: 8)) .clipShape(RoundedRectangle(cornerRadius: 8))
} else { } else {
RoundedRectangle(cornerRadius: 8) RoundedRectangle(cornerRadius: 8)
.fill(Color.secondary.opacity(0.2)) .fill(Color.softPressed)
.frame(width: 80, height: 120) .frame(width: 80, height: 120)
.overlay { .overlay {
Image(systemName: "photo") Image(systemName: "photo")
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
} }
VStack(alignment: .leading, spacing: 4) { VStack(alignment: .leading, spacing: 4) {
Text("此图片将作为 Live Photo 的静态封面") Text("此图片将作为 Live Photo 的静态封面")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
Text("拖动下方滑杆选择封面时刻") Text("拖动下方滑杆选择封面时刻")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
} }
} }
.padding(16) .padding(16)
.background(Color.secondary.opacity(0.1)) .background(Color.softElevated)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
} }
@@ -317,10 +317,10 @@ struct EditorView: View {
Text("Live Photo 壁纸推荐时长1 ~ 1.5 秒") Text("Live Photo 壁纸推荐时长1 ~ 1.5 秒")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
.padding(16) .padding(16)
.background(Color.secondary.opacity(0.1)) .background(Color.softElevated)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
} }
@@ -348,10 +348,10 @@ struct EditorView: View {
Text("选择视频中的某一帧作为 Live Photo 的封面") Text("选择视频中的某一帧作为 Live Photo 的封面")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
.padding(16) .padding(16)
.background(Color.secondary.opacity(0.1)) .background(Color.softElevated)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
} }
@@ -368,7 +368,7 @@ struct EditorView: View {
.font(.headline) .font(.headline)
Text("使用 AI 提升封面画质") Text("使用 AI 提升封面画质")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
} }
} }
@@ -388,7 +388,7 @@ struct EditorView: View {
.scaleEffect(0.8) .scaleEffect(0.8)
Text("正在下载 AI 模型...") Text("正在下载 AI 模型...")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
ProgressView(value: aiModelDownloadProgress) ProgressView(value: aiModelDownloadProgress)
@@ -396,7 +396,7 @@ struct EditorView: View {
Text(String(format: "%.0f%%", aiModelDownloadProgress * 100)) Text(String(format: "%.0f%%", aiModelDownloadProgress * 100))
.font(.caption2) .font(.caption2)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
.padding(.leading, 4) .padding(.leading, 4)
} }
@@ -434,7 +434,7 @@ struct EditorView: View {
.font(.caption) .font(.caption)
} }
} }
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
.padding(.leading, 4) .padding(.leading, 4)
} }
@@ -445,7 +445,7 @@ struct EditorView: View {
.font(.caption) .font(.caption)
Text("当前设备不支持 AI 增强") Text("当前设备不支持 AI 增强")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
.padding(.top, 4) .padding(.top, 4)
} }
@@ -472,7 +472,7 @@ struct EditorView: View {
.font(.headline) .font(.headline)
Text("适用于较旧设备或生成失败时") Text("适用于较旧设备或生成失败时")
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
} }
} }
@@ -509,12 +509,12 @@ struct EditorView: View {
.font(.caption) .font(.caption)
} }
} }
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
.padding(.leading, 4) .padding(.leading, 4)
} }
} }
.padding(16) .padding(16)
.background(Color.secondary.opacity(0.1)) .background(Color.softElevated)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
} }
@@ -543,7 +543,7 @@ struct EditorView: View {
Text(suggestion.description) Text(suggestion.description)
.font(.caption) .font(.caption)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
if let actionText = suggestion.actionText { if let actionText = suggestion.actionText {
Button { Button {
@@ -804,7 +804,7 @@ struct AspectRatioButton: View {
VStack(spacing: 4) { VStack(spacing: 4) {
// //
RoundedRectangle(cornerRadius: 4) RoundedRectangle(cornerRadius: 4)
.stroke(isSelected ? Color.accentColor : Color.secondary, lineWidth: 2) .stroke(isSelected ? Color.accentColor : Color.textSecondary, lineWidth: 2)
.frame(width: iconWidth, height: iconHeight) .frame(width: iconWidth, height: iconHeight)
.background( .background(
isSelected ? Color.accentColor.opacity(0.1) : Color.clear isSelected ? Color.accentColor.opacity(0.1) : Color.clear
@@ -814,14 +814,18 @@ struct AspectRatioButton: View {
Text(template.displayName) Text(template.displayName)
.font(.caption2) .font(.caption2)
.fontWeight(isSelected ? .semibold : .regular) .fontWeight(isSelected ? .semibold : .regular)
.foregroundStyle(isSelected ? .primary : .secondary) .foregroundColor(isSelected ? .textPrimary : .textSecondary)
} }
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.padding(.vertical, 8) .padding(.vertical, DesignTokens.Spacing.sm)
.background(isSelected ? Color.accentColor.opacity(0.1) : Color.clear) .background(isSelected ? Color.accentColor.opacity(0.1) : Color.clear)
.clipShape(RoundedRectangle(cornerRadius: 8)) .clipShape(RoundedRectangle(cornerRadius: DesignTokens.Radius.sm))
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.accessibilityElement(children: .ignore)
.accessibilityLabel(String(localized: "accessibility.aspectRatio \(template.displayName)"))
.accessibilityAddTraits(.isButton)
.accessibilityAddTraits(isSelected ? .isSelected : [])
} }
private var iconWidth: CGFloat { private var iconWidth: CGFloat {
@@ -957,7 +961,7 @@ struct ScaleButtonStyle: ButtonStyle {
configuration.label configuration.label
.scaleEffect(configuration.isPressed ? 0.96 : 1.0) .scaleEffect(configuration.isPressed ? 0.96 : 1.0)
.opacity(configuration.isPressed ? 0.9 : 1.0) .opacity(configuration.isPressed ? 0.9 : 1.0)
.animation(.easeInOut(duration: 0.15), value: configuration.isPressed) .animation(DesignTokens.Animation.quick, value: configuration.isPressed)
} }
} }

View File

@@ -38,7 +38,7 @@ struct HomeView: View {
.navigationBarTitleDisplayMode(.large) .navigationBarTitleDisplayMode(.large)
.toolbar { .toolbar {
ToolbarItem(placement: .navigationBarTrailing) { ToolbarItem(placement: .navigationBarTrailing) {
SoftIconButton("gearshape") { SoftIconButton("gearshape", accessibilityLabel: String(localized: "accessibility.settings")) {
appState.navigateTo(.settings) appState.navigateTo(.settings)
} }
} }
@@ -351,13 +351,17 @@ struct RecentWorkCard: View {
} }
.buttonStyle(.plain) .buttonStyle(.plain)
.scaleEffect(isPressed ? 0.97 : 1.0) .scaleEffect(isPressed ? 0.97 : 1.0)
.animation(.spring(response: 0.3, dampingFraction: 0.6), value: isPressed) .animation(DesignTokens.Animation.spring, value: isPressed)
.onLongPressGesture(minimumDuration: .infinity, pressing: { pressing in .onLongPressGesture(minimumDuration: .infinity, pressing: { pressing in
isPressed = pressing isPressed = pressing
}, perform: {}) }, perform: {})
.onAppear { .onAppear {
thumbnailLoader.load(assetId: work.assetLocalIdentifier) thumbnailLoader.load(assetId: work.assetLocalIdentifier)
} }
.accessibilityElement(children: .ignore)
.accessibilityLabel(String(localized: "accessibility.livePhoto"))
.accessibilityHint(Text("\(work.aspectRatioDisplayName), \(work.createdAt.formatted(.relative(presentation: .named)))"))
.accessibilityAddTraits(.isButton)
} }
} }
@@ -366,7 +370,7 @@ struct HomeButtonStyle: ButtonStyle {
func makeBody(configuration: Configuration) -> some View { func makeBody(configuration: Configuration) -> some View {
configuration.label configuration.label
.scaleEffect(configuration.isPressed ? 0.97 : 1.0) .scaleEffect(configuration.isPressed ? 0.97 : 1.0)
.animation(.spring(response: 0.3, dampingFraction: 0.6), value: configuration.isPressed) .animation(DesignTokens.Animation.spring, value: configuration.isPressed)
} }
} }

View File

@@ -63,7 +63,7 @@ struct SettingsView: View {
Label(String(localized: "settings.cacheSize"), systemImage: "internaldrive") Label(String(localized: "settings.cacheSize"), systemImage: "internaldrive")
Spacer() Spacer()
Text(cacheSize) Text(cacheSize)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
Button(role: .destructive) { Button(role: .destructive) {
@@ -111,7 +111,7 @@ struct SettingsView: View {
Label(String(localized: "settings.version"), systemImage: "info.circle") Label(String(localized: "settings.version"), systemImage: "info.circle")
Spacer() Spacer()
Text(appVersion) Text(appVersion)
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
} }
NavigationLink { NavigationLink {
@@ -175,7 +175,7 @@ struct SettingsView: View {
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
case .notDetermined: case .notDetermined:
Label(String(localized: "settings.notDetermined"), systemImage: "questionmark.circle.fill") Label(String(localized: "settings.notDetermined"), systemImage: "questionmark.circle.fill")
.foregroundStyle(.secondary) .foregroundColor(.textSecondary)
.labelStyle(.iconOnly) .labelStyle(.iconOnly)
@unknown default: @unknown default:
EmptyView() EmptyView()

View File

@@ -171,7 +171,7 @@ struct WallpaperGuideView: View {
) )
} }
.padding(12) .padding(12)
.background(Color.secondary.opacity(0.1)) .background(Color.softElevated)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
} }
} }
@@ -316,7 +316,7 @@ struct FAQRow: View {
} }
.padding(14) .padding(14)
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
.background(Color.secondary.opacity(0.08)) .background(Color.softElevated)
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
} }
} }