Web Components runtime for MoonBit, with an offscreen layout engine: the same view tree renders to custom elements or to geometry and SVG without a browser.
curl -fsSL https://cli.moonbitlang.com/install/unix.sh | bash # MoonBit toolchain
moon add chen333w/moon-element # this package///|
fn badge_view(props : @core.Props) -> @core.View {
let label = props.get_string("label").unwrap_or("?")
// VText is escaped: <b>hi</b> renders as literal text
@core.View::element("span", [("class", "pill")], [@core.View::text(label)])
}
///|
pub fn register() -> Unit {
@dom.define_component(
"x-badge",
[
@core.AttrSpec::string("label", "?"),
@core.AttrSpec::string("tone", "info"),
],
badge_view,
events=[@core.EventSpec::crossing("badge-changed")],
on_change=fn(id, _name) { @dom.dispatch(id, "badge-changed", "", false) },
)
}<x-badge label="styled by the component" tone="info"></x-badge>
<script type="module" src="./your-bundle.js"></script>moon build --target js --release
python3 -m http.server 8000 # from the repository root
open http://localhost:8000/examples/demo/node examples/demo/verify.mjslet view = @core.View::element("div", [("class", "card")], [
@core.View::element("h2", [], [@core.View::text("Total")]),
@core.View::element("span", [("class", "num")], [@core.View::text("42")]),
])
let css = #|.card { display: flex; padding: 12px; gap: 8px; width: 100%; }
let (style_root, box) = @pipeline.run(view, css, 640.0, @pipeline.measure_monospace)
println(@svg.to_json(style_root, box)) // deterministic geometry, one line per box
println(@svg.to_svg(style_root, box)) // a standalone SVG document| define_component(tag, specs, render_fn, events~, on_change~, styles~, mode~) | registers the element; observedAttributes comes from specs |
| AttrSpec::string/int/double/bool(name, default) | attribute spec; missing or invalid values fall back to the default, booleans are true when the attribute is present |
| Props::get_string/get_int/get_double/get_bool/keys | typed access inside the render function |
| View::element/text/raw/fragment/nothing | the view tree; text and attribute values are escaped, VRaw is not |
| EventSpec::crossing(name) / contained(name) | crossing = bubbles + composed, contained stays inside |
| on_action=[(ActionSpec::new("click", "inc"), handler)] | handles DOM events on elements marked data-act="inc" inside the component, including when a re-render has replaced them |
| set_attribute / remove_attribute | writes back to the component's own attributes, which re-renders it |
| dispatch(id, name, detail, cancelable) | sends an event from a component |
| scope_css(css, scope) | rewrites a :host stylesheet into prefixed form |
| mode | ShadowDom (default) or LightDom; in light mode styles is scoped to the tag name |
| Stylesheet::parse(css) / compute(nodes) | the cascade: selectors, specificity, !important, shorthand expansion, inheritance |
| layout(root, width, measure) | block flow and flexbox geometry as a tree of border boxes |
| to_json / to_svg | deterministic layout dump, and an SVG document |
| pipeline::run(view, css, width, measure) | the whole chain in one call |
moon check --deny-warn --target all # warnings are errors
moon test --target all --deny-warn # wasm, wasm-gc, native, js
moon test --target js --enable-coverage && moon coverage report -f summary
moon run cmd/main -- x-counter # generate a component skeleton (shadow DOM)
moon run cmd/main -- x-panel --mode light # generate a light DOM skeleton
moon build --target js --release # browser-loadable JS
moon run cmd/main -- layout --width 640 # lay the dashboard out headlessly (JSON)
moon run cmd/main -- layout --format svg # ... or emit an SVG document
python3 examples/dashboard/verify_layout.py # 18 checks over the CLI outputcore/ attribute parsing, lifecycle, props, rendering, CSS scoping, events, tag validation
dom/ js-only adapter: DOM callbacks, shadow/light rendering, CustomEvent dispatch
style/ CSS parsing, selector matching, cascade and computed values
layout/ box tree and flexbox geometry, with an injected text measurer
svg/ deterministic layout JSON and SVG output
pipeline/ view tree -> styled nodes -> geometry
examples/demo/ four components, an interactive page and verify.mjs
examples/generated-*/ CLI output committed so CI compiles and drift-checks it
cmd/main/ the CLIInstall
Download zipWeb Components runtime for MoonBit, with an offscreen layout engine: the same view tree renders to custom elements or to geometry and SVG without a browser.