@@ -3800,7 +3800,11 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
3800
3800
<p>Finally, the following terms are defined <cite>ARIA</cite>: <ref spec=ARIA></p>
3801
3801
3802
3802
<ul class="brief">
3803
+ <li><dfn data-x-href="https://w3c.github.io/aria/#dfn-role">role</dfn></li>
3803
3804
<li><dfn data-x-href="https://w3c.github.io/aria/#dfn-accessible-name" data-x="concept-accessible-name">accessible name</dfn></li>
3805
+ <li>The <dfn data-x-href="https://w3c.github.io/aria/#ARIAMixin"><code>ARIAMixin</code></dfn> interface, with its associated
3806
+ <dfn data-x-href="https://w3c.github.io/aria/#dfn-get-the-accessibility-idl-attribute">get the accessibility IDL attribute</dfn> and
3807
+ <dfn data-x-href="https://w3c.github.io/aria/#dfn-set-the-accessibility-idl-attribute">set the accessibility IDL attribute</dfn> hooks</li>
3804
3808
</ul>
3805
3809
</dd>
3806
3810
@@ -12643,8 +12647,41 @@ interface <dfn>DOMStringMap</dfn> {
12643
12647
<h4 id="wai-aria">Requirements related to ARIA and to platform accessibility APIs</h4>
12644
12648
12645
12649
<p>User agent requirements for implementing Accessibility API semantics on <span>HTML
12646
- elements</span> are defined in <cite>HTML Accessibility API Mappings</cite>. <ref
12647
- spec=HTMLAAM></p>
12650
+ elements</span> are defined in <cite>HTML Accessibility API Mappings</cite>. In addition to the
12651
+ rules there, for a <span>custom element</span> <var>element</var>, the default ARIA role
12652
+ semantics are determined as follows: <ref spec=HTMLAAM></p>
12653
+
12654
+ <ol>
12655
+ <li><p>Let <var>map</var> be <var>element</var>'s <span>native accessibility semantics
12656
+ map</span>.</p></li>
12657
+
12658
+ <li><p>If <var>map</var>["<code data-x="">role</code>"] <span data-x="map exists">exists</span>,
12659
+ then return it.</p></li>
12660
+
12661
+ <li><p>Otherwise, return no role.</p></li>
12662
+ </ol>
12663
+
12664
+ <p>Similarly, for a <span>custom element</span> <var>element</var>, the default ARIA state and
12665
+ property semantics, for a state or property named <var>stateOrProperty</var>, are determined as
12666
+ follows:</p>
12667
+
12668
+ <ol>
12669
+ <li><p>Let <var>map</var> be <var>element</var>'s <span>native accessibility semantics
12670
+ map</span>.</p></li>
12671
+
12672
+ <li><p>If <var>map</var>[<var>stateOrProperty</var>] <span data-x="map exists">exists</span>,
12673
+ then return it.</p></li>
12674
+
12675
+ <li><p>Otherwise, return the default value for <var>stateOrProperty</var>.</p></li>
12676
+ </ol>
12677
+
12678
+ <p class="note">The "default semantics" referred to here are sometimes also called "native",
12679
+ "implicit", or "host language" semantics in <cite>ARIA</cite>. <ref spec=ARIA></p>
12680
+
12681
+ <p>For an example of this in action, see <a href="#custom-elements-accessibility-example">the
12682
+ custom elements section</a>.</p>
12683
+
12684
+ <hr>
12648
12685
12649
12686
<p>Conformance checker requirements for checking use of ARIA <code
12650
12687
data-x="attr-aria-role">role</code> and <code data-x="attr-aria-*">aria-*</code> attributes on
@@ -65393,26 +65430,28 @@ document.body.appendChild(flagIcon)</code></pre>
65393
65430
65394
65431
<pre><code class="js">class MyCheckbox extends HTMLElement {
65395
65432
static get formAssociated() { return true; }
65433
+ static get observedAttributes() { return ['checked']; }
65396
65434
65397
65435
constructor() {
65398
65436
super();
65399
65437
this._internals = this.attachInternals();
65400
- this._checked = false;
65401
65438
this.addEventListener('click', this._onClick.bind(this));
65402
65439
}
65403
65440
65404
65441
get form() { return this._internals.form; }
65405
65442
get name() { return this.getAttribute('name'); }
65406
65443
get type() { return this.localName; }
65407
65444
65408
- get checked() { return this._checked; }
65409
- set checked(flag) {
65410
- this._checked = !!flag;
65411
- this._internals.setFormValue(this._checked ? 'on' : null);
65445
+ get checked() { return this.getAttribute('checked'); }
65446
+ set checked(flag) { this.toggleAttribute('checked', Boolean(flag)); }
65447
+
65448
+ attributeChangedCallback(name, oldValue, newValue) {
65449
+ // name will always be "checked" due to observedAttributes
65450
+ this._internals.setFormValue(this.checked ? 'on' : null);
65412
65451
}
65413
65452
65414
65453
_onClick(event) {
65415
- this.checked = !this._checked ;
65454
+ this.checked = !this.checked ;
65416
65455
}
65417
65456
}
65418
65457
customElements.define('my-checkbox', MyCheckbox);</code></pre>
@@ -65429,6 +65468,61 @@ customElements.define('my-checkbox', MyCheckbox);</code></pre>
65429
65468
</form>
65430
65469
</code></pre>
65431
65470
65471
+ <h5 id="custom-elements-accessibility-example">Creating a custom element with default accessible roles, states, and properties</h5>
65472
+
65473
+ <!-- NON-NORMATIVE SECTION -->
65474
+
65475
+ <p>By using the appropriate properties of <code>ElementInternals</code>, your custom element can
65476
+ have default accessibility semantics. The following code expands our form-associated checkbox from
65477
+ the previous section to properly set its default role and checkedness, as viewed by accessibility
65478
+ technology:</p>
65479
+
65480
+ <pre><code class="js" data-x="">class MyCheckbox extends HTMLElement {
65481
+ static get formAssociated() { return true; }
65482
+ static get observedAttributes() { return ['checked']; }
65483
+
65484
+ constructor() {
65485
+ super();
65486
+ this._internals = this.attachInternals();
65487
+ this.addEventListener('click', this._onClick.bind(this));
65488
+
65489
+ <mark> this._internals.role = 'checkbox';
65490
+ this._internals.ariaChecked = false;</mark>
65491
+ }
65492
+
65493
+ get form() { return this._internals.form; }
65494
+ get name() { return this.getAttribute('name'); }
65495
+ get type() { return this.localName; }
65496
+
65497
+ get checked() { return this.getAttribute('checked'); }
65498
+ set checked(flag) { this.toggleAttribute('checked', Boolean(flag)); }
65499
+
65500
+ attributeChangedCallback(name, oldValue, newValue) {
65501
+ // name will always be "checked" due to observedAttributes
65502
+ this._internals.setFormValue(this.checked ? 'on' : null);
65503
+ <mark> this._internals.ariaChecked = this.checked;</mark>
65504
+ }
65505
+
65506
+ _onClick(event) {
65507
+ this.checked = !this.checked;
65508
+ }
65509
+ }
65510
+ customElements.define('my-checkbox', MyCheckbox);</code></pre>
65511
+
65512
+ <p>Note that, like for built-in elements, these are only defaults, and can be overridden by the
65513
+ page author using the <code data-x="attr-aria-role">role</code> and <code
65514
+ data-x="attr-aria-*">aria-*</code> attributes:</p>
65515
+
65516
+ <pre class="bad"><code class="html" data-x=""><!-- This markup is non-conforming -->
65517
+ <input type="checkbox" checked role="button" aria-checked="false"></code></pre>
65518
+
65519
+ <pre class="bad"><code class="html" data-x=""><!-- This markup is probably not what the custom element author intended -->
65520
+ <my-checkbox role="button" checked aria-checked="false"></code></pre>
65521
+
65522
+ <p>Custom element authors are encouraged to state what aspects of their accessibility semantics
65523
+ are strong native semantics, i.e., should not be overriden by users of the custom element. In our
65524
+ example, the author of the <code data-x="">my-checkbox</code> element would state that its role
65525
+ and aria-checked values are strong native semantics, thus discouraging code such as the above.</p>
65432
65526
65433
65527
<h5 id="custom-elements-customized-builtin-example">Creating a customized built-in element</h5>
65434
65528
@@ -65546,16 +65640,16 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65546
65640
<code data-x="">taco-button</code> were to become logically disabled, the <code
65547
65641
data-x="attr-tabindex">tabindex</code> attribute would need to be removed.</p></li>
65548
65642
65549
- <li><p>The addition of various ARIA attributes helps convey semantics to accessibility
65550
- technology. For example, setting the <code data-x="attr-aria-role" >role</code> attribute to
65551
- "<code data-x="attr-aria-role-button">button</code>" will convey the semantics that this is a
65552
- button, enabling users to successfully interact with the control using usual button-like
65553
- interactions in their accessibility technology. Setting the <code
65554
- data-x="attr-aria-label">aria-label</code> attribute is necessary to give the button an
65555
- <span data-x="concept-accessible- name">accessible name </span>, instead of having accessibility
65556
- technology traverse its child text nodes and announce them. And setting <code
65557
- data-x="attr-aria-disabled">aria-disabled</ code> to "<code data-x="">true</code>" when the button
65558
- is logically disabled conveys to accessibility technology the button's disabled state.</p></li>
65643
+ <li><p>The addition of an ARIA role and various ARIA states and properties helps convey semantics
65644
+ to accessibility technology. For example, setting the <span >role</span> to "<code
65645
+ data-x="attr-aria-role-button">button</code>" will convey the semantics that this is a button,
65646
+ enabling users to successfully interact with the control using usual button-like interactions in
65647
+ their accessibility technology. Setting the <code data-x="attr-aria-label">aria-label</code>
65648
+ property is necessary to give the button an <span data-x="concept-accessible-name">accessible
65649
+ name</span>, instead of having accessibility technology traverse its child text nodes and
65650
+ announce them. And setting the <code data-x="attr-aria-disabled">aria-disabled</code> state to
65651
+ "< code data-x="">true</code>" when the button is logically disabled conveys to accessibility
65652
+ technology the button's disabled state.</p></li>
65559
65653
65560
65654
<li><p>The addition of event handlers to handle commonly-expected button behaviors helps convey
65561
65655
the semantics of the button to web browser users. In this case, the most relevant event handler
@@ -65579,9 +65673,11 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65579
65673
65580
65674
constructor() {
65581
65675
super();
65676
+ this._internals = this.attachInternals();
65677
+ this._internals.role = "button";
65582
65678
65583
65679
this.addEventListener("keydown", e => {
65584
- if (e.keyCode === 32 || e.keyCode === 13 ) {
65680
+ if (e.code === "Enter" || e.code === "Space" ) {
65585
65681
this.dispatchEvent(new MouseEvent("click", {
65586
65682
bubbles: true,
65587
65683
cancelable: true
@@ -65592,17 +65688,16 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65592
65688
this.addEventListener("click", e => {
65593
65689
if (this.disabled) {
65594
65690
e.preventDefault();
65595
- e.stopPropagation ();
65691
+ e.stopImmediatePropagation ();
65596
65692
}
65597
65693
});
65598
65694
65599
65695
this._observer = new MutationObserver(() => {
65600
- this.setAttribute("aria-label", this.textContent) ;
65696
+ this._internals.ariaLabel = this.textContent;
65601
65697
});
65602
65698
}
65603
65699
65604
65700
connectedCallback() {
65605
- this.setAttribute("role", "button");
65606
65701
this.setAttribute("tabindex", "0");
65607
65702
65608
65703
this._observer.observe(this, {
@@ -65619,33 +65714,29 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65619
65714
get disabled() {
65620
65715
return this.hasAttribute("disabled");
65621
65716
}
65622
-
65623
- set disabled(v) {
65624
- if (v) {
65625
- this.setAttribute("disabled", "");
65626
- } else {
65627
- this.removeAttribute("disabled");
65628
- }
65717
+ set disabled(flag) {
65718
+ this.toggleAttribute("disabled", Boolean(flag));
65629
65719
}
65630
65720
65631
- attributeChangedCallback() {
65632
- // only is called for the disabled attribute due to observedAttributes
65721
+ attributeChangedCallback(name, oldValue, newValue ) {
65722
+ // name will always be " disabled" due to observedAttributes
65633
65723
if (this.disabled) {
65634
65724
this.removeAttribute("tabindex");
65635
- this.setAttribute("aria-disabled", "true") ;
65725
+ this._internals.ariaDisabled = "true";
65636
65726
} else {
65637
65727
this.setAttribute("tabindex", "0");
65638
- this.setAttribute("aria-disabled", "false") ;
65728
+ this._internals.ariaDisabled = "false";
65639
65729
}
65640
65730
}
65641
65731
}</code></pre>
65642
65732
65643
65733
<p>Even with this rather-complicated element definition, the element is not a pleasure to use for
65644
- consumers: it will be continually "sprouting" <code data-x="attr-tabindex">tabindex</code> and
65645
- <code data-x="attr-aria-*">aria-*</code> attributes of its own volition. This is because as of now
65646
- there is no way to specify default accessibility semantics or focus behavior for custom elements,
65647
- forcing the use of these attributes to do so (even though they are usually reserved for allowing
65648
- the consumer to override default behavior).</p>
65734
+ consumers: it will be continually "sprouting" <code data-x="attr-tabindex">tabindex</code>
65735
+ attributes of its own volition, and its choice of <code data-x="">tabindex="0"</code> focusability
65736
+ behavior may not match the <code>button</code> behavior on the current platform. This is because
65737
+ as of now there is no way to specify default focus behavior for custom elements, forcing the use
65738
+ of the <code data-x="attr-tabindex">tabindex</code> attribute to do so (even though it is usually
65739
+ reserved for allowing the consumer to override default behavior).</p>
65649
65740
65650
65741
<p>In contrast, a simple <span>customized built-in element</span>, as shown in the previous
65651
65742
section, would automatically inherit the semantics and behavior of the <code>button</code>
@@ -66988,6 +67079,8 @@ interface <dfn>ElementInternals</dfn> {
66988
67079
readonly attribute <span>NodeList</span> <span data-x="dom-ElementInternals-labels">labels</span>;
66989
67080
};
66990
67081
67082
+ ElementInternals includes <span>ARIAMixin</span>;
67083
+
66991
67084
dictionary <dfn>ValidityStateFlags</dfn> {
66992
67085
boolean valueMissing = false;
66993
67086
boolean typeMismatch = false;
@@ -67072,6 +67165,17 @@ dictionary <dfn>ValidityStateFlags</dfn> {
67072
67165
<dd><p>Returns a <code>NodeList</code> of all the <code>label</code> elements that
67073
67166
<var>internals</var>'s <span data-x="internals-target">target element</span> is associated
67074
67167
with.</p></dd>
67168
+
67169
+ <dt><var>internals</var> . <code data-x=""><a href="#dom-ElementInternals-accessibility-idl-get">role</a></code> [ = <var>value</var> ]</dt>
67170
+ <dd><p>Sets or retrieves the default ARIA role for <var>internals</var>'s <span
67171
+ data-x="internals-target">target element</span>, which will be used unless the page author
67172
+ overrides it using the <code data-x="attr-aria-role">role</code> attribute.</p></dd>
67173
+
67174
+ <dt><var>internals</var> . <code data-x=""><a href="#dom-ElementInternals-accessibility-idl-get">aria*</a></code> [ = <var>value</var> ]</dt>
67175
+ <dd><p>Sets or retrieves various default ARIA states or property values for
67176
+ <var>internals</var>'s <span data-x="internals-target">target element</span>, which will be used
67177
+ unless the page author overrides them using the <code data-x="attr-aria-*">aria-*</code>
67178
+ attributes.</p></dd>
67075
67179
</dl>
67076
67180
67077
67181
<p>Each <code>ElementInternals</code> has a <dfn data-x="internals-target">target element</dfn>,
@@ -67281,6 +67385,55 @@ dictionary <dfn>ValidityStateFlags</dfn> {
67281
67385
67282
67386
</div>
67283
67387
67388
+ <hr>
67389
+
67390
+ <p w-nohtml>By using the <code data-x="">role</code> and <code data-x="">aria*</code> properties
67391
+ of <code>ElementInternals</code>, custom element can set default accessibile roles, states, and
67392
+ property values for their custom element, similar to how native elements behave. See <a
67393
+ href="#custom-elements-accessibility-example">the example above</a> for more details.</p>
67394
+
67395
+ <div w-nodev>
67396
+
67397
+ <p>Each <span>custom element</span> has a <dfn>native accessibility semantics map</dfn>, which is
67398
+ a <span>map</span>, initially empty. See the <a href="#wai-aria">Requirements related to ARIA and
67399
+ to platform accessibility APIs</a> section for information on how this impacts platform
67400
+ accessibility APIs.</p>
67401
+
67402
+ <p><code>ElementInternals</code> includes the <code>ARIAMixin</code> mixin. The IDL attributes
67403
+ provided by this mixin are used to manipulate the <span data-x="internals-target">target
67404
+ element</span>'s <span>native accessibility semantics map</span>, as follows:</p>
67405
+
67406
+ <p id="dom-ElementInternals-accessibility-idl">To <span>get the accessibility IDL attribute</span>
67407
+ for <code>ElementInternals</code>, given <var>internals</var>, <var>idlAttribute</var>, and
67408
+ <var>contentAttribute</var>:</p>
67409
+
67410
+ <ol>
67411
+ <li><p>Let <var>map</var> be <span>this</span>'s <span data-x="internals-target">target
67412
+ element</span>'s <span>native accessibility semantics map</span>.</p></li>
67413
+
67414
+ <li><p>If <var>map</var>[<var>contentAttribute</var>] <span data-x="map exists">exists</span>,
67415
+ then return it.</p></li>
67416
+
67417
+ <li><p>Return null.</p></li>
67418
+ </ol>
67419
+
67420
+ <p>To <span>set the accessibility IDL attribute</span> for <code>ElementInternals</code>, given
67421
+ <var>internals</var>, <var>idlAttribute</var>, <var>contentAttribute</var>, and
67422
+ <var>value</var>:</p>
67423
+
67424
+ <ol>
67425
+ <li><p>Let <var>map</var> be <span>this</span>'s <span data-x="internals-target">target
67426
+ element</span>'s <span>native accessibility semantics map</span>.</p></li>
67427
+
67428
+ <li><p>If <var>value</var> is null, then <span data-x="map remove">remove</span>
67429
+ <var>map</var>[<var>contentAttribute</var>].</p></li>
67430
+
67431
+ <li><p>Otherwise, <span data-x="map set">set</span> <var>map</var>[<var>contentAttribute</var>]
67432
+ to <var>value</var>.</p></li>
67433
+ </ol>
67434
+
67435
+ </div>
67436
+
67284
67437
<h3 split-filename="semantics-other" id="common-idioms">Common idioms without dedicated elements</h3>
67285
67438
67286
67439
<h4 id="rel-up">Bread crumb navigation</h4>
0 commit comments