mmo.core + mmo.client · v3

Template, logic and style
in one compiled file.

MMO is a full framework, not a component kit bolted onto someone else's build tool: a PHP application layer (mmo.core) and a native Web Component runtime (mmo.client) that share one manifest, one dependency graph, and one file format — .jhs.

No node, no bundler, no watcher Native web components One manifest, front and back
mmo-progress.jhs src/customElements/elements/ shipped
<mmo-progress value> <div id="content"> <div id="bar"></div> <div id="text"><slot></slot></div> </div> <div id="percent"></div> <script> class Progress { /** @param {number} value - between 0 and 1 */ set value(value) { if ((value > 1) || (value < 0)) { throw new RangeError('value must be between 0 and 1.'); } this.view.content.set('value', value); this.queryShadow('#bar').style.width = Math.floor(value * 100) + '%'; } } </script> <style> #content { @use lib::surface($radius.sm); padding: $space.xs; box-shadow: $shadow.sm; } // the filled part, which grows from the left #bar { background: $color.brand; color: $color.text-on-brand; border-radius: $radius.xs 0 0 $radius.xs; transition: width 0.5s ease-in; } </style> </mmo-progress>
1 file

markup, the element class and its styles, per component — compiled to one .js

0 deps

no dart, no java, no node, no watcher: .jhs, Simple CSS and the javascript bundle all compile in php

53 bytes

the css prelude an element pays, instead of a second copy of the design system

Source: src/JHSCompiler.php, src/JSCompiler.php, src/styles/shadow.scs.

Anatomy

One file, three languages

Every custom element is a single .jhs file, compiled by MMO\client\JHSCompiler, in PHP. The file above is one; here are its three regions, one at a time.

template
real markup, which becomes the element's shadow dom.
script
the element class, ES2022 — the compiler gives it its base class, its observed attributes and a render() built from the markup.
style
scoped Simple CSS, written against the same design tokens the document declares.
Nothing is hand-wired The root tag's attributes become the observed attributes, the class name is found by the compiler, and the JSDoc above it becomes the element's own documentation page — the one you are reading under Custom Elements.
Data

Reactive without the ceremony

<for path="config/values"> <label title="{label}"> <input type="radio" value="{=$key}" /> <if test="{icon}"> <mmo-icon name="{icon}">{label}</mmo-icon> </if> <else> <span>{label}</span> </else> </label> </for>

Every element and view carries a View over a Map. Bind a template path to it once and reads, writes, loops and conditionals all resolve against the same live object — no virtual DOM, no separate state library to wire in.

this.view.watch
re-runs a callback whenever a bound path changes — that's your only render loop.
{path} / bind-text
bindings resolved against the current scope: {path} for markup you wrote, bind-text for a value a user typed, set as text.
<for> / <if>
structural directives, not string templates — they compile to real DOM operations.
$this / $key
the element instance and the loop key, dropped straight into event handlers.
Text by default Since 3.1, a string handed to MMO.core.dom.createElement, a template or a message of the shell (notify, alert, confirm…) is a text node. Markup is what you ask for, with MMO.core.dom.html() — never what a user's input turns into.
Library

A component library that ships with the framework

Calendars, charts, trees, a PDF viewer, a Swagger explorer, a code editor — shipped, themed, and built from the exact same primitives you'll use for your own elements.

Layout

mmo-app mmo-grid mmo-tabs mmo-carousel mmo-workflow mmo-boarding

Inputs

mmo-table mmo-select mmo-radio mmo-address mmo-query mmo-filter mmo-palette

Modules

mmo-calendar mmo-markdown mmo-pdf mmo-swagger mmo-code mmo-console mmo-graphviz mmo-accordion

Elements & tools

mmo-chart mmo-tree mmo-tooltip mmo-notification mmo-inspector mmo-sandbox mmo-specimen

Browse them live

Shell

The whole application in one element

<mmo-app> is the chrome of this very site: the menu, the breadcrumb, the table of contents, the notifications and the search. A page only brings its content.

Three templates

admin, landing and blog, picked by the host's class: one shell for a back office, a product site and a journal — each in light and dark, from the same tokens.

One search box

Alt / opens the command palette: pages, commands and every remote source — the documentation included — grouped in one list.

A developer drawer

The status bar opens the journal of the request and, in dev, the server: php, its extensions and every package included.

See the three templates

Styling

A styling language we compile ourselves

The style block of a .jhs file is Simple CSS — .scs, a thin compile-time layer over css with variables, design tokens, reusable styles, functions, nesting and responsive blocks. The lexer, the parser, the evaluator and the emitter are php, so a stylesheet compiles anywhere the framework runs.

@theme { color {brand: $brand.s500; surface: $palette.n0;} space {md: 12px; lg: 16px;} } @style surface($radius = $radius.md) { background: $color.surface; border: 1px solid $color.line; border-radius: $radius; } .card { @use lib::surface($radius.md); padding: $space.lg; }

