Skip to content
Home Theme Gallery

Alpine

Home-UI does not know Alpine exists. It works with Alpine because it does two things Alpine expects of a native control:

  1. Value-carrying controls emit native-named input and change, so x-model binds with no adapter.
  2. Every event is dispatched with bubbles: true and composed: true, so it crosses the shadow boundary and an ancestor x-on sees it.
<div x-data="{ email: 'dev@example.com' }">
<hui-input name="email" type="email" x-model="email"></hui-input>
<p>You entered <span x-text="email"></span></p>
</div>

x-model listens for input on the host and writes the value property back. A component stops the internal native control’s own event at its boundary and dispatches one synthetic input/change from the host, so Alpine receives exactly one event per interaction and never a duplicate.

Component-specific events keep the hui- prefix. All carry detail where there is something to carry:

<div x-data="{ course: null }" x-on:hui-select="course = $event.detail.value">
<hui-calendar name="courseDate"></hui-calendar>
</div>
EventFromDetail
input, changeevery value controlnone; read the host’s value
hui-selecthui-dropdown-menu, hui-calendar, hui-command{ value } (and item/option)
hui-open, hui-closedialog, popover, select, tabs, commandnone
hui-dismissdialognone; cancelable — call $event.preventDefault() to refuse
hui-toast-dismisstoastnone
hui-confirm, hui-cancelconfirm-actionnone
hui-theme-changetheme-togglenone

Because every event bubbles and is composed, you can listen on a container rather than on each component:

<form x-on:change="dirty = true">
<hui-input name="a"></hui-input>
<hui-switch name="b"></hui-switch>
</form>

x-show toggles display on the component’s host element. That is safe: a hidden control is not focusable, and re-showing it does not disturb its internal state or listeners.

Controls Alpine would bind to
Source
<div style="display:flex;flex-direction:column;gap:.75rem;max-width:22rem">
<hui-input name="email" type="email" value="dev@example.com" aria-label="Email"></hui-input>
<hui-switch name="subscribe" checked>Subscribe</hui-switch>
</div>

With Alpine absent, every component behaves identically — there is no window.Alpine read anywhere in the library. Two components on one page emitting the same event are distinguished by their host element ($event.target). Alpine replacing a parent must not orphan a component’s listeners; teardown is handled in disconnectedCallback, so it does not.