moon-element

Standard Web Components written in MoonBit. Registered as custom elements, so they embed in any page or framework.

1 · In-component events + attribute-driven rendering

The + / - buttons are rendered inside the component and handled there through on_action + data-act: the adapter binds a click listener to the shadow root, which survives re-rendering, and the value stays within 0..max. The page-side buttons next to it drive the same state from outside with el.setAttribute("count", n). Either path re-renders the component and notifies the page through an event declared as crossing (bubbles + composed). An invalid value is not an error; it falls back to the default.

2 · shadow DOM style isolation

the page's .pill (painted pink by page styles)

The page gives .pill and span aggressive styles, yet the .pill inside the component is untouched: shadow DOM blocks them.

3 · HTML escaping

The markup still shows up as literal text: the component escapes its text nodes and never executes user input as tags.

4 · Composition and live updates

5 · Isolation without shadow DOM (light DOM mode)

the page's .pill (painted pink by page styles)

The contrast is visible: the component's .pill stays light grey (its selector is rewritten to x-panel .pill, which outranks the page's .pill), while the page's .pill is still pink.

The mechanism differs, and so do the limits: shadow DOM isolates structurally, so page rules cannot get in at all, while light DOM isolation only raises specificity with a prefix. This page's span { letter-spacing: .3em } still reaches the component (its <span> is spaced out too), and a page rule with !important overrides the component. Light DOM suits SSR output, or hosts that want their styles to bleed in.