Pulse · Alpine-style

UI directives

Six small directives that cover the fiddly parts of UI work: height animation to auto, state saved in localStorage, focus trapping, input masks, and visibility and size observers.

What's in the add-on

  • kj-collapse — animates height to and from auto alongside kj-show.
  • $persist(value) — keeps a kj-data property in localStorage (or .using(sessionStorage)) across reloads.
  • kj-trap — keeps keyboard focus inside an element while the expression is true, and restores it afterwards.
  • kj-mask — formats input as it is typed (9 digit, a letter, * anything); kj-mask:dynamic with $money($input) for currency.
  • kj-intersect and kj-resize — run an expression when the element enters the viewport or changes size.

Live · Collapse, persist, mask and focus trap

kj-collapse

A jQuery-compatible library with htmx, Alpine and GSAP features built in. This panel's height animates to auto.

$persist

You've loaded this page time(s). Reload — the count survives.

kj-mask

kj-trap

Tab cycles inside here. Escape or Close to leave.

View the source for this demo
HTML
<div class="grid g2" style="gap:14px" kj-data="{ open: false, visits: $persist(0).as('kj-demo-visits'), phone: '', price: '', trap: false }" kj-init="visits++">
  <div class="ui-box"><h4>kj-collapse</h4>
    <button type="button" @click="open = !open" :aria-expanded="open">What is jsKjs?</button>
    <div kj-show="open" kj-collapse><p style="margin:10px 0 0">A jQuery-compatible library with htmx, Alpine and GSAP features built in. This panel's height animates to <code>auto</code>.</p></div>
  </div>
  <div class="ui-box"><h4>$persist</h4>
    <p style="margin:0">You've loaded this page <b kj-text="visits"></b> time(s). Reload — the count survives.</p>
    <button type="button" style="margin-top:8px" @click="visits = 0">Reset</button>
  </div>
  <div class="ui-box"><h4>kj-mask</h4>
    <div class="field"><label for="ui-phone">Phone</label><input id="ui-phone" kj-mask="(999) 999-9999" kj-model="phone" inputmode="numeric" placeholder="(555) 123-4567"></div>
    <div class="field"><label for="ui-price">Price</label><input id="ui-price" kj-mask:dynamic="$money($input)" kj-model="price" inputmode="decimal" placeholder="1,234.00"></div>
  </div>
  <div class="ui-box"><h4>kj-trap</h4>
    <button type="button" @click="trap = true">Open trapped panel</button>
    <div kj-show="trap" kj-trap="trap" @keydown.escape="trap = false" style="margin-top:10px" role="group" aria-label="Trapped panel">
      <p style="margin:0 0 6px">Tab cycles inside here. Escape or Close to leave.</p>
      <input aria-label="First field" placeholder="First"> <input aria-label="Second field" placeholder="Second">
      <button type="button" @click="trap = false">Close</button>
    </div>
  </div>
</div>

More examples

Lazy work with kj-intersect, layout with kj-resize

HTML
<img kj-intersect.once="$el.src = $el.dataset.src" data-src="/big.jpg" alt="…">
<div kj-data="{ w: 0 }" kj-resize="w = $width"><span kj-text="w > 600 ? 'wide' : 'narrow'"></span></div>

API

$persist(v).as(key) · kj-intersect[.once.half] · kj-trap[.inert.noscroll] + $focus · kj-collapse[.duration.min] · kj-mask="99/99/9999" + $money · kj-resize

Alpine's official plugins, as Pulse directives.

Reference: jskjs.ui.js →
$el · $refs · $root · $data · $store · $dispatch(name, detail) · $watch(expr, cb) · $nextTick() · $id(name) · $event

Available inside every expression.

Reference: Magics →

Migrating

Coming from Alpine.jsThe Persist, Focus, Collapse, Mask, Intersect and Resize plugins are one add-on here. x-collapse → kj-collapse, $persist unchanged, x-trap → kj-trap.

Related features