@@ -3787,6 +3787,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
3787
3787
attributes are defined in <cite>ARIA</cite>: <ref spec=ARIA></p>
3788
3788
3789
3789
<ul class="brief">
3790
+ <li><dfn data-x-href="https://w3c.github.io/aria/#aria-checked"><code data-x="attr-aria-checked">aria-checked</code></dfn></li>
3790
3791
<li><dfn data-x-href="https://w3c.github.io/aria/#aria-describedby"><code data-x="attr-aria-describedby">aria-describedby</code></dfn></li>
3791
3792
<li><dfn data-x-href="https://w3c.github.io/aria/#aria-disabled"><code data-x="attr-aria-disabled">aria-disabled</code></dfn></li>
3792
3793
<li><dfn data-x-href="https://w3c.github.io/aria/#aria-label"><code data-x="attr-aria-label">aria-label</code></dfn></li>
@@ -3795,7 +3796,11 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
3795
3796
<p>Finally, the following terms are defined <cite>ARIA</cite>: <ref spec=ARIA></p>
3796
3797
3797
3798
<ul class="brief">
3799
+ <li><dfn data-x-href="https://w3c.github.io/aria/#dfn-role">role</dfn></li>
3798
3800
<li><dfn data-x-href="https://w3c.github.io/aria/#dfn-accessible-name" data-x="concept-accessible-name">accessible name</dfn></li>
3801
+ <li>The <dfn data-x-href="https://w3c.github.io/aria/#ARIAMixin"><code>ARIAMixin</code></dfn> interface, with its associated
3802
+ <dfn data-x-href="https://w3c.github.io/aria/#dfn-ariamixin-getter-steps"><code>ARIAMixin</code> getter steps</dfn> and
3803
+ <dfn data-x-href="https://w3c.github.io/aria/#dfn-ariamixin-setter-steps"><code>ARIAMixin</code> setter steps</dfn> hooks</li>
3799
3804
</ul>
3800
3805
</dd>
3801
3806
@@ -12635,8 +12640,46 @@ interface <dfn>DOMStringMap</dfn> {
12635
12640
<h4 id="wai-aria">Requirements related to ARIA and to platform accessibility APIs</h4>
12636
12641
12637
12642
<p>User agent requirements for implementing Accessibility API semantics on <span>HTML
12638
- elements</span> are defined in <cite>HTML Accessibility API Mappings</cite>. <ref
12639
- spec=HTMLAAM></p>
12643
+ elements</span> are defined in <cite>HTML Accessibility API Mappings</cite>. In addition to the
12644
+ rules there, for a <span>custom element</span> <var>element</var>, the default ARIA role
12645
+ semantics are determined as follows: <ref spec=HTMLAAM></p>
12646
+
12647
+ <ol>
12648
+ <li><p>Let <var>map</var> be <var>element</var>'s <span>native accessibility semantics
12649
+ map</span>.</p></li>
12650
+
12651
+ <li><p>If <var>map</var>["<code data-x="">role</code>"] <span data-x="map exists">exists</span>,
12652
+ then return it.</p></li>
12653
+
12654
+ <li><p>Return no role.</p></li>
12655
+ </ol>
12656
+
12657
+ <p>Similarly, for a <span>custom element</span> <var>element</var>, the default ARIA state and
12658
+ property semantics, for a state or property named <var>stateOrProperty</var>, are determined as
12659
+ follows:</p>
12660
+
12661
+ <ol>
12662
+ <li><p>Let <var>map</var> be <var>element</var>'s <span>native accessibility semantics
12663
+ map</span>.</p></li>
12664
+
12665
+ <li><p>If <var>map</var>[<var>stateOrProperty</var>] <span data-x="map exists">exists</span>,
12666
+ then return it.</p></li>
12667
+
12668
+ <li><p>Return the default value for <var>stateOrProperty</var>.</p></li>
12669
+ </ol>
12670
+
12671
+ <p class="note">The "default semantics" referred to here are sometimes also called "native",
12672
+ "implicit", or "host language" semantics in <cite>ARIA</cite>. <ref spec=ARIA></p>
12673
+
12674
+ <p class="note">One implication of these definitions is that the default semantics can change over
12675
+ time. This allows custom elements the same expressivity as built-in elements; e.g., compare to how
12676
+ the default ARIA role semantics of an <code>a</code> element change as the <code
12677
+ data-x="attr-hyperlink-href">href</code> attribute is added or removed.</p>
12678
+
12679
+ <p>For an example of this in action, see <a href="#custom-elements-accessibility-example">the
12680
+ custom elements section</a>.</p>
12681
+
12682
+ <hr>
12640
12683
12641
12684
<p>Conformance checker requirements for checking use of ARIA <code
12642
12685
data-x="attr-aria-role">role</code> and <code data-x="attr-aria-*">aria-*</code> attributes on
@@ -65519,26 +65562,28 @@ document.body.appendChild(flagIcon)</code></pre>
65519
65562
65520
65563
<pre><code class="js">class MyCheckbox extends HTMLElement {
65521
65564
static get formAssociated() { return true; }
65565
+ static get observedAttributes() { return ['checked']; }
65522
65566
65523
65567
constructor() {
65524
65568
super();
65525
65569
this._internals = this.attachInternals();
65526
- this._checked = false;
65527
65570
this.addEventListener('click', this._onClick.bind(this));
65528
65571
}
65529
65572
65530
65573
get form() { return this._internals.form; }
65531
65574
get name() { return this.getAttribute('name'); }
65532
65575
get type() { return this.localName; }
65533
65576
65534
- get checked() { return this._checked; }
65535
- set checked(flag) {
65536
- this._checked = !!flag;
65537
- this._internals.setFormValue(this._checked ? 'on' : null);
65577
+ get checked() { return this.getAttribute('checked'); }
65578
+ set checked(flag) { this.toggleAttribute('checked', Boolean(flag)); }
65579
+
65580
+ attributeChangedCallback(name, oldValue, newValue) {
65581
+ // name will always be "checked" due to observedAttributes
65582
+ this._internals.setFormValue(this.checked ? 'on' : null);
65538
65583
}
65539
65584
65540
65585
_onClick(event) {
65541
- this.checked = !this._checked ;
65586
+ this.checked = !this.checked ;
65542
65587
}
65543
65588
}
65544
65589
customElements.define('my-checkbox', MyCheckbox);</code></pre>
@@ -65555,6 +65600,62 @@ customElements.define('my-checkbox', MyCheckbox);</code></pre>
65555
65600
</form>
65556
65601
</code></pre>
65557
65602
65603
+ <h5 id="custom-elements-accessibility-example">Creating a custom element with default accessible roles, states, and properties</h5>
65604
+
65605
+ <!-- NON-NORMATIVE SECTION -->
65606
+
65607
+ <p>By using the appropriate properties of <code>ElementInternals</code>, your custom element can
65608
+ have default accessibility semantics. The following code expands our form-associated checkbox from
65609
+ the previous section to properly set its default role and checkedness, as viewed by accessibility
65610
+ technology:</p>
65611
+
65612
+ <pre><code class="js" data-x="">class MyCheckbox extends HTMLElement {
65613
+ static get formAssociated() { return true; }
65614
+ static get observedAttributes() { return ['checked']; }
65615
+
65616
+ constructor() {
65617
+ super();
65618
+ this._internals = this.attachInternals();
65619
+ this.addEventListener('click', this._onClick.bind(this));
65620
+
65621
+ <mark> this._internals.role = 'checkbox';
65622
+ this._internals.ariaChecked = false;</mark>
65623
+ }
65624
+
65625
+ get form() { return this._internals.form; }
65626
+ get name() { return this.getAttribute('name'); }
65627
+ get type() { return this.localName; }
65628
+
65629
+ get checked() { return this.getAttribute('checked'); }
65630
+ set checked(flag) { this.toggleAttribute('checked', Boolean(flag)); }
65631
+
65632
+ attributeChangedCallback(name, oldValue, newValue) {
65633
+ // name will always be "checked" due to observedAttributes
65634
+ this._internals.setFormValue(this.checked ? 'on' : null);
65635
+ <mark> this._internals.ariaChecked = this.checked;</mark>
65636
+ }
65637
+
65638
+ _onClick(event) {
65639
+ this.checked = !this.checked;
65640
+ }
65641
+ }
65642
+ customElements.define('my-checkbox', MyCheckbox);</code></pre>
65643
+
65644
+ <p>Note that, like for built-in elements, these are only defaults, and can be overridden by the
65645
+ page author using the <code data-x="attr-aria-role">role</code> and <code
65646
+ data-x="attr-aria-*">aria-*</code> attributes:</p>
65647
+
65648
+ <pre class="bad"><code class="html" data-x=""><!-- This markup is non-conforming -->
65649
+ <input type="checkbox" checked role="button" aria-checked="false"></code></pre>
65650
+
65651
+ <pre class="bad"><code class="html" data-x=""><!-- This markup is probably not what the custom element author intended -->
65652
+ <my-checkbox role="button" checked aria-checked="false"></code></pre>
65653
+
65654
+ <p>Custom element authors are encouraged to state what aspects of their accessibility semantics
65655
+ are strong native semantics, i.e., should not be overriden by users of the custom element. In our
65656
+ example, the author of the <code data-x="">my-checkbox</code> element would state that its
65657
+ <span>role</span> and <code data-x="attr-aria-checked">aria-checked</code> values are strong
65658
+ native semantics, thus discouraging code such as the above.</p>
65558
65659
65559
65660
<h5 id="custom-elements-customized-builtin-example">Creating a customized built-in element</h5>
65560
65661
@@ -65672,16 +65773,16 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65672
65773
<code data-x="">taco-button</code> were to become logically disabled, the <code
65673
65774
data-x="attr-tabindex">tabindex</code> attribute would need to be removed.</p></li>
65674
65775
65675
- <li><p>The addition of various ARIA attributes helps convey semantics to accessibility
65676
- technology. For example, setting the <code data-x="attr-aria-role" >role</code> attribute to
65677
- "<code data-x="attr-aria-role-button">button</code>" will convey the semantics that this is a
65678
- button, enabling users to successfully interact with the control using usual button-like
65679
- interactions in their accessibility technology. Setting the <code
65680
- data-x="attr-aria-label">aria-label</code> attribute is necessary to give the button an
65681
- <span data-x="concept-accessible- name">accessible name </span>, instead of having accessibility
65682
- technology traverse its child text nodes and announce them. And setting <code
65683
- data-x="attr-aria-disabled">aria-disabled</ code> to "<code data-x="">true</code>" when the button
65684
- is logically disabled conveys to accessibility technology the button's disabled state.</p></li>
65776
+ <li><p>The addition of an ARIA role and various ARIA states and properties helps convey semantics
65777
+ to accessibility technology. For example, setting the <span >role</span> to "<code
65778
+ data-x="attr-aria-role-button">button</code>" will convey the semantics that this is a button,
65779
+ enabling users to successfully interact with the control using usual button-like interactions in
65780
+ their accessibility technology. Setting the <code data-x="attr-aria-label">aria-label</code>
65781
+ property is necessary to give the button an <span data-x="concept-accessible-name">accessible
65782
+ name</span>, instead of having accessibility technology traverse its child text nodes and
65783
+ announce them. And setting the <code data-x="attr-aria-disabled">aria-disabled</code> state to
65784
+ "< code data-x="">true</code>" when the button is logically disabled conveys to accessibility
65785
+ technology the button's disabled state.</p></li>
65685
65786
65686
65787
<li><p>The addition of event handlers to handle commonly-expected button behaviors helps convey
65687
65788
the semantics of the button to web browser users. In this case, the most relevant event handler
@@ -65705,9 +65806,11 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65705
65806
65706
65807
constructor() {
65707
65808
super();
65809
+ this._internals = this.attachInternals();
65810
+ this._internals.role = "button";
65708
65811
65709
65812
this.addEventListener("keydown", e => {
65710
- if (e.keyCode === 32 || e.keyCode === 13 ) {
65813
+ if (e.code === "Enter" || e.code === "Space" ) {
65711
65814
this.dispatchEvent(new MouseEvent("click", {
65712
65815
bubbles: true,
65713
65816
cancelable: true
@@ -65718,17 +65821,16 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65718
65821
this.addEventListener("click", e => {
65719
65822
if (this.disabled) {
65720
65823
e.preventDefault();
65721
- e.stopPropagation ();
65824
+ e.stopImmediatePropagation ();
65722
65825
}
65723
65826
});
65724
65827
65725
65828
this._observer = new MutationObserver(() => {
65726
- this.setAttribute("aria-label", this.textContent) ;
65829
+ this._internals.ariaLabel = this.textContent;
65727
65830
});
65728
65831
}
65729
65832
65730
65833
connectedCallback() {
65731
- this.setAttribute("role", "button");
65732
65834
this.setAttribute("tabindex", "0");
65733
65835
65734
65836
this._observer.observe(this, {
@@ -65745,33 +65847,29 @@ console.log(plasticButton.outerHTML); // will output '<button is="plastic-but
65745
65847
get disabled() {
65746
65848
return this.hasAttribute("disabled");
65747
65849
}
65748
-
65749
- set disabled(v) {
65750
- if (v) {
65751
- this.setAttribute("disabled", "");
65752
- } else {
65753
- this.removeAttribute("disabled");
65754
- }
65850
+ set disabled(flag) {
65851
+ this.toggleAttribute("disabled", Boolean(flag));
65755
65852
}
65756
65853
65757
- attributeChangedCallback() {
65758
- // only is called for the disabled attribute due to observedAttributes
65854
+ attributeChangedCallback(name, oldValue, newValue ) {
65855
+ // name will always be " disabled" due to observedAttributes
65759
65856
if (this.disabled) {
65760
65857
this.removeAttribute("tabindex");
65761
- this.setAttribute("aria-disabled", "true") ;
65858
+ this._internals.ariaDisabled = "true";
65762
65859
} else {
65763
65860
this.setAttribute("tabindex", "0");
65764
- this.setAttribute("aria-disabled", "false") ;
65861
+ this._internals.ariaDisabled = "false";
65765
65862
}
65766
65863
}
65767
65864
}</code></pre>
65768
65865
65769
65866
<p>Even with this rather-complicated element definition, the element is not a pleasure to use for
65770
- consumers: it will be continually "sprouting" <code data-x="attr-tabindex">tabindex</code> and
65771
- <code data-x="attr-aria-*">aria-*</code> attributes of its own volition. This is because as of now
65772
- there is no way to specify default accessibility semantics or focus behavior for custom elements,
65773
- forcing the use of these attributes to do so (even though they are usually reserved for allowing
65774
- the consumer to override default behavior).</p>
65867
+ consumers: it will be continually "sprouting" <code data-x="attr-tabindex">tabindex</code>
65868
+ attributes of its own volition, and its choice of <code data-x="">tabindex="0"</code> focusability
65869
+ behavior may not match the <code>button</code> behavior on the current platform. This is because
65870
+ as of now there is no way to specify default focus behavior for custom elements, forcing the use
65871
+ of the <code data-x="attr-tabindex">tabindex</code> attribute to do so (even though it is usually
65872
+ reserved for allowing the consumer to override default behavior).</p>
65775
65873
65776
65874
<p>In contrast, a simple <span>customized built-in element</span>, as shown in the previous
65777
65875
section, would automatically inherit the semantics and behavior of the <code>button</code>
@@ -67115,6 +67213,8 @@ interface <dfn>ElementInternals</dfn> {
67115
67213
readonly attribute <span>NodeList</span> <span data-x="dom-ElementInternals-labels">labels</span>;
67116
67214
};
67117
67215
67216
+ ElementInternals includes <span>ARIAMixin</span>;
67217
+
67118
67218
dictionary <dfn>ValidityStateFlags</dfn> {
67119
67219
boolean valueMissing = false;
67120
67220
boolean typeMismatch = false;
@@ -67199,6 +67299,17 @@ dictionary <dfn>ValidityStateFlags</dfn> {
67199
67299
<dd><p>Returns a <code>NodeList</code> of all the <code>label</code> elements that
67200
67300
<var>internals</var>'s <span data-x="internals-target">target element</span> is associated
67201
67301
with.</p></dd>
67302
+
67303
+ <dt><var>internals</var> . <code data-x=""><a href="#dom-ElementInternals-accessibility-idl-get">role</a></code> [ = <var>value</var> ]</dt>
67304
+ <dd><p>Sets or retrieves the default ARIA role for <var>internals</var>'s <span
67305
+ data-x="internals-target">target element</span>, which will be used unless the page author
67306
+ overrides it using the <code data-x="attr-aria-role">role</code> attribute.</p></dd>
67307
+
67308
+ <dt><var>internals</var> . <code data-x=""><a href="#dom-ElementInternals-accessibility-idl-get">aria*</a></code> [ = <var>value</var> ]</dt>
67309
+ <dd><p>Sets or retrieves various default ARIA states or property values for
67310
+ <var>internals</var>'s <span data-x="internals-target">target element</span>, which will be used
67311
+ unless the page author overrides them using the <code data-x="attr-aria-*">aria-*</code>
67312
+ attributes.</p></dd>
67202
67313
</dl>
67203
67314
67204
67315
<p>Each <code>ElementInternals</code> has a <dfn data-x="internals-target">target element</dfn>,
@@ -67408,6 +67519,55 @@ dictionary <dfn>ValidityStateFlags</dfn> {
67408
67519
67409
67520
</div>
67410
67521
67522
+ <hr>
67523
+
67524
+ <p w-nohtml>By using the <code data-x="">role</code> and <code data-x="">aria*</code> properties
67525
+ of <code>ElementInternals</code>, custom element authors can set default accessibile roles,
67526
+ states, and property values for their custom element, similar to how native elements behave. See
67527
+ <a href="#custom-elements-accessibility-example">the example above</a> for more details.</p>
67528
+
67529
+ <div w-nodev>
67530
+
67531
+ <p>Each <span>custom element</span> has a <dfn>native accessibility semantics map</dfn>, which is
67532
+ a <span>map</span>, initially empty. See the <a href="#wai-aria">Requirements related to ARIA and
67533
+ to platform accessibility APIs</a> section for information on how this impacts platform
67534
+ accessibility APIs.</p>
67535
+
67536
+ <p><code>ElementInternals</code> includes the <code>ARIAMixin</code> mixin. The accessors provided
67537
+ by this mixin are used to manipulate the <span data-x="internals-target">target element</span>'s
67538
+ <span>native accessibility semantics map</span>, as follows:</p>
67539
+
67540
+ <p id="dom-ElementInternals-accessibility-idl">The <span><code>ARIAMixin</code> getter
67541
+ steps</span> for <code>ElementInternals</code>, given <var>internals</var>,
67542
+ <var>idlAttribute</var>, and <var>contentAttribute</var>, are:</p>
67543
+
67544
+ <ol>
67545
+ <li><p>Let <var>map</var> be <var>internals</var>'s <span data-x="internals-target">target
67546
+ element</span>'s <span>native accessibility semantics map</span>.</p></li>
67547
+
67548
+ <li><p>If <var>map</var>[<var>contentAttribute</var>] <span data-x="map exists">exists</span>,
67549
+ then return it.</p></li>
67550
+
67551
+ <li><p>Return null.</p></li>
67552
+ </ol>
67553
+
67554
+ <p>The <span><code>ARIAMixin</code> setter steps</span> for <code>ElementInternals</code>, given
67555
+ <var>internals</var>, <var>idlAttribute</var>, <var>contentAttribute</var>, and
67556
+ <var>value</var>, are:</p>
67557
+
67558
+ <ol>
67559
+ <li><p>Let <var>map</var> be <var>internals</var>'s <span data-x="internals-target">target
67560
+ element</span>'s <span>native accessibility semantics map</span>.</p></li>
67561
+
67562
+ <li><p>If <var>value</var> is null, then <span data-x="map remove">remove</span>
67563
+ <var>map</var>[<var>contentAttribute</var>].</p></li>
67564
+
67565
+ <li><p>Otherwise, <span data-x="map set">set</span> <var>map</var>[<var>contentAttribute</var>]
67566
+ to <var>value</var>.</p></li>
67567
+ </ol>
67568
+
67569
+ </div>
67570
+
67411
67571
<h3 split-filename="semantics-other" id="common-idioms">Common idioms without dedicated elements</h3>
67412
67572
67413
67573
<h4 id="rel-up">Bread crumb navigation</h4>
0 commit comments