<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tosiiko</title>
    <description>The latest articles on DEV Community by Tosiiko (@tosiiko).</description>
    <link>https://dev.to/tosiiko</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3959814%2Fa5789435-8504-4051-b206-0620d67a1c58.png</url>
      <title>DEV Community: Tosiiko</title>
      <link>https://dev.to/tosiiko</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tosiiko"/>
    <language>en</language>
    <item>
      <title>Every MDL behavior attribute for htmx and TypeScript</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Thu, 11 Jun 2026 21:46:16 +0000</pubDate>
      <link>https://dev.to/tosiiko/every-mdl-behavior-attribute-for-htmx-and-typescript-5401</link>
      <guid>https://dev.to/tosiiko/every-mdl-behavior-attribute-for-htmx-and-typescript-5401</guid>
      <description>&lt;p&gt;MDL has two small behavior paths:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MDL behavior attributes -&amp;gt; htmx hx-* output
MDL event attributes    -&amp;gt; exported JavaScript or TypeScript handlers
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means the source can stay compact:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@api(post /api/profile)@result(profileResult)@swap(replace)@trigger(submit)@loading(profileBusy):
  .input@id(profileName)@name(name)@required
  .btn-primary@type(submit)(Save)
  status@id(profileBusy):
    Saving.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the htmx adapter enabled, MDL emits validated htmx attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-form"&lt;/span&gt;
  &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/api/profile"&lt;/span&gt;
  &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#profileResult"&lt;/span&gt;
  &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;
  &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;
  &lt;span class="na"&gt;hx-indicator=&lt;/span&gt;&lt;span class="s"&gt;"#profileBusy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And with configured JavaScript or TypeScript modules, event attributes call&lt;br&gt;
exported functions by name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;card@id(plannerPanel)@mount(mountPlanner):
  .btn-primary@click(addTask)(Add task)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mountPlanner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MouseEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Enable the htmx adapter
&lt;/h2&gt;

&lt;p&gt;Configure htmx as the behavior adapter in &lt;code&gt;mdl.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"head_scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"https://cdn.jsdelivr.net/npm/htmx.org@2.0.10/dist/htmx.min.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"behavior"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"adapter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"htmx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MDL source should use MDL behavior attributes such as &lt;code&gt;@api(...)&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;@result(...)&lt;/code&gt;, and &lt;code&gt;@swap(...)&lt;/code&gt;. Raw &lt;code&gt;@hx-post(...)&lt;/code&gt;, &lt;code&gt;@hx-target(...)&lt;/code&gt;, and&lt;br&gt;
other raw htmx attributes are not emitted.&lt;/p&gt;
&lt;h2&gt;
  
  
  htmx behavior attribute map
&lt;/h2&gt;

&lt;p&gt;These are the MDL behavior attributes supported by the htmx adapter.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MDL attribute&lt;/th&gt;
&lt;th&gt;htmx output&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@api(get /api/search)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-get="/api/search"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;get&lt;/code&gt;, &lt;code&gt;post&lt;/code&gt;, &lt;code&gt;put&lt;/code&gt;, &lt;code&gt;patch&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@result(searchResults)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-target="#searchResults"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Targets &lt;code&gt;this&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;#id&lt;/code&gt;, or &lt;code&gt;.class&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@swap(inner)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-swap="innerHTML"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;See swap values below&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@trigger(input)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-trigger="input"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Uses a known trigger event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@loading(searchBusy)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-indicator="#searchBusy"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also accepts &lt;code&gt;this&lt;/code&gt; or &lt;code&gt;#id&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@confirm(Save changes?)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-confirm="Save changes?"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain text prompt, max 200 chars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@include(csrfToken)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-include="#csrfToken"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also accepts &lt;code&gt;this&lt;/code&gt; or &lt;code&gt;#id&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@select(resultFragment)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-select="#resultFragment"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Selects one response fragment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@select-oob(toast)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-select-oob="#toast"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Selects one out-of-band fragment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@swap-oob(true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-swap-oob="true"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also accepts known swap values&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@push(false)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-push-url="false"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, or safe root path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@replace(/profile)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-replace-url="/profile"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt;, &lt;code&gt;false&lt;/code&gt;, or safe root path&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@history(false)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-history="false"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@history-elt(true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-history-elt="true"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Emits only for &lt;code&gt;true&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@boost(true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-boost="true"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only on safe local links/forms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@disabled(saveButton)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-disabled-elt="#saveButton"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also accepts &lt;code&gt;this&lt;/code&gt; or &lt;code&gt;#id&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@disinherit(target swap)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-disinherit="hx-target hx-swap"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows known htmx attrs or &lt;code&gt;*&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@encoding(multipart)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-encoding="multipart/form-data"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Also supports urlencoded form encoding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@inherit(trigger)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-inherit="hx-trigger"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Allows known htmx attrs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@params(name email)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-params="name,email"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Explicit names or &lt;code&gt;none&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@preserve(true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-preserve="true"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requires a safe &lt;code&gt;@id(...)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@prompt(Security code?)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-prompt="Security code?"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Plain text prompt, max 200 chars&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@request(timeout=5000)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-request="{&amp;amp;quot;timeout&amp;amp;quot;:5000}"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Timeout from &lt;code&gt;1&lt;/code&gt; to &lt;code&gt;60000&lt;/code&gt; ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@sync(profileForm:queue-last)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-sync="#profileForm:queue last"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Coordinates concurrent requests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@validate(true)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;hx-validate="true"&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Supported &lt;code&gt;@api(...)&lt;/code&gt; methods
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;get
post
put
patch
delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;@api(...)&lt;/code&gt; only emits same-origin-safe paths. External API URLs are not&lt;br&gt;
emitted from this attribute.&lt;/p&gt;
&lt;h2&gt;
  
  
  Supported &lt;code&gt;@swap(...)&lt;/code&gt; values
&lt;/h2&gt;

&lt;p&gt;MDL gives short names to the htmx swap values:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;MDL&lt;/th&gt;
&lt;th&gt;htmx&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inner&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;innerHTML&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inner-html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;innerHTML&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;innerhtml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;innerHTML&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;replace&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outerHTML&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;outer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outerHTML&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;outer-html&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outerHTML&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;outerhtml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;outerHTML&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;append&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;beforeend&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;prepend&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;afterbegin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;before&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;beforebegin&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;after&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;afterend&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;none&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;@swap-oob(...)&lt;/code&gt; supports &lt;code&gt;true&lt;/code&gt; plus the same known swap values. &lt;code&gt;false&lt;/code&gt; is not&lt;br&gt;
emitted for &lt;code&gt;@swap-oob(...)&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Supported &lt;code&gt;@trigger(...)&lt;/code&gt; values
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;submit
click
change
input
load
revealed
intersect
keyup
keydown
focus
blur
mouseenter
mouseleave
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;MDL intentionally does not emit htmx trigger filters or JavaScript-like trigger&lt;br&gt;
expressions from &lt;code&gt;@trigger(...)&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Supported &lt;code&gt;@encoding(...)&lt;/code&gt; values
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;multipart
multipart/form-data
form
urlencoded
application/x-www-form-urlencoded
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;These normalize to either &lt;code&gt;multipart/form-data&lt;/code&gt; or&lt;br&gt;
&lt;code&gt;application/x-www-form-urlencoded&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Supported &lt;code&gt;@sync(...)&lt;/code&gt; strategies
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drop
abort
replace
queue
queue-first
queue-last
queue-all
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You can pass the strategy by itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@sync(queue-last):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or scope it to one explicit target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@sync(profileForm:queue-last):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Supported &lt;code&gt;@inherit(...)&lt;/code&gt; and &lt;code&gt;@disinherit(...)&lt;/code&gt; names
&lt;/h2&gt;

