# hx-live playground

Open DevTools. Type, click, change inputs. Verify state updates without server round-trips.

## 1. Simple form: `:attr="expr"` — boolean attributes

Lock everything below

Fieldset disables on lock

    :disabled="q('#lock').checked"
    :hidden="!q('#lock').checked"

## 2. `:text` — computed text content

Price: Qty:

Subtotal: $

With 10% tax: $

## 3. `:html` — innerHTML binding

Markdown-ish input:

Note: `:html` is XSS-unsafe if value comes from untrusted source. Prefer `:text`.

## 4. `:class` — string and object forms

### String form (class-set swap, leaves managed classes alone)

Use `:class` only when CSS pseudo-classes can't express the rule (cross-element comparison, arithmetic). For checkbox/focus/invalid state, use CSS (`:has()`, `peer-checked:`). Example below: branch on value-vs-budget comparison — CSS can't see it.

Cost: Budget:

External class stays; managed classes swap. (Goes red+faded when cost > budget.)

    :class="q('#cost').valueAsNumber > q('#budget').valueAsNumber
                ? 'hidden faded'
                : 'visible'"

### Object form (toggle each class independently)

Price:

$

    :class="{ big: price > 1000, expensive: price > 100 }"

## 5. `:style` — string and object forms

### Object form

Width: %

### String form (CSS custom property)

Progress: %

    :style="'--pct: ' + q('#p').valueAsNumber / 100"

## 6. `:aria-*` — always "true"/"false" (never removed)

Expand

Hidden until expanded.

## 7. `matches()` — bare alias for `this.matches()`

Required field:
Disabled while form has any :invalid input

    :disabled="matches(':has(input:invalid)')"

## 8. `:checked` and `:value` — property + attribute sync

Source checkbox

Mirror: (DOM attribute AND .checked property stay in sync)

Source text:

Uppercased mirror:

## 9. ARIA-as-state: tabs via `take()`

Home content.
Profile content.
Settings content.

    hx-on:click="take('aria-selected', '[role=tab]')"
    :hidden="q('#tab-id').matches('[aria-selected=false]')"

## 10. `toggle()` — binary flip + N-value cycling

(class.active + aria-pressed)

(cycles through light → dark → auto)

## 11. `<output>` as computed variable

Rate: Nights: 10% promo

Subtotal: $

Total: $

`<output :value="...">` acts as a named computed value. `output.value` is an alias for `textContent`, so the result is readable via `q('#subtotal').value` elsewhere. Add your own `display:none` (or a global rule) if you want it invisible. Use `:text` on `<output>` when you want it visible.

    <output id="subtotal" style="display:none" :value="..."></output>
    q('#subtotal').value                                    // read elsewhere

## 12. `.focused` via `:class` + `:focus-within`

    :class="{ focused: matches(':focus-within') }"

## 13. Extended form: debounced live search

Search:

(empty)

    hx-live="
      let term = q('#q').value;
      if (!term) { this.textContent = '(empty)'; return; }
      await debounce(300);
      this.textContent = 'searching for \"' + term + '\"…';
    "

## 14. Extended form: cross-element writes

Width:

## 15. Extended form: imperative DOM ops (filter)

Filter:

- apple
- apricot
- banana
- blueberry
- cherry
- date
- elderberry

    hx-live="
      let f = q('#filter').value.toLowerCase();
      for (let li of q('ul li'))
        li.hidden = f && !li.textContent.toLowerCase().includes(f);
    "

## 16. `attr()` scope helper — unified attribute access

## 17. `q()` directional selectors

## 18. `htmx.live.refresh()` for non-DOM state

    localStorage.setItem(...)
    htmx.live.refresh()  // expressions reading localStorage recompute

## 19. Cascading `data` proxy (Alpine `x-data` style)

State lives on a container as `data-*` attribute. Descendants inherit reads and writes via the `data` scope helper (Proxy). `data.foo` resolves to the nearest ancestor with `data-foo`. Writes target the same ancestor.

Count (read at top level):

Nested child reads ancestor:

Deeper still:

    <div data-count="0">
      <button hx-on:click="data.count++">+</button>
      <span :text="data.count"></span>       <!-- reactive -->
    </div>

## 20. `with (data) { ... }` — multi-write handlers

JavaScript `with` statement scopes property lookups to `data`, letting you mutate multiple keys without prefixing. Use sparingly — works only in non-strict mode (hx-on / hx-live expressions, not modules).

❤️ · 🔁 · 👁

    <div data-likes="0" data-shares="0">
      <button hx-on:click="with (data) { likes++; shares++ }">Boost</button>
    </div>

## 21. Cascading data + `:class` for branching UI

Tab state via cascading `data-tab` instead of `aria-selected`. One state attribute, descendants read it. No `take()` needed since the state lives in one place.

Home panel content.
Profile panel content.
Settings panel content.

    <div data-tab="home">
      <button hx-on:click="data.tab = 'home'" :class="{ active: data.tab === 'home' }">Home</button>
      <div :hidden="data.tab !== 'home'">...</div>
    </div>