diff --git a/fast_htmlcs/HTMLCS.ts b/fast_htmlcs/HTMLCS.ts
index 2f031fcb..c1751505 100755
--- a/fast_htmlcs/HTMLCS.ts
+++ b/fast_htmlcs/HTMLCS.ts
@@ -384,7 +384,9 @@ _global.HTMLCS = new (function () {
if (options.include && options.include.length > 0) {
// Included sniffs.
ruleSet.sniffs = options.include;
- } else if (options.exclude) {
+ }
+
+ if (options.exclude) {
// Excluded sniffs.
for (const exclude of options.exclude) {
// @ts-ignore
@@ -423,7 +425,7 @@ _global.HTMLCS = new (function () {
standard,
sniffs.shift(),
function () {
- _registerSniffs(standard, sniffs, callback, failCallback);
+ _registerSniffs(standard, sniffs, callback, failCallback);
},
failCallback
);
diff --git a/fast_htmlcs/Standards/Section508/Sniffs/D.ts b/fast_htmlcs/Standards/Section508/Sniffs/D.ts
index 19eb310e..348b5df1 100644
--- a/fast_htmlcs/Standards/Section508/Sniffs/D.ts
+++ b/fast_htmlcs/Standards/Section508/Sniffs/D.ts
@@ -28,7 +28,7 @@ _global.HTMLCS_Section508_Sniffs_D = {
_global.HTMLCS_WCAG2AAA_Sniffs_Principle1_Guideline1_3_1_3_1.testPresentationMarkup(
top
- )
+ );
this.testHeadingOrder(top);
// Look for any script elements, and fire off another notice regarding
diff --git a/kayle/bench/fast_axecore-playwright.ts b/kayle/bench/fast_axecore-playwright.ts
index bace8587..9f2707e4 100644
--- a/kayle/bench/fast_axecore-playwright.ts
+++ b/kayle/bench/fast_axecore-playwright.ts
@@ -53,7 +53,7 @@ async function launchBench() {
unit: "OPS/S",
value: 1000 / avg,
},
- ]),
+ ])
);
}
diff --git a/kayle/bench/fast_htmlcs-playwright.ts b/kayle/bench/fast_htmlcs-playwright.ts
index 08925d20..fc495d85 100644
--- a/kayle/bench/fast_htmlcs-playwright.ts
+++ b/kayle/bench/fast_htmlcs-playwright.ts
@@ -53,7 +53,7 @@ async function launchBench() {
unit: "OPS/S",
value: 1000 / avg,
},
- ]),
+ ])
);
}
diff --git a/kayle/builder/build-extension.ts b/kayle/builder/build-extension.ts
index c28ab6b3..91cdbeda 100644
--- a/kayle/builder/build-extension.ts
+++ b/kayle/builder/build-extension.ts
@@ -52,7 +52,7 @@ window.addEventListener("kayle_send", async (event) => {
writeFileSync(
`${ext}/content-script.js`,
- `${extensionRunner}\n${extensionAxe}\n${extensionHtmlcs}\n${extensionRawEnd}`,
+ `${extensionRunner}\n${extensionAxe}\n${extensionHtmlcs}\n${extensionRawEnd}`
);
const extensionManifest = `{
diff --git a/kayle/builder/build-rules.ts b/kayle/builder/build-rules.ts
index f6a4a3e3..31ee54e9 100644
--- a/kayle/builder/build-rules.ts
+++ b/kayle/builder/build-rules.ts
@@ -1,6 +1,8 @@
import { chromium } from "playwright";
-import { Standard, kayle } from "kayle";
-import { writeFile } from "fs/promises";
+import { Standard, kayle, htmlcsLocales, axeLocales } from "kayle";
+import { writeFile, mkdir } from "fs/promises";
+import { existsSync } from "node:fs";
+
import { format } from "prettier";
import { htmlcsRuleMap } from "./htmlcs-rule-map";
import { processParams } from "./build-htmlcs-params";
@@ -8,84 +10,121 @@ import type { Rule } from "./build-types";
(async () => {
const browser = await chromium.launch({ headless: true });
- const page = await browser.newPage();
- page.on("console", (msg) => console.log("PAGE LOG:", msg.text()));
-
- const fast_htmlcs_rules: Rule[] = [];
- const fast_axe_rules: Rule[] = [];
-
- // inject the scripts
- await kayle(
- {
- page,
- browser,
- runners: ["htmlcs", "axe"],
- html: "
>Build Rules list
",
- standard: Standard.WCAG2AA,
- origin: "https://www.example.com",
- },
- true,
- );
- const paramList = await processParams();
+ const pConfig = {
+ singleQuote: true,
+ semi: false,
+ parser: "babel",
+ };
- await page.evaluate((o) => {
- window.paramList = o;
- }, paramList);
+ const runBuildRules = async (language: string) => {
+ const page = await browser.newPage();
- await page.addScriptTag({
- content: `window.htmlcsRuleMap = ${htmlcsRuleMap.toString()};`,
- });
+ const fast_htmlcs_rules: Rule[] = [];
+ const fast_axe_rules: Rule[] = [];
- await page.exposeFunction("pushHtmlcsRule", (t: Rule) =>
- fast_htmlcs_rules.push(t),
- );
+ // inject the scripts
+ await kayle(
+ {
+ page,
+ browser,
+ runners: ["htmlcs", "axe"],
+ html: "Build Rules list
",
+ standard: Standard.WCAG2AA,
+ origin: "https://www.example.com",
+ language,
+ },
+ true
+ );
- await page.exposeFunction("pushAxeRule", (t: Rule) => fast_axe_rules.push(t));
+ const paramList = await processParams();
- await page.evaluate(() => {
- for (const r of window.axe.getRules()) {
- window.pushAxeRule(r);
- }
- for (const k of window.paramList) {
- if (k && k.length >= 4) {
- window.pushHtmlcsRule(window.htmlcsRuleMap(k));
+ await page.evaluate((o) => {
+ window.paramList = o;
+ }, paramList);
+
+ await page.addScriptTag({
+ content: `window.htmlcsRuleMap = ${htmlcsRuleMap.toString()};`,
+ });
+
+ await page.exposeFunction("pushHtmlcsRule", (t: Rule) =>
+ fast_htmlcs_rules.push(t)
+ );
+
+ await page.exposeFunction("pushAxeRule", (t: Rule) =>
+ fast_axe_rules.push(t)
+ );
+
+ await page.evaluate(() => {
+ for (const r of window.axe.getRules()) {
+ window.pushAxeRule(r);
+ }
+ for (const k of window.paramList) {
+ if (k && k.length >= 4) {
+ window.pushHtmlcsRule(window.htmlcsRuleMap(k));
+ }
}
+ });
+
+ if (!existsSync(`./lib/rules/${language}`)) {
+ await mkdir(`./lib/rules/${language}`);
}
- });
- const pConfig = {
- singleQuote: true,
- semi: false,
- parser: "babel",
- };
+ const DNE = "THIS FILE WAS CREATED DYNAMICALLY - DO NOT EDIT";
- const DNE = "THIS FILE WAS CREATED DYNAMICALLY - DO NOT EDIT";
+ await writeFile(
+ `./lib/rules/${language}/htmlcs-rules.ts`,
+ Buffer.from(
+ await format(
+ `/* ${DNE} */\nexport const htmlcsRules = ${JSON.stringify(
+ fast_htmlcs_rules.filter((r) => r.description)
+ )};`,
+ pConfig
+ )
+ ),
+ "utf8"
+ );
- await writeFile(
- "./lib/rules/htmlcs-rules.ts",
- Buffer.from(
- await format(
- `/* ${DNE} */\nexport const htmlcsRules = ${JSON.stringify(
- fast_htmlcs_rules.filter((r) => r.description),
- )};`,
- pConfig,
+ await writeFile(
+ `./lib/rules/${language}/axe-rules.ts`,
+ Buffer.from(
+ await format(
+ `/* ${DNE} */\nexport const axeRules = ${JSON.stringify(
+ fast_axe_rules
+ )};`,
+ pConfig
+ )
),
- ),
- "utf8",
+ "utf8"
+ );
+
+ await page.close({
+ runBeforeUnload: true,
+ });
+ };
+
+ const localesList: string[] = Array.from(
+ new Set(htmlcsLocales.concat(axeLocales))
);
+ localesList.push("en");
+
+ await Promise.all(localesList.map((r) => runBuildRules(r)));
+
await writeFile(
- "./lib/rules/axe-rules.ts",
+ `./lib/rules/index.ts`,
Buffer.from(
await format(
- `/* ${DNE} */\nexport const axeRules = ${JSON.stringify(
- fast_axe_rules,
- )};`,
- pConfig,
- ),
+ localesList
+ .map((l) => {
+ return `export { axeRules as axeRules${l.toUpperCase()} } from "./${l}/axe-rules";
+ export { htmlcsRules as htmlcsRules${l.toUpperCase()} } from "./${l}/htmlcs-rules";`;
+ })
+ .join(""),
+ pConfig
+ )
),
- "utf8",
+ "utf8"
);
await browser.close();
diff --git a/kayle/builder/htmlcs-rule-map.ts b/kayle/builder/htmlcs-rule-map.ts
index 3fe4f09f..020c6270 100644
--- a/kayle/builder/htmlcs-rule-map.ts
+++ b/kayle/builder/htmlcs-rule-map.ts
@@ -1,4 +1,4 @@
-import type { Rule, ParamList } from "./build-types";
+import type { ParamList } from "./build-types";
// Hand stich the rules to map from htmlcs.
// We need to map the ruleId to the translation file key.
@@ -26,14 +26,20 @@ export const htmlcsRuleMap = (rule: ParamList) => {
const inlineTranslation =
rule[2] && rule[2].startsWith("HTMLCS.getTranslation");
- const description =
- (inlineTranslation && eval(rule[2])) ||
- rule[2] ||
- window.HTMLCS.getTranslation(`${rule[4]}_${rule[3]}`) ||
- window.HTMLCS.getTranslation(`${rule[4]}${rule[3]}`) ||
- "";
+ let description = "";
- const baseRule = `${rule[4]}${directRuleAssignments.includes(rule[3]) ? "." : "_"}${rule[3]}`;
+ try {
+ description =
+ (inlineTranslation && eval(rule[2])) ||
+ rule[2] ||
+ window.HTMLCS.getTranslation(`${rule[4]}_${rule[3]}`) ||
+ window.HTMLCS.getTranslation(`${rule[4]}${rule[3]}`) ||
+ "";
+ } catch (_e) {}
+
+ const baseRule = `${rule[4]}${
+ directRuleAssignments.includes(rule[3]) ? "." : "_"
+ }${rule[3]}`;
const baseRuleId =
section508 || !description ? rule[3] || baseRule : baseRule;
@@ -41,6 +47,7 @@ export const htmlcsRuleMap = (rule: ParamList) => {
? rule[2].replace("HTMLCS.getTranslation(", "").replace(")", "")
: "";
+ // FIXME: we need to get the BASE GUIDELINES upfront like WCAG2AA.Principle1.Guideline1_4 to concat the id with.
const ruleId = inlineTranslation
? inlineRule.substring(1, inlineRule.length - 1)
: baseRuleId;
diff --git a/kayle/lib/action.ts b/kayle/lib/action.ts
index 909222c7..e336375a 100644
--- a/kayle/lib/action.ts
+++ b/kayle/lib/action.ts
@@ -56,13 +56,13 @@ export const actions = [
target.dispatchEvent(
new Event("input", {
bubbles: true,
- }),
+ })
);
return Promise.resolve();
},
selector,
- value,
+ value
);
} catch (error) {
throw new Error(`${failedActionElement} "${selector}"`);
@@ -91,7 +91,7 @@ export const actions = [
target.dispatchEvent(
new Event("input", {
bubbles: true,
- }),
+ })
);
return Promise.resolve();
}, selector);
@@ -117,12 +117,12 @@ export const actions = [
target.dispatchEvent(
new Event("change", {
bubbles: true,
- }),
+ })
);
return Promise.resolve();
},
selector,
- checked,
+ checked
);
} catch (error) {
throw new Error(`${failedActionElement} "${selector}"`);
@@ -176,7 +176,7 @@ export const actions = [
{},
property,
expectedValue,
- negated,
+ negated
);
},
},
@@ -216,7 +216,7 @@ export const actions = [
polling: 200,
},
selector,
- state,
+ state
);
},
},
@@ -244,11 +244,11 @@ export const actions = [
},
{
once: true,
- },
+ }
);
},
selector,
- eventType,
+ eventType
);
await page.waitForFunction(
@@ -263,7 +263,7 @@ export const actions = [
},
{
polling: 200,
- },
+ }
);
} catch (error) {
throw new Error(`${failedActionElement} "${selector}"`);
@@ -283,7 +283,7 @@ export const actions = [
*/
export async function runAction(browser, page, options, act, customActions?) {
const action = (customActions ?? actions).find((item) =>
- item.match.test(act),
+ item.match.test(act)
);
if (!action) {
diff --git a/kayle/lib/auto.ts b/kayle/lib/auto.ts
index dcde82d9..e92df798 100644
--- a/kayle/lib/auto.ts
+++ b/kayle/lib/auto.ts
@@ -17,7 +17,7 @@ export async function autoKayle(
cb?: ((result: Audit) => Promise) | ((result: Audit) => void);
} = {},
ignoreSet?: Set,
- _results?: Audit[],
+ _results?: Audit[]
): Promise {
if (!write) {
const { writeFile } = await import("fs/promises");
@@ -51,7 +51,7 @@ export async function autoKayle(
if (o.store) {
await write(
`${o.store}/${encodeURIComponent(o.page.url())}`,
- await o.page.content(),
+ await o.page.content()
);
}
@@ -84,7 +84,7 @@ export async function autoKayle(
origin: link,
},
ignoreSet,
- _results,
+ _results
);
})
.catch((e) => {
@@ -94,7 +94,7 @@ export async function autoKayle(
console.error(e);
}
});
- }),
+ })
);
return _results;
diff --git a/kayle/lib/config.ts b/kayle/lib/config.ts
index 020b7dbd..e51ab9b4 100644
--- a/kayle/lib/config.ts
+++ b/kayle/lib/config.ts
@@ -42,7 +42,7 @@ type BrowserContext = {
newCDPSession?(page: Partial | Frame): Partial;
overridePermissions?(
origin: string,
- permissions: Permission[],
+ permissions: Permission[]
): Promise;
};
@@ -89,7 +89,7 @@ type Page = {
_routes?: { url: string }[];
route(
path: string,
- intercept: (config: any, next: any) => Promise | Promise,
+ intercept: (config: any, next: any) => Promise | Promise
): Promise;
setRequestInterception?(enable?: boolean): Promise;
listenerCount?(name: string): number;
@@ -99,19 +99,19 @@ type Page = {
| Function
| {
default: Function;
- },
+ }
): Promise;
addInitScript?(script: { content?: string }): Promise;
evaluateOnNewDocument?<
Params extends unknown[],
- Func extends (...args: Params) => unknown = (...args: Params) => unknown,
+ Func extends (...args: Params) => unknown = (...args: Params) => unknown
>(
pageFunction: Func | string,
...args: Params
): Promise<{ identifier: string }>;
evaluate<
Params extends unknown[],
- Func extends EvaluateFunc = EvaluateFunc,
+ Func extends EvaluateFunc = EvaluateFunc
>(
pageFunction: Func | string,
...args: Params
diff --git a/kayle/lib/index.ts b/kayle/lib/index.ts
index f23b72ab..d556d60f 100644
--- a/kayle/lib/index.ts
+++ b/kayle/lib/index.ts
@@ -26,5 +26,5 @@ export {
type WaitForOptions,
} from "./config";
export { extractLinks, innateBuilder } from "./wasm";
-export { htmlcsRules } from "./rules/htmlcs-rules";
-export { axeRules } from "./rules/axe-rules";
+export { htmlcsLocales } from "./runners/htmlcs";
+export { axeLocales } from "./runners/axe";
diff --git a/kayle/lib/kayle.ts b/kayle/lib/kayle.ts
index f4a2b6fa..b6864277 100644
--- a/kayle/lib/kayle.ts
+++ b/kayle/lib/kayle.ts
@@ -32,7 +32,7 @@ const audit = async (config: RunnerConfig): Promise => {
origin: config.origin,
language: config.language,
clip: config.clip,
- },
+ }
);
};
@@ -61,7 +61,7 @@ const injectRunners = async (config: RunnerConfig) => {
return await Promise.allSettled([
config.page.evaluate(runnersJavascript["kayle"]),
...config.runners.map((r) =>
- config.page.evaluate(getRunner(config.language, r)),
+ config.page.evaluate(getRunner(config.language, r))
),
]);
}
@@ -77,7 +77,7 @@ export const auditExtension = async (config: RunnerConfig): Promise => {
}
window.addEventListener("kayle_receive", (event: CustomEvent) =>
- resolve(event.detail.data),
+ resolve(event.detail.data)
);
window.dispatchEvent(
@@ -86,7 +86,7 @@ export const auditExtension = async (config: RunnerConfig): Promise => {
name: "kayle",
options: runOptions,
},
- }),
+ })
);
});
},
@@ -100,7 +100,7 @@ export const auditExtension = async (config: RunnerConfig): Promise => {
origin: config.origin,
language: config.language,
clip: config.clip,
- },
+ }
);
};
@@ -121,7 +121,7 @@ const auditPageInnate = async (config: RunnerConf, results: Audit) => {
const innateAudit: InnateIssue[] = await kayle_innate.audit(
html,
css,
- config.clip,
+ config.clip
);
for (const innateIssue of innateAudit) {
@@ -163,7 +163,7 @@ const auditPageInnate = async (config: RunnerConf, results: Audit) => {
*/
export const kayle = async (
o: RunnerConf = {},
- preventClose?: boolean,
+ preventClose?: boolean
): Promise => {
const watcher = new Watcher();
const navigate = o.page.url() === "about:blank" && (o.origin || o.html);
@@ -212,7 +212,7 @@ export const kayle = async (
}
return item;
- }),
+ })
);
}
diff --git a/kayle/lib/lint.ts b/kayle/lib/lint.ts
index 2b420f1a..d8c36e36 100644
--- a/kayle/lib/lint.ts
+++ b/kayle/lib/lint.ts
@@ -12,7 +12,7 @@ export const kayleLint = async (
source?: string, // url or html source
o: Partial = {},
runner?: keyof typeof runnersJavascript,
- forward?: boolean, // forward messages to console
+ forward?: boolean // forward messages to console
) => {
const config = extractArgs(o);
let html = source;
@@ -55,7 +55,7 @@ export const kayleLint = async (
dom.window.eval(runnersJavascript.kayle);
dom.window.eval(
- runner ? runnersJavascript[runner] : runnersJavascript["htmlcs"],
+ runner ? runnersJavascript[runner] : runnersJavascript["htmlcs"]
);
const results = await dom.window.__a11y.run({
diff --git a/kayle/lib/rules/ar/axe-rules.ts b/kayle/lib/rules/ar/axe-rules.ts
new file mode 100644
index 00000000..84010e65
--- /dev/null
+++ b/kayle/lib/rules/ar/axe-rules.ts
@@ -0,0 +1,978 @@
+/* THIS FILE WAS CREATED DYNAMICALLY - DO NOT EDIT */
+export const axeRules = [
+ {
+ ruleId: "accesskeys",
+ description: "يضمن أن قيمة خاصية الوصول لكل مفتاح فريدة",
+ help: "يجب أن تكون قيمة خاصية الوصول فريدة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/accesskeys?application=axeAPI&lang=ar",
+ tags: ["cat.keyboard", "best-practice"],
+ },
+ {
+ ruleId: "area-alt",
+ description: "يضمن أن عناصر لخرائط الصور لها نص بديل",
+ help: "يجب أن تحتوي عناصر الفعالة على نص بديل",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/area-alt?application=axeAPI&lang=ar",
+ tags: [
+ "cat.text-alternatives",
+ "wcag2a",
+ "wcag244",
+ "wcag412",
+ "section508",
+ "section508.22.a",
+ "ACT",
+ ],
+ actIds: ["c487ae"],
+ },
+ {
+ ruleId: "aria-allowed-attr",
+ description: "يضمن أن خصائص ARIA مسموحة لدور عنصر معين",
+ help: "يجب أن تستخدم العناصر خصائص ARIA المسموحة فقط",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-allowed-attr?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ actIds: ["5c01ea"],
+ },
+ {
+ ruleId: "aria-allowed-role",
+ description: "يضمن أن خاصية الدور لها قيمة مناسبة للعنصر",
+ help: "يجب أن يكون دور ARIA مناسباً للعنصر",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-allowed-role?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "best-practice"],
+ },
+ {
+ ruleId: "aria-command-name",
+ description: "يضمن أن كل أمر ARIA ورابط وعنصر قائمة له اسم متاح",
+ help: "يجب أن تحتوي أوامر ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-command-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412", "ACT"],
+ actIds: ["97a4e1"],
+ },
+ {
+ ruleId: "aria-dialog-name",
+ description: "يضمن أن كل عقدة حوار وتنبيه حوار في ARIA لها اسم متاح",
+ help: "يجب أن تحتوي عقد الحوار وتنبيه الحوار في ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-dialog-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "best-practice"],
+ },
+ {
+ ruleId: "aria-hidden-body",
+ description: "يضمن عدم وجود 'aria-hidden=\"true\"' على جسم الوثيقة",
+ help: "يجب ألا يتواجد 'aria-hidden=\"true\"' على جسم الوثيقة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-hidden-body?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ },
+ {
+ ruleId: "aria-hidden-focus",
+ description:
+ "يضمن أن العناصر المخفية بـ aria-hidden ليست قابلة للتركيز ولا تحتوي على عناصر قابلة للتركيز",
+ help: "يجب ألا يكون العنصر المخفي بـ ARIA قابل للتركيز أو يحتوي على عناصر قابلة للتركيز",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-hidden-focus?application=axeAPI&lang=ar",
+ tags: ["cat.name-role-value", "wcag2a", "wcag412"],
+ actIds: ["6cfa84"],
+ },
+ {
+ ruleId: "aria-input-field-name",
+ description: "يضمن أن كل حقل إدخال ARIA له اسم متاح",
+ help: "يجب أن تحتوي حقول إدخال ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-input-field-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412", "ACT"],
+ actIds: ["e086e5"],
+ },
+ {
+ ruleId: "aria-meter-name",
+ description: "يضمن أن كل عقدة قياس في ARIA لها اسم متاح",
+ help: "يجب أن تحتوي عقد القياس في ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-meter-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag111"],
+ },
+ {
+ ruleId: "aria-progressbar-name",
+ description: "يضمن أن كل عقدة شريط تقدم في ARIA لها اسم متاح",
+ help: "يجب أن تحتوي عقد شريط التقدم في ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-progressbar-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag111"],
+ },
+ {
+ ruleId: "aria-required-attr",
+ description: "يضمن أن العناصر بأدوار ARIA تحتوي على كل خصائص ARIA المطلوبة",
+ help: "يجب توفير خصائص ARIA المطلوبة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-required-attr?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ actIds: ["4e8ab6"],
+ },
+ {
+ ruleId: "aria-required-children",
+ description: "يضمن أن العناصر بدور ARIA يحتوي على الأدوار الفرعية المطلوبة",
+ help: "يجب أن تحتوي بعض الأدوار في ARIA على أدوار فرعية محددة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-required-children?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag131"],
+ actIds: ["bc4a75", "ff89c9"],
+ },
+ {
+ ruleId: "aria-required-parent",
+ description: "يضمن أن العناصر بدور ARIA تحتوي على الأدوار الأصلية المطلوبة",
+ help: "يجب أن تحتوي بعض الأدوار في ARIA على أدوار أصلية محددة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-required-parent?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag131"],
+ actIds: ["ff89c9"],
+ },
+ {
+ ruleId: "aria-roledescription",
+ description:
+ "يضمن استخدام aria-roledescription فقط على العناصر بدور ضمني أو صريح",
+ help: "يجب استخدام aria-roledescription على العناصر بدور دلالي",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-roledescription?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ },
+ {
+ ruleId: "aria-roles",
+ description: "يضمن أن كل العناصر بخاصية دور تستخدم قيمة صالحة",
+ help: "يجب أن تتوافق الأدوار في ARIA مع قيم صالحة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-roles?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ actIds: ["674b10"],
+ },
+ {
+ ruleId: "aria-text",
+ description:
+ 'يضمن استخدام "role=text" على العناصر بدون أحفاد قابلين للتركيز',
+ help: '"role=text" يجب ألا يحتوي على أحفاد قابلين للتركيز',
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-text?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "best-practice"],
+ },
+ {
+ ruleId: "aria-toggle-field-name",
+ description: "يضمن أن كل حقل تبديل في ARIA له اسم متاح",
+ help: "يجب أن تحتوي حقول التبديل في ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-toggle-field-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412", "ACT"],
+ actIds: ["e086e5"],
+ },
+ {
+ ruleId: "aria-tooltip-name",
+ description: "يضمن أن كل عقدة تلميح في ARIA لها اسم متاح",
+ help: "يجب أن تحتوي عقد التلميح في ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-tooltip-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ },
+ {
+ ruleId: "aria-treeitem-name",
+ description: "يضمن أن كل عقدة عنصر شجرة في ARIA لها اسم متاح",
+ help: "يجب أن تحتوي عقد عناصر الشجرة في ARIA على اسم متاح",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-treeitem-name?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "best-practice"],
+ },
+ {
+ ruleId: "aria-valid-attr-value",
+ description: "يضمن أن كل خصائص ARIA لها قيم صالحة",
+ help: "يجب أن تتوافق خصائص ARIA مع قيم صالحة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-valid-attr-value?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ actIds: ["6a7281"],
+ },
+ {
+ ruleId: "aria-valid-attr",
+ description: "يضمن أن الخصائص التي تبدأ بـ aria- هي خصائص ARIA صالحة",
+ help: "يجب أن تتوافق خصائص ARIA مع أسماء صالحة",
+ helpUrl:
+ "https://dequeuniversity.com/rules/axe/4.6/aria-valid-attr?application=axeAPI&lang=ar",
+ tags: ["cat.aria", "wcag2a", "wcag412"],
+ actIds: ["5f99a7"],
+ },
+ {
+ ruleId: "audio-caption",
+ description: "يضمن أن عناصر