diff --git a/Baccarat.xcodeproj/project.pbxproj b/Baccarat.xcodeproj/project.pbxproj index ef7cf50..ba796ea 100644 --- a/Baccarat.xcodeproj/project.pbxproj +++ b/Baccarat.xcodeproj/project.pbxproj @@ -205,6 +205,10 @@ knownRegions = ( en, Base, + "es-US", + es, + fr, + "fr-CA", ); mainGroup = EAD890AE2EF1E9CE006DBA80; minimizedProjectReferenceProxies = 1; @@ -344,6 +348,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; @@ -401,6 +406,7 @@ MTL_FAST_MATH = YES; SDKROOT = iphoneos; SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/Baccarat/Agents.md b/Baccarat/Agents.md index 599ec98..746311c 100644 --- a/Baccarat/Agents.md +++ b/Baccarat/Agents.md @@ -101,7 +101,7 @@ If SwiftData is configured to use CloudKit: static let small: CGFloat = 8 static let medium: CGFloat = 12 } - enum FontSize { + enum BaseFontSize { static let body: CGFloat = 14 static let title: CGFloat = 24 } @@ -125,7 +125,37 @@ If SwiftData is configured to use CloudKit: } ``` - Reference design constants in views: `Design.Spacing.medium`, `Design.CornerRadius.large`, `Color.Primary.accent`. -- Keep design constants organized by category: Spacing, CornerRadius, FontSize, IconSize, Size, Animation, Opacity, LineWidth, Shadow. +- Keep design constants organized by category: Spacing, CornerRadius, BaseFontSize, IconSize, Size, Animation, Opacity, LineWidth, Shadow. + + +## Dynamic Type instructions + +- Always support Dynamic Type for accessibility; never use fixed font sizes without scaling. +- Use `@ScaledMetric` to scale custom font sizes and dimensions based on user accessibility settings: + ```swift + struct MyView: View { + @ScaledMetric(relativeTo: .body) private var bodyFontSize: CGFloat = 14 + @ScaledMetric(relativeTo: .title) private var titleFontSize: CGFloat = 24 + @ScaledMetric(relativeTo: .caption) private var chipTextSize: CGFloat = 11 + + var body: some View { + Text("Hello") + .font(.system(size: bodyFontSize, weight: .medium)) + } + } + ``` +- Choose the appropriate `relativeTo` text style based on the semantic purpose: + - `.largeTitle`, `.title`, `.title2`, `.title3` for headings + - `.headline`, `.subheadline` for emphasized content + - `.body` for main content + - `.callout`, `.footnote`, `.caption`, `.caption2` for smaller text +- For constrained UI elements (chips, cards, badges) where overflow would break the design, you may use fixed sizes but document the reason: + ```swift + // Fixed size: chip face has strict space constraints + private let chipValueFontSize: CGFloat = 11 + ``` +- Prefer system text styles when possible: `.font(.body)`, `.font(.title)`, `.font(.caption)`. +- Test with accessibility settings: Settings > Accessibility > Display & Text Size > Larger Text. ## Project structure diff --git a/Baccarat/Resources/Localizable.xcstrings b/Baccarat/Resources/Localizable.xcstrings index 8cc60ad..9024c52 100644 --- a/Baccarat/Resources/Localizable.xcstrings +++ b/Baccarat/Resources/Localizable.xcstrings @@ -3,141 +3,151 @@ "strings" : { "%lld" : { "comment" : "The number of rounds a player has played in the game.", - "isCommentAutoGenerated" : true + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" + } + }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld" + } + } + } }, "$" : { "comment" : "The currency symbol \"$\".", - "isCommentAutoGenerated" : true + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "$" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "$" + } + }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "$" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "€" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "$" + } + } + } }, "$%lldK" : { - "comment" : "A button that allows the user to select a starting balance for the game. The text inside the button changes based on whether the button is currently selected or not.", - "isCommentAutoGenerated" : true - }, - "1 Deck" : { - "extractionState" : "stale", + "comment" : "A button that allows the user to select a starting balance for the game.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "1 Deck" + "value" : "$%lldK" } }, "es-MX" : { "stringUnit" : { "state" : "translated", - "value" : "1 Baraja" + "value" : "$%lldK" + } + }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "$%lldK" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "%lld K €" } }, "fr-CA" : { "stringUnit" : { "state" : "translated", - "value" : "1 Jeu" - } - } - } - }, - "6 Decks" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "6 Decks" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "6 Barajas" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "6 Jeux" - } - } - } - }, - "8 Decks" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "8 Decks" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "8 Barajas" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "8 Jeux" - } - } - } - }, - "Animate dealing and flipping" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Animate dealing and flipping" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Animar reparto y volteo" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Animer la distribution et le retournement" - } - } - } - }, - "ANIMATIONS" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "ANIMATIONS" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "ANIMACIONES" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "ANIMATIONS" + "value" : "%lld K $" } } } }, "B" : { "comment" : "The letter \"B\" displayed in the center of the playing card's back.", - "isCommentAutoGenerated" : true - }, - "BALANCE" : { "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "BALANCE" + "value" : "B" + } + }, + "es-MX" : { + "stringUnit" : { + "state" : "translated", + "value" : "B" + } + }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "B" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "B" + } + }, + "fr-CA" : { + "stringUnit" : { + "state" : "translated", + "value" : "B" + } + } + } + }, + "BALANCE" : { + "comment" : "The label for the user's balance in the top bar.", + "localizations" : { + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "SALDO" } }, "es-MX" : { @@ -146,6 +156,18 @@ "value" : "SALDO" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "SALDO" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "SOLDE" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -154,35 +176,13 @@ } } }, - "Balanced gameplay" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Balanced gameplay" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Juego equilibrado" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Jeu équilibré" - } - } - } - }, "BANKER" : { + "comment" : "A label displayed above the banker's hand in the baccarat table view.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "BANKER" + "value" : "BANCA" } }, "es-MX" : { @@ -191,6 +191,18 @@ "value" : "BANCA" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "BANCA" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "BANQUE" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -200,7 +212,7 @@ } }, "BANKER WINS" : { - "extractionState" : "stale", + "comment" : "Result banner text when banker wins.", "localizations" : { "en" : { "stringUnit" : { @@ -208,12 +220,30 @@ "value" : "BANKER WINS" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GANA BANCA" + } + }, "es-MX" : { "stringUnit" : { "state" : "translated", "value" : "GANA BANCA" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "GANA BANCA" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "BANQUE GAGNE" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -223,11 +253,12 @@ } }, "Cancel" : { + "comment" : "The label of a button to cancel making changes in the settings.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Cancel" + "value" : "Cancelar" } }, "es-MX" : { @@ -236,6 +267,18 @@ "value" : "Cancelar" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancelar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Annuler" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -244,35 +287,13 @@ } } }, - "Card Animations" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Card Animations" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Animaciones de cartas" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Animations des cartes" - } - } - } - }, "Clear" : { + "comment" : "The label of a button that clears all current bets in the game.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Clear" + "value" : "Borrar" } }, "es-MX" : { @@ -281,6 +302,18 @@ "value" : "Borrar" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Borrar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Effacer" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -289,35 +322,13 @@ } } }, - "Common in casinos" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Common in casinos" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Común en casinos" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Courant dans les casinos" - } - } - } - }, "Deal" : { + "comment" : "The label of a button that deals cards in a game.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Deal" + "value" : "Repartir" } }, "es-MX" : { @@ -326,6 +337,18 @@ "value" : "Repartir" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repartir" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Distribuer" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -335,11 +358,12 @@ } }, "Dealing Speed" : { + "comment" : "A label describing the speed at which cards are dealt in the game.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Dealing Speed" + "value" : "Velocidad de reparto" } }, "es-MX" : { @@ -348,6 +372,18 @@ "value" : "Velocidad de reparto" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velocidad de reparto" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vitesse de distribution" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -357,11 +393,12 @@ } }, "Dealing..." : { + "comment" : "A placeholder text shown while a game is being dealt.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Dealing..." + "value" : "Repartiendo..." } }, "es-MX" : { @@ -370,6 +407,18 @@ "value" : "Repartiendo..." } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repartiendo..." + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Distribution..." + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -378,104 +427,13 @@ } } }, - "DECK SETTINGS" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "DECK SETTINGS" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "CONFIGURACIÓN DE BARAJAS" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "PARAMÈTRES DU JEU" - } - } - } - }, - "DISPLAY" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "DISPLAY" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "PANTALLA" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "AFFICHAGE" - } - } - } - }, - "Display deck counter at top" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Display deck counter at top" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar contador de cartas arriba" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Afficher le compteur en haut" - } - } - } - }, - "Display result road map" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Display result road map" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar historial de resultados" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Afficher l'historique des résultats" - } - } - } - }, "Done" : { + "comment" : "The text for a button that confirms and saves settings.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Done" + "value" : "Listo" } }, "es-MX" : { @@ -484,6 +442,18 @@ "value" : "Listo" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Listo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Terminé" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -492,58 +462,13 @@ } } }, - "Fast" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Fast" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rápido" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Rapide" - } - } - } - }, - "For experienced players" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "For experienced players" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Para jugadores experimentados" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Pour joueurs expérimentés" - } - } - } - }, "GAME OVER" : { + "comment" : "The title of the game over screen.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "GAME OVER" + "value" : "FIN DEL JUEGO" } }, "es-MX" : { @@ -552,6 +477,18 @@ "value" : "FIN DEL JUEGO" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "FIN DEL JUEGO" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FIN DE PARTIE" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -560,35 +497,13 @@ } } }, - "High Roller" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "High Roller" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Apostador alto" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Gros joueur" - } - } - } - }, "HISTORY" : { + "comment" : "A label displayed above the road map view, indicating that it shows a history of past game results.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "HISTORY" + "value" : "HISTORIAL" } }, "es-MX" : { @@ -597,6 +512,18 @@ "value" : "HISTORIAL" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "HISTORIAL" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "HISTORIQUE" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -605,35 +532,13 @@ } } }, - "Low Stakes" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Low Stakes" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Apuestas bajas" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mises basses" - } - } - } - }, "MAX" : { + "comment" : "A label displayed as a badge on top-right of a chip to indicate it's the maximum bet.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "MAX" + "value" : "MÁX" } }, "es-MX" : { @@ -642,6 +547,18 @@ "value" : "MÁX" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "MÁX" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "MAX" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -650,35 +567,13 @@ } } }, - "Maximum excitement" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Maximum excitement" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Máxima emoción" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Excitation maximale" - } - } - } - }, "New Round" : { + "comment" : "The label of a button that starts a new round of the game.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "New Round" + "value" : "Nueva ronda" } }, "es-MX" : { @@ -687,6 +582,18 @@ "value" : "Nueva ronda" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nueva ronda" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nouvelle manche" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -695,35 +602,13 @@ } } }, - "Normal" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Normal" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Normal" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Normal" - } - } - } - }, "PAYS 0.95 TO 1" : { + "comment" : "A description of the payout for betting on the banker in a mini baccarat table.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "PAYS 0.95 TO 1" + "value" : "PAGA 0,95 A 1" } }, "es-MX" : { @@ -732,6 +617,18 @@ "value" : "PAGA 0.95 A 1" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAGA 0.95 A 1" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAIE 0,95 POUR 1" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -741,11 +638,12 @@ } }, "PAYS 1 TO 1" : { + "comment" : "A description of the payout ratio for the player bet.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "PAYS 1 TO 1" + "value" : "PAGA 1 A 1" } }, "es-MX" : { @@ -754,6 +652,18 @@ "value" : "PAGA 1 A 1" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAGA 1 A 1" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAIE 1 POUR 1" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -763,11 +673,12 @@ } }, "PAYS 8 TO 1" : { + "comment" : "A description of the payout for betting on a tie in mini baccarat.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "PAYS 8 TO 1" + "value" : "PAGA 8 A 1" } }, "es-MX" : { @@ -776,6 +687,18 @@ "value" : "PAGA 8 A 1" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAGA 8 A 1" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "PAIE 8 POUR 1" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -784,35 +707,13 @@ } } }, - "Perfect for beginners" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Perfect for beginners" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Perfecto para principiantes" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Parfait pour les débutants" - } - } - } - }, "Play Again" : { + "comment" : "A button label that says \"Play Again\".", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Play Again" + "value" : "Jugar de nuevo" } }, "es-MX" : { @@ -821,6 +722,18 @@ "value" : "Jugar de nuevo" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jugar de nuevo" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rejouer" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -830,11 +743,12 @@ } }, "PLAYER" : { + "comment" : "The label for the player's hand in the cards display area.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "PLAYER" + "value" : "JUGADOR" } }, "es-MX" : { @@ -843,6 +757,18 @@ "value" : "JUGADOR" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "JUGADOR" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "JOUEUR" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -852,7 +778,7 @@ } }, "PLAYER WINS" : { - "extractionState" : "stale", + "comment" : "Result banner text when player wins.", "localizations" : { "en" : { "stringUnit" : { @@ -860,12 +786,30 @@ "value" : "PLAYER WINS" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "GANA JUGADOR" + } + }, "es-MX" : { "stringUnit" : { "state" : "translated", "value" : "GANA JUGADOR" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "GANA JUGADOR" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "JOUEUR GAGNE" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -874,35 +818,13 @@ } } }, - "Practice mode" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Practice mode" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Modo práctica" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mode pratique" - } - } - } - }, "Reset" : { + "comment" : "A button that resets the game to its initial state.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reset" + "value" : "Reiniciar" } }, "es-MX" : { @@ -911,6 +833,18 @@ "value" : "Reiniciar" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reiniciar" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialiser" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -920,11 +854,12 @@ } }, "Reset to Defaults" : { + "comment" : "A button label that resets game settings to their default values.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Reset to Defaults" + "value" : "Restaurar valores predeterminados" } }, "es-MX" : { @@ -933,6 +868,18 @@ "value" : "Restaurar valores predeterminados" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Restaurar valores predeterminados" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Réinitialiser par défaut" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -942,11 +889,12 @@ } }, "Rounds Played" : { + "comment" : "A label displayed next to the number of rounds played in the game over screen.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Rounds Played" + "value" : "Rondas jugadas" } }, "es-MX" : { @@ -955,6 +903,18 @@ "value" : "Rondas jugadas" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rondas jugadas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Manches jouées" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -964,11 +924,12 @@ } }, "Settings" : { + "comment" : "The label of a button that navigates to the settings screen.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "Settings" + "value" : "Configuración" } }, "es-MX" : { @@ -977,6 +938,18 @@ "value" : "Configuración" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "Configuración" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paramètres" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -985,170 +958,8 @@ } } }, - "Show Cards Remaining" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Show Cards Remaining" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar cartas restantes" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Afficher les cartes restantes" - } - } - } - }, - "Show History" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Show History" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Mostrar historial" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Afficher l'historique" - } - } - } - }, - "Slow" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Slow" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lento" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Lent" - } - } - } - }, - "Standard" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Standard" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estándar" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Standard" - } - } - } - }, - "Standard (Recommended)" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Standard (Recommended)" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "Estándar (Recomendado)" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "Standard (Recommandé)" - } - } - } - }, - "STARTING BALANCE" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "STARTING BALANCE" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "SALDO INICIAL" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "SOLDE DE DÉPART" - } - } - } - }, - "TABLE LIMITS" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "TABLE LIMITS" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "LÍMITES DE MESA" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "LIMITES DE TABLE" - } - } - } - }, "tableLimitsFormat" : { - "comment" : "Format for table limits display", - "extractionState" : "stale", + "comment" : "Format string for table limits display. First argument is min bet, second is max bet.", "localizations" : { "en" : { "stringUnit" : { @@ -1156,12 +967,30 @@ "value" : "TABLE LIMITS: $%@ - $%@" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "LÍMITES: $%@ - $%@" + } + }, "es-MX" : { "stringUnit" : { "state" : "translated", "value" : "LÍMITES: $%@ - $%@" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "LÍMITES: $%@ - $%@" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "LIMITES: %@ € - %@ €" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -1171,11 +1000,12 @@ } }, "TIE" : { + "comment" : "The text displayed in the TIE betting zone.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "TIE" + "value" : "EMPATE" } }, "es-MX" : { @@ -1184,6 +1014,18 @@ "value" : "EMPATE" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "EMPATE" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ÉGALITÉ" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -1193,7 +1035,7 @@ } }, "TIE GAME" : { - "extractionState" : "stale", + "comment" : "Result banner text when the game is a tie.", "localizations" : { "en" : { "stringUnit" : { @@ -1201,12 +1043,30 @@ "value" : "TIE GAME" } }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "EMPATE" + } + }, "es-MX" : { "stringUnit" : { "state" : "translated", "value" : "EMPATE" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "EMPATE" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "ÉGALITÉ" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -1215,35 +1075,13 @@ } } }, - "VIP" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "VIP" - } - }, - "es-MX" : { - "stringUnit" : { - "state" : "translated", - "value" : "VIP" - } - }, - "fr-CA" : { - "stringUnit" : { - "state" : "translated", - "value" : "VIP" - } - } - } - }, "WIN" : { + "comment" : "The text that appears as a badge when a player wins a hand in baccarat.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "WIN" + "value" : "GANA" } }, "es-MX" : { @@ -1252,6 +1090,18 @@ "value" : "GANA" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "GANA" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "GAGNÉ" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -1261,11 +1111,12 @@ } }, "You've run out of chips!" : { + "comment" : "A message displayed when a player runs out of money in the game over screen.", "localizations" : { - "en" : { + "es" : { "stringUnit" : { "state" : "translated", - "value" : "You've run out of chips!" + "value" : "¡Te quedaste sin fichas!" } }, "es-MX" : { @@ -1274,6 +1125,18 @@ "value" : "¡Te quedaste sin fichas!" } }, + "es-US" : { + "stringUnit" : { + "state" : "translated", + "value" : "¡Te quedaste sin fichas!" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vous n'avez plus de jetons!" + } + }, "fr-CA" : { "stringUnit" : { "state" : "translated", @@ -1283,5 +1146,5 @@ } } }, - "version" : "1.1" -} \ No newline at end of file + "version" : "1.0" +} diff --git a/Baccarat/Theme/DesignConstants.swift b/Baccarat/Theme/DesignConstants.swift index 83ce7ab..226dae8 100644 --- a/Baccarat/Theme/DesignConstants.swift +++ b/Baccarat/Theme/DesignConstants.swift @@ -34,9 +34,11 @@ enum Design { static let xxxLarge: CGFloat = 28 } - // MARK: - Font Sizes + // MARK: - Base Font Sizes + // These are base values for use with @ScaledMetric in views. + // They will scale automatically based on user accessibility settings. - enum FontSize { + enum BaseFontSize { static let xxSmall: CGFloat = 7 static let xSmall: CGFloat = 9 static let small: CGFloat = 10 diff --git a/Baccarat/Views/GameTableView.swift b/Baccarat/Views/GameTableView.swift index c632cac..31d14f3 100644 --- a/Baccarat/Views/GameTableView.swift +++ b/Baccarat/Views/GameTableView.swift @@ -148,13 +148,16 @@ struct GameOverView: View { @State private var showContent = false + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .largeTitle) private var iconSize: CGFloat = Design.BaseFontSize.display + @ScaledMetric(relativeTo: .largeTitle) private var titleFontSize: CGFloat = Design.BaseFontSize.largeTitle + @ScaledMetric(relativeTo: .body) private var messageFontSize: CGFloat = Design.BaseFontSize.xLarge + @ScaledMetric(relativeTo: .body) private var statsFontSize: CGFloat = 17 + @ScaledMetric(relativeTo: .headline) private var buttonFontSize: CGFloat = Design.BaseFontSize.xLarge + // MARK: - Layout Constants - private let iconSize = Design.FontSize.display - private let titleFontSize = Design.FontSize.largeTitle - private let messageFontSize = Design.FontSize.xLarge - private let statsFontSize: CGFloat = 17 - private let buttonFontSize = Design.FontSize.xLarge private let modalCornerRadius = Design.CornerRadius.xxxLarge private let statsCornerRadius = Design.CornerRadius.large private let cardPadding = Design.Spacing.xxxLarge @@ -287,6 +290,10 @@ struct CardsDisplayArea: View { let bankerIsWinner: Bool let isTie: Bool + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .headline) private var labelFontSize: CGFloat = 14 + var body: some View { HStack(spacing: 32) { // Player side @@ -294,14 +301,14 @@ struct CardsDisplayArea: View { // Label with value HStack(spacing: 8) { Text("PLAYER") - .font(.system(size: 14, weight: .bold, design: .rounded)) + .font(.system(size: labelFontSize, weight: .bold, design: .rounded)) .foregroundStyle(.white) if !playerCards.isEmpty && playerCardsFaceUp.contains(true) { ValueBadge(value: playerValue, color: .blue) } } - .frame(height: 30) + .frame(minHeight: 30) // Cards CompactHandView( @@ -316,14 +323,14 @@ struct CardsDisplayArea: View { // Label with value HStack(spacing: 8) { Text("BANKER") - .font(.system(size: 14, weight: .bold, design: .rounded)) + .font(.system(size: labelFontSize, weight: .bold, design: .rounded)) .foregroundStyle(.white) if !bankerCards.isEmpty && bankerCardsFaceUp.contains(true) { ValueBadge(value: bankerValue, color: .red) } } - .frame(height: 30) + .frame(minHeight: 30) // Cards CompactHandView( @@ -350,12 +357,21 @@ struct CompactHandView: View { let cardsFaceUp: [Bool] let isWinner: Bool + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .caption) private var winBadgeFontSize: CGFloat = 10 + + // MARK: - Layout Constants + // Fixed size: cards have strict visual constraints + + private let cardWidth: CGFloat = 45 + var body: some View { HStack(spacing: -12) { if cards.isEmpty { // Placeholders ForEach(0..<2, id: \.self) { _ in - CardPlaceholderView(width: 45) + CardPlaceholderView(width: cardWidth) } } else { ForEach(cards.indices, id: \.self) { index in @@ -363,7 +379,7 @@ struct CompactHandView: View { CardView( card: cards[index], isFaceUp: isFaceUp, - cardWidth: 45 + cardWidth: cardWidth ) .zIndex(Double(index)) } @@ -380,7 +396,7 @@ struct CompactHandView: View { .overlay(alignment: .bottom) { if isWinner { Text("WIN") - .font(.system(size: 10, weight: .black)) + .font(.system(size: winBadgeFontSize, weight: .black)) .foregroundStyle(.black) .padding(.horizontal, 8) .padding(.vertical, 2) @@ -399,11 +415,16 @@ struct ValueBadge: View { let value: Int let color: Color + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .headline) private var valueFontSize: CGFloat = 15 + @ScaledMetric(relativeTo: .headline) private var badgeSize: CGFloat = 26 + var body: some View { Text("\(value)") - .font(.system(size: 15, weight: .black, design: .rounded)) + .font(.system(size: valueFontSize, weight: .black, design: .rounded)) .foregroundStyle(.white) - .frame(width: 26, height: 26) + .frame(width: badgeSize, height: badgeSize) .background( Circle() .fill(color) @@ -461,22 +482,30 @@ struct TopBarView: View { let onReset: () -> Void let onSettings: () -> Void + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .caption2) private var labelFontSize: CGFloat = 9 + @ScaledMetric(relativeTo: .body) private var currencyFontSize: CGFloat = 14 + @ScaledMetric(relativeTo: .title3) private var balanceFontSize: CGFloat = 20 + @ScaledMetric(relativeTo: .caption) private var smallFontSize: CGFloat = 12 + @ScaledMetric(relativeTo: .body) private var buttonFontSize: CGFloat = 16 + var body: some View { HStack { // Balance display VStack(alignment: .leading, spacing: 2) { Text("BALANCE") - .font(.system(size: 9, weight: .medium, design: .rounded)) + .font(.system(size: labelFontSize, weight: .medium, design: .rounded)) .foregroundStyle(.white.opacity(0.6)) .tracking(1) HStack(spacing: 4) { Text("$") - .font(.system(size: 14, weight: .bold)) + .font(.system(size: currencyFontSize, weight: .bold)) .foregroundStyle(.yellow.opacity(0.8)) Text(balance, format: .number) - .font(.system(size: 20, weight: .black, design: .rounded)) + .font(.system(size: balanceFontSize, weight: .black, design: .rounded)) .foregroundStyle(.white) .contentTransition(.numericText()) .animation(.spring(duration: 0.3), value: balance) @@ -495,9 +524,9 @@ struct TopBarView: View { if showCardsRemaining { HStack(spacing: 4) { Image(systemName: "rectangle.portrait.on.rectangle.portrait.fill") - .font(.system(size: 12)) + .font(.system(size: smallFontSize)) Text("\(cardsRemaining)") - .font(.system(size: 12, weight: .medium)) + .font(.system(size: smallFontSize, weight: .medium)) } .foregroundStyle(.white.opacity(0.5)) @@ -507,7 +536,7 @@ struct TopBarView: View { // Settings button Button("Settings", systemImage: "gearshape.fill", action: onSettings) .labelStyle(.iconOnly) - .font(.system(size: 16)) + .font(.system(size: buttonFontSize)) .foregroundStyle(.white.opacity(0.6)) .padding(8) .background( @@ -517,7 +546,7 @@ struct TopBarView: View { // Reset button Button("Reset", systemImage: "arrow.counterclockwise", action: onReset) - .font(.system(size: 12, weight: .medium)) + .font(.system(size: smallFontSize, weight: .medium)) .foregroundStyle(.white.opacity(0.6)) .padding(.horizontal, 10) .padding(.vertical, 6) @@ -538,25 +567,31 @@ struct ActionButtonsView: View { let onClear: () -> Void let onNewRound: () -> Void + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .body) private var clearButtonFontSize: CGFloat = 14 + @ScaledMetric(relativeTo: .headline) private var primaryButtonFontSize: CGFloat = 16 + @ScaledMetric(relativeTo: .body) private var statusFontSize: CGFloat = 14 + var body: some View { HStack(spacing: 12) { if gameState.currentPhase == .betting { // Clear bets button Button("Clear", systemImage: "xmark.circle", action: onClear) - .font(.system(size: 14, weight: .semibold)) + .font(.system(size: clearButtonFontSize, weight: .semibold)) .foregroundStyle(.white) .padding(.horizontal, 20) .padding(.vertical, 12) .background( Capsule() - .fill(Color(red: 0.6, green: 0.2, blue: 0.2)) + .fill(Color.Button.destructive) ) - .opacity(gameState.currentBets.isEmpty ? 0.5 : 1.0) + .opacity(gameState.currentBets.isEmpty ? Design.Opacity.disabled : 1.0) .disabled(gameState.currentBets.isEmpty) // Deal button Button("Deal", systemImage: "play.fill", action: onDeal) - .font(.system(size: 16, weight: .bold)) + .font(.system(size: primaryButtonFontSize, weight: .bold)) .foregroundStyle(.black) .padding(.horizontal, 32) .padding(.vertical, 12) @@ -564,22 +599,19 @@ struct ActionButtonsView: View { Capsule() .fill( LinearGradient( - colors: [ - Color(red: 1.0, green: 0.85, blue: 0.3), - Color(red: 0.9, green: 0.7, blue: 0.2) - ], + colors: [Color.Button.goldLight, Color.Button.goldDark], startPoint: .top, endPoint: .bottom ) ) ) - .shadow(color: .yellow.opacity(0.3), radius: 6) - .opacity(gameState.canDeal ? 1.0 : 0.5) + .shadow(color: .yellow.opacity(Design.Opacity.light), radius: Design.Shadow.radiusMedium) + .opacity(gameState.canDeal ? 1.0 : Design.Opacity.disabled) .disabled(!gameState.canDeal) } else if gameState.currentPhase == .roundComplete { // New round button Button("New Round", systemImage: "arrow.right.circle", action: onNewRound) - .font(.system(size: 16, weight: .bold)) + .font(.system(size: primaryButtonFontSize, weight: .bold)) .foregroundStyle(.black) .padding(.horizontal, 32) .padding(.vertical, 12) @@ -587,16 +619,13 @@ struct ActionButtonsView: View { Capsule() .fill( LinearGradient( - colors: [ - Color(red: 1.0, green: 0.85, blue: 0.3), - Color(red: 0.9, green: 0.7, blue: 0.2) - ], + colors: [Color.Button.goldLight, Color.Button.goldDark], startPoint: .top, endPoint: .bottom ) ) ) - .shadow(color: .yellow.opacity(0.3), radius: 6) + .shadow(color: .yellow.opacity(Design.Opacity.light), radius: Design.Shadow.radiusMedium) } else { // Playing indicator HStack(spacing: 6) { @@ -604,8 +633,8 @@ struct ActionButtonsView: View { .tint(.white) .scaleEffect(0.8) Text("Dealing...") - .font(.system(size: 14, weight: .medium)) - .foregroundStyle(.white.opacity(0.8)) + .font(.system(size: statusFontSize, weight: .medium)) + .foregroundStyle(.white.opacity(Design.Opacity.heavy)) } .padding(.horizontal, 20) .padding(.vertical, 12) diff --git a/Baccarat/Views/MiniBaccaratTableView.swift b/Baccarat/Views/MiniBaccaratTableView.swift index 56c0c6e..b478ad0 100644 --- a/Baccarat/Views/MiniBaccaratTableView.swift +++ b/Baccarat/Views/MiniBaccaratTableView.swift @@ -12,9 +12,12 @@ struct MiniBaccaratTableView: View { @Bindable var gameState: GameState let selectedChip: ChipDenomination + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .caption) private var tableLimitsFontSize: CGFloat = Design.BaseFontSize.small + // MARK: - Layout Constants - private let tableLimitsFontSize = Design.FontSize.small private let tieZoneHeight: CGFloat = 55 private let mainZoneHeight: CGFloat = 60 private let tieHorizontalPadding: CGFloat = 50 @@ -198,11 +201,14 @@ struct TieBettingZone: View { var isAtMax: Bool = false let action: () -> Void + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .headline) private var titleFontSize: CGFloat = Design.BaseFontSize.medium + @ScaledMetric(relativeTo: .caption2) private var subtitleFontSize: CGFloat = Design.BaseFontSize.xSmall + // MARK: - Layout Constants private let cornerRadius = Design.CornerRadius.small - private let titleFontSize = Design.FontSize.medium - private let subtitleFontSize = Design.FontSize.xSmall private let chipTrailingPadding = Design.Spacing.small // MARK: - Computed Properties @@ -264,11 +270,14 @@ struct BankerBettingZone: View { var isAtMax: Bool = false let action: () -> Void + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .headline) private var titleFontSize: CGFloat = Design.BaseFontSize.large + @ScaledMetric(relativeTo: .caption2) private var subtitleFontSize: CGFloat = Design.BaseFontSize.xSmall + // MARK: - Layout Constants private let cornerRadius = Design.CornerRadius.medium - private let titleFontSize = Design.FontSize.large - private let subtitleFontSize = Design.FontSize.xSmall private let chipTrailingPadding = Design.Spacing.medium private let selectionShadowRadius = Design.Shadow.radiusSmall @@ -346,11 +355,14 @@ struct PlayerBettingZone: View { var isAtMax: Bool = false let action: () -> Void + // MARK: - Scaled Font Sizes (Dynamic Type) + + @ScaledMetric(relativeTo: .headline) private var titleFontSize: CGFloat = Design.BaseFontSize.large + @ScaledMetric(relativeTo: .caption2) private var subtitleFontSize: CGFloat = Design.BaseFontSize.xSmall + // MARK: - Layout Constants private let cornerRadius = Design.CornerRadius.medium - private let titleFontSize = Design.FontSize.large - private let subtitleFontSize = Design.FontSize.xSmall private let chipTrailingPadding = Design.Spacing.medium private let selectionShadowRadius = Design.Shadow.radiusSmall @@ -426,11 +438,12 @@ struct ChipOnTable: View { var showMax: Bool = false // MARK: - Layout Constants + // Fixed sizes: chip face has strict space constraints private let chipSize = Design.Size.chipSmall private let innerRingSize: CGFloat = 26 private let gradientEndRadius: CGFloat = 20 - private let maxBadgeFontSize = Design.FontSize.xxSmall + private let maxBadgeFontSize = Design.BaseFontSize.xxSmall private let maxBadgeOffsetX: CGFloat = 6 private let maxBadgeOffsetY: CGFloat = -4 @@ -451,7 +464,7 @@ struct ChipOnTable: View { } private var textFontSize: CGFloat { - amount >= 1000 ? Design.FontSize.small : 11 + amount >= 1000 ? Design.BaseFontSize.small : 11 } // MARK: - Body