Add typed aria.* access to hx-live - #3928
Conversation
b2e741f to
33b015d
Compare
aria.* to hx-live
010b990 to
c6ef0a1
Compare
aria.* to hx-livearia.* to hx-live
6fb0bae to
68ce0ac
Compare
68ce0ac to
e0a6eb9
Compare
e0a6eb9 to
1909a74
Compare
|
dedicating 10% more code length for additional Aria support may not be worth the investment maybe. could maybe simplify the aria data access only like: four-dev...MichaelWest22:aria-hx-live . setting things like aria busy in the example is something that is ideally done via htmx events during active requests and I don't think the parent proxy access is worth the extra complexity to add. For data parent access proxy is really useful but I don't think this will see as much practical use. With htmx we should be encouraging people to use native semantic element tags which by design do not need almost any custom aria handling which is why I don't think there is a lot of demand for it. Each proxy object we add to the dynamic expressions also has a performance cost and slow down LLM's writing the code. |
|
@MichaelWest22 I agree that native HTML should come first. The useful cases for For example, tabs: <div role="tablist">
<button
role="tab"
aria-selected="true"
hx-on:click="
q('[role=tab]').aria.selected = false
this.aria.selected = true
"
>
Profile
</button>
<button
role="tab"
aria-selected="false"
hx-on:click="
q('[role=tab]').aria.selected = false
this.aria.selected = true
"
>
Security
</button>
</div>There is no native HTML attribute for “this tab is selected”. Here, A sortable table is another strong case: <th aria-sort="ascending">
<button hx-on:click="
aria.sort =
aria.sort === 'ascending'
? 'descending'
: 'ascending'
">
Name
</button>
</th>HTML has table semantics, but no native sort-direction state. A combobox also needs state that native <div
role="combobox"
aria-expanded="false"
aria-activedescendant=""
>
<input
hx-on:focus="aria.expanded = true"
hx-on:keydown.escape="
aria.expanded = false
aria.activeDescendant = null
"
>
<ul role="listbox" :hidden="!aria.expanded">
<li
id="open-file"
role="option"
:aria-selected="aria.activeDescendant === this.id"
hx-on:mousemove="aria.activeDescendant = this.id"
>
Open file
</li>
</ul>
</div>The parent lookup matters here because the option updates state owned by the combobox root: aria.activeDescendant = this.idinstead of: q('closest [role=combobox]')
.attr('aria-activedescendant', this.id)That is the main value for me: ARIA becomes the actual DOM state, not a second copy next to One concern: I do not think generic <div aria-valuenow="50"></div> <!-- number -->
<div aria-label="50"></div> <!-- string -->
<div aria-controls="50"></div> <!-- ID list -->Generic parsing returns the number |
|
Truth is that for the examples above, you could also use The main idea with aria.busy // boolean
aria.valueNow // number
aria.controls // array
aria.current // string/tokenSo you can write: aria.valueNow += 10
aria.controls.push('help')
aria.expanded = !aria.expandedwithout doing
I think in many cases you'd better reach for e.g. And this change makes it possible to do that with an easy API where the DOM just assembles itself as it should, while you do don't have to work with strings. |
Why
Before:
After:
Changes
Read and write ARIA attributes in three ways:
Read each value as its ARIA type:
Use
attr()to read or write the exact DOM text:Use
nullordeleteto remove an ARIA attribute:Related to #3931. Either PR can merge first.