The value of a bound attribute is a JavaScript expression. Three rules cover every case:
false,nullorundefinedremove the attribute — exactly whatdisabledandaria-currentneed.truesets an empty attribute (disabled="").:classis added to the element's static classes instead of replacing them.
Binding ARIA attributes this way keeps what assistive technology hears in lockstep with what sighted users see.
View the source for this demo
HTML
<div kj-data="gallery" tabindex="0" @keydown="key($event)" aria-label="Gallery. Use the left and right arrow keys." role="group">
<figure class="gal-main">
<img :src="art(current)" :alt="titles[current]" width="640" height="360">
<figcaption kj-text="(current + 1) + ' / ' + titles.length + ' — ' + titles[current]" aria-live="polite"></figcaption>
</figure>
<div class="row" style="margin-top:12px">
<button type="button" @click="current--" :disabled="current === 0" aria-label="Previous">←</button>
<button type="button" class="thumb" @click="current = 0" :class="current === 0 ? 'on' : ''" :aria-current="current === 0 ? 'true' : false" aria-label="Artwork 1"><img :src="art(0)" alt="" width="64" height="36"></button>
<button type="button" class="thumb" @click="current = 1" :class="current === 1 ? 'on' : ''" :aria-current="current === 1 ? 'true' : false" aria-label="Artwork 2"><img :src="art(1)" alt="" width="64" height="36"></button>
<button type="button" class="thumb" @click="current = 2" :class="current === 2 ? 'on' : ''" :aria-current="current === 2 ? 'true' : false" aria-label="Artwork 3"><img :src="art(2)" alt="" width="64" height="36"></button>
<button type="button" class="thumb" @click="current = 3" :class="current === 3 ? 'on' : ''" :aria-current="current === 3 ? 'true' : false" aria-label="Artwork 4"><img :src="art(3)" alt="" width="64" height="36"></button>
<button type="button" @click="current++" :disabled="current === titles.length - 1" aria-label="Next">→</button>
</div>
</div>JavaScript
$.component('gallery', () => ({
current: 0,
titles: ['Signal, blue hour', 'Hypermedia at dusk', 'Mint pulse', 'Amber motion'],
colors: ['#4D7CFF', '#FF4F8B', '#22CFA2', '#FFB224'],
art(i) { // a generated SVG "photo" per slide
const c = this.colors[i];
const svg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 360"><rect width="640" height="360" fill="#13152B"/>' +
'<circle cx="' + (170 + i * 100) + '" cy="170" r="' + (80 + i * 14) + '" fill="' + c + '" opacity=".9"/>' +
'<path d="M0 ' + (300 - i * 25) + ' C 210 ' + (110 + i * 35) + ', 430 380, 640 ' + (130 + i * 30) + '" stroke="' + c + '" stroke-width="12" fill="none" opacity=".55"/></svg>';
return 'data:image/svg+xml,' + encodeURIComponent(svg);
},
key(e) {
if (e.key === 'ArrowRight' && this.current < this.titles.length - 1) this.current++;
if (e.key === 'ArrowLeft' && this.current > 0) this.current--;
},
}));More examples
Bindings
HTML
<a :href="'/users/' + user.id" kj-text="user.name"></a>
<button :disabled="saving" :aria-busy="saving">Save</button>
<li class="tab" :class="active ? 'is-active' : ''">…</li>
<div :style="'width:' + progress + '%'"></div>API
:src="expr" · kj-bind:class="expr"
Bind any attribute to an expression. :class merges with the element’s static classes; false/null removes the attribute.
Migrating
Coming from Alpine.js
x-bind:/: work the same for strings. Alpine's object syntax for :class="{ active: on }" and :style="{ … }" is not included — use a string expression.Related features
Pulse
Reactive state
Declare state on an element with kj-data and every binding inside updates when it changes.
Open →PulseTwo-way binding
Bind form controls to state in both directions with kj-model — text, number, range, checkbox, radio and select.
Open →PulseReusable components
Register a component once with $.component(name, factory) and reuse it anywhere with kj-data="name(args)".
Open →