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.
Can become plain HTML:
html
<form method="post" action="/api/login">
Or HTMX:
html
<form hx-post="/api/login" hx-target="#loginResult" hx-swap="outerHTML">
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):
Instead, MDL uses intent-based attributes:
mdl
form@api(post /api/login):
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(...)
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.
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">
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)