# Settings View Styling Guide A comprehensive guide to achieving the polished casino-style settings interface with golden accents, proper section separation, and consistent styling. ## Table of Contents 1. [Overview](#overview) 2. [The Complete Stack](#the-complete-stack) 3. [Step 1: Set Up Design Constants](#step-1-set-up-design-constants) 4. [Step 2: Use SheetContainerView](#step-2-use-sheetcontainerview) 5. [Step 3: Structure with SheetSection](#step-3-structure-with-sheetsection) 6. [Step 4: Use Proper Settings Components](#step-4-use-proper-settings-components) 7. [Step 5: Apply Consistent Colors](#step-5-apply-consistent-colors) 8. [Complete Example](#complete-example) 9. [Color Reference](#color-reference) 10. [Troubleshooting](#troubleshooting) --- ## Overview The polished casino settings interface you see in Blackjack and Baccarat is achieved through a combination of: 1. **SheetContainerView** - Dark background, proper navigation bar styling 2. **SheetSection** - Icon + title headers with card-like content containers 3. **CasinoKit components** - Pre-styled toggles, pickers, and selectable rows 4. **Color.Sheet constants** - Consistent golden accent and background colors 5. **Design constants** - Standardized spacing, corner radius, and opacity values ### Key Styling Elements - **Golden accent color**: `Color.Sheet.accent = Color(red: 0.9, green: 0.75, blue: 0.3)` - **Dark blue background**: `Color.Sheet.background = Color(red: 0.08, green: 0.12, blue: 0.18)` - **Section cards**: White opacity fill with rounded corners - **Radio buttons**: Golden checkmark circles for selected items - **Proper spacing**: xxLarge (24pt) between sections --- ## The Complete Stack ### Required Components from CasinoKit ```swift import CasinoKit ``` **Views:** - `SheetContainerView` - Outer container with navigation and dark background - `SheetSection` - Section container with icon/title header and card - `SelectableRow` - Radio button rows for pickers - `SelectionIndicator` - Golden checkmark circles - `SettingsToggle` - Toggle switches with titles and subtitles - `SpeedPicker`, `VolumePicker`, `BalancePicker` - Specialized pickers - `BadgePill` - Badge for displaying values like "$10 - $1,000" **Constants:** - `CasinoDesign` - Spacing, corner radius, opacity, font sizes, etc. - `Color.Sheet` - Sheet-specific colors (background, accent, etc.) ### Local Design Constants Setup Create a `DesignConstants.swift` file in your app that imports and typealias CasinoKit constants: ```swift import SwiftUI import CasinoKit enum Design { // Import shared constants via typealias 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 BaseFontSize = CasinoDesign.BaseFontSize typealias IconSize = CasinoDesign.IconSize // Your game-specific constants enum Size { static let cardWidth: CGFloat = 90 // ... other game-specific sizes } } ``` This allows you to write `Design.Spacing.large` instead of `CasinoDesign.Spacing.large` throughout your app. --- ## Step 1: Set Up Design Constants ### 1.1 Create DesignConstants.swift Create a file called `DesignConstants.swift` in your app's `Theme/` folder: ```swift // // DesignConstants.swift // YourGame // import SwiftUI import CasinoKit enum Design { // 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: - Game-Specific Sizes enum Size { // Add your game-specific sizes here static let cardWidth: CGFloat = 90 } } // MARK: - App Colors extension Color { // Import CasinoKit table colors typealias Table = CasinoTable // Add game-specific colors here } ``` ### 1.2 Why This Matters By typealiasing `CasinoDesign`, you get: - Consistent spacing throughout your app - Proper opacity values for layering - Standardized corner radii - Matching font sizes **Don't hardcode values!** Use constants instead: ❌ **Bad:** ```swift .padding(24) .opacity(0.1) .cornerRadius(12) ``` ✅ **Good:** ```swift .padding(Design.Spacing.xxLarge) .opacity(Design.Opacity.subtle) .clipShape(.rect(cornerRadius: Design.CornerRadius.large)) ``` --- ## Step 2: Use SheetContainerView ### 2.1 Basic Structure `SheetContainerView` provides the foundation: dark background, navigation bar, and done button. ```swift import SwiftUI import CasinoKit struct SettingsView: View { @Environment(\.dismiss) private var dismiss var body: some View { SheetContainerView( title: "Settings", content: { // Your sections go here }, onCancel: nil, // Optional cancel button onDone: { dismiss() }, doneButtonText: "Done" ) } } ``` ### 2.2 What SheetContainerView Provides **Automatic Styling:** - Dark blue background: `Color.Sheet.background` - NavigationStack with inline title - Golden "Done" button: `Color.Sheet.accent` - Optional "Cancel" button - Toolbar with proper dark scheme - ScrollView with proper vertical spacing **Key Parameters:** - `title` - The navigation bar title - `content` - A `@ViewBuilder` closure for your sections - `onCancel` - Optional cancel action (if nil, no cancel button) - `onDone` - Done button action - `doneButtonText` - Text for done button (default: "Done") - `cancelButtonText` - Text for cancel button (default: "Cancel") ### 2.3 Section Spacing SheetContainerView automatically adds `xxLarge` (24pt) spacing between sections: ```swift VStack(spacing: CasinoDesign.Spacing.xxLarge) { content // Your sections } ``` This creates the clean separation you see between sections. --- ## Step 3: Structure with SheetSection ### 3.1 Basic SheetSection Each logical group of settings should be in a `SheetSection`: ```swift SheetSection(title: "DISPLAY", icon: "eye") { // Settings content here } ``` ### 3.2 What SheetSection Provides **Header:** - Icon in golden color with opacity - Title in uppercase, bold, rounded font - Subtle spacing and padding **Content Card:** - White opacity background (`Color.Sheet.sectionFill`) - Rounded corners (`CornerRadius.large`) - Proper padding - Horizontal margins **Visual Structure:** ``` ┌─────────────────────────────────────┐ │ 👁 DISPLAY │ ← Header (icon + title) │ ┌─────────────────────────────────┐ │ │ │ │ │ ← Content card │ │ [Settings content here] │ │ (white opacity fill) │ │ │ │ │ └─────────────────────────────────┘ │ └─────────────────────────────────────┘ ``` ### 3.3 Section Best Practices **Use semantic grouping:** - GAME STYLE - Game variants and modes - RULES - Rule customization - TABLE LIMITS - Betting limits - DECK SETTINGS - Shoe configuration - STARTING BALANCE - Balance picker - DISPLAY - Visual settings - SOUND - Audio settings - DATA - Reset and export **Choose appropriate icons:** ```swift SheetSection(title: "GAME STYLE", icon: "suit.club.fill") SheetSection(title: "RULES", icon: "list.bullet.clipboard") SheetSection(title: "TABLE LIMITS", icon: "banknote") SheetSection(title: "DECK SETTINGS", icon: "rectangle.portrait.on.rectangle.portrait") SheetSection(title: "STARTING BALANCE", icon: "dollarsign.circle") SheetSection(title: "DISPLAY", icon: "eye") SheetSection(title: "SOUND", icon: "speaker.wave.2.fill") SheetSection(title: "DATA", icon: "externaldrive") ``` --- ## Step 4: Use Proper Settings Components ### 4.1 SelectableRow (Radio Button Rows) Use `SelectableRow` for picker options with radio buttons: ```swift SheetSection(title: "DECK SETTINGS", icon: "rectangle.portrait.on.rectangle.portrait") { VStack(spacing: Design.Spacing.small) { SelectableRow( title: "1 Deck", subtitle: "Single deck, higher variance", isSelected: deckCount == 1, accentColor: Color.Sheet.accent, action: { deckCount = 1 } ) SelectableRow( title: "6 Decks", subtitle: "Standard casino shoe", isSelected: deckCount == 6, accentColor: Color.Sheet.accent, action: { deckCount = 6 } ) } } ``` **What SelectableRow Provides:** - Title in white, large font - Subtitle in white with medium opacity - Optional badge (e.g., "$10 - $1,000") - Golden checkmark circle when selected - Outlined circle when not selected - Golden border when selected - Subtle golden background fill when selected - Proper padding and rounded corners **With Badge:** ```swift SelectableRow( title: "Low Stakes", subtitle: "Standard mini table", isSelected: true, accentColor: Color.Sheet.accent, badge: { BadgePill(text: "$10 - $1,000", isSelected: true) }, action: { } ) ``` ### 4.2 SettingsToggle (Toggle Switches) Use `SettingsToggle` for on/off options: ```swift SheetSection(title: "DISPLAY", icon: "eye") { VStack(spacing: Design.Spacing.small) { SettingsToggle( title: "Show Animations", subtitle: "Card dealing animations", isOn: $showAnimations, accentColor: Color.Sheet.accent ) Divider().background(Color.white.opacity(Design.Opacity.hint)) SettingsToggle( title: "Show Hints", subtitle: "Basic strategy suggestions", isOn: $showHints, accentColor: Color.Sheet.accent ) } } ``` **What SettingsToggle Provides:** - Title in large font, white - Subtitle in body font, white with medium opacity - Toggle switch with golden accent color - Proper vertical alignment - Minimum touch target height (44pt) ### 4.3 Specialized Pickers **SpeedPicker:** ```swift SpeedPicker(speed: $dealingSpeed, accentColor: Color.Sheet.accent) ``` **VolumePicker:** ```swift VolumePicker(volume: $soundVolume, accentColor: Color.Sheet.accent) ``` **BalancePicker:** ```swift BalancePicker(balance: $startingBalance, accentColor: Color.Sheet.accent) ``` ### 4.4 Dividers Between Items Use dividers to separate items within a section: ```swift VStack(spacing: Design.Spacing.small) { SettingsToggle(title: "Option 1", isOn: $option1, accentColor: accent) Divider() .background(Color.white.opacity(Design.Opacity.hint)) SettingsToggle(title: "Option 2", isOn: $option2, accentColor: accent) } ``` **Divider opacity levels:** - `Design.Opacity.hint` (0.2) - Very subtle, recommended - `Design.Opacity.subtle` (0.1) - Almost invisible - `Design.Opacity.light` (0.3) - More prominent --- ## Step 5: Apply Consistent Colors ### 5.1 Use Color.Sheet Constants **Always use `Color.Sheet.accent` for highlights:** ```swift private let accent = Color.Sheet.accent // Then use it everywhere: SelectableRow(..., accentColor: accent) SettingsToggle(..., accentColor: accent) SpeedPicker(..., accentColor: accent) ``` ### 5.2 Color Constants Available **From CasinoKit:** ```swift Color.Sheet.background // Dark blue: Color(red: 0.08, green: 0.12, blue: 0.18) Color.Sheet.accent // Gold: Color(red: 0.9, green: 0.75, blue: 0.3) Color.Sheet.sectionFill // White.opacity(0.1) Color.Sheet.cardBackground // White.opacity(0.05) Color.Sheet.secondaryText // White.opacity(0.6) Color.Sheet.cancelText // White.opacity(0.7) ``` ### 5.3 Text Colors **Primary text (titles):** ```swift .foregroundStyle(.white) ``` **Secondary text (subtitles, descriptions):** ```swift .foregroundStyle(.white.opacity(Design.Opacity.medium)) // or .foregroundStyle(Color.Sheet.secondaryText) ``` **Accent highlights:** ```swift .foregroundStyle(Color.Sheet.accent) ``` ### 5.4 Background Colors **Section card fill:** ```swift .background( RoundedRectangle(cornerRadius: Design.CornerRadius.large) .fill(Color.Sheet.sectionFill) ) ``` **Selection highlight:** ```swift .background( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .fill(isSelected ? accent.opacity(Design.Opacity.subtle) : .clear) ) ``` --- ## Complete Example Here's a complete settings view demonstrating all the concepts: ```swift // // SettingsView.swift // YourGame // import SwiftUI import CasinoKit struct SettingsView: View { @Bindable var settings: GameSettings @Environment(\.dismiss) private var dismiss @State private var showClearDataAlert = false /// Accent color for settings components private let accent = Color.Sheet.accent var body: some View { SheetContainerView( title: "Settings", content: { // 1. Table Limits SheetSection(title: "TABLE LIMITS", icon: "banknote") { TableLimitsPicker(selection: $settings.tableLimits) } // 2. Deck Settings SheetSection(title: "DECK SETTINGS", icon: "rectangle.portrait.on.rectangle.portrait") { DeckCountPicker(selection: $settings.deckCount) } // 3. Starting Balance SheetSection(title: "STARTING BALANCE", icon: "dollarsign.circle") { BalancePicker(balance: $settings.startingBalance, accentColor: accent) } // 4. Display SheetSection(title: "DISPLAY", icon: "eye") { VStack(spacing: Design.Spacing.small) { SettingsToggle( title: "Show Animations", subtitle: "Card dealing animations", isOn: $settings.showAnimations, accentColor: accent ) if settings.showAnimations { Divider() .background(Color.white.opacity(Design.Opacity.hint)) SpeedPicker(speed: $settings.dealingSpeed, accentColor: accent) } Divider() .background(Color.white.opacity(Design.Opacity.hint)) SettingsToggle( title: "Show Hints", subtitle: "Strategy suggestions", isOn: $settings.showHints, accentColor: accent ) } } // 5. Sound SheetSection(title: "SOUND", icon: "speaker.wave.2.fill") { VStack(spacing: Design.Spacing.small) { SettingsToggle( title: "Sound Effects", subtitle: "Play game sounds", isOn: $settings.soundEnabled, accentColor: accent ) if settings.soundEnabled { Divider() .background(Color.white.opacity(Design.Opacity.hint)) VolumePicker(volume: $settings.soundVolume, accentColor: accent) } Divider() .background(Color.white.opacity(Design.Opacity.hint)) SettingsToggle( title: "Haptics", subtitle: "Vibration feedback", isOn: $settings.hapticsEnabled, accentColor: accent ) } } // 6. Data SheetSection(title: "DATA", icon: "externaldrive") { Button { showClearDataAlert = true } label: { HStack { VStack(alignment: .leading, spacing: Design.Spacing.xxSmall) { Text("Clear All Data") .font(.system(size: Design.BaseFontSize.large, weight: .semibold)) .foregroundStyle(.red) Text("Reset progress and statistics") .font(.system(size: Design.BaseFontSize.body)) .foregroundStyle(.white.opacity(Design.Opacity.medium)) } Spacer() Image(systemName: "trash") .foregroundStyle(.red) } .padding() .background( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .fill(.clear) ) .overlay( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .strokeBorder(Color.white.opacity(Design.Opacity.subtle), lineWidth: Design.LineWidth.thin) ) } .buttonStyle(.plain) } // Version info Text("YourGame v1.0 (1)") .font(.system(size: Design.BaseFontSize.body)) .foregroundStyle(.white.opacity(Design.Opacity.light)) .frame(maxWidth: .infinity) .padding(.top, Design.Spacing.large) .padding(.bottom, Design.Spacing.medium) }, onCancel: nil, onDone: { settings.save() dismiss() } ) .alert("Clear All Data?", isPresented: $showClearDataAlert) { Button("Cancel", role: .cancel) { } Button("Clear", role: .destructive) { // Clear data logic } } message: { Text("This will delete all saved progress and statistics. This cannot be undone.") } } } // MARK: - Custom Pickers struct TableLimitsPicker: View { @Binding var selection: TableLimits var body: some View { VStack(spacing: Design.Spacing.small) { ForEach(TableLimits.allCases) { limit in SelectableRow( title: limit.displayName, subtitle: limit.description, isSelected: selection == limit, accentColor: Color.Sheet.accent, badge: { BadgePill( text: "$\(limit.minBet) - $\(limit.maxBet)", isSelected: selection == limit ) }, action: { selection = limit } ) } } } } struct DeckCountPicker: View { @Binding var selection: Int var body: some View { VStack(spacing: Design.Spacing.small) { ForEach([1, 2, 4, 6, 8], id: \.self) { count in SelectableRow( title: "\(count) Deck\(count == 1 ? "" : "s")", subtitle: subtitleFor(count: count), isSelected: selection == count, accentColor: Color.Sheet.accent, action: { selection = count } ) } } } private func subtitleFor(count: Int) -> String { switch count { case 1: return "Single deck, higher variance" case 2: return "Lower house edge" case 4: return "Common shoe game" case 6: return "Standard casino" case 8: return "Maximum penetration" default: return "" } } } ``` --- ## Color Reference ### Complete Color.Sheet Definitions From `CasinoKit/Sources/CasinoKit/Theme/CasinoDesign.swift`: ```swift public extension Color { enum Sheet { /// Dark background for sheets and popups. public static let background = Color(red: 0.08, green: 0.12, blue: 0.18) /// Subtle fill for section cards. public static let sectionFill = Color.white.opacity(CasinoDesign.Opacity.subtle) /// Card background in settings/sheets. public static let cardBackground = Color.white.opacity(CasinoDesign.Opacity.verySubtle) /// Accent color for buttons and highlights (gold). public static let accent = Color(red: 0.9, green: 0.75, blue: 0.3) /// Secondary text color. public static let secondaryText = Color.white.opacity(CasinoDesign.Opacity.accent) /// Cancel button color. public static let cancelText = Color.white.opacity(CasinoDesign.Opacity.strong) } } ``` ### Opacity Reference ```swift public enum Opacity { public static let verySubtle: Double = 0.05 // Barely visible backgrounds public static let subtle: Double = 0.1 // Section fills public static let selection: Double = 0.15 // Selection highlights public static let hint: Double = 0.2 // Dividers, borders public static let quarter: Double = 0.25 // Quarter opacity public static let light: Double = 0.3 // Light text, borders public static let overlay: Double = 0.4 // Modal overlays public static let medium: Double = 0.5 // Subtitles, secondary info public static let secondary: Double = 0.5 // Same as medium public static let disabled: Double = 0.5 // Disabled controls public static let accent: Double = 0.6 // Accent text public static let strong: Double = 0.7 // Cancel buttons public static let heavy: Double = 0.8 // Icons, strong text public static let nearOpaque: Double = 0.85 // Almost solid public static let almostFull: Double = 0.9 // Very solid } ``` ### Common Color Patterns **Selected row:** ```swift .background( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .fill(accent.opacity(Design.Opacity.subtle)) // 0.1 ) .overlay( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .strokeBorder(accent.opacity(Design.Opacity.medium), lineWidth: Design.LineWidth.thin) // 0.5 ) ``` **Unselected row:** ```swift .background( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .fill(.clear) ) .overlay( RoundedRectangle(cornerRadius: Design.CornerRadius.medium) .strokeBorder(Color.white.opacity(Design.Opacity.subtle), lineWidth: Design.LineWidth.thin) // 0.1 ) ``` --- ## Troubleshooting ### Issue: Colors don't look golden **Problem:** Accent colors appear white or wrong color. **Solution:** Ensure you're passing `Color.Sheet.accent` to the `accentColor` parameter: ```swift // ❌ Wrong SelectableRow(..., accentColor: .yellow) // ✅ Correct private let accent = Color.Sheet.accent SelectableRow(..., accentColor: accent) ``` ### Issue: Sections not separated properly **Problem:** Sections appear cramped together. **Solution:** Ensure you're using `SheetContainerView` which automatically adds `xxLarge` (24pt) spacing: ```swift SheetContainerView(title: "Settings", content: { SheetSection(title: "SECTION 1", icon: "icon1") { ... } SheetSection(title: "SECTION 2", icon: "icon2") { ... } // Auto-spaced by xxLarge (24pt) }) ``` ### Issue: Section headers not styled properly **Problem:** Section headers missing icon, wrong font, or poor spacing. **Solution:** Use `SheetSection`, not custom VStack: ```swift // ❌ Wrong VStack { Text("DISPLAY") // content } // ✅ Correct SheetSection(title: "DISPLAY", icon: "eye") { // content } ``` ### Issue: Radio buttons not golden **Problem:** Selection indicators are white or blue circles. **Solution:** Pass `accentColor: Color.Sheet.accent` to `SelectableRow`: ```swift SelectableRow( title: "Option", subtitle: "Description", isSelected: true, accentColor: Color.Sheet.accent, // ← Must include this action: { } ) ``` ### Issue: Background is wrong color **Problem:** Background is white, gray, or wrong shade of blue. **Solution:** Use `SheetContainerView` which sets `Color.Sheet.background` automatically: ```swift // ❌ Wrong var body: some View { NavigationStack { ScrollView { // sections } .background(Color.gray) // Wrong! } } // ✅ Correct var body: some View { SheetContainerView(title: "Settings", content: { // sections - background automatically applied }) } ``` ### Issue: Dividers are too prominent or invisible **Problem:** Dividers within sections are too thick or can't be seen. **Solution:** Use white with `Design.Opacity.hint` (0.2): ```swift Divider() .background(Color.white.opacity(Design.Opacity.hint)) ``` ### Issue: Text not readable **Problem:** Text appears dark gray or hard to read. **Solution:** Always use `.foregroundStyle(.white)` for primary text: ```swift Text("Title") .foregroundStyle(.white) Text("Subtitle") .foregroundStyle(.white.opacity(Design.Opacity.medium)) ``` ### Issue: Spacing inconsistent **Problem:** Some spacing is 10pt, some 12pt, some 15pt. **Solution:** Always use `Design.Spacing` constants: ```swift VStack(spacing: Design.Spacing.small) { // 8pt // items } .padding(Design.Spacing.medium) // 12pt ``` ### Issue: Missing import **Problem:** `SheetContainerView`, `SelectableRow`, or other components not found. **Solution:** Add `import CasinoKit` to the top of your file: ```swift import SwiftUI import CasinoKit // ← Required for CasinoKit components ``` ### Issue: Design constants not available **Problem:** `Design.Spacing`, `Design.Opacity`, etc. not found. **Solution:** Create `DesignConstants.swift` with typealias to CasinoKit constants (see Step 1). --- ## Quick Checklist Use this checklist to ensure your settings view has the proper styling: - [ ] Import CasinoKit at top of file - [ ] Create local `DesignConstants.swift` with typealiases - [ ] Wrap entire settings view in `SheetContainerView` - [ ] Each logical group in a `SheetSection` with icon and title - [ ] Use `SelectableRow` for radio button options - [ ] Use `SettingsToggle` for on/off switches - [ ] Pass `accentColor: Color.Sheet.accent` to all components - [ ] Use `Design.Spacing.small` between items in a section - [ ] Use `Divider().background(Color.white.opacity(Design.Opacity.hint))` between items - [ ] Use `.foregroundStyle(.white)` for primary text - [ ] Use `.foregroundStyle(.white.opacity(Design.Opacity.medium))` for secondary text - [ ] No hardcoded spacing, opacity, or corner radius values - [ ] Version info at bottom with subtle opacity --- ## Summary The polished casino settings interface is achieved through: 1. **SheetContainerView** for the dark background and navigation 2. **SheetSection** for icon headers and card-like content containers 3. **SelectableRow** for radio button pickers with golden checkmarks 4. **SettingsToggle** for toggle switches 5. **Color.Sheet.accent** for consistent golden highlights 6. **Design constants** for spacing, opacity, and font sizes 7. **Proper text colors** (white for primary, white with opacity for secondary) 8. **Subtle dividers** with 0.2 opacity between items Follow this guide and your settings will match the professional casino aesthetic! 🎰✨