Core Concepts
Coralite is built on a "Native Web" philosophy. We believe that the most performant and maintainable web applications are those that embrace standard DOM APIs and minimize framework abstraction layers like the Virtual DOM.
Smart State, Dumb Template #
In Coralite, we follow a strict separation of concerns called Smart State, Dumb Template. This approach eliminates the "Virtual DOM tax" by performing direct token replacement during rendering.
- Smart State: Your component's logic is responsible for preparing a flat, unified
stateobject. Whether the data comes from an HTML attribute, a database query on the server, or a derived calculation, it all ends up in a single reactive proxy. - Dumb Template: HTML templates are strictly declarative. They do not support JavaScript logic, loops, or complex expressions (e.g.,
{{ user.name }}or{{ count + 1 }}are forbidden). They simply use{{ state_key }}placeholders for flat keys. This allows Coralite to perform lightning-fast updates without the overhead of a diffing engine.
The Reactivity Engine #
Coralite's reactivity is powered by a sophisticated Proxy-based system that manages the flow of data between your server-side data, your derived logic, and your client-side interactions.
+-------------------+ +-------------------+
| Attributes | | Server State |
| (HTML Attributes) | | (Async Fetching) |
+---------+---------+ +---------+---------+
| |
+---------------+---------------+
|
+--------v--------+
| Unified State | <-----------+
| (Reactive Proxy)| |
+--------+--------+ |
| |
+---------------+---------------+ | (triggers)
| | |
+-------v-------+ +-------^------+-------+
| Template | | Getters |
| (Static HTML) | | (Derived, Read-Only) |
+---------------+ +----------------------+
|
| (imperative control)
|
+-------v-------+
| Client |
| (Read / Write)| -- (mutates state) --> [Unified State]
+---------------+
There are two primary ways to interact with state:
- Read-Only Access (Getters): The
gettersblock provides a safe environment for deriving data. It receives a Read-Only Proxy, ensuring that calculating a value (like a filtered list) can never accidentally trigger a side-effect or an infinite loop. - Read/Write Access (Client): The
clientblock is your component's controller. It receives a full Read/Write Proxy, allowing you to imperatively update state in response to user events.
Lazy Deep Reactivity #
A common performance bottleneck in modern frameworks is the cost of making large objects or arrays reactive. Coralite solves this with Lazy Deep Reactivity.
Instead of recursively walking your entire data structure the moment it's assigned, Coralite only wraps nested objects in a Proxy the exact moment they are accessed. If your component has a list of 1,000 users but only renders the first 10, you only pay the reactivity cost for those 10 users. This makes Coralite exceptionally efficient for data-heavy applications.
Native Web Standards #
Coralite doesn't invent new ways to solve old problems. We lean on the platform:
- Communication: Components talk to each other using standard
CustomEvent. - Lifecycle: Cleanup is handled via
AbortSignal, the standard way to cancel ongoing operations in JavaScript. - Packaging: Components are authored as HTML Modules, leveraging the proposed standard for better encapsulation.
- Separation of Concerns: Pages are strictly consumers of components. All logic is reserved for components, ensuring that pages remain clean, declarative, and easy to maintain.
Pages as Consumers #
A fundamental architectural principle in Coralite is that Pages are consumers, not controllers. This means:
- No Page-Level Logic: Standard
.htmlpages should not contain<script>tags that attempt to query or manipulate components. - Encapsulated Interaction: Any interaction with the DOM must happen within a component's
clientblock using therefsystem. Attempting to select an element inside a component from a page script is an anti-pattern. - Parent Coordination: If you find yourself needing to coordinate logic between two separate components on a page, those components should be wrapped in a "parent" component which encapsulates that coordination logic.