Spacing, colour, radius and elevation are names, not memorised utility classes, and the recipes built from them are fragments a rule inlines. Change the brand ramp once and everything follows: buttons, links, focus rings, charts, the active menu item, the dark theme.

@theme
the only construct that emits runtime css variables
@style / @use
a block of declarations, inlined where it is used
lib:: / controls::
a file is a namespace
@screen / @order
named breakpoints, explicit cascade order

A shadow root never sees the document's css — but it does inherit its custom properties. So the document declares the tokens once and an element only references them: 53 bytes of prelude per element instead of a second, drifting copy of the design system. The same recipes paint both sides, which is why a control looks identical whether it was written in a page or inside an element.

Tokens — the vocabulary

$color.brand $color.surface $space.md $radius.sm $text.sm $shadow.md $motion.fast $layout.control

Styles — the recipes

lib::surface($radius.md) lib::col($space.sm) lib::control($layout.control) lib::interactive lib::truncate controls::button controls::input controls::table controls::tabs controls::tag($color.ok-soft)

And a whole design system comes with it — buttons, forms, tables, lists, cards, panels, tabs, callouts, overlays, prose, marketing and editorial sections, in light and dark. Every class, every variant and the html that produces it is listed under Design system; the language itself is under Simple CSS.

Reskin it without writing css The theme configurator edits every value the tokens declare — ramps, scales, both themes — with a live preview, and writes the sources back as .scs: a colour derived from the brand stays derived.
Packages

One manifest ties the backend to the browser

{ "name": "mmo.client", "namespace": "MMO\\client", "dependencies": { "mmo.core": "20.*" }, "documentations": ["docs"], "bootstrap": "src/bootstrap.php", "build": "build/{name}.{version}.phar" }

Every MMO package — mmo.core, mmo.client, your own app — declares itself in a single mmo.json: dependencies, bootstrap entry, i18n, docs and version, resolved together by the same PackageManager.

The backend is a real application framework, not a thin API shim: entities and repositories, an HTTP router and controllers, schemas, an event system — all resolvable from the same package graph that ships your UI.

Entities & repositories Routing & controllers Schemas Events Versioned .phar builds

And the libraries built on it, each one a package like your own:

mmo.core mmo.client mmo.doc mmo.admin mmo.git mmo.pdo mmo.mongodb mmo.oauth mmo.pdf mmo.ocr mmo.math
What this server is running Open the console from the status bar and take the serveur tab: php, its extensions and every package the framework included, straight from AdminController.
Ship

From a commit to production, and back

The framework carries its own release tooling. This site is deployed by it, to a shared webspace that only lets ssh in.

php scripts/admin.php plan --targets=demo php scripts/admin.php release --targets=demo php scripts/admin.php rollback --targets=demo

mmo.admin

Targets in deploy.json, one immutable release built from a git ref, an atomic symlink swap, a health check and a one-command rollback — on a server of yours or a shared webspace.

mmo.doc

The reference documentation built from the source itself: PHP and JavaScript doc comments, markdown guides, runnable examples.

Tests

PHPUnit for every library, and the whole JavaScript runtime under Node: npm test, no browser needed.

Tooling

Your framework already speaks MCP

Every package's mmo.json already lists its documentations folder. Drop markdown in it, and the framework's own MCP server — full JSON-RPC 2.0, in MCPController — discovers and serves it automatically. So do the agent skills a package ships, for the PHP side and for .jhs: an assistant reads how the framework is written before it writes any of it.

search_documentation

Full-text search over every package's markdown, documented symbols first.

documentation_index

Every namespace, class, function, element and guide mmo.doc documented.

describe_class

Introspects any class on the manifest graph: parent, role, public methods.

list_routes

Every connected #[APIRoute]: method, url, operation id.

get_entity_schema

An entity's full JSON Schema, straight from its class.

generate_controller_stub

A new controller that follows the framework's conventions, as text.

Connect your AI to MMOFramework

That server runs in public at https://mcp.mmoframework.fr/, with the documentation and the API of every mmo.* library. Point an MCP client at it and your assistant reads the framework's guide, searches the documentation and introspects its classes — instead of guessing from its training data. Read-only: nothing it exposes writes a file or a record.

MCP server url https://mcp.mmoframework.fr/mcp.json — JSON-RPC 2.0 over http (POST), no authentication.

Claude Code

Run once, from any folder: the server is then available in every session.

claude mcp add --transport http mmoframework https://mcp.mmoframework.fr/mcp.json

Claude Desktop & claude.ai

Settings › Connectors › Add custom connector: name it, paste the url, no authentication.

https://mcp.mmoframework.fr/mcp.json

Cursor, VS Code & others

Any client speaking MCP over http: add it to the client's MCP configuration (.mcp.json, .cursor/mcp.json, .vscode/mcp.json…).

{ "mcpServers": { "mmoframework": { "type": "http", "url": "https://mcp.mmoframework.fr/mcp.json" } } }

Read the source. Then read the docs it writes about itself.

Everything above is real, running code — not a curated highlight reel. The full API is generated straight from that source by mmo.doc.