&lt;p&gt;These attributes accept MDL names or htmx names. MDL normalizes them to htmx&lt;br&gt;
attribute names.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;target / result       -&amp;gt; hx-target
swap                  -&amp;gt; hx-swap
trigger               -&amp;gt; hx-trigger
indicator / loading   -&amp;gt; hx-indicator
confirm               -&amp;gt; hx-confirm
include               -&amp;gt; hx-include
select                -&amp;gt; hx-select
select-oob            -&amp;gt; hx-select-oob
swap-oob              -&amp;gt; hx-swap-oob
push / push-url       -&amp;gt; hx-push-url
replace / replace-url -&amp;gt; hx-replace-url
history               -&amp;gt; hx-history
history-elt           -&amp;gt; hx-history-elt
boost                 -&amp;gt; hx-boost
disabled / disabled-elt -&amp;gt; hx-disabled-elt
disinherit            -&amp;gt; hx-disinherit
encoding              -&amp;gt; hx-encoding
inherit               -&amp;gt; hx-inherit
params                -&amp;gt; hx-params
preserve              -&amp;gt; hx-preserve
prompt                -&amp;gt; hx-prompt
request               -&amp;gt; hx-request
sync                  -&amp;gt; hx-sync
validate              -&amp;gt; hx-validate
get                   -&amp;gt; hx-get
post                  -&amp;gt; hx-post
put                   -&amp;gt; hx-put
patch                 -&amp;gt; hx-patch
delete                -&amp;gt; hx-delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@disinherit(*)&lt;/code&gt; is also supported. &lt;code&gt;@inherit(*)&lt;/code&gt; is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure TypeScript handlers
&lt;/h2&gt;

&lt;p&gt;MDL &lt;code&gt;scripts&lt;/code&gt; can point at JavaScript or TypeScript module entries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"scripts/app.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During &lt;code&gt;mdl build&lt;/code&gt;, MDL compiles local &lt;code&gt;.ts&lt;/code&gt; module scripts to browser-ready&lt;br&gt;
JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scripts/app.ts -&amp;gt; dist/scripts/app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated HTML imports the JavaScript URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mdlModule0&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./scripts/app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During &lt;code&gt;mdl serve&lt;/code&gt;, the browser still asks for &lt;code&gt;./scripts/app.js&lt;/code&gt;, and the dev&lt;br&gt;
server serves JavaScript compiled from &lt;code&gt;scripts/app.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Configured TypeScript entries can import local TypeScript trees:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scripts/app.ts
scripts/state/store.ts
scripts/dom/render.ts
scripts/utils/format.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MDL preserves the folder tree in output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dist/scripts/app.js
dist/scripts/state/store.js
dist/scripts/dom/render.js
dist/scripts/utils/format.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Local TypeScript imports may use &lt;code&gt;.ts&lt;/code&gt;, omit the extension, or use the&lt;br&gt;
browser-facing &lt;code&gt;.js&lt;/code&gt; extension:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;snapshot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./state/store.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;renderTaskList&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dom/render.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;formatCount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./utils/format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./state/model.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MDL does not create &lt;code&gt;package.json&lt;/code&gt;, &lt;code&gt;node_modules&lt;/code&gt;, or &lt;code&gt;tsconfig.json&lt;/code&gt; for this.&lt;br&gt;
It is transpile-only behavior support, not a bundler or project type checker.&lt;/p&gt;

&lt;p&gt;Inline TypeScript blocks are not supported yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script ts:
  // not supported yet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use inline JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script js:
  document.body.dataset.ready = "true"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or an external configured &lt;code&gt;.ts&lt;/code&gt; module.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript event attributes
&lt;/h2&gt;

&lt;p&gt;Event attributes compile to &lt;code&gt;data-mdl-on-*&lt;/code&gt; markers, and MDL's generated module&lt;br&gt;
runtime calls exported handlers by name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@submit(createTaskFromForm):
  .input@input(updateDraftPreview)
  .btn-primary@click(saveTask)(Save)

canvas@id(scene)@mount(drawScene):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-form"&lt;/span&gt; &lt;span class="na"&gt;data-mdl-on-submit=&lt;/span&gt;&lt;span class="s"&gt;"createTaskFromForm"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-input"&lt;/span&gt; &lt;span class="na"&gt;data-mdl-on-input=&lt;/span&gt;&lt;span class="s"&gt;"updateDraftPreview"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-btn-primary"&lt;/span&gt; &lt;span class="na"&gt;data-mdl-on-click=&lt;/span&gt;&lt;span class="s"&gt;"saveTask"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-canvas"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"scene"&lt;/span&gt; &lt;span class="na"&gt;data-mdl-on-mount=&lt;/span&gt;&lt;span class="s"&gt;"drawScene"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@mount(handler)&lt;/code&gt; is not a browser event. It runs once after configured modules&lt;br&gt;
are imported and receives the mounted element.&lt;/p&gt;

&lt;p&gt;All other handlers receive the browser event.&lt;/p&gt;
&lt;h2&gt;
  
  
  Complete event alias list
&lt;/h2&gt;

&lt;p&gt;These event attributes can be handled from JavaScript or TypeScript modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@click              @dblclick           @auxclick          @contextmenu
@command
@submit             @reset              @formdata          @beforeinput
@input              @change             @invalid           @search
@select
@compositionstart   @compositionupdate  @compositionend
@focus              @blur               @focusin           @focusout
@keydown            @keypress           @keyup
@mousedown          @mouseup            @mousemove         @mouseover
@mouseout           @mouseenter         @mouseleave
@pointerdown        @pointerup          @pointermove       @pointerover
@pointerout         @pointerenter       @pointerleave      @pointercancel
@pointerrawupdate   @gotpointercapture  @lostpointercapture
@touchstart         @touchmove          @touchend          @touchcancel
@wheel              @scroll             @scrollend
@load               @error              @abort             @resize
@canplay            @canplaythrough     @play              @playing
@pause              @ended              @durationchange    @emptied
@loadeddata         @loadedmetadata     @loadstart         @progress
@ratechange         @seeked             @seeking           @stalled
@suspend            @timeupdate         @volumechange      @waiting
@dragstart          @drag               @dragenter         @dragover
@dragleave          @drop               @dragend
@copy               @cut                @paste
@cuechange          @slotchange
@toggle             @beforetoggle       @beforematch       @close
@cancel             @contextlost        @contextrestored
@securitypolicyviolation
@fullscreenchange   @fullscreenerror
@animationstart     @animationiteration @animationend      @animationcancel
@transitionrun      @transitionstart    @transitioncancel  @transitionend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  TypeScript handler example
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@id(taskForm)@submit(createTaskFromForm):
  .input@id(taskTitle)@name(title)@type(text)@required@input(updateDraftPreview)
  .btn-primary@type(submit)(Add task)

