{"id":23786299,"url":"https://github.com/drwpow/html-aria","last_synced_at":"2025-09-06T04:31:07.608Z","repository":{"id":269886917,"uuid":"908753487","full_name":"drwpow/html-aria","owner":"drwpow","description":"Get an element’s WAI ARIA role for building accessible interfaces","archived":false,"fork":false,"pushed_at":"2024-12-26T23:16:18.000Z","size":0,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-12-26T23:24:55.805Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/drwpow.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-26T22:01:04.000Z","updated_at":"2024-12-26T23:17:22.000Z","dependencies_parsed_at":"2024-12-26T23:25:31.576Z","dependency_job_id":"2a95187f-330c-494a-9c97-dd652aa2209b","html_url":"https://github.com/drwpow/html-aria","commit_stats":null,"previous_names":["drwpow/html-aria"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drwpow%2Fhtml-aria","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drwpow%2Fhtml-aria/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drwpow%2Fhtml-aria/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/drwpow%2Fhtml-aria/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/drwpow","download_url":"https://codeload.github.com/drwpow/html-aria/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":232086386,"owners_count":18470636,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-01T14:15:50.059Z","updated_at":"2025-09-06T04:31:07.555Z","avatar_url":"https://github.com/drwpow.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# html-aria\n\nUtilities for creating accessible HTML based on the [latest ARIA 1.3 specs](https://www.w3.org/TR/wai-aria-1.3) and latest [HTML in ARIA](https://www.w3.org/TR/html-aria/) recommendations. Lightweight, performant, tree-shakeable, and 0 dependencies.\n\nThis is designed to be a better replacement for aria-query when working with HTML. The reasons are:\n\n- html-aria is designed to reduce mistakes, while aria-query’s APIs are easy to “hold it wrong.” The information may not be _incorrect_, but often are locked behind several successful operations you must know to connect to get the right result.\n- html-aria and aria-query both follow the [ARIA 1.3 spec](https://w3c.github.io/aria/), but that’s only one part. There are also the [HTML Accessibility API Mappings](https://www.w3.org/TR/html-aam-1.0/) and [HTML to ARIA](https://www.w3.org/TR/html-aria) specs that are critical to working with HTML. While aria-query follows these other documents when it can, its design makes it difficult to apply the advice from all specs, often producing incomplete or incorrect results.\n- html-aria supports ARIA 1.3 while aria-query is still on ARIA 1.2\n\nhtml-aria is also ESM-compatible and [more performant](./test/node/html-aria.bench.ts).\n\n## Usage\n\n### Setup\n\n```sh\nnpm i html-aria\n```\n\n### Environments\n\nThis library works both in Node.js and the browser. But works best **when the DOM is accessible**, either the actual DOM or a virtualized one like JSDOM. The reason is the spec requires DOM traversal—identifying an element’s context in parents and children, as well as attributes of the element. In a DOM environment, html-aria will do all the work for you; in Node.js you must provide complete information about attributes, and sometimes ancestors.\n\n### Examples\n\nThough this library is NOT a lint plugin, it can do most of the work for you. You only need to traverse the AST of the language you’re using (e.g. HTML vs React vs Svelte), and html-aria can validate the nodes.\n\n#### Node.js (ESLint + React plugin)\n\n```ts\nimport { ESLintUtils, TSESTree } from \"@typescript-eslint/utils\";\nimport {\n  getSupportedAttributes,\n  type AriaAttribute,\n  type TagName,\n} from \"html-aria\";\n\nconst createRule = ESLintUtils.RuleCreator(\n  (name) =\u003e `https://example.com/rule/${name}`\n);\n\nexport default createRule({\n  name: \"no-unsupported-aria\",\n  meta: {\n    type: \"problem\",\n    docs: { description: \"Ensure that ARIA attributes match their role\" },\n    messages: {\n      \"not-allowed\": \"Attribute {{ name }} not allowed\",\n    },\n  },\n  create(context) {\n    return {\n      JSXOpeningElement(node) {\n        if (node.name.type !== TSESTree.AST_NODE_TYPES.JSXIdentifier) {\n          return; // this is a React component; ignore\n        }\n\n        const tagName = node.name.name as TagName;\n\n        // 1. assemble attributes into a map\n        const attributes: Record\u003c\n          string,\n          string | number | boolean | undefined | null\n        \u003e = {};\n        for (const attr of node.attributes) {\n          if (\n            attr.type === TSESTree.JSXSpreadAttribute ||\n            attr.name.type === TSESTree.JSXNamespacedName ||\n            attr.value?.type === TSESTree.Literal\n          ) {\n            continue;\n          }\n          attributes[attr.name.name] = attr.value.value as\n            | string\n            | number\n            | boolean\n            | undefined\n            | null;\n        }\n\n        // 2. get supported attributes from html-aria (which MUST include the attributes to work properly)\n        const tag: VirtualElement = { tagName, attributes };\n        const supportedAttributes = getSupportedAttributes(tag);\n\n        // 3. validate\n        for (const attr of node.attributes) {\n          if (attr.type !== TSESTree.AST_NODE_TYPES.JSXAttribute) {\n            continue;\n          }\n          const name =\n            typeof attr.name.name === \"string\"\n              ? attr.name.name\n              : (attr.name.name.name as ARIAAttribute);\n          if (name.startsWith(\"aria-\") \u0026\u0026 !supportedAttributes.includes(name)) {\n            context.report({\n              node: name,\n              messageId: \"not-allowed\",\n              data: { name },\n            });\n          }\n        }\n      },\n    };\n  },\n});\n```\n\n_Have an improvement to suggest? Please open a PR!_\n\n## API\n\n### getRole()\n\nDetermine which HTML maps to which default ARIA role.\n\n```ts\nimport { getRole } from \"html-aria\";\n\n// DOM\nconst el = document.querySelector(\"article\");\ngetRole(el); // \"article\"\n\n// Node.js (no DOM)\ngetRole({ tagName: \"input\", attributes: { type: \"checkbox\" } }); // \"checkbox\"\ngetRole({ tagName: \"div\", attributes: { role: \"button\" } }); // \"button\"\n```\n\nIt’s important to note that inferring ARIA roles from HTML isn’t always straightforward! There are 3 types of role inference:\n\n1. **Tag map**: 1 tag → 1 ARIA role.\n2. **Tag + attribute map**: Tags + attributes are needed to determine the ARIA role (e.g. `input[type=\"radio\"]` → `radio`)\n3. **Tag + DOM tree**: Tags + DOM tree structure are needed to determine the ARIA role.\n\n[See a list of all elements](#aria-roles-from-html).\n\n### getSupportedRoles() / isSupportedRole()\n\nThe spec dictates that **certain elements may NOT receive certain roles.** For example, `\u003cdiv role=\"button\"\u003e` is allowed (not recommended, but allowed), but `\u003cselect role=\"button\"\u003e` is not. `getSupportedRoles()` will return all valid roles for a given element + attributes.\n\n```ts\nimport { getSupportedRoles } from \"html-aria\";\n\n// DOM\nconst el = document.querySelector(\"img\");\ngetSupportedRoles(el); // [\"none\", \"presentation\", \"img\"]\n\n// Node.js (no DOM)\ngetSupportedRoles({ tagName: \"img\", attributes: { alt: \"Image caption\" } }); //  [\"button\", \"checkbox\", \"link\", (15 more)]\n```\n\nThere is also a helper method `isSupportedRole()` to make individual assertions:\n\n```ts\nimport { isSupportedRole } from \"html-aria\";\n\nisSupportedRole({ tagName: \"select\" }, \"combobox\"); // true\nisSupportedRole(\n  { tagName: \"select\", attributes: { multiple: true } },\n  \"listbox\"\n); // true\nisSupportedRole({ tagName: \"select\" }, \"listbox\"); // false\nisSupportedRole({ tagName: \"select\" }, \"button\"); // false\n```\n\n### getSupportedAttributes() / isSupportedAttribute()\n\nFor any element, list all supported [aria-\\* attributes](https://www.w3.org/TR/wai-aria-1.3/#states_and_properties), including attributes inherited from superclasses. This takes in an HTML element, not an ARIA role, because in some cases the HTML element actually affects the list ([see full list](#aria--attributes-from-html)).\n\n```ts\nimport { getSupportedAttributes } from \"html-aria\";\n\ngetSupportedAttributes({ tagName: \"button\" }); // [\"aria-atomic\", \"aria-braillelabel\", …]\n```\n\nIf you want to look up by ARIA role instead, just pass in a placeholder element:\n\n```ts\ngetSupportedAttributes({ tagName: \"div\", attributes: { role: \"combobox\" } });\n```\n\nThere’s also a helper method `isSupportedAttribute()` to test individual attributes:\n\n```ts\nimport { isSupportedAttribute } from \"html-aria\";\n\nisSupportedAttribute({ tagName: \"button\" }, \"aria-pressed\"); // true\nisSupportedAttribute({ tagName: \"button\" }, \"aria-checked\"); // false\n```\n\nIt’s worth noting that **HTML elements may factor in** according to the spec—providing the `role` isn’t enough. [See aria-\\* attributes from HTML](#aria--attributes-from-html).\n\n### getElements()\n\nReturn all HTML elements that represent a given ARIA role, if any. If no HTML elements represent this role, `undefined` will be returned. This is essentially the inverse of [`getRole()`](#getrole).\n\n```ts\nimport { getElements } from \"html-aria\";\n\ngetElements(\"button\"); // [{ tagName: \"button\" }]\ngetElements(\"radio\"); // [{ tagName: 'input', attributes: { type: \"radio\" } }]\ngetElements(\"rowheader\"); // [{ tagName: \"th\", attributes: { scope: \"row\" } }]\ngetElements(\"tab\"); // undefined\n```\n\nWorth noting that this is slightly-different from a [related concept](https://www.w3.org/TR/wai-aria-1.3/#relatedConcept) or [base concept](https://www.w3.org/TR/wai-aria-1.3/#baseConcept).\n\n### isInteractive()\n\nReturn `true` if a given HTML tag _MAY_ be interacted with or not. It does NOT check if an element is interactive in the moment (e.g. it won’t account for `display: none` or more advanced scenarios).\n\n```ts\nisInteractive({ tagName: \"button\" }); // true\nisInteractive({ tagName: \"div\" }); // false\nisInteractive({ tagName: \"div\", attributes: { tabindex: 0 } }); // false\nisInteractive({ tagName: \"div\", attributes: { role: \"button\", tabindex: 0 } }); // true\nisInteractive({ tagName: \"hr\" }); // false\nisInteractive({\n  tagName: \"hr\",\n  attributes: { tabindex: 0, \"aria-valuenow\": 10 },\n}); // true (see https://www.w3.org/TR/wai-aria-1.3/#separator)\n```\n\nThe methodology for this follows the complete ARIA specification:\n\n1. If the role is a [widget](https://www.w3.org/TR/wai-aria-1.3/#widget_roles) or [window](https://www.w3.org/TR/wai-aria-1.3/#window_roles) subclass, then it is interactive\n   - If the element manually specifies `role`, and if it natively is NOT a widget or window role, `tabindex` must also be supplied\n1. If the element is `disabled` or `aria-disabled`, then it is NOT interactive\n1. Handle some explicit edge cases like [separator](https://www.w3.org/TR/wai-aria-1.3/#separator)\n\nNote that `aria-hidden` elements MAY be interactive (even if it’s not best practice) as a part of [2.4.5 Multiple Ways](https://www.w3.org/WAI/WCAG21/Understanding/multiple-ways.html) if an alternative is made for screenreaders, etc.\n\n### isNameRequired()\n\nFor a role, return whether or not an [accessible name](https://www.w3.org/TR/wai-aria-1.3/#namecalculation) is required for screenreaders.\n\n```ts\nimport { isNameRequired } from \"html-aria\";\n\nisNameRequired(\"link\"); // true\nisNameRequired(\"cell\"); // false\n```\n\n_Note: this does NOT mean `aria-label` is required! Quite the opposite—if a name is required, it’s always best to have the name visible in content. See [ARIA 1.3 Accessible Name Calculation](https://www.w3.org/TR/wai-aria-1.3/#namecalculation) for more info._\n\n### isValidAttributeValue()\n\nSome aria-\\* attributes require specific values. `isValidAttributeValue()` returns `false` if, given a specific aria-\\* attribute, the value is invalid according to the spec.\n\n```ts\nimport { isValidAttributeValue } from \"html-aria\";\n\n// string attributes\n// Note: string attributes will always return `true` except for an empty string\nisValidAttributeValue(\"aria-label\", \"This is a label\"); // true\nisValidAttributeValue(\"aria-label\", \"\"); // false\n\n// boolean attributes\nisValidAttributeValue(\"aria-disabled\", true); // true\nisValidAttributeValue(\"aria-disabled\", false); // true\nisValidAttributeValue(\"aria-disabled\", \"true\"); // true\nisValidAttributeValue(\"aria-disabled\", 1); // false\nisValidAttributeValue(\"aria-disabled\", \"disabled\"); // false\n\n// enum attributes\nisValidAttributeValue(\"aria-checked\", \"true\"); // true\nisValidAttributeValue(\"aria-checked\", \"mixed\"); // true\nisValidAttributeValue(\"aria-checked\", \"checked\"); // false\n\n// number attributes\nisValidAttribute(\"aria-valuenow\", \"15\"); // true\nisValidAttribute(\"aria-valuenow\", 15); // true\nisValidAttribute(\"aria-valuenow\", 0); // true\n```\n\n⚠️ _Be mindful of cases where a valid value may still be valid, but invoke different behavior according to the ARIA role, e.g. [`mixed` behavior for `radio`/`menuitemradio`/`switch`](https://www.w3.org/TR/wai-aria-1.3/#aria-checked)_\n\n### getAccNameAndDescription()\n\nGet the [accessible name and description](https://www.w3.org/TR/html-aam-1.0/#accessible-name-and-description-computation) for an HTML element.\n\n```ts\nconst el = document.querySelector(\"input\");\ngetAccNameAndDescription(el);\n// { name: \"My Input\", description: undefined }\n```\n\n\u003e [!NOTE]\n\u003e There are limitations in Node.js with complex accessible name calculation such as `aria-labelledby`. But simple calculations like `aria-label` are well-supported.\n\n## Reference\n\n### ARIA roles from HTML\n\nThis outlines the requirements to adhere to the [W3C spec](https://www.w3.org/TR/html-aria/#docconformance) when it comes to inferring the correct ARIA roles from HTML. Essentially, there are 3 types of inference:\n\n1. **Tag map**: 1 tag → 1 ARIA role.\n2. **Tag + attribute map**: Tags + attributes are needed to determine the ARIA role (e.g. `input[type=\"radio\"]` → `radio`)\n3. **Tag + DOM tree**: Tags + DOM tree structure are needed to determine the ARIA role.\n\nHere are all the HTML elements where either attributes, hierarchy, or both are necessary to determine the correct role. Any HTML elements not listed here follow the simple “tag map” approach (keep in mind that [aria-\\* attributes may not follow the same rules](#aria--attributes-from-html)!).\n\n| Element     |                                                  Role                                                   | Attribute-based | Hierarchy-based |\n| :---------- | :-----------------------------------------------------------------------------------------------------: | :-------------: | :-------------: |\n| **a**       |                                           `generic` \\| `link`                                           |       ✅        |                 |\n| **area**    |                                           `generic` \\| `link`                                           |       ✅        |                 |\n| **footer**  |                                       `contentinfo` \\| `generic`                                        |                 |       ✅        |\n| **header**  |                                          `banner` \\| `generic`                                          |                 |       ✅        |\n| **input**   | `button` \\| `checkbox` \\| `combobox` \\| `radio` \\| `searchbox` \\| `slider` \\| `spinbutton` \\| `textbox` |       ✅        |                 |\n| **li**      |                                         `listitem` \\| `generic`                                         |                 |       ✅        |\n| **section** |                                          `generic` \\| `region`                                          |       ✅        |                 |\n| **select**  |                                         `combobox` \\| `listbox`                                         |       ✅        |                 |\n| **td**      |                                        `cell`\\| `gridcell` \\| —                                         |                 |       ✅        |\n| **th**      |                                   `columnheader` \\| `rowheader` \\| —                                    |       ✅        |       ✅        |\n\n_Note: `—` = [no corresponding role](#whats-the-difference-between-no-corresponding-role-and-the-none-role)_\n\n### aria-\\* attributes from HTML\n\nFurther, a common mistake many simple accessibility libraries make is mapping aria-\\* attributes to ARIA roles. While that _mostly_ works, there are a few exceptions where HTML information is needed. That is why [`getSupportedAttributes()`](#getsupportedattributes--issupportedattribute) takes an HTML element. Here is a full list:\n\n| Element                  | Default Role | Notes                                                                                     |\n| :----------------------- | :----------: | :---------------------------------------------------------------------------------------- |\n| **audio**                |      —       | Accepts `application` aria-\\* attributes by default                                       |\n| **base**                 |  `generic`   | No aria-\\* attributes allowed                                                             |\n| **body**                 |  `generic`   | Does NOT allow `aria-hidden=\"true\"`                                                       |\n| **br**                   |  `generic`   | No aria-\\* attributes allowed EXCEPT `aria-hidden`                                        |\n| **col**                  |      —       | No aria-\\* attributes allowed                                                             |\n| **colgroup**             |      —       | No aria-\\* attributes allowed                                                             |\n| **datalist**             |  `listbox`   | No aria-\\* attributes allowed                                                             |\n| **head**                 |      —       | No aria-\\* attributes allowed                                                             |\n| **html**                 |      —       | No aria-\\* attributes allowed                                                             |\n| **img** (no `alt`)       |    `none`    | No aria-\\* attributes allowed EXCEPT `aria-hidden`                                        |\n| **input[type=checkbox]** |      —       | Forbids `aria-checked`                                                                    |\n| **input[type=color]**    |      —       | Acts as a generic element but allows `aria-disabled`                                      |\n| **input[type=files]**    |      —       | Acts as a generic element but allows `aria-disabled`, `aria-invalid`, and `aria-required` |\n| **input[type=hidden]**   |      —       | No aria-\\* attributes allowed                                                             |\n| **input[type=radio]**    |      —       | Forbids `aria-checked`                                                                    |\n| **link**                 |      —       | No aria-\\* attributes allowed                                                             |\n| **map**                  |      —       | No aria-\\* attributes allowed                                                             |\n| **meta**                 |      —       | No aria-\\* attributes allowed                                                             |\n| **noscript**             |      —       | No aria-\\* attributes allowed                                                             |\n| **picture**              |      —       | No aria-\\* attributes allowed EXCEPT `aria-hidden`                                        |\n| **script**               |      —       | No aria-\\* attributes allowed                                                             |\n| **slot**                 |      —       | No aria-\\* attributes allowed                                                             |\n| **source**               |      —       | No aria-\\* attributes allowed                                                             |\n| **style**                |      —       | No aria-\\* attributes allowed                                                             |\n| **summary**              |      —       | Allows `aria-disabled` and `aria-haspopup` regardless of role                             |\n| **template**             |      —       | No aria-\\* attributes allowed                                                             |\n| **title**                |      —       | No aria-\\* attributes allowed                                                             |\n| **track**                |      —       | No aria-\\* attributes allowed EXCEPT `aria-hidden`                                        |\n| **video**                |      —       | Accepts `application` aria-\\* attributes by default                                       |\n| **wbr**                  |      —       | No aria-\\* attributes allowed EXCEPT `aria-hidden`                                        |\n\n_Note: `—` = [no corresponding role](#whats-the-difference-between-no-corresponding-role-and-the-none-role). Also worth pointing out that in other cases, [global aria-\\* attributes](https://www.w3.org/TR/wai-aria-1.3/#global_states) are allowed, so this is unique to the element and NOT the ARIA role._\n\n### Discrepancies between specs\n\nThough the [HTML in ARIA](https://www.w3.org/TR/html-aria) spec was the foundation for this library, at points it conflicts with [AAM](https://www.w3.org/TR/html-aam-1.0). We also have browsers sometimes showing inconsistent roles, too. For these discrepancies, we compare what the specs recommend, along with the library’s current decision in an attempt to follow the most helpful path.\n\n| Element        | [HTML in ARIA](https://www.w3.org/TR/html-aria) | [AAM](https://www.w3.org/TR/html-aam-1.0) | Browsers\\*                                                        | html-aria             |\n| :------------- | :---------------------------------------------- | :---------------------------------------- | :---------------------------------------------------------------- | --------------------- |\n| `\u003cdd\u003e`         | No corresponding role                           | `definition`                              | `definition`                                                      | `definition`          |\n| `\u003cdl\u003e`         | No corresponding role                           | `list`                                    | (inconsistent)                                                    | No corresponding role |\n| `\u003cdt\u003e`         | No corresponding role                           | `term`                                    | `term`                                                            | `term`                |\n| `\u003cfigcaption\u003e` | No corresponding role                           | `caption`                                 | `caption` (`Figcaption` in Chrome)                                | `caption`             |\n| `\u003cmark\u003e`       | No corresponding role                           | `mark`                                    | `mark`                                                            | `mark`                |\n| `\u003csvg\u003e`        | `graphics-document`                             | `graphics-document`                       | `graphics-document` (Firefox), `img` (Chrome), `generic` (Safari) | `graphics-document`   |\n\n_\\* Chrome 132, Safari 18, Firefox 135._\n\n### Node.js vs DOM behavior\n\n#### Node.js ignores necessary ancestor-based roles\n\nThere are 2 categories of context-dependent element usage: **necessary** and **conditional**.\n\n“Necessary“ context elements require certain parents to use correctly, like table-based elements (`\u003ctr\u003e`, `\u003ctd\u003e`, `\u003cth\u003e`, etc.) requiring table parents (`\u003ctable\u003e`, `\u003ctable role=\"grid\"\u003e`, etc.) and list-based elements `\u003cli\u003e` requiring list parents (`\u003col\u003e`, `\u003cul\u003e`, `\u003cmenu\u003e`, etc.). Without their parents, they have no purpose and their behavior is unpredictable, with some browsers even stripping elements out of the DOM. These elements will 99% of the time be used in their intended contexts.\n\nThe DOM environment follows the ARIA spec. But in a Node.js context, it’s likely we are statically analyzing a component where the parents aren’t immediatly reachable—they may be in another file. If we assume the elements are used correctly even when we can’t see the ancestors, we can show more accurate errors and warnings, rather than requiring the consumer to do work that is technically and computationally difficult.\n\nSo for the reasons above, assuming the elements are used out of context is more likely to result in less predictable behavior that could lead to mistakes. To treat elements as if they _are_ used out of their context in Node.js, pass an empty `ancestors` array as an explicit way to declare it.\n\n```ts\nimport { getRole } from \"html-aria\";\n\ngetRole({ tagName: \"td\" }, { ancestors: [] }); // undefined\ngetRole({ tagName: \"th\" }, { ancestors: [] }); // undefined\ngetRole({ tagName: \"li\" }, { ancestors: [] }); // \"generic\"\n```\n\nThese are all the elements that have assumed context (i.e. different behavior in Node.js): `\u003ccol\u003e`, `\u003ccolgroup\u003e`, `\u003ccaption\u003e`, `\u003cli\u003e`, `\u003crowgroup\u003e`, `\u003ctbody\u003e`, `\u003ctd\u003e`, `\u003ctfoot\u003e`, `\u003cth\u003e`, `\u003cthead\u003e`, `\u003ctr\u003e`.\n\n“Conditional” context elements may either have certain parents or not, all of which are valid. `\u003caside\u003e` used in the body is a landmark `complementary` role; inside a `\u003csection\u003e` it’s `generic` (unless it has an accessible name, then it’s `complementary` again). `\u003cheader\u003e` is a `banner` landmark itself, or inside another landmark is `generic`. Since there’s no “wrong” usage here, In Node.js they behave as expected, so they don’t deviate from DOM behavior or the spec.\n\n```ts\nimport { getRole } from \"html-aria\";\n\ngetRole({ tagName: \"header\" }); // \"banner\"\ngetRole({ tagName: \"header\" }, { ancestors: [{ tagName: \"main\" }]); // \"generic\"\ngetRole({ tagName: \"aside\" }); // \"complementary\"\ngetRole({ tagName: \"aside\" }, { ancestors: [{ tagName: \"section\" }] } }); // \"generic\"\n```\n\n### FAQ\n\n#### Why the `{ tagName: string }` object syntax?\n\nMost of the time this library will be used in a Node.js environment, likely outside the DOM (e.g. an ESLint plugin traversing an AST). While most methods also allow an [HTMLElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement) as input, the object syntax is universal and works in any context.\n\n#### What’s the difference between “no corresponding role” and the `none` role?\n\nFrom the [spec](https://www.w3.org/TR/html-aria/#dfn-no-corresponding-role):\n\n**No corresponding role**\n\n\u003e The elements marked with _**No corresponding role**_, in the second column of the table do not have any [implicit ARIA semantics](https://www.w3.org/TR/wai-aria-1.2/#implicit_semantics), but they do have meaning and this meaning may be represented in roles, states and properties not provided by ARIA, and exposed to users of assistive technology via accessibility APIs. It is therefore recommended that authors add a `role` attribute to a semantically neutral element such as a [`div`](https://html.spec.whatwg.org/multipage/grouping-content.html#the-div-element) or [span](https://html.spec.whatwg.org/multipage/text-level-semantics.html#the-span-element), rather than overriding the semantics of the listed elements.\n\n**`none` role**\n\n\u003e An [element](https://dom.spec.whatwg.org/#concept-element) whose implicit native role semantics will not be mapped to the [accessibility API](https://www.w3.org/TR/wai-aria-1.3/#dfn-accessibility-api). See synonym [presentation](https://www.w3.org/TR/wai-aria-1.3/#presentation).\n\nIn other words, `none` is more of a decisive “this element is presentational and can be ignored” labeling, while “no corresponding role” means “this element doesn’t have predefined behavior that can be automatically determined, and the author should provide additional information such as explicit `role`s and ARIA states and properties.”\n\nIn html-aria, “no corresponding role” is represented as `undefined`.\n\n#### What is the difference between “unsupported attributes” and “prohibited attributes?”\n\nIn the spec, you’ll find language describing both roles and attributes in 4 categories:\n\n1. **Supported and recommended:** valid and recommended to use\n2. **Supported but not recommended:** valid, but may [cause unpredictable behavior](https://www.w3.org/TR/html-aria/#author-guidance-to-avoid-incorrect-use-of-aria)\n3. **Unsupported, but not prohibited:** these are omitted both from supported and prohibited lists\n4. **Unsupported and prohibited:** explicitly [prohibited](https://www.w3.org/TR/wai-aria-1.3/#prohibitedattributes)\n\nAs stated in [Project Goals](#about), html-aria aims to not conflate non-normative recommendations as normative guidelines. So in the API, [getSupportedRoles()](#getsupportedroles--issupportedrole) and [getSupportedAttributes()](#getsupportedattributes--issupportedattribute) will return 1 and 2, but not 3 or 4.\n\nWhile there is a technical distinction between 3 and 4, for the purposees of html-aria they’re treated the same (because 3 specifically is not explicitly allowed, we can make a choice to read it as prohibited).\n\n## About\n\n### Project Goals\n\n1. Implement _all_ ARIA spec docs, not just the roles specification\n1. Stick to _normative_ guidelines (i.e. only implement “MUST” language, not “SHOULD”—the latter is the area of linters)\n1. Reduce mistakes with explicit methods and user-friendly API design.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrwpow%2Fhtml-aria","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdrwpow%2Fhtml-aria","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdrwpow%2Fhtml-aria/lists"}