CascadeIntroductioninfoGetting StartedConvenient UsageAdvanced Usage
Examples
Reactive FormFocus StoreToolbar EllipsisHybrid Modal DialogWord ProcessorHydrationJSXProgrammatic Reactive LayoutRecursive ExampleAnimationDrawerFile SystemThemespalette
Getting Started

Getting Started

From an empty folder to a running Cascade app, with a logotype and a counter - this one, running right here:

Cascade

Hello Cascade

Edit HelloWorld.js and save - the page follows.

1. Create a project

Any bundler that handles ES modules will do - here, a fresh Vite project. One package brings in the rest of Cascade with it:

npm create vite@latest hello-cascade -- --template vanilla
cd hello-cascade
npm install @liquefy/cascade.ui

You can delete what Vite put in src/ as an example - counter.js, style.css and assets/.

2. The page

Replace index.html with this - all it needs is an element for the app to render into:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Hello Cascade</title>
  </head>
  <body style="margin: 0; padding: 24px; font-family: sans-serif;">
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

3. The render target and the service locator

Two things tie an app to the page. The render target is where it all goes: here, the #app element. The service locator, provided to every component through the render context, is where components get what they build with - here, the basic theme, providing a themed widget when they ask for button(). Nothing in an app names a theme: change the service locator, and the whole app changes with it.

In src/main.js:

import { RenderContext } from "@liquefy/cascade.component";
import { DOMElementTarget } from "@liquefy/cascade.dom";
import { basicTheme } from "@liquefy/cascade.ui";
import { HelloWorld } from "./HelloWorld.js";

// The render target: where it all goes - the #app element in index.html.
const target = DOMElementTarget.forElement(document.getElementById("app"));

// The service locator - here, the basic theme: it provides the themed
// widgets your components ask for (button(), card(), ...). HTML elements
// (div(), h1(), ...) are real DOM elements unless a locator says otherwise.
new HelloWorld().establish().renderOnto(target, new RenderContext({ serviceLocator: basicTheme }));

4. Your first component

In src/HelloWorld.js - the app at the top of this page, as it is. initialState() sets up its state; build() describes what it shows, and runs again whenever something it read changes - only what actually changed is updated in the page. Each run's result is matched to the previous one - the same kind of component in the same place is the same component - so its state and its elements are kept. Only what can move, appear or disappear among its siblings - the items of a list - needs a key.

import { Component, callback } from "@liquefy/cascade.component";
import { h1, img, p, text } from "@liquefy/cascade.dom";
import { button, column } from "@liquefy/cascade.ui";

export class HelloWorld extends Component {
  // State: set up once, then changed by the user - here, by the button.
  initialState() {
    return { count: 0 };
  }

  // Runs again whenever something it read changes (this.count): only what
  // actually changed is updated in the page.
  build() {
    return column(
      {
        style: {
          alignItems: "center", gap: "16px", padding: "40px 24px", borderRadius: "12px",
          background: "#1f2d3b", color: "#e6eef6", textAlign: "center",
        },
      },
      img({
        src: "https://erobwen.github.io/liquefy/cascade-logo.svg",
        alt: "Cascade",
        style: { width: "100%", maxWidth: "320px" },
      }),
      h1({ style: { margin: 0 } }, text("Hello Cascade")),
      button(text("Count is " + this.count), callback("count", () => { this.count++; })),
      p({ style: { margin: 0, opacity: 0.75 } }, text("Edit HelloWorld.js and save - the page follows.")),
    );
  }
}

5. Run it

npm run dev

Open the address it prints - typically http://localhost:5173 - and count.

Next steps

Try the Material theme: install it, and render with it instead of the basic one - the same app, every widget replaced.

npm install @liquefy/cascade.ui.material
import { materialTheme } from "@liquefy/cascade.ui.material";

new HelloWorld().establish().renderOnto(target, new RenderContext({ serviceLocator: materialTheme }));

Using icons (icon(), iconButton())? Link the icon fonts the themes draw them with, in index.html's head:

<!-- Material Symbols: the basic theme's icons. Material Icons: the Material theme's. -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined" rel="stylesheet" />
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />

Every page of this demo has a button in the top bar showing its own code - there's a lot more to see: layout from real measurements, animations, routing, portals, themes and their colors. And each package has a README of its own, from the repository's on.