card@id(plannerPanel)@mount(mountPlanner):
  status@id(boardStatus)@aria-live(polite):
    Waiting for TypeScript.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mountPlanner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createTaskFromForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubmitEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;HTMLFormElement&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// Update app state, then render.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateDraftPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preview&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#draftPreview&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  General MDL attribute rules
&lt;/h2&gt;

&lt;p&gt;Normal HTML attributes are still direct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.input@id(email)@name(email)@type(email)@required@autocomplete(email)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That emits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-input"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt; &lt;span class="na"&gt;autocomplete=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core rules are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@attr(value)&lt;/code&gt; emits &lt;code&gt;attr="value"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@attr&lt;/code&gt; emits a boolean attribute.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@class(...)&lt;/code&gt; appends author classes after the generated &lt;code&gt;mdl-*&lt;/code&gt; class.&lt;/li&gt;
&lt;li&gt;Raw browser event attributes such as &lt;code&gt;@onclick(...)&lt;/code&gt; are not emitted.&lt;/li&gt;
&lt;li&gt;Raw htmx attributes such as &lt;code&gt;@hx-post(...)&lt;/code&gt; are not emitted.&lt;/li&gt;
&lt;li&gt;Inline &lt;code&gt;@style(...)&lt;/code&gt; and iframe &lt;code&gt;@srcdoc(...)&lt;/code&gt; are not emitted.&lt;/li&gt;
&lt;li&gt;URL attributes are filtered to safe URL shapes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The reason is boring in the best way: MDL keeps authoring short, but the output&lt;br&gt;
should remain inspectable and hard to accidentally turn into a script sink.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try the examples
&lt;/h2&gt;

&lt;p&gt;The htmx example app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node examples/htmx/server.mjs
target/debug/mdl serve examples/htmx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:4010
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The TypeScript example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;examples/typescript
../../bin/mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:3996
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://getmdl.site" rel="noopener noreferrer"&gt;https://getmdl.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/tosiiko/mdl-code" rel="noopener noreferrer"&gt;https://github.com/tosiiko/mdl-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href="https://www.npmjs.com/package/@tosiiko/mdl" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@tosiiko/mdl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;htmx example: &lt;code&gt;examples/htmx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;TypeScript example: &lt;code&gt;examples/typescript&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Attribute spec: &lt;code&gt;docs/spec/ATTRIBUTES.md&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Script spec: &lt;code&gt;docs/spec/SCRIPTS.md&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MDL is still early, but the shape is already useful: htmx gets validated&lt;br&gt;
hypermedia attributes, TypeScript gets typed behavior modules, and the browser&lt;br&gt;
still receives plain HTML, CSS, and JavaScript.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>htmx</category>
      <category>typescript</category>
      <category>html</category>
    </item>
    <item>
      <title>MDL 0.1.9: optional TypeScript behavior scripts without a TypeScript project setup</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Thu, 11 Jun 2026 20:19:47 +0000</pubDate>
      <link>https://dev.to/tosiiko/mdl-019-optional-typescript-behavior-scripts-without-a-typescript-project-setup-383h</link>
      <guid>https://dev.to/tosiiko/mdl-019-optional-typescript-behavior-scripts-without-a-typescript-project-setup-383h</guid>
      <description>&lt;p&gt;MDL &lt;code&gt;0.1.9&lt;/code&gt; adds optional external TypeScript behavior scripts.&lt;/p&gt;

&lt;p&gt;The important word is optional.&lt;/p&gt;

&lt;p&gt;MDL is still:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.mdl  -&amp;gt; structure and content
.css  -&amp;gt; layout and design
.js   -&amp;gt; optional behavior
HTML  -&amp;gt; compiler output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This release does not turn MDL into a JavaScript-app workflow. It does not make&lt;br&gt;
TypeScript a requirement for normal MDL sites. It simply means that if you want&lt;br&gt;
typed behavior modules, MDL can compile local &lt;code&gt;.ts&lt;/code&gt; files for the browser during&lt;br&gt;
build and serve.&lt;/p&gt;
&lt;h2&gt;
  
  
  What changed
&lt;/h2&gt;

&lt;p&gt;Before this release, an MDL project config could include JavaScript module&lt;br&gt;
scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"scripts/app.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mdl build&lt;/code&gt; copied those files unchanged, and generated HTML imported them as&lt;br&gt;
module scripts.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;0.1.9&lt;/code&gt;, the same &lt;code&gt;scripts&lt;/code&gt; array can also point at a local TypeScript entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"scripts/app.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During &lt;code&gt;mdl build&lt;/code&gt;, MDL transpiles that entry to JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scripts/app.ts -&amp;gt; dist/scripts/app.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generated document imports the browser-ready JavaScript URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"module"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;mdlModule0&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./scripts/app.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During &lt;code&gt;mdl serve&lt;/code&gt;, the page still imports &lt;code&gt;./scripts/app.js&lt;/code&gt;. The dev server&lt;br&gt;
serves that compiled JavaScript from the configured TypeScript source, so the&lt;br&gt;
development URL matches the production URL.&lt;/p&gt;
&lt;h2&gt;
  
  
  TypeScript trees are supported
&lt;/h2&gt;

&lt;p&gt;The first pass is not limited to a single file. A configured TypeScript entry&lt;br&gt;
can import other local TypeScript modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;scripts/app.ts
scripts/state/store.ts
scripts/state/model.ts
scripts/dom/forms.ts
scripts/dom/render.ts
scripts/utils/id.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MDL follows the local static import/export graph, preserves the folder tree, and&lt;br&gt;
emits matching JavaScript modules:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;dist/scripts/app.js
dist/scripts/state/store.js
dist/scripts/state/model.js
dist/scripts/dom/forms.js
dist/scripts/dom/render.js
dist/scripts/utils/id.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Local TypeScript imports may use &lt;code&gt;.ts&lt;/code&gt;, omit the extension, or use the&lt;br&gt;
browser-facing &lt;code&gt;.js&lt;/code&gt; extension. MDL rewrites local TypeScript module specifiers&lt;br&gt;
to emitted &lt;code&gt;.js&lt;/code&gt; URLs.&lt;/p&gt;

&lt;p&gt;For example, this is valid in source:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;snapshot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./state/store.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;renderTaskList&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dom/render.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;byId&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dom/query&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;TaskFilter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./state/model.ts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The emitted JavaScript uses browser-safe JavaScript module URLs for the local&lt;br&gt;
TypeScript files.&lt;/p&gt;

&lt;p&gt;Type-only imports stay type-only. They do not become runtime dependencies.&lt;/p&gt;
&lt;h2&gt;
  
  
  What MDL does not do
&lt;/h2&gt;

&lt;p&gt;This is transpile-only support for behavior modules.&lt;/p&gt;

&lt;p&gt;MDL does not:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scaffold &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;create &lt;code&gt;node_modules&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;create &lt;code&gt;tsconfig.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;require users to install TypeScript&lt;/li&gt;
&lt;li&gt;require deployed sites to carry TypeScript&lt;/li&gt;
&lt;li&gt;bundle npm packages&lt;/li&gt;
&lt;li&gt;type-check the project yet&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bare package imports and external URLs are left alone. MDL is not trying to be&lt;br&gt;
a bundler in this release.&lt;/p&gt;

