Skip to content

The Frontend ​

In Carotene, the frontend block defines your client-side environment. This is where you declare your UI components, manage local memory, and handle offline data caching.

Carotene treats the frontend declaratively. You define what the UI should render and what data it needs, and the compiler generates the reactive boilerplate, the routing trees, and the API clients.

Cross-Platform Generation (Targets) ​

Carotene is truly language-agnostic. You do not need to write separate contracts for your web app and your mobile app. By using the config targets array, you instruct the compiler to generate multiple frontends from a single architectural blueprint.

dart
frontend {
  // Generates a React web app, a SwiftUI iOS app, and a Kotlin Android app simultaneously
  config { targets: ["react", "swift", "kotlin"] }
  
  view Dashboard() { ... }
}

When the AI implements the Dashboard view, Carotene provisions three separate sandbox environments, prompting the AI to write idiomatic JSX for the web, SwiftUI for iOS, and Jetpack Compose for Android—all guaranteed to share the exact same data pipeline.

Local State vs. Offline Storage ​

Modern frontends require complex data management. Carotene strictly separates volatile memory from persistent offline caching, ensuring the AI cannot accidentally wipe user data on a page refresh.

  • state (Volatile RAM): Generates a reactive memory container. For Web, this becomes Redux, Zustand, or Pinia. For iOS, it becomes ObservableObject / Environment state. It is cleared when the app closes.
  • store (Persistent Disk): Generates durable local storage. For Web, this defaults to IndexedDB. For Mobile, it defaults to SQLite. This is used for offline-first architectures.
dart
frontend {
  // 1. Session Memory (RAM)
  state {
    model UIState { sidebarOpen: Boolean { default: false } }
  }

  // 2. Offline Cache (Disk)
  store {
    model DraftPost { id: UUID, content: String }
  }

  function SaveDraft() {
    creates store.DraftPost
  }
}

UI Composition: The view Primitive ​

A view is a declarative UI component. You use behavioral verbs to dictate exactly what data the component can read, what child components it mounts, and where it can navigate.

Passing Data (reads and renders) ​

To prevent "prop drilling" and unpredictable state mutations, Carotene enforces explicit data passing. If a view needs data, it must read it from the state or have it passed down via renders.

dart
frontend {
  view UserProfile() {
    // Subscribes the view to the reactive state
    reads state.CurrentUser
    
    // Explicitly passes data down to the child component
    renders Avatar(url: state.CurrentUser.avatarUrl)
    renders SettingsButton()
  }

  view Avatar(url: String) {
    // The AI generates an image component using the provided URL prop
  }
}

Routing (navigates) ​

You do not write routing files (like react-router configs). The compiler builds the routing tree automatically by tracing the navigates verb.

dart
  view SettingsButton() {
    // Tells the compiler to generate a route link to the Settings view
    navigates Settings
  }

Connecting to the Backend ​

The frontend communicates with the backend seamlessly. You do not write fetch calls, manage Axios instances, or write WebSocket handshakes. The compiler generates fully typed client SDKs.

  • Synchronous Calls (calls): Triggers a standard HTTP/RPC request to the backend. The generated code automatically handles loading states, error catching, and JWT injection.
  • Real-Time Streams (observes): Subscribes to a backend stream. The compiler automatically generates the WebSocket connection and handles reconnection logic.
dart
frontend {
  view LiveScoreboard() {
    // Auto-generates a WebSocket client and subscribes to the stream
    observes backend.MatchTick
    
    // Auto-generates a fetch client for a standard POST request
    calls backend.SubmitScore
  }
}

AI Implementation Boundaries ​

When the AI acts as your "excavator" to build out the frontend components, the Carotene contract ensures it cannot break the architecture:

  1. No Rogue State: If the AI tries to write const [localState, setLocalState] = useState() for shared data, the Carotene linter flags it. Shared data must live in the state block.
  2. No Rogue Network Calls: If the AI tries to fetch('https://api.github.com') inside a button click, the compiler blocks it. All external network calls must be routed through the backend or an explicitly defined integration.
  3. Guaranteed Props: The AI knows exactly what variables it has access to because they are strictly defined in the view signature (e.g., view Avatar(url: String)).