From 73019babdeec45c812bb0913daaabed6c6ca154e Mon Sep 17 00:00:00 2001 From: Matt Bruce Date: Wed, 17 Dec 2025 16:10:54 -0600 Subject: [PATCH] Signed-off-by: Matt Bruce --- Baccarat/Theme/DesignConstants.swift | 202 ++++-------------- Baccarat/Views/RulesHelpView.swift | 1 + Blackjack/Resources/Localizable.xcstrings | 12 -- Blackjack/Theme/DesignConstants.swift | 155 ++++---------- Blackjack/Views/BlackjackTableView.swift | 95 +++++--- Blackjack/Views/GameTableView.swift | 62 +++--- .../CasinoKit/Theme/CasinoDesign.swift | 132 ++++++++---- .../Views/Table/TableBackgroundView.swift | 91 ++++---- 8 files changed, 321 insertions(+), 429 deletions(-) diff --git a/Baccarat/Theme/DesignConstants.swift b/Baccarat/Theme/DesignConstants.swift index ef46171..9ca7735 100644 --- a/Baccarat/Theme/DesignConstants.swift +++ b/Baccarat/Theme/DesignConstants.swift @@ -3,190 +3,75 @@ // Baccarat // // Design system constants for consistent styling across the app. +// Uses CasinoDesign from CasinoKit for shared values, with game-specific overrides. // import SwiftUI +import CasinoKit /// Design constants for the Baccarat app. +/// Shared constants are imported from CasinoDesign; game-specific values are defined here. enum Design { - // MARK: - Spacing + // MARK: - Shared Constants (from CasinoKit) - enum Spacing { - static let xxSmall: CGFloat = 2 - static let xSmall: CGFloat = 4 - static let small: CGFloat = 8 - static let medium: CGFloat = 12 - static let large: CGFloat = 16 - static let xLarge: CGFloat = 20 - static let xxLarge: CGFloat = 24 - static let xxxLarge: CGFloat = 32 - } + typealias Spacing = CasinoDesign.Spacing + typealias CornerRadius = CasinoDesign.CornerRadius + typealias LineWidth = CasinoDesign.LineWidth + typealias Shadow = CasinoDesign.Shadow + typealias Opacity = CasinoDesign.Opacity + typealias Animation = CasinoDesign.Animation + typealias Scale = CasinoDesign.Scale + typealias MinScaleFactor = CasinoDesign.MinScaleFactor + typealias BaseFontSize = CasinoDesign.BaseFontSize + typealias IconSize = CasinoDesign.IconSize - // MARK: - Corner Radii - - enum CornerRadius { - static let small: CGFloat = 8 - static let medium: CGFloat = 10 - static let large: CGFloat = 12 - static let xLarge: CGFloat = 14 - static let xxLarge: CGFloat = 20 - static let xxxLarge: CGFloat = 28 - } - - // MARK: - Base Font Sizes - // These are base values for use with @ScaledMetric in views. - // They will scale automatically based on user accessibility settings. - - enum BaseFontSize { - static let xxSmall: CGFloat = 7 - static let xSmall: CGFloat = 9 - static let small: CGFloat = 10 - static let body: CGFloat = 12 - static let callout: CGFloat = 13 - static let medium: CGFloat = 14 - static let subheadline: CGFloat = 15 - static let large: CGFloat = 16 - static let xLarge: CGFloat = 18 - static let xxLarge: CGFloat = 20 - static let title: CGFloat = 32 - static let largeTitle: CGFloat = 36 - static let display: CGFloat = 70 - } - - // MARK: - Icon Sizes - - enum IconSize { - static let small: CGFloat = 12 - static let medium: CGFloat = 16 - static let large: CGFloat = 22 - static let xLarge: CGFloat = 60 - static let xxLarge: CGFloat = 70 - } - - // MARK: - Component Sizes + // MARK: - Baccarat-Specific Component Sizes enum Size { - static let chipSmall: CGFloat = 36 - static let chipMedium: CGFloat = 50 - static let chipSelector: CGFloat = 50 - static let chipBadge: CGFloat = 32 - static let chipBadgeInner: CGFloat = 28 - static let cardWidthSmall: CGFloat = 45 - static let cardWidthMedium: CGFloat = 55 - static let cardWidthLarge: CGFloat = 65 - static let valueBadge: CGFloat = 26 - static let checkmark: CGFloat = 22 + // Cards - use CasinoDesign values + static let cardWidthSmall: CGFloat = CasinoDesign.Size.cardWidthSmall + static let cardWidthMedium: CGFloat = CasinoDesign.Size.cardWidthMedium + static let cardWidthLarge: CGFloat = CasinoDesign.Size.cardWidthLarge + + // Chips - use CasinoDesign values + static let chipSmall: CGFloat = CasinoDesign.Size.chipSmall + static let chipMedium: CGFloat = CasinoDesign.Size.chipMedium + static let chipSelector: CGFloat = CasinoDesign.Size.chipMedium + static let chipBadge: CGFloat = CasinoDesign.Size.chipBadge + static let chipBadgeInner: CGFloat = CasinoDesign.Size.chipBadgeInner + + // Shared values + static let valueBadge: CGFloat = CasinoDesign.Size.valueBadge + static let checkmark: CGFloat = CasinoDesign.Size.checkmark + static let maxContentWidthPortrait: CGFloat = CasinoDesign.Size.maxContentWidthPortrait + static let maxContentWidthLandscape: CGFloat = CasinoDesign.Size.maxContentWidthLandscape + static let maxModalWidth: CGFloat = CasinoDesign.Size.maxModalWidth + + // Baccarat-specific table layout static let tableAspectRatio: CGFloat = 1.6 static let roadMapCell: CGFloat = 16 static let diamondIcon: CGFloat = 24 static let topBetRowHeight: CGFloat = 52 static let mainBetRowHeight: CGFloat = 65 static let bonusZoneWidth: CGFloat = 80 - - // iPad max widths - static let maxContentWidthPortrait: CGFloat = 500 - static let maxContentWidthLandscape: CGFloat = 800 - static let maxModalWidth: CGFloat = 450 - } - - // MARK: - Animation - - enum Animation { - static let quick: Double = 0.3 - static let springDuration: Double = 0.4 - static let springBounce: Double = 0.3 - static let cardFlipBounce: Double = 0.2 - static let fadeInDuration: Double = 0.3 - static let cardFlipDuration: Double = 0.5 - static let selectionDuration: Double = 0.2 - static let staggerDelay1: Double = 0.1 - static let staggerDelay2: Double = 0.25 - static let staggerDelay3: Double = 0.4 - } - - // MARK: - Opacity - - enum Opacity { - static let verySubtle: Double = 0.05 - static let subtle: Double = 0.1 - static let selection: Double = 0.15 - static let hint: Double = 0.2 - static let quarter: Double = 0.25 - static let light: Double = 0.3 - static let overlay: Double = 0.4 - static let medium: Double = 0.5 - static let secondary: Double = 0.5 - static let disabled: Double = 0.5 - static let accent: Double = 0.6 - static let strong: Double = 0.7 - static let heavy: Double = 0.8 - static let nearOpaque: Double = 0.85 - static let almostFull: Double = 0.9 - } - - // MARK: - Scale Effects - - enum Scale { - static let shrunk: Double = 0.5 - static let slightShrink: Double = 0.8 - static let normal: Double = 1.0 - static let selected: Double = 1.1 - } - - // MARK: - Minimum Scale Factor (for text) - - enum MinScaleFactor { - static let tight: Double = 0.5 - static let comfortable: Double = 0.6 - static let relaxed: Double = 0.7 - } - - // MARK: - Line Widths - - enum LineWidth { - static let thin: CGFloat = 1 - static let standard: CGFloat = 2 - static let medium: CGFloat = 2 - static let thick: CGFloat = 3 - static let heavy: CGFloat = 4 - } - - // MARK: - Shadow - - enum Shadow { - static let radiusSmall: CGFloat = 2 - static let radiusMedium: CGFloat = 6 - static let radiusLarge: CGFloat = 10 - static let radiusXLarge: CGFloat = 12 - static let radiusXXLarge: CGFloat = 30 - - static let offsetSmall: CGFloat = 1 - static let offsetMedium: CGFloat = 3 - static let offsetLarge: CGFloat = 5 } } -// MARK: - App Colors +// MARK: - Baccarat App Colors extension Color { - // MARK: - Table Colors + // MARK: - Table Colors (use CasinoTable from CasinoKit) - enum Table { - static let feltDark = Color(red: 0.0, green: 0.28, blue: 0.12) - static let feltLight = Color(red: 0.0, green: 0.35, blue: 0.18) - static let backgroundDark = Color(red: 0.01, green: 0.12, blue: 0.06) - static let backgroundLight = Color(red: 0.03, green: 0.25, blue: 0.12) - static let baseDark = Color(red: 0.02, green: 0.15, blue: 0.08) - static let preview = Color(red: 0.0, green: 0.3, blue: 0.15) - } + /// Typealias for consistent table colors across all games. + typealias Table = CasinoTable // MARK: - Border Colors enum Border { - static let goldLight = Color(red: 0.85, green: 0.7, blue: 0.35) - static let goldDark = Color(red: 0.65, green: 0.5, blue: 0.2) + static let goldLight = CasinoTable.goldLight + static let goldDark = CasinoTable.goldDark static let gold = Color(red: 0.7, green: 0.55, blue: 0.25) static let silver = Color(red: 0.6, green: 0.6, blue: 0.65) } @@ -223,12 +108,12 @@ extension Color { static let destructive = Color(red: 0.6, green: 0.2, blue: 0.2) } - // MARK: - Chip Colors + // MARK: - Chip Colors (for theming) enum Chip { static let gold = Color(red: 0.8, green: 0.65, blue: 0.2) - // Chip base colors + // Chip base colors by denomination static let tenBase = Color(red: 0.2, green: 0.4, blue: 0.8) static let tenHighlight = Color(red: 0.3, green: 0.5, blue: 0.9) static let twentyFiveBase = Color(red: 0.1, green: 0.6, blue: 0.3) @@ -301,4 +186,3 @@ extension String { return String(format: format, arguments: arguments) } } - diff --git a/Baccarat/Views/RulesHelpView.swift b/Baccarat/Views/RulesHelpView.swift index 6e69931..672c5b1 100644 --- a/Baccarat/Views/RulesHelpView.swift +++ b/Baccarat/Views/RulesHelpView.swift @@ -6,6 +6,7 @@ // import SwiftUI +import CasinoKit /// The available rule pages. enum RulesPage: Int, CaseIterable, Identifiable { diff --git a/Blackjack/Resources/Localizable.xcstrings b/Blackjack/Resources/Localizable.xcstrings index 7bc6b98..c64b344 100644 --- a/Blackjack/Resources/Localizable.xcstrings +++ b/Blackjack/Resources/Localizable.xcstrings @@ -39,14 +39,6 @@ "comment" : "A step in the process of exporting app icons.", "isCommentAutoGenerated" : true }, - "$%lld" : { - "comment" : "A label displaying the current bet amount in the betting zone. The argument is the amount of the current bet.", - "isCommentAutoGenerated" : true - }, - "$%lld bet" : { - "comment" : "An accessibility label and hint for the betting zone button.", - "isCommentAutoGenerated" : true - }, "2-10: Face value" : { "comment" : "Description of the card values for cards with values 2 through 10.", "isCommentAutoGenerated" : true @@ -1541,10 +1533,6 @@ "comment" : "Description of the insurance payout when the player wins.", "isCommentAutoGenerated" : true }, - "Place bet" : { - "comment" : "An accessibility label for the betting zone when no bet is placed.", - "isCommentAutoGenerated" : true - }, "Play Again" : { "localizations" : { "en" : { diff --git a/Blackjack/Theme/DesignConstants.swift b/Blackjack/Theme/DesignConstants.swift index 3fbf77a..74ca33f 100644 --- a/Blackjack/Theme/DesignConstants.swift +++ b/Blackjack/Theme/DesignConstants.swift @@ -3,6 +3,7 @@ // Blackjack // // Centralized design constants for the Blackjack app. +// Uses CasinoDesign from CasinoKit for shared values, with game-specific overrides. // import SwiftUI @@ -10,132 +11,57 @@ import CasinoKit // MARK: - Design Namespace +/// Design constants for the Blackjack app. +/// Shared constants are imported from CasinoDesign; game-specific values are defined here. enum Design { - // Reuse CasinoDesign where appropriate + + // MARK: - Shared Constants (from CasinoKit) + + typealias Spacing = CasinoDesign.Spacing + typealias CornerRadius = CasinoDesign.CornerRadius + typealias LineWidth = CasinoDesign.LineWidth + typealias Shadow = CasinoDesign.Shadow + typealias Opacity = CasinoDesign.Opacity typealias Animation = CasinoDesign.Animation typealias Scale = CasinoDesign.Scale typealias MinScaleFactor = CasinoDesign.MinScaleFactor + typealias BaseFontSize = CasinoDesign.BaseFontSize + typealias IconSize = CasinoDesign.IconSize - // MARK: - Spacing - - enum Spacing { - static let xxSmall: CGFloat = 2 - static let xSmall: CGFloat = 4 - static let small: CGFloat = 8 - static let medium: CGFloat = 12 - static let large: CGFloat = 16 - static let xLarge: CGFloat = 20 - static let xxLarge: CGFloat = 24 - static let xxxLarge: CGFloat = 32 - } - - // MARK: - Corner Radius - - enum CornerRadius { - static let xSmall: CGFloat = 4 - static let small: CGFloat = 8 - static let medium: CGFloat = 12 - static let large: CGFloat = 16 - static let xLarge: CGFloat = 20 - static let xxLarge: CGFloat = 24 - static let xxxLarge: CGFloat = 32 - } - - // MARK: - Base Font Sizes - - enum BaseFontSize { - static let xxSmall: CGFloat = 8 - static let xSmall: CGFloat = 10 - static let small: CGFloat = 12 - static let body: CGFloat = 14 - static let medium: CGFloat = 16 - static let large: CGFloat = 18 - static let xLarge: CGFloat = 20 - static let xxLarge: CGFloat = 24 - static let title: CGFloat = 28 - static let largeTitle: CGFloat = 32 - static let display: CGFloat = 48 - } - - // MARK: - Opacity - - enum Opacity { - static let verySubtle: Double = 0.05 - static let subtle: Double = 0.1 - static let hint: Double = 0.2 - static let light: Double = 0.3 - static let medium: Double = 0.5 - static let accent: Double = 0.6 - static let strong: Double = 0.7 - static let heavy: Double = 0.8 - static let almostFull: Double = 0.9 - } - - // MARK: - Line Width - - enum LineWidth { - static let thin: CGFloat = 1 - static let medium: CGFloat = 2 - static let thick: CGFloat = 3 - static let heavy: CGFloat = 4 - } - - // MARK: - Shadow - - enum Shadow { - static let radiusSmall: CGFloat = 2 - static let radiusMedium: CGFloat = 6 - static let radiusLarge: CGFloat = 10 - static let radiusXLarge: CGFloat = 15 - static let offsetSmall: CGFloat = 1 - static let offsetMedium: CGFloat = 3 - static let offsetLarge: CGFloat = 5 - } - - // MARK: - Sizes + // MARK: - Blackjack-Specific Component Sizes enum Size { - // Cards - static let cardWidth: CGFloat = 55 - static let cardWidthSmall: CGFloat = 45 - static let cardOverlap: CGFloat = -15 + // Cards - slightly larger than medium for better visibility + static let cardWidth: CGFloat = 60 + static let cardWidthSmall: CGFloat = CasinoDesign.Size.cardWidthSmall + static let cardOverlap: CGFloat = CasinoDesign.Size.cardOverlap - // Table + // Chips - use CasinoDesign values + static let chipBadgeSize: CGFloat = CasinoDesign.Size.chipBadge + + // Buttons - use CasinoDesign values + static let actionButtonHeight: CGFloat = CasinoDesign.Size.actionButtonHeight + static let actionButtonMinWidth: CGFloat = CasinoDesign.Size.actionButtonMinWidth + static let bettingZoneHeight: CGFloat = CasinoDesign.Size.bettingZoneHeight + + // Responsive - use CasinoDesign values + static let maxContentWidthPortrait: CGFloat = CasinoDesign.Size.maxContentWidthPortrait + static let maxContentWidthLandscape: CGFloat = CasinoDesign.Size.maxContentWidthLandscape + static let maxModalWidth: CGFloat = CasinoDesign.Size.maxModalWidth + + // Blackjack-specific static let tableHeight: CGFloat = 280 - static let bettingZoneHeight: CGFloat = 80 - static let chipBadgeSize: CGFloat = 32 - - // Buttons - static let actionButtonHeight: CGFloat = 50 - static let actionButtonMinWidth: CGFloat = 80 - - // Responsive - static let maxContentWidthPortrait: CGFloat = 500 - static let maxContentWidthLandscape: CGFloat = 800 - static let maxModalWidth: CGFloat = 450 - } - - // MARK: - Icon Sizes - - enum IconSize { - static let small: CGFloat = 16 - static let medium: CGFloat = 20 - static let large: CGFloat = 24 - static let xLarge: CGFloat = 32 } } -// MARK: - Color Extensions +// MARK: - Blackjack App Colors extension Color { - // MARK: - Table Colors - enum Table { - static let felt = Color(red: 0.05, green: 0.35, blue: 0.15) - static let feltDark = Color(red: 0.03, green: 0.25, blue: 0.1) - static let feltLight = Color(red: 0.08, green: 0.45, blue: 0.2) - static let border = Color(red: 0.6, green: 0.5, blue: 0.3) - } + // MARK: - Table Colors (use CasinoTable from CasinoKit for consistency) + + /// Typealias for consistent table colors across all games. + typealias Table = CasinoTable // MARK: - Betting Zone Colors @@ -152,7 +78,7 @@ extension Color { static let player = Color(red: 0.2, green: 0.5, blue: 0.8) static let dealer = Color(red: 0.8, green: 0.3, blue: 0.3) static let active = Color.yellow - static let inactive = Color.white.opacity(0.5) + static let inactive = Color.white.opacity(CasinoDesign.Opacity.medium) } // MARK: - Result Colors @@ -164,7 +90,7 @@ extension Color { static let blackjack = Color.yellow } - // MARK: - Button Colors + // MARK: - Action Button Colors enum Button { static let hit = Color(red: 0.2, green: 0.6, blue: 0.3) @@ -182,7 +108,7 @@ extension Color { enum Settings { static let background = Color(red: 0.08, green: 0.12, blue: 0.18) - static let cardBackground = Color.white.opacity(Design.Opacity.verySubtle) + static let cardBackground = Color.white.opacity(CasinoDesign.Opacity.verySubtle) static let accent = Color(red: 0.9, green: 0.75, blue: 0.3) } @@ -199,4 +125,3 @@ extension Color { static let balance = Color(red: 0.95, green: 0.85, blue: 0.4) } } - diff --git a/Blackjack/Views/BlackjackTableView.swift b/Blackjack/Views/BlackjackTableView.swift index 0cebf8f..55be6d7 100644 --- a/Blackjack/Views/BlackjackTableView.swift +++ b/Blackjack/Views/BlackjackTableView.swift @@ -195,18 +195,46 @@ struct PlayerHandsView: View { let cardWidth: CGFloat let cardSpacing: CGFloat + /// Adaptive card width based on number of hands (smaller cards for more splits) + private var adaptiveCardWidth: CGFloat { + switch hands.count { + case 1, 2: + return cardWidth + case 3: + return cardWidth * 0.85 + default: // 4+ hands + return cardWidth * 0.75 + } + } + + /// Adaptive spacing based on number of hands + private var adaptiveSpacing: CGFloat { + switch hands.count { + case 1, 2: + return Design.Spacing.xxLarge + case 3: + return Design.Spacing.large + default: // 4+ hands + return Design.Spacing.medium + } + } + var body: some View { - HStack(spacing: Design.Spacing.xxLarge) { - ForEach(hands.indices, id: \.self) { index in + HStack(spacing: adaptiveSpacing) { + // Display hands in reverse order (right to left play order) + // So hand 0 (played first) appears on the right + ForEach(hands.indices.reversed(), id: \.self) { index in PlayerHandView( hand: hands[index], isActive: index == activeHandIndex && isPlayerTurn, - handNumber: hands.count > 1 ? index + 1 : nil, - cardWidth: cardWidth, + // Hand numbers: rightmost is Hand 1, leftmost is Hand 2, etc. + handNumber: hands.count > 1 ? hands.count - index : nil, + cardWidth: adaptiveCardWidth, cardSpacing: cardSpacing ) } } + .frame(maxWidth: .infinity) // Center the hands horizontally } } @@ -222,32 +250,37 @@ struct PlayerHandView: View { var body: some View { VStack(spacing: Design.Spacing.small) { - // Cards - ZStack { - // Active indicator - if isActive { - RoundedRectangle(cornerRadius: Design.CornerRadius.medium) - .strokeBorder(Color.Hand.active, lineWidth: Design.LineWidth.medium) - .frame(width: containerWidth, height: containerHeight) - .animation(.easeInOut(duration: 0.5).repeatForever(autoreverses: true), value: isActive) - } - - HStack(spacing: hand.cards.isEmpty ? Design.Spacing.small : cardSpacing) { - if hand.cards.isEmpty { - CardPlaceholderView(width: cardWidth) - CardPlaceholderView(width: cardWidth) - } else { - ForEach(hand.cards.indices, id: \.self) { index in - CardView( - card: hand.cards[index], - isFaceUp: true, - cardWidth: cardWidth - ) - .zIndex(Double(index)) - } + // Cards with container - uses dynamic sizing based on card count + HStack(spacing: hand.cards.isEmpty ? Design.Spacing.small : cardSpacing) { + if hand.cards.isEmpty { + CardPlaceholderView(width: cardWidth) + CardPlaceholderView(width: cardWidth) + } else { + ForEach(hand.cards.indices, id: \.self) { index in + CardView( + card: hand.cards[index], + isFaceUp: true, + cardWidth: cardWidth + ) + .zIndex(Double(index)) } } } + .padding(.horizontal, Design.Spacing.medium) + .padding(.vertical, Design.Spacing.medium) + .background( + RoundedRectangle(cornerRadius: Design.CornerRadius.medium) + .fill(Color.Table.feltDark.opacity(Design.Opacity.light)) + .overlay( + RoundedRectangle(cornerRadius: Design.CornerRadius.medium) + .strokeBorder( + isActive ? Color.Hand.active : Color.white.opacity(Design.Opacity.hint), + lineWidth: isActive ? Design.LineWidth.thick : Design.LineWidth.thin + ) + ) + ) + .contentShape(Rectangle()) // Ensure tap area matches visual + .animation(.easeInOut(duration: Design.Animation.quick), value: isActive) // Hand info HStack(spacing: Design.Spacing.small) { @@ -298,14 +331,6 @@ struct PlayerHandView: View { .accessibilityLabel(playerAccessibilityLabel) } - private var containerWidth: CGFloat { - cardWidth + (cardWidth + cardSpacing) * 2 + Design.Spacing.medium - } - - private var containerHeight: CGFloat { - cardWidth * CasinoDesign.Size.cardAspectRatio + Design.Spacing.medium - } - private var valueColor: Color { if hand.isBlackjack { return .yellow } if hand.isBusted { return .red } diff --git a/Blackjack/Views/GameTableView.swift b/Blackjack/Views/GameTableView.swift index 0989ec7..9cd9f2f 100644 --- a/Blackjack/Views/GameTableView.swift +++ b/Blackjack/Views/GameTableView.swift @@ -71,10 +71,7 @@ struct GameTableView: View { private func mainGameView(state: GameState) -> some View { ZStack { // Background - TableBackgroundView( - feltColor: Color.Table.felt, - edgeColor: Color.Table.feltDark - ) + TableBackgroundView() VStack(spacing: 0) { // Top bar @@ -124,8 +121,8 @@ struct GameTableView: View { ) } - // Confetti for blackjack - if state.showResultBanner && (state.lastRoundResult?.wasBlackjack ?? false) { + // Confetti for wins (matching Baccarat pattern) + if state.showResultBanner && (state.lastRoundResult?.totalWinnings ?? 0) > 0 { ConfettiView() } @@ -276,10 +273,10 @@ struct ActionButton: View { let action: () -> Void enum ButtonStyle { - case primary - case destructive - case secondary - case custom(Color) + case primary // Gold gradient (Deal, New Round) + case destructive // Red (Clear) + case secondary // Subtle white + case custom(Color) // Game-specific colors (Hit, Stand, etc.) var foregroundColor: Color { switch self { @@ -287,15 +284,6 @@ struct ActionButton: View { case .destructive, .secondary, .custom: return .white } } - - var backgroundColor: Color { - switch self { - case .primary: return .yellow - case .destructive: return .red.opacity(Design.Opacity.heavy) - case .secondary: return .white.opacity(Design.Opacity.hint) - case .custom(let color): return color - } - } } init(_ title: String, icon: String? = nil, style: ButtonStyle = .primary, action: @escaping () -> Void) { @@ -313,18 +301,38 @@ struct ActionButton: View { } Text(title) } - .font(.system(size: Design.BaseFontSize.medium, weight: .bold)) + .font(.system(size: Design.BaseFontSize.large, weight: .semibold)) .foregroundStyle(style.foregroundColor) - .padding(.horizontal, Design.Spacing.xLarge) - .padding(.vertical, Design.Spacing.medium) - .background( - Capsule() - .fill(style.backgroundColor) - ) - .shadow(color: style.backgroundColor.opacity(Design.Opacity.light), radius: Design.Shadow.radiusMedium) + .padding(.horizontal, Design.Spacing.xxLarge) + .padding(.vertical, Design.Spacing.medium + Design.Spacing.xxSmall) + .background(backgroundView) } .accessibilityLabel(title) } + + @ViewBuilder + private var backgroundView: some View { + switch style { + case .primary: + Capsule() + .fill( + LinearGradient( + colors: [Color.Button.goldLight, Color.Button.goldDark], + startPoint: .top, + endPoint: .bottom + ) + ) + case .destructive: + Capsule() + .fill(Color.red.opacity(Design.Opacity.heavy)) + case .secondary: + Capsule() + .fill(Color.white.opacity(Design.Opacity.hint)) + case .custom(let color): + Capsule() + .fill(color) + } + } } // MARK: - Preview diff --git a/CasinoKit/Sources/CasinoKit/Theme/CasinoDesign.swift b/CasinoKit/Sources/CasinoKit/Theme/CasinoDesign.swift index e992011..7ec5f80 100644 --- a/CasinoKit/Sources/CasinoKit/Theme/CasinoDesign.swift +++ b/CasinoKit/Sources/CasinoKit/Theme/CasinoDesign.swift @@ -28,17 +28,18 @@ public enum CasinoDesign { public enum CornerRadius { public static let xSmall: CGFloat = 4 public static let small: CGFloat = 8 - public static let medium: CGFloat = 12 - public static let large: CGFloat = 16 - public static let xLarge: CGFloat = 20 - public static let xxLarge: CGFloat = 24 - public static let xxxLarge: CGFloat = 32 + public static let medium: CGFloat = 10 + public static let large: CGFloat = 12 + public static let xLarge: CGFloat = 14 + public static let xxLarge: CGFloat = 20 + public static let xxxLarge: CGFloat = 28 } // MARK: - Line Width public enum LineWidth { public static let thin: CGFloat = 1 + public static let standard: CGFloat = 2 public static let medium: CGFloat = 2 public static let thick: CGFloat = 3 public static let heavy: CGFloat = 4 @@ -61,25 +62,37 @@ public enum CasinoDesign { // MARK: - Opacity public enum Opacity { - public static let verySubtle: CGFloat = 0.05 - public static let subtle: CGFloat = 0.1 - public static let hint: CGFloat = 0.2 - public static let light: CGFloat = 0.3 - public static let quarter: CGFloat = 0.25 - public static let medium: CGFloat = 0.5 - public static let accent: CGFloat = 0.6 - public static let strong: CGFloat = 0.7 - public static let heavy: CGFloat = 0.8 - public static let nearOpaque: CGFloat = 0.95 + public static let verySubtle: Double = 0.05 + public static let subtle: Double = 0.1 + public static let selection: Double = 0.15 + public static let hint: Double = 0.2 + public static let quarter: Double = 0.25 + public static let light: Double = 0.3 + public static let overlay: Double = 0.4 + public static let medium: Double = 0.5 + public static let secondary: Double = 0.5 + public static let disabled: Double = 0.5 + public static let accent: Double = 0.6 + public static let strong: Double = 0.7 + public static let heavy: Double = 0.8 + public static let nearOpaque: Double = 0.85 + public static let almostFull: Double = 0.9 } // MARK: - Animation public enum Animation { - public static let quick: Double = 0.2 + public static let quick: Double = 0.3 public static let standard: Double = 0.3 public static let springDuration: Double = 0.4 - public static let springBounce: Double = 0.2 + public static let springBounce: Double = 0.3 + public static let cardFlipBounce: Double = 0.2 + public static let fadeInDuration: Double = 0.3 + public static let cardFlipDuration: Double = 0.5 + public static let selectionDuration: Double = 0.2 + public static let staggerDelay1: Double = 0.1 + public static let staggerDelay2: Double = 0.25 + public static let staggerDelay3: Double = 0.4 } // MARK: - Sizes @@ -90,11 +103,18 @@ public enum CasinoDesign { public static let chipMedium: CGFloat = 50 public static let chipLarge: CGFloat = 60 - /// Default card width. + /// Default card widths. + public static let cardWidthSmall: CGFloat = 45 + public static let cardWidthMedium: CGFloat = 55 + public static let cardWidthLarge: CGFloat = 65 public static let cardWidth: CGFloat = 70 + /// Card overlap for stacking. + public static let cardOverlap: CGFloat = -15 + /// Card aspect ratio (height = width * this value). - public static let cardAspectRatio: CGFloat = 1.4 + /// Standard poker is 1.4, but 1.35 looks better on screen. + public static let cardAspectRatio: CGFloat = 1.35 /// Pattern dimensions for decorative elements. public static let patternSpacing: CGFloat = 12 @@ -114,24 +134,48 @@ public enum CasinoDesign { /// Value badge size. public static let valueBadge: CGFloat = 26 - /// Icon sizes. - public static let iconSmall: CGFloat = 16 - public static let iconMedium: CGFloat = 20 - public static let iconLarge: CGFloat = 24 + /// Chip badge for bet indicators. + public static let chipBadge: CGFloat = 32 + public static let chipBadgeInner: CGFloat = 28 + + /// Checkmark size. + public static let checkmark: CGFloat = 22 + + /// Common button dimensions. + public static let actionButtonHeight: CGFloat = 50 + public static let actionButtonMinWidth: CGFloat = 80 + + /// Betting zone height. + public static let bettingZoneHeight: CGFloat = 80 + } + + // MARK: - Icon Sizes + + public enum IconSize { + public static let small: CGFloat = 12 + public static let medium: CGFloat = 16 + public static let large: CGFloat = 22 + public static let xLarge: CGFloat = 32 + public static let xxLarge: CGFloat = 60 + public static let xxxLarge: CGFloat = 70 } // MARK: - Font Sizes (Base values for @ScaledMetric) public enum BaseFontSize { - public static let xxSmall: CGFloat = 8 - public static let xSmall: CGFloat = 10 - public static let small: CGFloat = 12 - public static let body: CGFloat = 14 - public static let medium: CGFloat = 16 - public static let large: CGFloat = 20 - public static let xLarge: CGFloat = 24 - public static let xxLarge: CGFloat = 28 + public static let xxSmall: CGFloat = 7 + public static let xSmall: CGFloat = 9 + public static let small: CGFloat = 10 + public static let body: CGFloat = 12 + public static let callout: CGFloat = 13 + public static let medium: CGFloat = 14 + public static let subheadline: CGFloat = 15 + public static let large: CGFloat = 16 + public static let xLarge: CGFloat = 18 + public static let xxLarge: CGFloat = 20 + public static let title: CGFloat = 32 public static let largeTitle: CGFloat = 36 + public static let display: CGFloat = 70 } // MARK: - Scale @@ -199,16 +243,30 @@ public extension Color { public static let backgroundDark = Color(white: 0.08) } - /// Table colors. + /// Table colors - shared across all casino games. enum CasinoTable { - /// Casino table green felt. - public static let felt = Color(red: 0.05, green: 0.25, blue: 0.15) - /// Darker felt for gradients. - public static let feltDark = Color(red: 0.02, green: 0.15, blue: 0.08) + /// Casino table green felt (main color). + public static let felt = Color(red: 0.0, green: 0.3, blue: 0.15) + /// Darker felt for gradients and shadows. + public static let feltDark = Color(red: 0.0, green: 0.2, blue: 0.1) + /// Lighter felt for highlights. + public static let feltLight = Color(red: 0.0, green: 0.35, blue: 0.18) + /// Background gradient dark. + public static let backgroundDark = Color(red: 0.01, green: 0.12, blue: 0.06) + /// Background gradient light. + public static let backgroundLight = Color(red: 0.03, green: 0.25, blue: 0.12) + /// Base dark (for table edges). + public static let baseDark = Color(red: 0.02, green: 0.15, blue: 0.08) + /// Preview background (same as felt). + public static let preview = felt /// Table edge border. - public static let border = Color(red: 0.3, green: 0.2, blue: 0.1) + public static let border = Color(red: 0.6, green: 0.5, blue: 0.3) /// Gold accent for table elements. public static let gold = Color(red: 0.85, green: 0.65, blue: 0.2) + /// Gold light for highlights. + public static let goldLight = Color(red: 0.85, green: 0.7, blue: 0.35) + /// Gold dark for gradients. + public static let goldDark = Color(red: 0.65, green: 0.5, blue: 0.2) } /// Top bar colors. diff --git a/CasinoKit/Sources/CasinoKit/Views/Table/TableBackgroundView.swift b/CasinoKit/Sources/CasinoKit/Views/Table/TableBackgroundView.swift index 95caa0d..2a4d628 100644 --- a/CasinoKit/Sources/CasinoKit/Views/Table/TableBackgroundView.swift +++ b/CasinoKit/Sources/CasinoKit/Views/Table/TableBackgroundView.swift @@ -8,43 +8,46 @@ import SwiftUI /// A casino table felt background with radial gradient. +/// This creates a consistent look across all casino games. public struct TableBackgroundView: View { - /// The primary felt color (center of gradient). - public let feltColor: Color - - /// The darker edge color for the gradient. - public let edgeColor: Color - /// Whether to show the decorative felt pattern. public let showPattern: Bool - /// Creates a table background. - /// - Parameters: - /// - feltColor: The main felt color (default: casino green). - /// - edgeColor: The darker edge color (default: dark green). - /// - showPattern: Whether to show the decorative pattern (default: true). + /// Creates a table background with the standard casino felt colors. + /// - Parameter showPattern: Whether to show the decorative pattern (default: true). + public init(showPattern: Bool = true) { + self.showPattern = showPattern + } + + /// Legacy initializer for backwards compatibility (colors are ignored). + @available(*, deprecated, message: "Use init(showPattern:) - colors are now standardized") public init( - feltColor: Color = Color.CasinoTable.felt, - edgeColor: Color = Color.CasinoTable.feltDark, + feltColor: Color, + edgeColor: Color, showPattern: Bool = true ) { - self.feltColor = feltColor - self.edgeColor = edgeColor self.showPattern = showPattern } public var body: some View { ZStack { - // Base gradient + // Base dark layer (deepest background) + Color.CasinoTable.baseDark + .ignoresSafeArea() + + // Radial gradient for depth (lighter center, darker edges) RadialGradient( - colors: [feltColor, edgeColor], + colors: [ + Color.CasinoTable.backgroundLight, + Color.CasinoTable.backgroundDark + ], center: .center, startRadius: 50, endRadius: 600 ) .ignoresSafeArea() - // Optional pattern overlay + // Optional subtle felt texture pattern if showPattern { FeltPatternView() .opacity(CasinoDesign.Opacity.verySubtle) @@ -55,42 +58,42 @@ public struct TableBackgroundView: View { } } -/// A subtle decorative pattern for the felt. +/// A subtle decorative pattern for the felt (random dot texture). public struct FeltPatternView: View { public init() {} public var body: some View { - GeometryReader { geometry in - Canvas { context, size in - let spacing = CasinoDesign.Size.patternSpacing - let diamondSize = CasinoDesign.Size.patternDiamondSize + Canvas { context, size in + // Use deterministic pseudo-random for consistent appearance + var rng = SeededRandomNumberGenerator(seed: 12345) + let dotCount = 2000 + + for _ in 0.. UInt64 { + state = state &* 6364136223846793005 &+ 1442695040888963407 + return state + } +} + #Preview { TableBackgroundView() }