&lt;p&gt;The TypeScript parser/transpiler lives inside MDL tooling, so a site can use a&lt;br&gt;
configured &lt;code&gt;.ts&lt;/code&gt; behavior module without becoming a TypeScript project.&lt;/p&gt;
&lt;h2&gt;
  
  
  Inline scripts stay JavaScript for now
&lt;/h2&gt;

&lt;p&gt;Inline JavaScript blocks keep working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script js:
  const form = document.querySelector("#loginForm")
  form?.classList.add("ready")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inline TypeScript is still unsupported:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;script ts:
  // not supported yet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For TypeScript, use an external configured module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"scripts/app.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Event bindings still work the same way
&lt;/h2&gt;

&lt;p&gt;Full-document MDL output imports configured modules and binds exported functions&lt;br&gt;
to MDL behavior attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@id(taskForm)@submit(createTaskFromForm):
  .input@id(taskTitle)@name(title)@type(text)@input(updateDraftPreview)
  .btn-primary@type(submit)(Add task)

card@id(plannerPanel)@mount(mountPlanner):
  status@id(boardStatus):
    Waiting for the TypeScript module tree.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your TypeScript module exports the handlers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;mountPlanner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HTMLElement&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;refresh&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Planner mounted from compiled TypeScript.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;createTaskFromForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SubmitEvent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="c1"&gt;// read the form, update state, re-render&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateDraftPreview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// update live preview while the user types&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MDL handles the wiring. The browser receives JavaScript modules.&lt;/p&gt;

