import SwiftUI struct CardView: View { let card: Card? let cardFaceStyle: CardFaceStyle let cardBackDesign: CardBackDesign let size: CGSize var isHighlighted: Bool = false @Environment(\.accessibilityReduceMotion) private var reduceMotion var body: some View { Group { if let card { if card.isFaceUp { cardFront(card) } else { cardBack } } else { emptySlot } } .frame(width: size.width, height: size.height) .clipShape(RoundedRectangle(cornerRadius: size.width * 0.08)) .shadow(color: .black.opacity(0.15), radius: 2, y: 1) .overlay { if isHighlighted { RoundedRectangle(cornerRadius: size.width * 0.08) .stroke(Color.yellow, lineWidth: 3) .animation(reduceMotion ? nil : .easeInOut(duration: 0.8).repeatForever(), value: isHighlighted) } } .accessibilityElement(children: .ignore) .accessibilityLabel(accessibilityLabel) .accessibilityHint(accessibilityHint) .accessibilityAddTraits(card?.isFaceUp == true ? .isButton : []) } private var accessibilityLabel: Text { guard let card else { return Text("Empty card slot") } if card.isFaceUp { return Text("\(card.rank.displayName) of \(card.suit.displayName)") } else { return Text("Face down card") } } private var accessibilityHint: Text { guard let card, card.isFaceUp else { return Text("") } return Text("Double tap to move to best available position") } private func cardFront(_ card: Card) -> some View { let imageName = card.frontImageName(style: cardFaceStyle) return ZStack { RoundedRectangle(cornerRadius: size.width * 0.08) .fill(.white) if let uiImage = loadImage(named: imageName) { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fit) .padding(1) .accessibilityHidden(true) } else { VStack(spacing: 2) { Text(card.rank.shortName) .font(.system(size: size.width * 0.28, weight: .bold)) Text(card.suit.symbol) .font(.system(size: size.width * 0.22)) } .foregroundStyle(card.color == .red ? .red : .black) .accessibilityHidden(true) } } } private var cardBack: some View { let imageName = cardBackDesign.imageName return ZStack { RoundedRectangle(cornerRadius: size.width * 0.08) .fill(Color.blue) if let uiImage = loadImage(named: imageName) { Image(uiImage: uiImage) .resizable() .aspectRatio(contentMode: .fit) .padding(2) .accessibilityHidden(true) } else { RoundedRectangle(cornerRadius: size.width * 0.08) .strokeBorder(Color.white.opacity(0.3), lineWidth: 2) .padding(3) } } } private var emptySlot: some View { RoundedRectangle(cornerRadius: size.width * 0.08) .strokeBorder(Color.white.opacity(0.3), lineWidth: 2) } #if os(macOS) private func loadImage(named name: String) -> NSImage? { NSImage(named: name) } private func Image(uiImage: NSImage) -> SwiftUI.Image { SwiftUI.Image(nsImage: uiImage) } #else private func loadImage(named name: String) -> UIImage? { UIImage(named: name) } private func Image(uiImage: UIImage) -> SwiftUI.Image { SwiftUI.Image(uiImage: uiImage) } #endif }