Skip to content

v4 #694

@titouanmathis

Description

@titouanmathis

Vision

v3: a micro-framework for writing JS components.
v4: a toolkit of HTML-first meta-components for adding interactivity.

HTML becomes the primary interface. Custom JS is only needed for complex cases (Slider, Drag, etc.). Meta-components (Action, Fetch, DataBind, Transition) cover 80% of use cases directly from markup.

Principles:

  • 0 runtime dependencies
  • HTML-first, progressive enhancement, Light DOM
  • What the browser does natively, we do not reimplement
  • The registry is the catalog of components available to HTML

Architectural changes

$emit — bubbling events (#630)

$emit dispatches a standard CustomEvent with bubbles: true and an __source property referencing the emitting instance:

$emit(event, ...args) {
  const e = new CustomEvent(event, { bubbles: true, detail: args });
  e.__source = this;
  this.$el.dispatchEvent(e);
}
  • Events bubble up the DOM natively — enables event delegation
  • __source identifies the emitting instance for on{Child}{Event} resolution
  • Action keeps working as-isdata-on:fetch-before listens to fetch-before which bubbles normally
  • $on / $off keep working as-is — direct listener on $el

on{Child}{Event} — event delegation

The parent listens on this.$el instead of binding on each child. The handler walks up from event.target to this.$el, looking for mounted instances.

handler(event) {
  let el = event.target;
  while (el && el !== this.$el) {
    if (el.__base__) {
      for (const [name, instance] of el.__base__) {
        if (instance === "terminated") continue;
        const method = `on${name}${pascalCase(event.type)}`;
        if (typeof this[method] === "function") {
          this[method]({ event, target: instance });
          return;
        }
      }
    }
    el = el.parentElement;
  }
}
  • el.__base__ is a Map<string, Base> keyed by config.name (already implemented on develop)
  • Dynamically added children work automatically (no binding/unbinding needed)
  • The parent still needs config.components to know which events to listen to (resolving method name ambiguity: onSliderDragStartSliderDrag + Start)
  • mouseenter/mouseleave do not bubble — these keep direct binding on the child element (accepted limitation)

$parent$closest(name)

$parent is removed. Replaced by an explicit query:

// v3
this.$parent.goNext();

// v4
this.$closest("Slider").goNext();

$children$query(name)

// v3
for (const item of this.$children.SliderItem) { ... }

// v4
for (const item of this.$query("SliderItem")) { ... }

Sugar for queryComponentAll(name, { from: this.$el }) (already implemented as helper, #629).

createApp removed

The registry + MutationObserver handle everything. Features are configured separately (#691):

// v4
import { defineFeatures, registerComponent } from "@studiometa/js-toolkit";
defineFeatures({ breakpoints: { ... } });
registerComponent(MyComponent);

No root "App" component required. Each component is independent.

SafeAction — CSP-safe declarative commands

New component alongside Action. No new Function(), just declarative method calls:

<!-- Action (CSP-unsafe, kept for backwards compat) -->
<button data-component="Action" data-on:click="Modal->target.open()">

<!-- SafeAction (CSP-safe, recommended) -->
<button data-component="SafeAction" data-on:click="Modal.open">
<button data-component="SafeAction" data-on:click="Modal(#my-modal).open">
<button data-component="SafeAction" data-on:click="Modal(.sidebar).close">

Format: Component[(selector)].method — parsed with a simple regex, resolved via the registry.


Breaking changes

  • Make custom events bubble up by default with __source annotation ([Feature] Enable bubbling and cancelation for custom events #630)
  • Refactor EventsManager — event delegation on this.$el instead of binding on each child
  • Remove createApp (replaced by registerComponent + defineFeatures)
  • Remove $parent property (replaced by $closest(name))
  • Remove $root property
  • Remove $children property (replaced by $query(name))
  • Remove LoadService
  • Remove KeyService
  • Remove ability to configure the blocking feature
  • Maybe remove prefix and attributes feature configuration (to reduce size)
  • Update default breakpoints to match @studiometa/tailwind-config
  • Make ResponsiveOptionsManager the default
  • Config merge strategy for refs ([Base] Maybe improve config merge strategy? #627) — merge by default instead of override

New features

Meta-components integrated into core

Some components from @studiometa/ui are promoted to the core framework:

  • Action — event → JS expression on a target (CSP-unsafe)
  • SafeAction — event → declarative command on a target (CSP-safe)
  • Fetch — HTTP request → DOM swap
  • Transition — enter/leave animations
  • DataBind, DataModel, DataEffect, DataComputed — reactive data binding

UI components removed (replaced by web platform)

These components no longer need to exist in @studiometa/ui:

Component Native replacement
Modal <dialog> + showModal() + Invoker Commands (commandfor)
Accordion / AccordionItem <details> + <summary>
AnchorScrollTo scroll-behavior: smooth + <a href="#id">
Sticky position: sticky
Figure loading="lazy"
Hoverable CSS :hover + :has()
Prefetch <link rel="prefetch"> + Speculation Rules API
Sentinel IntersectionObserver (trivial)

Services

Service v4 status
RafService ✅ Keep — read/write scheduling has no native equivalent
DragService ✅ Keep — custom drag + inertia has no native equivalent
ScrollService ✅ Keep — aggregation + throttle still useful
ResizeService ⚠️ Simplify — recommend ResizeObserver
PointerService ⚠️ Simplify
LoadService 🗑 Remove
KeyService 🗑 Remove
MutationService ⚠️ Internal use only (registry)

Already done on develop (v3.4.x)

Migration path

Phase 1 — v3.x (non-breaking)

Phase 2 — v4 (breaking)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions