Thinking in Hyperclay.

In modern web development, you maintain two apps: the one in your JavaScript state, and the one on the screen. Most of our time is spent keeping these in sync.

We wouldn't need to keep anything in sync if there was just one source of truth: the DOM.

Let's explore this way of thinking. It isn't the only way to build on Hyperclay, but it's the most interesting.

Let's cram state, behavior, styles, and content into a single hierarchical object

This is a card from Planning Canvas, a sticky-note app built with Hyperclay.

<html editmode="true">
  ...
  <div class="card" movable style="transform: translate(300px, 20px)">

    <div movable-handle option:editmode="true"></div>

    <div class="card-text" editmode:contenteditable>
      Ship the artifact. The document is the app.
    </div>

    <select viewmode:disabled>
      <option>Critical</option>
      <option selected>High</option>
      <option>Medium</option>
      <option>Low</option>
      <option>None</option>
    </select>

    <div option:editmode="true"></div>

  </div>
  ...
</html>

HyperclayJS handles the custom attributes.

What you don't need anymore:


Interactive Demo

Try editing this text. Drag the handle. Open the menu.

This is a working app. Let's unpack each piece.

Our Religion: Use the thing itself as the thing

When we want to edit text on the page, we modify the actual text on the page. When we want to modify the position, we modify the position. When we want to modify the status, we modify an attribute directly in the DOM.

Position is position

<div class="card" movable style="transform: translate(300px, 20px)">

The card is at (300, 20) because its transform says so. The movable attribute makes it draggable and directly updates the transform.

Content is content

<div class="card-text" editmode:contenteditable>
  Ship the artifact. The document is the app.
</div>

When you need to update text, you just update it directly on the page. You don't want everyone to be able to update it, so you put it behind an edit mode flag.

Persist form inputs

<select persist viewmode:disabled>
  <option>Critical</option>
  <option selected>High</option>
  ...
</select>

The persist attribute syncs the selected value to the DOM on save, so it's preserved across saves and page loads. viewmode:disabled means the dropdown is visible to everyone but only interactive for editors.

The document is the source of truth. Keep as much of it around as possible.

<div movable-handle option:editmode="true"></div>
...
<div option:editmode="true"></div>

Even in view mode, we don't discard the edit controls — we just disable or hide them. Toggling between edit mode and view mode is trivial, and the whole application becomes a lot easier to think about.


When This Doesn't Work

Sometimes you don't want a DOM-first approach — and that's fine. You can still use Hyperclay and malleable HTML files. Just store state in a <script> tag on the page and render from there. You still get a single portable file, saved and shared the same way. You just manage state yourself instead of letting the DOM hold it.

If I was building a dashboard with multiple pages and panels and widgets, I'd reach for a modern toolkit myself.

When This Really Doesn't Work

Building a video editor or a drag-and-drop game? Yes. Building a high-frequency trading terminal pulling from five external APIs? No.


Remember jQuery?

There was an ancient time, back around 2010, when everyone was using this magical technology called jQuery. You'd get state from the server, use it to update the DOM, and you were done. The user could click a button to send a request to update state, and that was it. A much simpler model than what we have now. It treated the UI much more like a source of truth.

jQuery's model was beautiful for things you just wanted to release into the wild and forget about. The document was the whole story.

The document is a natural hierarchy, and it creates its own natural components. If you embrace the idea that state = UI = style = behavior, then your mental model collapses into something singular. You're just working with one object instead of a lot of different pieces you're trying to smush together.

There's a feeling of elegance in working with one thing. It feels like working with a real object. You can put it down and come back to it later, and it will still work and look and behave the same way.

If an app outgrows the DOM-as-source-of-truth approach, converting to React or Vue is straightforward. But most Hyperclay apps built this way stay this way: pure DOM, no framework, and they don't need one. It's a genuinely good alternative to the modern stack if you don't want to split your attention across layers of code.