&lt;h2&gt;
  
  
  New example: &lt;code&gt;examples/typescript&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This release adds a more advanced TypeScript example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;examples/typescript
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is a small typed task planner with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a configured &lt;code&gt;scripts/app.ts&lt;/code&gt; entry&lt;/li&gt;
&lt;li&gt;nested state, DOM, and utility modules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;import type&lt;/code&gt; usage&lt;/li&gt;
&lt;li&gt;imports that use &lt;code&gt;.ts&lt;/code&gt;, &lt;code&gt;.js&lt;/code&gt;, and extensionless specifiers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@mount&lt;/code&gt;, &lt;code&gt;@submit&lt;/code&gt;, &lt;code&gt;@input&lt;/code&gt;, &lt;code&gt;@change&lt;/code&gt;, and &lt;code&gt;@click&lt;/code&gt; handlers&lt;/li&gt;
&lt;li&gt;dashboard metrics&lt;/li&gt;
&lt;li&gt;task filters&lt;/li&gt;
&lt;li&gt;form parsing&lt;/li&gt;
&lt;li&gt;toast updates&lt;/li&gt;
&lt;li&gt;generated &lt;code&gt;dist/scripts/*.js&lt;/code&gt; output&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;examples/typescript
../../bin/mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:3996
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Build it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;../../bin/mdl build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example intentionally has no &lt;code&gt;package.json&lt;/code&gt;, no &lt;code&gt;node_modules&lt;/code&gt;, and no&lt;br&gt;
&lt;code&gt;tsconfig.json&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this shape?
&lt;/h2&gt;

&lt;p&gt;I want MDL to keep a narrow architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MDL source + CSS + optional behavior scripts -&amp;gt; static browser output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some pages only need HTML and CSS. Some need a little inline JavaScript. Some&lt;br&gt;
need external JavaScript modules. And some behavior is easier to maintain with&lt;br&gt;
TypeScript types.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0.1.9&lt;/code&gt; supports that last case without changing the default model.&lt;/p&gt;

&lt;p&gt;The output is still plain browser assets. The project is still inspectable. The&lt;br&gt;
site is still deployable as static files.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try MDL
&lt;/h2&gt;

&lt;p&gt;Install locally in a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-mdl-site
&lt;span class="nb"&gt;cd &lt;/span&gt;my-mdl-site
npm &lt;span class="nb"&gt;install&lt;/span&gt; @tosiiko/mdl
npm &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; mdl init
&lt;span class="nb"&gt;source &lt;/span&gt;bin/activate
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @tosiiko/mdl
mdl new my-mdl-site
&lt;span class="nb"&gt;cd &lt;/span&gt;my-mdl-site
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdl check
mdl format &lt;span class="nt"&gt;--check&lt;/span&gt;
mdl build
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://getmdl.site" rel="noopener noreferrer"&gt;https://getmdl.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/tosiiko/mdl-code" rel="noopener noreferrer"&gt;https://github.com/tosiiko/mdl-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href="https://www.npmjs.com/package/@tosiiko/mdl" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@tosiiko/mdl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;TypeScript example: &lt;code&gt;examples/typescript&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Script spec: &lt;code&gt;docs/spec/SCRIPTS.md&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MDL is still early, but &lt;code&gt;0.1.9&lt;/code&gt; makes optional behavior code easier to grow:&lt;br&gt;
JavaScript still works exactly as before, TypeScript can live in local external&lt;br&gt;
modules, and deployed sites only need the compiled JavaScript.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>html</category>
      <category>rust</category>
    </item>
    <item>
      <title>MDL 0.1.8: htmx examples, safer npm binaries, and portable static builds</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Sat, 06 Jun 2026 11:53:19 +0000</pubDate>
      <link>https://dev.to/tosiiko/mdl-018-htmx-examples-safer-npm-binaries-and-portable-static-builds-20p4</link>
      <guid>https://dev.to/tosiiko/mdl-018-htmx-examples-safer-npm-binaries-and-portable-static-builds-20p4</guid>
      <description>&lt;p&gt;MDL &lt;code&gt;0.1.8&lt;/code&gt; is a release focused on two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Making hypermedia workflows easier to see and copy.&lt;/li&gt;
&lt;li&gt;Making the npm CLI package easier to inspect and trust.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;MDL is a small authoring language for building clean HTML without writing raw&lt;br&gt;
HTML by hand. You write &lt;code&gt;.mdl&lt;/code&gt; files, regular CSS, and optional JavaScript; the&lt;br&gt;
CLI checks, builds, and serves the project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.mdl  -&amp;gt; structure and content
.css  -&amp;gt; layout and design
HTML  -&amp;gt; compiler output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  New: Complete htmx Example App
&lt;/h2&gt;

&lt;p&gt;This release adds a complete htmx example under:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;examples/htmx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app demonstrates MDL behavior attributes compiled through the &lt;code&gt;htmx&lt;/code&gt;&lt;br&gt;
adapter. Instead of writing raw &lt;code&gt;hx-*&lt;/code&gt; attributes in MDL source, you describe&lt;br&gt;
the intent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@api(get /api/search)@result(searchResults)@swap(inner)@trigger(input)@loading(searchBusy):
  field:
    label@for(searchQuery):
      Search cookbook entries
    .input@id(searchQuery)@name(q)@type(search)@placeholder(Type card, form, swap, or history)
  status@id(searchBusy)@class(indicator):
    Searching.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the htmx adapter enabled, MDL emits the corresponding &lt;code&gt;hx-*&lt;/code&gt; output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
  &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-form"&lt;/span&gt;
  &lt;span class="na"&gt;hx-get=&lt;/span&gt;&lt;span class="s"&gt;"/api/search"&lt;/span&gt;
  &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#searchResults"&lt;/span&gt;
  &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"innerHTML"&lt;/span&gt;
  &lt;span class="na"&gt;hx-trigger=&lt;/span&gt;&lt;span class="s"&gt;"input"&lt;/span&gt;
  &lt;span class="na"&gt;hx-indicator=&lt;/span&gt;&lt;span class="s"&gt;"#searchBusy"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example app includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;live search&lt;/li&gt;
&lt;li&gt;todo list swaps&lt;/li&gt;
&lt;li&gt;cart updates&lt;/li&gt;
&lt;li&gt;profile form validation&lt;/li&gt;
&lt;li&gt;out-of-band toast updates&lt;/li&gt;
&lt;li&gt;request indicators&lt;/li&gt;
&lt;li&gt;disabled submit buttons&lt;/li&gt;
&lt;li&gt;parameter filtering&lt;/li&gt;
&lt;li&gt;request timeouts&lt;/li&gt;
&lt;li&gt;sync behavior&lt;/li&gt;
&lt;li&gt;prompted requests&lt;/li&gt;
&lt;li&gt;preserved islands&lt;/li&gt;
&lt;li&gt;boosted safe forms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run the API server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node examples/htmx/server.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In another terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;target/debug/mdl serve examples/htmx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:4010
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example uses htmx &lt;code&gt;2.0.10&lt;/code&gt; from a pinned CDN URL in &lt;code&gt;mdl.json&lt;/code&gt;, and you can&lt;br&gt;
vendor that script locally if you want an offline demo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Behavior Adapters
&lt;/h2&gt;

&lt;p&gt;MDL now has a clearer adapter story:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"behavior"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"adapter"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"htmx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Supported behavior adapters are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;static&lt;/code&gt;: normal HTML behavior where possible.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mdl&lt;/code&gt;: &lt;code&gt;data-mdl-api-*&lt;/code&gt; attributes for future MDL runtimes.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;htmx&lt;/code&gt;: validated &lt;code&gt;hx-*&lt;/code&gt; output for htmx v2.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The important bit is that MDL source stays framework-neutral. You write&lt;br&gt;
attributes such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@api(post /api/profile)
@result(profileResult)
@swap(replace)
@trigger(submit)
@loading(profileBusy)
@select-oob(toast)
@request(timeout=5000)
@sync(profileForm:queue-last)
@validate(true)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The adapter decides how that intent becomes HTML.&lt;/p&gt;

&lt;h2&gt;
  
  
  Safer npm Package
&lt;/h2&gt;

&lt;p&gt;The npm package is also hardened in this release.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@tosiiko/mdl&lt;/code&gt; ships a small Node launcher and prebuilt Rust binaries for&lt;br&gt;
supported platforms. That is convenient, but native binaries deserve a clear&lt;br&gt;
audit trail.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;0.1.8&lt;/code&gt; adds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SECURITY.md&lt;/code&gt; in the npm package&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;BINARY-PROVENANCE.md&lt;/code&gt; in the npm package&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SHASUMS256.txt&lt;/code&gt; in the staged public package&lt;/li&gt;
&lt;li&gt;runtime checksum verification before executing the selected binary&lt;/li&gt;
&lt;li&gt;explicit &lt;code&gt;shell: false&lt;/code&gt; process spawning in the launcher&lt;/li&gt;
&lt;li&gt;provenance-enabled GitHub publishing with &lt;code&gt;npm publish --provenance&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The package still has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no runtime npm dependencies&lt;/li&gt;
&lt;li&gt;no npm lifecycle or install scripts&lt;/li&gt;
&lt;li&gt;no install-time binary downloads&lt;/li&gt;
&lt;li&gt;no runtime binary downloads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The launcher selects a binary from &lt;code&gt;vendor/&amp;lt;platform&amp;gt;/&lt;/code&gt;, verifies its SHA-256&lt;br&gt;
checksum when &lt;code&gt;SHASUMS256.txt&lt;/code&gt; is present, and only then starts the CLI.&lt;/p&gt;
&lt;h2&gt;
  
  
  Portable Static Builds
&lt;/h2&gt;

&lt;p&gt;This release also includes &lt;code&gt;routing.links&lt;/code&gt; in &lt;code&gt;mdl.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Use root links when deploying from a site root:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use relative links when publishing &lt;code&gt;dist/&lt;/code&gt; under a subfolder or opening the&lt;br&gt;
generated files directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"relative"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Relative mode rewrites local page and asset links so the output is easier to&lt;br&gt;
move between static hosts.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try MDL
&lt;/h2&gt;

&lt;p&gt;Install locally in a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-mdl-site
&lt;span class="nb"&gt;cd &lt;/span&gt;my-mdl-site
npm &lt;span class="nb"&gt;install&lt;/span&gt; @tosiiko/mdl
npm &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; mdl init
&lt;span class="nb"&gt;source &lt;/span&gt;bin/activate
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or install globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @tosiiko/mdl
mdl new my-mdl-site
&lt;span class="nb"&gt;cd &lt;/span&gt;my-mdl-site
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Useful commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdl check
mdl format &lt;span class="nt"&gt;--check&lt;/span&gt;
mdl build
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Website: &lt;a href="https://getmdl.site" rel="noopener noreferrer"&gt;https://getmdl.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repository: &lt;a href="https://github.com/tosiiko/mdl-code" rel="noopener noreferrer"&gt;https://github.com/tosiiko/mdl-code&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href="https://www.npmjs.com/package/@tosiiko/mdl" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@tosiiko/mdl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;htmx examples: &lt;code&gt;examples/htmx&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MDL is still early, but &lt;code&gt;0.1.8&lt;/code&gt; makes the project much easier to evaluate: you&lt;br&gt;
can inspect the generated HTML, run a real htmx example, check the package&lt;br&gt;
contents, and build static output that fits more hosting setups.&lt;/p&gt;

</description>
      <category>html</category>
      <category>htmx</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title># Next MDL Update: Security-First Adapters and HTMX Support</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Sat, 06 Jun 2026 09:35:46 +0000</pubDate>
      <link>https://dev.to/tosiiko/-next-mdl-update-security-first-adapters-and-htmx-support-loe</link>
      <guid>https://dev.to/tosiiko/-next-mdl-update-security-first-adapters-and-htmx-support-loe</guid>
      <description>&lt;p&gt;In the last update, I introduced &lt;strong&gt;MDL&lt;/strong&gt; as an HTML-first language for building websites and apps with less noise.&lt;/p&gt;

&lt;p&gt;This update is about the next layer: &lt;strong&gt;adapters&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The goal is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;MDL source should describe intent. Adapters decide how that intent becomes deployable HTML.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why adapters?
&lt;/h2&gt;

&lt;p&gt;I don’t want MDL to become locked into one frontend framework.&lt;/p&gt;

&lt;p&gt;Instead, the same MDL structure should be able to target different deployment styles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;static HTML&lt;/li&gt;
&lt;li&gt;MDL-native runtime attributes&lt;/li&gt;
&lt;li&gt;HTMX&lt;/li&gt;
&lt;li&gt;future template or framework adapters&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;form@api(post /api/login)@result(loginResult)@swap(replace):
  .input@type(email)@required
  .btn-primary@type(submit)(Sign in)

  status@id(loginResult):
    Waiting.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can become plain HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;html

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt; &lt;span class="na"&gt;action=&lt;/span&gt;&lt;span class="s"&gt;"/api/login"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or HTMX:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;html

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/api/login"&lt;/span&gt; &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#loginResult"&lt;/span&gt; &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Security before convenienceThe important part is that MDL does not pass behavior attributes through blindly.&lt;br&gt;
Raw HTMX attributes like this are blocked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mdl

form@hx-post(/api/login):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead, MDL uses intent-based attributes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mdl

form@api(post /api/login):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mdl

@select-oob(...)
@swap-oob(...)
@disabled(...)
@disinherit(...)
@encoding(...)
@history-elt(...)
@inherit(...)
@params(...)
@preserve(...)
@prompt(...)
@replace(...)
@request(...)
@sync(...)
@validate(...)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the HTMX adapter, MDL can emit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;html

&lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt;
  &lt;span class="na"&gt;hx-post=&lt;/span&gt;&lt;span class="s"&gt;"/api/profile"&lt;/span&gt;
  &lt;span class="na"&gt;hx-target=&lt;/span&gt;&lt;span class="s"&gt;"#profileResult"&lt;/span&gt;
  &lt;span class="na"&gt;hx-swap=&lt;/span&gt;&lt;span class="s"&gt;"outerHTML"&lt;/span&gt;
  &lt;span class="na"&gt;hx-params=&lt;/span&gt;&lt;span class="s"&gt;"email,csrfToken"&lt;/span&gt;
  &lt;span class="na"&gt;hx-disabled-elt=&lt;/span&gt;&lt;span class="s"&gt;"#saveButton"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>htmx</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I Built MDL: A Small Language for Writing Static Websites as Readable Outlines</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Mon, 01 Jun 2026 18:10:50 +0000</pubDate>
      <link>https://dev.to/tosiiko/i-built-mdl-a-small-language-for-writing-static-websites-as-readable-outlines-1o5n</link>
      <guid>https://dev.to/tosiiko/i-built-mdl-a-small-language-for-writing-static-websites-as-readable-outlines-1o5n</guid>
      <description>&lt;p&gt;HTML is an excellent output format.&lt;/p&gt;

&lt;p&gt;As an authoring format, though, it can become noisy when you're building simple documentation sites, landing pages, examples, or other static content.&lt;/p&gt;

&lt;p&gt;I wanted something that let me write pages as readable outlines while still producing normal HTML at build time.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;MDL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;MDL is a small authoring language that compiles to clean static HTML.&lt;/p&gt;

&lt;p&gt;It does not replace HTML, CSS, or JavaScript.&lt;/p&gt;

&lt;p&gt;Instead, it sits one step above them and lets you write page structure in a format that is easier to scan and maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What MDL Looks Like
&lt;/h2&gt;

&lt;p&gt;A simple MDL page might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page:
  hero:
    .badge(New)
    # Build clean static sites

    Write readable page structure,
    then compile to HTML.

    actions:
      .href@href(/docs.html)@class(primary)
      (Read the docs)

  section:
    ## Why MDL?

    Keep content readable.
    Keep styling in CSS.
    Add JavaScript only when behavior belongs.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it compiles to ordinary HTML:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-page"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-hero"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-badge"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;New&lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Build clean static sites&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is intentionally boring.&lt;/p&gt;

&lt;p&gt;That is the goal.&lt;/p&gt;

&lt;p&gt;No runtime.&lt;br&gt;
No framework lock-in.&lt;br&gt;
No custom browser behavior.&lt;/p&gt;

&lt;p&gt;Just static HTML.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why I Built It
&lt;/h2&gt;

&lt;p&gt;I kept finding myself wanting a format that felt closer to writing a structured note than writing a template.&lt;/p&gt;

&lt;p&gt;For many sites I don't need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Component systems&lt;/li&gt;
&lt;li&gt;Hydration&lt;/li&gt;
&lt;li&gt;Build-time JavaScript frameworks&lt;/li&gt;
&lt;li&gt;Large configuration layers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I just want to describe the page structure clearly and generate predictable HTML.&lt;/p&gt;

&lt;p&gt;MDL is my attempt at that.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Browser Platform Stays Visible
&lt;/h2&gt;

&lt;p&gt;One thing that mattered to me from the start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CSS is still CSS&lt;/li&gt;
&lt;li&gt;JavaScript is still JavaScript&lt;/li&gt;
&lt;li&gt;HTML is still the final artifact&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MDL is only the authoring layer.&lt;/p&gt;

&lt;p&gt;You can inspect the generated pages and immediately understand what is happening.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;Install from npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-mdl-site
&lt;span class="nb"&gt;cd &lt;/span&gt;my-mdl-site

npm &lt;span class="nb"&gt;install&lt;/span&gt; @tosiiko/mdl

npm &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; mdl init

&lt;span class="nb"&gt;source &lt;/span&gt;bin/activate

mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;http://127.0.0.1:3999
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  CLI Commands
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdl init
mdl new my-site
mdl check
mdl format
mdl build
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Recent Improvements
&lt;/h2&gt;

&lt;p&gt;Current release: &lt;strong&gt;0.1.7&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Some highlights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Public documentation site is now live&lt;/li&gt;
&lt;li&gt;Improved static hosting support&lt;/li&gt;
&lt;li&gt;Routing control via &lt;code&gt;mdl.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Better package and editor metadata alignment&lt;/li&gt;
&lt;li&gt;Expanded documentation&lt;/li&gt;
&lt;li&gt;Deployment guidance and examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example routing configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"relative"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes generated sites work cleanly when hosted under subfolders or served directly as static files.&lt;/p&gt;

&lt;p&gt;For root-domain hosting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"routing"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"root"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Where I Think MDL Fits
&lt;/h2&gt;

&lt;p&gt;MDL is especially useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation sites&lt;/li&gt;
&lt;li&gt;Product pages&lt;/li&gt;
&lt;li&gt;Landing pages&lt;/li&gt;
&lt;li&gt;Project websites&lt;/li&gt;
&lt;li&gt;Examples and demos&lt;/li&gt;
&lt;li&gt;Small static sites&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're already happy writing HTML directly, that's great.&lt;/p&gt;

&lt;p&gt;If you've ever wished your page source looked more like an outline and less like markup, MDL might be interesting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Feedback Welcome
&lt;/h2&gt;

&lt;p&gt;The project is still early and I'm actively improving:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Documentation&lt;/li&gt;
&lt;li&gt;Editor support&lt;/li&gt;
&lt;li&gt;Templates&lt;/li&gt;
&lt;li&gt;Diagnostics&lt;/li&gt;
&lt;li&gt;Real-world examples&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd love feedback from people who enjoy static sites, small tools, Markdown-style authoring, and clean HTML.&lt;/p&gt;

&lt;p&gt;Documentation:&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Documentation: &lt;a href="https://getmdl.site" rel="noopener noreferrer"&gt;https://getmdl.site&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm package: &lt;a href="https://www.npmjs.com/package/@tosiiko/mdl" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@tosiiko/mdl&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/tosiiko/mdl-code" rel="noopener noreferrer"&gt;https://github.com/tosiiko/mdl-code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Package:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @tosiiko/mdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Questions, criticism, feature requests, and ideas are all welcome.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>staticweb</category>
      <category>markdown</category>
      <category>tooling</category>
    </item>
    <item>
      <title>What's coming in MDL 0.1.3 — source maps, metadata, diagnostics, and more</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Sun, 31 May 2026 18:55:26 +0000</pubDate>
      <link>https://dev.to/tosiiko/whats-coming-in-mdl-013-source-maps-metadata-diagnostics-and-more-4o29</link>
      <guid>https://dev.to/tosiiko/whats-coming-in-mdl-013-source-maps-metadata-diagnostics-and-more-4o29</guid>
      <description>&lt;p&gt;I've been building MDL — a tiny authoring language that compiles to clean HTML. If you haven't seen it yet:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page:
  hero:
    ## Sign in
    Welcome back.

  form@id(loginForm)@submit(handleLogin):
    field:
      label:
        Email
      .input@type(email)@id(email)@required

    actions:
      .btn-primary@id(loginBtn)(Sign in)
      .btn-ghost@click(handleForgot)(Forgot password?)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiles to clean, semantic HTML. No angle brackets written by hand. CSS owns all layout. JS handles all logic.&lt;/p&gt;

&lt;p&gt;Install: &lt;code&gt;npm install -g @tosiiko/mdl&lt;/code&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What's already shipped in 0.1.x
&lt;/h2&gt;

&lt;p&gt;Before getting into what's coming — here's what's already working:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Indentation-based sections that compile to semantic HTML (&lt;code&gt;page:&lt;/code&gt; → &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, &lt;code&gt;nav:&lt;/code&gt; → &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;, &lt;code&gt;form:&lt;/code&gt; → &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Full CommonMark Markdown inside every section — headings, lists, tables, code blocks, blockquotes&lt;/li&gt;
&lt;li&gt;Dot inline elements — &lt;code&gt;.badge(text)&lt;/code&gt;, &lt;code&gt;.btn-primary(text)&lt;/code&gt;, &lt;code&gt;.input@type(email)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Attributes — &lt;code&gt;@id(value)&lt;/code&gt;, &lt;code&gt;@required&lt;/code&gt;, &lt;code&gt;@placeholder(text)&lt;/code&gt;, &lt;code&gt;@submit(fn)&lt;/code&gt;, &lt;code&gt;@click(fn)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;CSS bundling — multiple CSS files merged into one &lt;code&gt;app.css&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;JavaScript event wiring — &lt;code&gt;@submit(handleLogin)&lt;/code&gt; compiles to a proper &lt;code&gt;addEventListener&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Live reload dev server — &lt;code&gt;mdl serve&lt;/code&gt; at &lt;code&gt;localhost:3999&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Multi-page projects via &lt;code&gt;mdl.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mdl init&lt;/code&gt; and &lt;code&gt;mdl new&lt;/code&gt; — scaffold a full project in one command&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mdl check&lt;/code&gt; and &lt;code&gt;mdl format&lt;/code&gt; — lint and auto-format&lt;/li&gt;
&lt;li&gt;VS Code extension with syntax highlighting — already on the marketplace&lt;/li&gt;
&lt;li&gt;Prebuilt Rust binary distributed via npm — no Rust toolchain needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's 0.1. Here's what's coming next.&lt;/p&gt;




&lt;h2&gt;
  
  
  Source maps
&lt;/h2&gt;

&lt;p&gt;Every compiled HTML page will write a JSON sidecar map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pages/login.mdl  →  dist/login.html
                 →  dist/login.html.mdlmap.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The map links every generated HTML element back to the MDL source line that produced it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pages/login.mdl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"login.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mappings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"generated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end_line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;31&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end_column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end_line"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"end_column"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;39&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"kind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"section"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"form"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This unlocks editor navigation, error overlays with exact MDL line and column, and eventually go-to-source from generated HTML back to the &lt;code&gt;.mdl&lt;/code&gt; file that produced it — the same way browser source maps let you debug TypeScript instead of compiled JS.&lt;/p&gt;

&lt;p&gt;A project-level &lt;code&gt;dist/mdl-manifest.json&lt;/code&gt; links every route to its source and source map:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/login"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"login.mdl"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"output"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"login.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source_map"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"login.html.mdlmap.json"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Document metadata
&lt;/h2&gt;

&lt;p&gt;Right now metadata lives in &lt;code&gt;mdl.json&lt;/code&gt;. The next release formalises and extends this with a complete metadata system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"lang"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"en"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"My Site"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A small MDL site."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"canonical"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"favicon"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/favicon.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"social"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"enabled"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"assets/social-card.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"twitter_site"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@mysite"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;social&lt;/code&gt; preset derives Open Graph and Twitter card tags automatically from &lt;code&gt;title&lt;/code&gt;, &lt;code&gt;description&lt;/code&gt;, and &lt;code&gt;canonical&lt;/code&gt;. One config, full social sharing coverage:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:title"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"My Site"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:description"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"A small MDL site."&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;property=&lt;/span&gt;&lt;span class="s"&gt;"og:image"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"assets/social-card.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:card"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"summary_large_image"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"twitter:site"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"@mysite"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;mdl check validates all local file references — favicon, social image, manifest links — at build time, not when a user finds a broken image in production.&lt;/p&gt;




&lt;h2&gt;
  
  
  Better diagnostics
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mdl check&lt;/code&gt; is getting significantly smarter. The next release adds:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accessibility checks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Missing &lt;code&gt;alt&lt;/code&gt; on images&lt;/li&gt;
&lt;li&gt;Missing &lt;code&gt;title&lt;/code&gt; on iframes&lt;/li&gt;
&lt;li&gt;Duplicate IDs&lt;/li&gt;
&lt;li&gt;Unlabeled form controls&lt;/li&gt;
&lt;li&gt;Invalid or empty &lt;code&gt;aria-*&lt;/code&gt; attributes&lt;/li&gt;
&lt;li&gt;Missing ARIA references&lt;/li&gt;
&lt;li&gt;Landmark ordering issues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Form checks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Label and input pairing mistakes&lt;/li&gt;
&lt;li&gt;Missing label targets&lt;/li&gt;
&lt;li&gt;Ungrouped radio buttons&lt;/li&gt;
&lt;li&gt;Missing autocomplete hints&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Asset checks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Broken &lt;code&gt;.link()&lt;/code&gt; and &lt;code&gt;.href()&lt;/code&gt; targets&lt;/li&gt;
&lt;li&gt;Missing local images and media files&lt;/li&gt;
&lt;li&gt;Recursive CSS &lt;code&gt;url()&lt;/code&gt; and &lt;code&gt;@import&lt;/code&gt; validation&lt;/li&gt;
&lt;li&gt;Font file references&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Machine-readable output:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdl check &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outputs structured diagnostics with 1-based line and column ranges — ready for editor integrations, CI pipelines, and language server consumers.&lt;/p&gt;




&lt;h2&gt;
  
  
  Multi-app workspaces
&lt;/h2&gt;

&lt;p&gt;One install, multiple apps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mdl init &lt;span class="nt"&gt;--app&lt;/span&gt; admin &lt;span class="nt"&gt;--port&lt;/span&gt; 4001
mdl serve apps/admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each app owns its own &lt;code&gt;mdl.json&lt;/code&gt;, pages, CSS, and scripts. They share the same MDL install and environment. Useful for projects with a public site and a separate admin panel, or a marketing page and a docs site.&lt;/p&gt;




&lt;h2&gt;
  
  
  Advanced browser primitives
&lt;/h2&gt;

&lt;p&gt;The next release adds first-class MDL sections for:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;canvas:      →  &amp;lt;canvas&amp;gt;   WebGL, charts, custom drawing
frame:       →  &amp;lt;iframe&amp;gt;   embedded content
picture:     →  &amp;lt;picture&amp;gt;  responsive images
progress:    →  &amp;lt;progress&amp;gt; loading bars
meter:       →  &amp;lt;meter&amp;gt;    scalar measurements
template:    →  &amp;lt;template&amp;gt; inert DOM templates
slot:        →  &amp;lt;slot&amp;gt;     web component slots
island:      →  &amp;lt;div&amp;gt;      hydrated JS island host
component:   →  &amp;lt;div&amp;gt;      custom component host
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;@mount(handler)&lt;/code&gt; wiring for JS initialisation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;canvas@id(scene)@mount(drawScene):
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiles to a &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; that calls &lt;code&gt;drawScene()&lt;/code&gt; from your configured scripts on mount.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I'm building toward
&lt;/h2&gt;

&lt;p&gt;MDL's goal is simple: you should be able to build a complete, production-quality web page without writing a single HTML tag. Structure in &lt;code&gt;.mdl&lt;/code&gt;. Design in &lt;code&gt;.css&lt;/code&gt;. Logic in &lt;code&gt;.js&lt;/code&gt;. The compiler handles everything else.&lt;/p&gt;

&lt;p&gt;The next release gets closer to that by making the tooling smarter — better errors, better source maps, better metadata, better workspace support.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try it now
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @tosiiko/mdl
mdl new my-site
&lt;span class="nb"&gt;cd &lt;/span&gt;my-site
mdl serve
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;npm — &lt;a href="https://www.npmjs.com/package/@tosiiko/mdl/v/0.1.2" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@tosiiko/mdl/v/0.1.2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VS Code — &lt;a href="https://marketplace.visualstudio.com/items?itemName=MDLLanguageSupport.mdl-language" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=MDLLanguageSupport.mdl-language&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub — &lt;a href="https://github.com/tosiiko/mdl-code" rel="noopener noreferrer"&gt;https://github.com/tosiiko/mdl-code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Would love to hear what you think — and what you'd want to see in MDL before you'd use it on a real project.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>rust</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>npm i @tosiiko/mdl</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Sat, 30 May 2026 10:44:07 +0000</pubDate>
      <link>https://dev.to/tosiiko/npm-i-tosiikomdl-280n</link>
      <guid>https://dev.to/tosiiko/npm-i-tosiikomdl-280n</guid>
      <description>&lt;p&gt;The parser is written in Rust using pulldown-cmark for Markdown. It ships as a prebuilt binary via npm so no Rust toolchain is needed.&lt;/p&gt;

&lt;p&gt;npm: &lt;a href="https://www.npmjs.com/package/@tosiiko/mdl/v/0.1.2" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@tosiiko/mdl/v/0.1.2&lt;/a&gt;&lt;br&gt;
VS Code: &lt;a href="https://marketplace.visualstudio.com/items?itemName=MDLLanguageSupport.mdl-language" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=MDLLanguageSupport.mdl-language&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/tosiiko/mdl-code" rel="noopener noreferrer"&gt;https://github.com/tosiiko/mdl-code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>rust</category>
      <category>showdev</category>
      <category>frontend</category>
    </item>
    <item>
      <title>I built MDL — a tiny authoring language so you never write raw HTML again</title>
      <dc:creator>Tosiiko</dc:creator>
      <pubDate>Sat, 30 May 2026 10:35:39 +0000</pubDate>
      <link>https://dev.to/tosiiko/i-built-mdl-a-tiny-authoring-language-so-you-never-write-raw-html-again-1mkf</link>
      <guid>https://dev.to/tosiiko/i-built-mdl-a-tiny-authoring-language-so-you-never-write-raw-html-again-1mkf</guid>
      <description>&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Every web project starts the same way. You open a blank file and start typing angle brackets.&lt;/p&gt;

&lt;p&gt;HTML is fine. But writing it by hand is noisy, repetitive, and forces you to think about tags instead of content.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;MDL (Markdown Language) is a tiny authoring language that compiles to clean, semantic HTML.&lt;/p&gt;

&lt;p&gt;The idea is three rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.mdl&lt;/code&gt; files handle structure and content&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.css&lt;/code&gt; files handle layout and design&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.js&lt;/code&gt; files handle logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You never write a single HTML tag.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;page:
  hero:
    ## Sign in
    Welcome back.

  form@id(loginForm)@submit(handleLogin):
    field:
      label:
        Email
      .input@type(email)@id(email)@required

    actions:
      .btn-primary@id(loginBtn)(Sign in)
      .btn-ghost@click(handleForgot)(Forgot password?)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That compiles to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-page"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-hero"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Sign in&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Welcome back.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-form"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"loginForm"&lt;/span&gt; &lt;span class="na"&gt;data-mdl-on-submit=&lt;/span&gt;&lt;span class="s"&gt;"handleLogin"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-field"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-label"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-input"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-actions"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-btn-primary"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"loginBtn"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Sign in&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mdl-btn-ghost"&lt;/span&gt; &lt;span class="na"&gt;data-mdl-on-click=&lt;/span&gt;&lt;span class="s"&gt;"handleForgot"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Forgot password?&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clean, semantic HTML. No divs you didn't ask for. No inline styles. Every element has a predictable &lt;code&gt;.mdl-*&lt;/code&gt; class that CSS can target.&lt;/p&gt;

&lt;h2&gt;
  
  
  How CSS and JS talk to it
&lt;/h2&gt;

&lt;p&gt;CSS targets &lt;code&gt;.mdl-*&lt;/code&gt; classes directly. No configuration needed.&lt;/p&gt;

&lt;p&gt;JS uses standard DOM APIs. The &lt;code&gt;@id()&lt;/code&gt; attribute compiles to a real HTML &lt;code&gt;id&lt;/code&gt;. The &lt;code&gt;@submit()&lt;/code&gt; and &lt;code&gt;@click()&lt;/code&gt; attributes wire events to functions in your external JS file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// scripts/auth.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleLogin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#email&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;
  &lt;span class="c1"&gt;// your logic here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No framework. No runtime. No magic. Just the platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to install
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; @tosiiko/mdl
mdl &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CLI is a prebuilt Rust binary. Fast, no dependencies, works on Mac, Linux, and Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;npm — &lt;a href="https://www.npmjs.com/package/@tosiiko/mdl/v/0.1.2" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@tosiiko/mdl/v/0.1.2&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;VS Code extension — &lt;a href="https://marketplace.visualstudio.com/items?itemName=MDLLanguageSupport.mdl-language" rel="noopener noreferrer"&gt;https://marketplace.visualstudio.com/items?itemName=MDLLanguageSupport.mdl-language&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub — &lt;a href="https://github.com/tosiiko/mdl-code" rel="noopener noreferrer"&gt;https://github.com/tosiiko/mdl-code&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Would love to hear what you think.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>rust</category>
      <category>css</category>
    </item>
  </channel>
</rss>
