RuledWDLRuledWDL

Note

WebQuad: Structure, Interaction, Action and Design

RuledWDL + Alpine.js + HTMX + Tailwind CSS — four purpose-built tools, not a designed stack.

The observation

The web already has increasingly capable purpose-built tools. Tailwind CSS is a vocabulary for design. Alpine.js is a small layer for local UI behavior. HTMX declares server communication on the element itself.

None of those tools try to own the whole interface. What felt missing was an independent way to define the view structure itself. That is the role I am exploring for RuledWDL.

Where this thought came from

I developed RuledWDL around abstraction that earns its keep, separation of concerns, reusability, purpose-built tooling, structured definitions, AI-first authoring, and staying close to existing web primitives.

The lesson was not invent a new authoring language. It was: one structure plus one spec. RuledWDL is a Web Definition Language.

Why split the concerns at all

Putting structure, styling, interactions, requests, and application behavior into one abstraction is a reasonable choice. The cost is that every change travels through the same surface.

WebQuad is that split, named so it can be discussed. It is not a claim that four tools beat one framework.

RuledWDL — Structure

RuledWDL defines the view as JSON with one grammar. See Getting started and the language reference.

layers

{
  "layers": [
    "section.app",
    "> form.add_form",
    "> input.title_input",
    "+ button.add_btn",
    "< ul.task_list",
    "> li.task*tasks",
    "> label.task_row",
    "> input.done",
    "+ span.task_name"
  ]
}

One semantic id per node. Classes and Alpine / HTMX live in attr. Content lives in DATA.

Tailwind CSS — Styles / Design

REGISTRY maps a semantic class name to Tailwind utilities. RuledWDL does not invent a second styling language.

REGISTRY

{
  "REGISTRY": {
    "app": {
      "class": "rounded-xl border border-slate-200 bg-white p-4 shadow-sm space-y-4"
    },
    "add_btn": {
      "class": "rounded-md bg-slate-900 px-3 py-2 text-sm text-white"
    },
    "task": {
      "class": "rounded-md border border-slate-200 px-3 py-2"
    }
  }
}

The layers node stays section.app. The visual recipe lives on the token app.

Alpine.js — Interactions

Alpine is the local interaction layer. In WDL the alpine object flattens onto the element.

attr.alpine

{
  "attr": {
    ".task": {
      "alpine": {
        "x-data": "{ done: false }"
      }
    },
    ".done": {
      "type": "checkbox",
      "alpine": {
        "x-model": "done"
      }
    },
    ".task_name": {
      "text": "${title}",
      "alpine": {
        ":class": "done ? 'line-through text-slate-400' : ''"
      }
    }
  }
}

Conceptual responsibility: Alpine handles interaction within the interface. Checking a task does not talk to the server.

HTMX — Actions

HTMX is the action layer. The htmx object flattens the same way alpine does.

attr.htmx

{
  "attr": {
    ".add_form": {
      "htmx": {
        "hx-post": "/tasks",
        "hx-target": "#task-list",
        "hx-swap": "beforeend"
      }
    }
  }
}

Conceptual responsibility: HTMX connects interface actions to the server. Add task is a POST. The CodePen demo intercepts that POST and composePage-renders the new row.

Four concerns, one example

Add a task. Structure is layers. Design is REGISTRY. The checkbox is Alpine. Submit is HTMX. The row that comes back is composePage again.

Full interactive example is the CodePen below — HTML tab plus preview.

Why this becomes interesting for an AI-first web

An AI agent does not necessarily need to generate one large application abstraction. A change can aim at one surface: structure, design, interaction, action, or content.

The hypothesis is that separation can reduce the context an operation needs.

What this is not

WebQuad is not a framework shipped as a package. It is a way to keep four jobs from becoming one job by default.

An open question

If structure, interaction, action, and design can remain independently addressable while still composing into one web view, how much framework-level abstraction do we actually need?

Interactive example

CodePen embed via the codepen-embed WDL component. default-tab=html,result shows the HTML pane next to the preview. Click HTML / Result in the embed to switch.

WebQuad — Add tasks (HTML + preview)

Related on RuledWDL

Docs, examples, and the language model this note sits next to.