MPA-first · TypeScript-native · DOM-direct

Build for the web.
Stay close to it.

Maya turns TypeScript pages into static HTML, then brings them alive with signals that update the exact DOM nodes you intended—no virtual DOM, hydration, or app server in the way.

Try the live Signal demo
100% TypeScript UI0 virtual DOM3 app targets
TypeScript inDOM alive
01page.tsm.Html(…)
build
02Brahmastatic output
mount
03Real DOMexact updates
STATIC SHELL

No virtual tree between your code and the interface.

A smaller conceptual stack for

STATIC-FIRSTSIGNAL-POWEREDCOMPONENT-DRIVENEDGE-READY

The browser platform,
written in TypeScript.

Maya’s templating syntax and NoCSS styling source are TypeScript. Brahma turns them into static pages, page-local JavaScript, and the generated stylesheet.

ONE LANGUAGE

A page is just a typed expression.

Markup, behavior, and composition stay together in TypeScript. Native element factories keep the shape familiar while the compiler keeps it honest.

Explore Maya syntax →
page.ts
import { m } from "@cyftec/maya/core";
import { signal, tmpl } from "@cyftec/maya/signals";
import { css } from "./assets/styles.js";

const count = signal(0);

export default m.Button({
  class: css("pointer ph3 pv2 br2"),
  onclick: () => count.value++,
  children: tmpl`Count: ${count}`,
});

Real pages.
Real DOM.

Maya is MPA-first. Brahma writes static pages, Maya mounts the real DOM, and signals keep the parts that need to move precisely reactive.

MULTI-PAGE BY DEFAULT

Files become pages—not routes in disguise.

A Maya app uses independent static documents, generated NoCSS, and page-local JavaScript with no client router required.

MPA map3 routes · 0 routers
dev/page.ts/index.html
dev/docs/page.ts/docs/index.html
dev/about.page.ts/about.html
static pagesreactive islands

COMPONENTS + DOM

Compose freely. Reach the node directly.

Component-driven architecture does not put the browser behind a framework escape hatch. Element getters resolve to actual DOM nodes.

direct-dom.ts
const panel = m.Article({
  children: "Native DOM node",
});

m.Button({
  onclick: () =>
    panel().toggleAttribute("data-open"),
  children: "Toggle panel",
});

CONTROL COMPONENTS

Say If, For, and Switch when that is what you mean.

Built-in control components replace noisy conditional markup. Use them for clarity—or use ordinary TypeScript whenever it fits better.

state
Iftruthy / falsy
Forkeyed lists
Switchnamed cases

Readable control flow is part of the element tree.

Change the data.
Only the right node moves.

Signals are small reactive data units that notify the computations that read them. Maya uses those effects to surgically update a text node, child position, or attribute—not re-render an entire component tree.

interactive playgroundno virtual DOM

TRY IT NOW

Make the DOM move.Tap the bright button and watch the highlighted values update instantly.
source12
exact text node ×224
<main> <aside>untouched</aside> <output>24</output></main>

Convenient at author-time.
Careful at run-time.

Maya keeps its low-level promises visible: browser attributes remain typed, keyed nodes keep their identity, and removed subtrees release their reactive work.

TYPED BROWSER CONTRACTS

Autocomplete for the platform—not an invented prop API.

Tag-specific attribute grammars guide valid values in TypeScript. NoCSS validates application classes, while runtime sanitizers reject dangerous href schemes.

typed-elements.ts
m.Input({
  type: "email",
  required: true,
});

m.Button({ type: "submit" });
m.Link({ rel: "stylesheet" });

// runtime URL sanitization
m.A({ href: trustedUrl });

KEYED BY IDENTITY

Move the list without throwing its nodes away.

Give m.For an itemKey and Maya preserves matching element getters through shuffles and updates, while per-item signals carry the changed data.

before
#a · Alpha
#b · Beta
#c · Gamma
shuffle
after
#c · Gamma
#a · Alpha
#b · Beta

same nodes · new positions

LIFECYCLE-AWARE

Reactive work ends when its DOM leaves.

When onunmount is used, Maya observes removed subtrees, calls lifecycle listeners from the deepest child upward, and disposes the signal effects attached to those elements.

<section> <card> <button /> </card></section>
onunmountcleanup completelistener calledeffects disposed

MutationObserver walks removed subtrees deepest-first.

Brahma handles the journey.
Karma keeps the map.

Create, continuously develop, build, and publish Maya apps from one dedicated CLI—with project behavior and NoCSS generation controlled by typed configuration.

BRAHMA CLI

From empty folder to publishable app.

Spawn a project, install its declared dependencies, run a watched staging server, and emit minified production files.

Open Brahma docs →
terminal
$ brahma create hello-maya
✓ web scaffold created

$ cd hello-maya && brahma install
✓ dependencies synchronized from karma.ts

$ brahma stage
→ watching dev/ at localhost:3000

$ brahma publish
✓ static production build ready

WEB · PWA · EXT

Choose the shell. Keep the same mental model.

Brahma creates web, PWA, or Chrome extension scaffolds. Manifests stay in TypeScript, so platform fields come with autocomplete and type checking.

Webstatic MPA
PWAtyped manifest
ExtensionManifest V3
one TypeScript toolchain

KARMA.TS

The configuration center, not another config pile.

Build paths, NoCSS source, serve behavior, dependencies, TypeScript, Git ignores, and editor settings live in one project-level TypeScript file.

karma.ts
export const karma = {
  brahma: {
    build: {
      publishDir: "docs",
      buildableStylesheetFileName: "styles.ts",
      assetsDirName: "assets",
    },
    serve: { port: 3000 },
  },
  maya: {
    appType: "web",
    dependencies: { "@cyftec/maya": "0.2.1" },
    devDependencies: { typescript: "7.0.2" },
  },
};

Ship the app.
Skip the app server.

Brahma emits static HTML, generated NoCSS, and page-local JavaScript before deployment. The reactive interface is ready for static hosting without an application server.

See the production workflow →
BUILDBrahma publishHTML · CSS · JS
CDNedge-ready
GHPages-ready
S3static-ready

$0 app server required

2.04× React throughput

A direct path pays off.

In the Maya Benchmark’s 10,000-row keyed mutation test, Maya’s exact-node updates deliver 188,000 DOM operations per second.

Open the full benchmarkMaya Benchmark v1 · Chromium 140 · Apple M3 Pro · median of 50 runs. Exact result set used by the linked benchmark site.
DOM operations / secondhigher is better
Maya
188k
Svelte
151k
Vue
119k
React
92k
Angular
74k
050k100k150k200k

START CLOSE TO THE PLATFORM

Build something that feels
like the web—because it is.