html-validate 8.15.0 → 8.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core.js +111 -13
- package/dist/cjs/core.js.map +1 -1
- package/dist/cjs/elements.js +13 -0
- package/dist/cjs/elements.js.map +1 -1
- package/dist/es/core.js +111 -13
- package/dist/es/core.js.map +1 -1
- package/dist/es/elements.js +13 -0
- package/dist/es/elements.js.map +1 -1
- package/dist/schema/elements.json +4 -0
- package/dist/types/browser.d.ts +19 -0
- package/dist/types/index.d.ts +19 -0
- package/package.json +4 -4
package/dist/es/core.js
CHANGED
|
@@ -801,6 +801,10 @@ const definitions = {
|
|
|
801
801
|
type: "object",
|
|
802
802
|
additionalProperties: false,
|
|
803
803
|
properties: {
|
|
804
|
+
disablable: {
|
|
805
|
+
type: "boolean",
|
|
806
|
+
title: "Disablable elements can be disabled using the disabled attribute."
|
|
807
|
+
},
|
|
804
808
|
listed: {
|
|
805
809
|
type: "boolean",
|
|
806
810
|
title: "Listed elements have a name attribute and is listed in the form and fieldset elements property."
|
|
@@ -1126,6 +1130,7 @@ function migrateElement(src) {
|
|
|
1126
1130
|
}
|
|
1127
1131
|
if (src.formAssociated) {
|
|
1128
1132
|
result.formAssociated = {
|
|
1133
|
+
disablable: Boolean(src.formAssociated.disablable),
|
|
1129
1134
|
listed: Boolean(src.formAssociated.listed)
|
|
1130
1135
|
};
|
|
1131
1136
|
} else {
|
|
@@ -2290,6 +2295,7 @@ class TextNode extends DOMNode {
|
|
|
2290
2295
|
}
|
|
2291
2296
|
|
|
2292
2297
|
const ROLE = Symbol("role");
|
|
2298
|
+
const TABINDEX = Symbol("tabindex");
|
|
2293
2299
|
var NodeClosed = /* @__PURE__ */ ((NodeClosed2) => {
|
|
2294
2300
|
NodeClosed2[NodeClosed2["Open"] = 0] = "Open";
|
|
2295
2301
|
NodeClosed2[NodeClosed2["EndTag"] = 1] = "EndTag";
|
|
@@ -2570,6 +2576,43 @@ class HtmlElement extends DOMNode {
|
|
|
2570
2576
|
}
|
|
2571
2577
|
this.attr[key].push(new Attribute(key, value, keyLocation, valueLocation, originalAttribute));
|
|
2572
2578
|
}
|
|
2579
|
+
/**
|
|
2580
|
+
* Get parsed tabindex for this element.
|
|
2581
|
+
*
|
|
2582
|
+
* - If `tabindex` attribute is not present `null` is returned.
|
|
2583
|
+
* - If attribute value is omitted or the empty string `null` is returned.
|
|
2584
|
+
* - If attribute value cannot be parsed `null` is returned.
|
|
2585
|
+
* - If attribute value is dynamic `0` is returned.
|
|
2586
|
+
* - Otherwise the parsed value is returned.
|
|
2587
|
+
*
|
|
2588
|
+
* This property does *NOT* take into account if the element have a default
|
|
2589
|
+
* `tabindex` (such as `<input>` have). Instead use the `focusable` metadata
|
|
2590
|
+
* property to determine this.
|
|
2591
|
+
*
|
|
2592
|
+
* @public
|
|
2593
|
+
* @since 8.16.0
|
|
2594
|
+
*/
|
|
2595
|
+
get tabIndex() {
|
|
2596
|
+
const cached = this.cacheGet(TABINDEX);
|
|
2597
|
+
if (cached !== void 0) {
|
|
2598
|
+
return cached;
|
|
2599
|
+
}
|
|
2600
|
+
const tabindex = this.getAttribute("tabindex");
|
|
2601
|
+
if (!tabindex) {
|
|
2602
|
+
return this.cacheSet(TABINDEX, null);
|
|
2603
|
+
}
|
|
2604
|
+
if (tabindex.value === null) {
|
|
2605
|
+
return this.cacheSet(TABINDEX, null);
|
|
2606
|
+
}
|
|
2607
|
+
if (tabindex.value instanceof DynamicValue) {
|
|
2608
|
+
return this.cacheSet(TABINDEX, 0);
|
|
2609
|
+
}
|
|
2610
|
+
const parsed = parseInt(tabindex.value, 10);
|
|
2611
|
+
if (isNaN(parsed)) {
|
|
2612
|
+
return this.cacheSet(TABINDEX, null);
|
|
2613
|
+
}
|
|
2614
|
+
return this.cacheSet(TABINDEX, parsed);
|
|
2615
|
+
}
|
|
2573
2616
|
/**
|
|
2574
2617
|
* Get a list of all attributes on this node.
|
|
2575
2618
|
*/
|
|
@@ -3366,6 +3409,7 @@ function isKeywordIgnored(options, keyword, matcher = (list, it) => list.include
|
|
|
3366
3409
|
|
|
3367
3410
|
const ARIA_HIDDEN_CACHE = Symbol(isAriaHidden.name);
|
|
3368
3411
|
const HTML_HIDDEN_CACHE = Symbol(isHTMLHidden.name);
|
|
3412
|
+
const INERT_CACHE = Symbol(isInert.name);
|
|
3369
3413
|
const ROLE_PRESENTATION_CACHE = Symbol(isPresentation.name);
|
|
3370
3414
|
const STYLE_HIDDEN_CACHE = Symbol(isStyleHidden.name);
|
|
3371
3415
|
function inAccessibilityTree(node) {
|
|
@@ -3378,6 +3422,9 @@ function inAccessibilityTree(node) {
|
|
|
3378
3422
|
if (isHTMLHidden(node)) {
|
|
3379
3423
|
return false;
|
|
3380
3424
|
}
|
|
3425
|
+
if (isInert(node)) {
|
|
3426
|
+
return false;
|
|
3427
|
+
}
|
|
3381
3428
|
if (isStyleHidden(node)) {
|
|
3382
3429
|
return false;
|
|
3383
3430
|
}
|
|
@@ -3419,6 +3466,24 @@ function isHTMLHidden(node, details) {
|
|
|
3419
3466
|
const result = node.cacheSet(HTML_HIDDEN_CACHE, isHTMLHiddenImpl(node));
|
|
3420
3467
|
return details ? result : result.byParent || result.bySelf;
|
|
3421
3468
|
}
|
|
3469
|
+
function isInertImpl(node) {
|
|
3470
|
+
const isInert2 = (node2) => {
|
|
3471
|
+
const inert = node2.getAttribute("inert");
|
|
3472
|
+
return inert !== null && inert.isStatic;
|
|
3473
|
+
};
|
|
3474
|
+
return {
|
|
3475
|
+
byParent: node.parent ? isInert2(node.parent) : false,
|
|
3476
|
+
bySelf: isInert2(node)
|
|
3477
|
+
};
|
|
3478
|
+
}
|
|
3479
|
+
function isInert(node, details) {
|
|
3480
|
+
const cached = node.cacheGet(INERT_CACHE);
|
|
3481
|
+
if (cached) {
|
|
3482
|
+
return details ? cached : cached.byParent || cached.bySelf;
|
|
3483
|
+
}
|
|
3484
|
+
const result = node.cacheSet(INERT_CACHE, isInertImpl(node));
|
|
3485
|
+
return details ? result : result.byParent || result.bySelf;
|
|
3486
|
+
}
|
|
3422
3487
|
function isStyleHiddenImpl(node) {
|
|
3423
3488
|
const isHidden = (node2) => {
|
|
3424
3489
|
const style = node2.getAttribute("style");
|
|
@@ -6450,6 +6515,46 @@ class HeadingLevel extends Rule {
|
|
|
6450
6515
|
}
|
|
6451
6516
|
}
|
|
6452
6517
|
|
|
6518
|
+
const FOCUSABLE_CACHE = Symbol(isFocusable.name);
|
|
6519
|
+
function isDisabled(element, meta) {
|
|
6520
|
+
var _a;
|
|
6521
|
+
if (!((_a = meta.formAssociated) == null ? void 0 : _a.disablable)) {
|
|
6522
|
+
return false;
|
|
6523
|
+
}
|
|
6524
|
+
const disabled = element.matches("[disabled]");
|
|
6525
|
+
if (disabled) {
|
|
6526
|
+
return true;
|
|
6527
|
+
}
|
|
6528
|
+
const fieldset = element.closest("fieldset[disabled]");
|
|
6529
|
+
if (fieldset) {
|
|
6530
|
+
return true;
|
|
6531
|
+
}
|
|
6532
|
+
return false;
|
|
6533
|
+
}
|
|
6534
|
+
function isFocusableImpl(element) {
|
|
6535
|
+
if (isHTMLHidden(element) || isInert(element) || isStyleHidden(element)) {
|
|
6536
|
+
return false;
|
|
6537
|
+
}
|
|
6538
|
+
const { tabIndex, meta } = element;
|
|
6539
|
+
if (tabIndex !== null) {
|
|
6540
|
+
return tabIndex >= 0;
|
|
6541
|
+
}
|
|
6542
|
+
if (!meta) {
|
|
6543
|
+
return false;
|
|
6544
|
+
}
|
|
6545
|
+
if (isDisabled(element, meta)) {
|
|
6546
|
+
return false;
|
|
6547
|
+
}
|
|
6548
|
+
return Boolean(meta == null ? void 0 : meta.focusable);
|
|
6549
|
+
}
|
|
6550
|
+
function isFocusable(element) {
|
|
6551
|
+
const cached = element.cacheGet(FOCUSABLE_CACHE);
|
|
6552
|
+
if (cached) {
|
|
6553
|
+
return cached;
|
|
6554
|
+
}
|
|
6555
|
+
return element.cacheSet(FOCUSABLE_CACHE, isFocusableImpl(element));
|
|
6556
|
+
}
|
|
6557
|
+
|
|
6453
6558
|
class HiddenFocusable extends Rule {
|
|
6454
6559
|
documentation(context) {
|
|
6455
6560
|
const byParent = context === "parent" ? " In this case it is being hidden by an ancestor with `aria-hidden.`" : "";
|
|
@@ -6463,7 +6568,8 @@ class HiddenFocusable extends Rule {
|
|
|
6463
6568
|
"To fix this either:",
|
|
6464
6569
|
" - Remove `aria-hidden`.",
|
|
6465
6570
|
" - Remove the element from the DOM instead.",
|
|
6466
|
-
|
|
6571
|
+
' - Use `tabindex="-1"` to remove the element from tab order.',
|
|
6572
|
+
" - Use `hidden`, `inert` or similar means to hide or disable the element."
|
|
6467
6573
|
].join("\n"),
|
|
6468
6574
|
url: "https://html-validate.org/rules/hidden-focusable.html"
|
|
6469
6575
|
};
|
|
@@ -6474,21 +6580,13 @@ class HiddenFocusable extends Rule {
|
|
|
6474
6580
|
this.on("dom:ready", (event) => {
|
|
6475
6581
|
const { document } = event;
|
|
6476
6582
|
for (const element of document.querySelectorAll(selector)) {
|
|
6477
|
-
if (
|
|
6478
|
-
|
|
6479
|
-
}
|
|
6480
|
-
if (isAriaHidden(element)) {
|
|
6481
|
-
this.validateElement(element);
|
|
6583
|
+
if (isFocusable(element) && isAriaHidden(element)) {
|
|
6584
|
+
this.reportElement(element);
|
|
6482
6585
|
}
|
|
6483
6586
|
}
|
|
6484
6587
|
});
|
|
6485
6588
|
}
|
|
6486
|
-
|
|
6487
|
-
const { meta } = element;
|
|
6488
|
-
const tabindex = element.getAttribute("tabindex");
|
|
6489
|
-
if (meta && !meta.focusable && !tabindex) {
|
|
6490
|
-
return;
|
|
6491
|
-
}
|
|
6589
|
+
reportElement(element) {
|
|
6492
6590
|
const attribute = element.getAttribute("aria-hidden");
|
|
6493
6591
|
const message = attribute ? `aria-hidden cannot be used on focusable elements` : `aria-hidden cannot be used on focusable elements (hidden by ancestor element)`;
|
|
6494
6592
|
const location = attribute ? attribute.keyLocation : element.location;
|
|
@@ -12488,7 +12586,7 @@ class HtmlValidate {
|
|
|
12488
12586
|
}
|
|
12489
12587
|
|
|
12490
12588
|
const name = "html-validate";
|
|
12491
|
-
const version = "8.
|
|
12589
|
+
const version = "8.16.0";
|
|
12492
12590
|
const bugs = "https://gitlab.com/html-validate/html-validate/issues/new";
|
|
12493
12591
|
|
|
12494
12592
|
function definePlugin(plugin) {
|