Skip to content

(WIP) Redesign hx-live with a unified DOM state API - #3957

Open
scriptogre wants to merge 42 commits into
bigskysoftware:four-devfrom
scriptogre:4.0/hx-live-redesign
Open

(WIP) Redesign hx-live with a unified DOM state API#3957
scriptogre wants to merge 42 commits into
bigskysoftware:four-devfrom
scriptogre:4.0/hx-live-redesign

Conversation

@scriptogre

@scriptogre scriptogre commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

four-dev mixes callable attribute helpers with inherited data. This PR gives DOM scripts one property-based state model.

Current element

Start with the bare state bags:

<section data-count="0">
  <button hx-on:click="
    data.count++              // updates nearest data-count
    aria.pressed = true       // adds aria-pressed=true
    aria.busy = false         // adds aria-busy=false
    attr.disabled = true      // adds disabled
    attr.hidden = false       // removes hidden
    delete attr.title         // removes title
    class.active = true       // adds active class
  ">Select</button>
</section>
data.*        nearest owner
aria.*        current element
attr.*        current element
class.*       current element

Selected elements

Use q() when state lives on other elements:

<button hx-on:click="
  q('.item').data.state = 'ready'
  q('.item').aria.busy = false
  q('.item').attr.hidden = false
  q('.item').class.active = true
">Prepare items</button>
selected   A  B  C
read       A
write      A  B  C

Deletes and value cycles use the same selection:

<button hx-on:click="
  delete q('.item').data.draft
  q('.item').toggle('data-view', 'grid', 'list')
">Change view</button>

q(...).attr.data, q(...).attr.aria, and q(...).attr.class alias the same cached local bags.

Classes

The class bag supports state, groups, and classList methods:

<button hx-on:click="
  q('.item').class.assign({ active: true, loading: false })
  q('.item').class.add('selected')
  q('.item').class.toggle('open')
">Update classes</button>

Nearest owners

Add .closest to walk up from each selected element:

<button hx-on:click="
  q('.item').closest.data.state = 'ready'
  q('.item').closest.aria.busy = false
  q('.item').closest.attr.hidden = false
  q('.item').closest.class.active = true
">Update owners</button>
selected   A  B  C
owner      X  X  Y
read       X
write      X     Y

Shared owners receive one write.

The next value may derive from the current typed value:

<button hx-on:click="
  q('.item').closest.data.count = count => count + 1
  q('.item').attr.hidden = hidden => !hidden
  q('.item').class.active = active => !active
  q('input').value = value => value.trim()
">Update items</button>

Updater functions must return synchronously.

Migration

attr(...)                       → attr.*
attr('.active', value)          → class.active = value
attr('class', object)           → class.assign(object)
q(...).attr(...)                → q(...).attr.*
q(...).data.* (inherited)       → q(...).closest.data.*
htmx.live.attr(...)             → htmx.live.q(...).attr.*

All callable attr() forms are removed.

@scriptogre
scriptogre force-pushed the 4.0/hx-live-redesign branch from 9f84c72 to bcff682 Compare August 11, 2026 17:17
Assigning a function to data.*, a q() property, or attr() calls it with
the current value and stores the result. A property that already holds a
function keeps the literal value. Async functions throw.

# Conflicts:
#	src/ext/hx-live.js
#	www/src/content/extensions/06-hx-live.md
@scriptogre scriptogre changed the title Redesign hx-live around DOM state Add local and nearest-owner state access to hx-live Aug 11, 2026
@scriptogre scriptogre changed the title Add local and nearest-owner state access to hx-live Redesign hx-live with a unified DOM state API Aug 11, 2026
@scriptogre scriptogre changed the title Redesign hx-live with a unified DOM state API Redesign hx-live with a unified DOM state API (WIP) Aug 11, 2026
@scriptogre scriptogre changed the title Redesign hx-live with a unified DOM state API (WIP) (WIP) Redesign hx-live with a unified DOM state API Aug 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant