DEV Community

Tosiiko
Tosiiko

Posted on

# Next MDL Update: Security-First Adapters and HTMX Support

In the last update, I introduced MDL as an HTML-first language for building websites and apps with less noise.

This update is about the next layer: adapters.

The goal is simple:

MDL source should describe intent. Adapters decide how that intent becomes deployable HTML.

Why adapters?

I don’t want MDL to become locked into one frontend framework.

Instead, the same MDL structure should be able to target different deployment styles:

  • static HTML
  • MDL-native runtime attributes
  • HTMX
  • future template or framework adapters

So this:

form@api(post /api/login)@result(loginResult)@swap(replace):
  .input@type(email)@required
  .btn-primary@type(submit)(Sign in)

  status@id(loginResult):
    Waiting.
Enter fullscreen mode Exit fullscreen mode

Can become plain HTML:

html

<form method="post" action="/api/login">
Enter fullscreen mode Exit fullscreen mode

Or HTMX:

html

<form hx-post="/api/login" hx-target="#loginResult" hx-swap="outerHTML">
Enter fullscreen mode Exit fullscreen mode

Security before convenienceThe important part is that MDL does not pass behavior attributes through blindly.
Raw HTMX attributes like this are blocked:

mdl

form@hx-post(/api/login):
Enter fullscreen mode Exit fullscreen mode

Instead, MDL uses intent-based attributes:

mdl

form@api(post /api/login):
Enter fullscreen mode Exit fullscreen mode

Then the adapter validates and translates it.
Current safety rules include:
external api(...) URLs are rejected
raw @hx-* attributes are blocked
raw browser events like onclick(...) are blocked
unsafe URL schemes like javascript: are blocked
broad form inclusion like @params() is blocked
@inherit(
) is blocked
@disinherit(*) is allowed because disabling inherited behavior is safer
That means MDL can support HTMX without making MDL source depend directly on HTMX’s full raw surface area.
New HTMX adapter attributesThe HTMX v2 adapter now supports more behavior attributes:

mdl

@select-oob(...)
@swap-oob(...)
@disabled(...)
@disinherit(...)
@encoding(...)
@history-elt(...)
@inherit(...)
@params(...)
@preserve(...)
@prompt(...)
@replace(...)
@request(...)
@sync(...)
@validate(...)
Enter fullscreen mode Exit fullscreen mode

Example:

mdl

form@api(post /api/profile)@result(profileResult)@swap(replace)@params(email csrfToken)@disabled(saveButton):
  .input@name(email):
  .input@name(csrfToken):
  .btn-primary@id(saveButton)@type(submit)(Save)

  status@id(profileResult):
    Waiting.
Enter fullscreen mode Exit fullscreen mode

With the HTMX adapter, MDL can emit:

html

<form
  hx-post="/api/profile"
  hx-target="#profileResult"
  hx-swap="outerHTML"
  hx-params="email,csrfToken"
  hx-disabled-elt="#saveButton">
Enter fullscreen mode Exit fullscreen mode

Notice that @params(email csrfToken) is explicit. MDL does not allow @params(*), because accidentally sending extra form fields is exactly the kind of thing a language layer should help prevent.

Top comments (0)