Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ImLukzy/ChefDash/llms.txt

Use this file to discover all available pages before exploring further.

ChefDash follows a single-source-of-truth architecture: one ObservableObject class called GameState owns every piece of mutable data in the application — the active tab, the player’s coin balance, the level list, the shop inventory, and the live burger stack mid-round. ChefDashApp creates this object once, injects it at the root via .environmentObject(), and every view in the tree reads and writes it through @EnvironmentObject var gameState: GameState with no prop-drilling required.

Top-Level View Hierarchy

ChefDashApp
└── ContentView                   (@EnvironmentObject: GameState)
    ├── HomeView                  (activeTab == "house.fill"  — default)
    ├── MainMapView               (activeTab == "map.fill")
    ├── ShopView                  (activeTab == "cart.fill")
    ├── ProfileView               (activeTab == "profile")
    ├── SettingsView              (activeTab == "settings")
    ├── RecipesView               (activeTab == "kitchen_round")
    └── VictoryView               (activeTab == "level_complete")
ContentView sits between the app entry point and all screen-level views. It is the single place in the codebase where screen routing happens — it reads gameState.activeTab and uses a switch statement to decide which view to render. No NavigationStack or NavigationLink is used for primary navigation; the entire routing model is driven by mutating one @Published string property.

Tab Routing via gameState.activeTab

activeTab is declared in GameState as:
@Published var activeTab: String = "house.fill"
ContentView switches on this value to render the correct screen:
switch gameState.activeTab {
case "cart.fill":
    ShopView()

case "profile":
    ProfileView()

case "settings":
    SettingsView()

case "kitchen_round":
    RecipesView()

case "level_complete":
    VictoryView()

case "map.fill":
    MainMapView()

default:
    HomeView()
}
Any view can navigate to any screen by setting gameState.activeTab to one of the valid string values. The full set of recognised values is:
ValueDestinationTab bar visible?
"house.fill"HomeView✅ Yes
"map.fill"MainMapView✅ Yes
"cart.fill"ShopView✅ Yes
"profile"ProfileView✅ Yes
"settings"SettingsView✅ Yes
"kitchen_round"RecipesView❌ Hidden
"level_complete"VictoryView❌ Hidden

The CustomTabBar

CustomTabBar is a floating, arcade-styled navigation bar rendered inside a ZStack in ContentView, overlaid above all screen content. It is explicitly hidden during kitchen_round and level_complete states so that the gameplay and victory screens are unobstructed:
if gameState.activeTab != "kitchen_round" && gameState.activeTab != "level_complete" {
    CustomTabBar()
        .transition(.move(edge: .bottom).combined(with: .opacity))
        .padding(.bottom, 10)
}
The tab bar exposes exactly three navigation destinations — ["house.fill", "map.fill", "cart.fill"] — rendered as SF Symbol icon buttons. Tapping a tab animates the transition with a spring:
withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
    gameState.activeTab = tab
}
Active tabs are highlighted with an orange accent (Color(red: 0.96, green: 0.44, blue: 0.13)) and a subtle circular background at 12% opacity. Inactive tabs use white at 40% opacity, ensuring readability against the dark arcade background. A 4 pt filled dot beneath the icon serves as a minimal active-state indicator.
ChefDashApp sets .preferredColorScheme(.light) on ContentView, but ContentView itself immediately overrides this by calling .preferredColorScheme(.dark) on its root ZStack. The dark override takes precedence in the SwiftUI view tree, so the entire app renders in dark mode regardless of the user’s system appearance setting. This is intentional — the deep-dark arcade background (Color(red: 0.08, green: 0.08, blue: 0.10)) and the floating tab bar are designed exclusively for the dark environment.

GameState as the Single Source of Truth

GameState is an ObservableObject class holding every category of app-wide data as @Published properties. The full surface area is: Navigation
  • activeTab: String — drives the ContentView screen router.
Player Economy & Settings
  • coins: Int — current coin balance (starts at 150).
  • isHapticsEnabled: Bool — haptic feedback toggle.
  • playerName: String — display name (default: "Chef Novato").
  • playerAvatar: String — emoji avatar (default: "👨‍🍳").
Shop Inventory
  • shopInventory: [ShopItem] — the three consumable power-ups; each item tracks quantityOwned.
  • selectedUpgradesForRound: Set<String> — IDs of consumables the player has queued for the next round; cleared and consumed when startNewRound() is called.
Level Progression
  • levels: [Level] — the full array of 20 Level structs, built by setupLevels() at init().
  • selectedLevelIndex: Int — index into levels pointing at the level currently being played or previewed.
Active Round State
  • currentBurgerStack: [String] — emojis the player has tapped so far in the current order.
  • targetRecipeEmojis: [String] — the recipe the player must match.
  • currentRecipeName: String — human-readable name derived from the recipe contents.
  • ordersCompletedInSession: Int — how many orders have been successfully served this round.
  • comboMultiplier: Int — current score multiplier (1–5); increments on success, resets to 1 on error.
  • showPerfectMessage: Bool / showErrorMessage: Bool — transient flags for visual feedback, auto-cleared after 0.8 seconds.
Data Flow Summary
ChefDashApp (owns @StateObject GameState)

    └── .environmentObject(gameState)


        ContentView (@EnvironmentObject GameState)
            │  reads: gameState.activeTab → picks which View to show
            │  renders: CustomTabBar (also reads gameState.activeTab)

            ├── HomeView       → writes gameState.activeTab = "map.fill"
            ├── MainMapView    → writes gameState.selectedLevelIndex
            │                  → writes gameState.activeTab = "kitchen_round"
            ├── RecipesView    → calls gameState.addIngredient(_:)
            │                  → calls gameState.startNewRound()
            │                  → calls gameState.finishRound(starsEarned:)
            ├── VictoryView    → writes gameState.activeTab = "map.fill"
            └── ShopView       → mutates gameState.shopInventory[].quantityOwned
                               → decrements gameState.coins

The Two Model Types

Level

A value-type struct conforming to Identifiable. Each Level is immutable except for isUnlocked and starsEarned, which are var properties updated by GameState.finishRound(starsEarned:).
struct Level: Identifiable {
    let id: Int
    let name: String
    let targetOrders: Int
    let availableIngredients: [String]
    let possibleRecipes: [[String]]
    let baseTime: Int
    var isUnlocked: Bool = false
    var starsEarned: Int = 0
}

ShopItem

A value-type struct conforming to Identifiable, paired with an UpgradeType enum. The quantityOwned var is mutated when the player buys or consumes a power-up.
enum UpgradeType: String, Codable {
    case extraTime      // +15s bonus time (Horno Industrial)
    case comboBooster   // Start with ×2 combo (Cuchillo Afilado)
    case instantServe   // +10 coins per perfect serve (Salsa Secreta)
}

struct ShopItem: Identifiable {
    let id: String
    let title: String
    let description: String
    let emoji: String
    let price: Int
    let type: UpgradeType
    var quantityOwned: Int = 0
}

Color System — GameColors.swift

All shared UI colours are defined as static properties on a Color extension, forming the “Culinary Pop” design system:
PropertyRGBUsage
Color.gameBackground(0.98, 0.97, 0.95)Cream background for light-mode views
Color.gameOrange(0.96, 0.37, 0.05)Primary action buttons (burnt orange)
Color.gameYellow(0.94, 0.69, 0.13)Mustard-yellow grid accents on HomeView
Color.gameCardBackgroundColor.whiteCard surfaces and modal backgrounds
Color.gameTextDark(0.22, 0.12, 0.05)Primary body text (organic dark brown)
Color.gameDarkBrown(0.38, 0.20, 0.11)Price-tag label backgrounds
Color.gameLightGray(0.95, 0.94, 0.92)Ingredient button backgrounds
extension Color {
    static let gameBackground     = Color(red: 0.98, green: 0.97, blue: 0.95)
    static let gameOrange         = Color(red: 0.96, green: 0.37, blue: 0.05)
    static let gameYellow         = Color(red: 0.94, green: 0.69, blue: 0.13)
    static let gameCardBackground = Color.white
    static let gameTextDark       = Color(red: 0.22, green: 0.12, blue: 0.05)
    static let gameDarkBrown      = Color(red: 0.38, green: 0.20, blue: 0.11)
    static let gameLightGray      = Color(red: 0.95, green: 0.94, blue: 0.92)
}
CustomTabBar defines its own local orange accent (Color(red: 0.96, green: 0.44, blue: 0.13)) rather than referencing Color.gameOrange (0.96, 0.37, 0.05). The values are nearly identical but not exactly the same — a minor inconsistency worth noting if you are extending the design system or building new views.

Build docs developers (and LLMs) love