MDL MDL
Language

MDL language reference

MDL is made of a few readable moves: section blocks, Markdown prose, dot elements, attributes, comments, and explicit JavaScript hooks.

File anatomy

A .mdl file is indentation-based. Lines ending in : open sections, Markdown fills the body, and dot syntax creates compact inline or control elements.

document:
    topbar:
      .href@href(/)(Home)
    hero:
      .badge(New)
      # Welcome
      Write the page in a readable outline.
    section:
      ## Next section
      Markdown content lives here.

Keep related content indented under its parent. Blank lines are fine and help prose breathe.

Sections

A section name followed by : becomes an HTML element with a matching mdl-* class. Unknown names are valid and fall back to a div.

hero:
    # Welcome
<div class="mdl-hero">
    <h1>Welcome</h1>
  </div>

Common structural sections include page, topbar, nav, section, hero, grid, card, form, field, actions, and footer.

Common browser and UI sections include table, thead, tbody, tr, th, td, tabs, modal, drawer, toast, canvas, picture, source, template, component, and island.

Classes

Every section receives a stable generated class:

feature-grid:
    card@class(feature-card highlighted):
      ## Fast
<div class="mdl-feature-grid">
    <div class="mdl-card feature-card highlighted">
      <h2>Fast</h2>
    </div>
  </div>

Use generated classes for broad styling and @class(...) for page-specific or utility classes.

Inline elements

Dot syntax creates named inline elements:

.badge(beta)
  .btn-primary(Choose)
  .href@href(/docs.html)(Docs)
  .input@type(email)@autocomplete(email)@aria-label(Email)@required

Known names map to useful HTML. For example, buttons become button elements and .href becomes an anchor when you provide @href(...). Unknown names become spans with matching classes.

Attributes

Attributes attach directly to sections or inline elements:

form@id(login)@method(post):
    field:
      label@for(email):
        Email
      .input@id(email)@type(email)@autocomplete(email)@aria-label(Email)@placeholder(you@example.com)@required

Boolean attributes use no value. Author classes use @class(...) and are appended after the generated MDL class.

Markdown

Markdown owns prose inside sections. MDL renders headings, paragraphs, lists, blockquotes, code fences, tables, strikethrough, and task lists through CommonMark.

section:
    ## Markdown works here
  
    - Write lists
    - Add `inline code`
    - Keep content readable

When a line is not an MDL section, dot element, attribute continuation, or comment, treat it as Markdown prose.

Comments

Use // for source comments that compile to HTML comments:

// This note is visible in the generated HTML source.
  section:
    ## Content

JavaScript owns comments inside script js: blocks, so // there remains JavaScript.

JavaScript

Inline JavaScript uses script js::

script js:
    console.log("ready")

Configured module scripts can export handlers for event attributes:

form@submit(handleSignup):
    .btn-primary@click(handleSignup)(Join)

Event handlers receive the browser event. @mount(handler) initializes mounted elements and receives the mounted element.

UI patterns

MDL includes named sections for common patterns while CSS and JavaScript own presentation and state:

tabs@id(settingsTabs):
    tablist@role(tablist):
      .btn-secondary@click(showProfile)(Profile)
      .btn-ghost@click(showBilling)(Billing)
    tab@id(profileTab)@data-state(active):
      Profile content.
    tab@id(billingTab):
      Billing content.

Escape hatches

MDL is intentionally small, but it still has explicit exits for platform features.

Use element@tag(...) when you need a custom tag:

element@tag(my-widget)@data-mode(compact):
    Widget content.

Use raw-html: only for trusted markup that should pass through unchanged:

raw-html:
    <custom-element data-ready="true"></custom-element>

Prefer normal MDL sections first; escape hatches are best for interoperability.