├── .eslintignore ├── .npmignore ├── __tests__ └── integrations │ ├── __fixtures__ │ ├── flat-config │ │ ├── .npmrc │ │ ├── a.vue │ │ ├── eslint.config.js │ │ └── package.json │ └── legacy-config │ │ ├── .npmrc │ │ ├── a.vue │ │ ├── package.json │ │ └── .eslintrc.json │ ├── helper.ts │ ├── flat-config.spec.ts │ └── legacy-config.spec.ts ├── .gitignore ├── .prettierignore ├── src ├── utils │ ├── makeDocsURL.ts │ ├── makeKebabCase.ts │ ├── hasOnDirectives.ts │ ├── isPresentationRole.ts │ ├── hasAriaLabel.ts │ ├── isAriaHidden.ts │ ├── interactiveHandlers.json │ ├── getElementAttributeValue.ts │ ├── isAttribute.ts │ ├── isInteractiveRole.ts │ ├── getAttributeName.ts │ ├── getInteractiveRoles.ts │ ├── isHiddenFromScreenReader.ts │ ├── isMatchingElement.ts │ ├── getElementAttribute.ts │ ├── isCustomComponent.ts │ ├── hasFocusableElement.ts │ ├── hasOnDirective.ts │ ├── getLiteralAttributeValue.ts │ ├── getElementType.ts │ ├── getAttributeValue.ts │ ├── hasAccessibleChild.ts │ ├── __tests__ │ │ └── defineTemplateBodyVisitor.test.ts │ ├── matchesElementRole.ts │ ├── isInteractiveElement.ts │ ├── defineTemplateBodyVisitor.ts │ ├── htmlElements.json │ └── hasContent.ts ├── rules │ ├── __tests__ │ │ ├── no-access-key.test.ts │ │ ├── no-onchange.test.ts │ │ ├── iframe-has-title.test.ts │ │ ├── aria-props.test.ts │ │ ├── aria-unsupported-elements.test.ts │ │ ├── aria-role.test.ts │ │ ├── no-autofocus.test.ts │ │ ├── tabindex-no-positive.test.ts │ │ ├── no-distracting-elements.test.ts │ │ ├── no-redundant-roles.test.ts │ │ ├── mouse-events-have-key-events.test.ts │ │ ├── heading-has-content.test.ts │ │ ├── anchor-has-content.test.ts │ │ ├── no-aria-hidden-on-focusable.test.ts │ │ ├── no-role-presentation-on-focusable.test.ts │ │ ├── role-has-required-aria-props.test.ts │ │ ├── label-has-for.test.ts │ │ ├── makeRuleTester.ts │ │ ├── alt-text.test.ts │ │ ├── click-events-have-key-events.test.ts │ │ ├── form-control-has-label.test.ts │ │ ├── no-static-element-interactions.test.ts │ │ ├── media-has-caption.test.ts │ │ └── interactive-supports-focus.test.ts │ ├── no-access-key.ts │ ├── tabindex-no-positive.ts │ ├── no-aria-hidden-on-focusable.ts │ ├── no-onchange.ts │ ├── iframe-has-title.ts │ ├── no-role-presentation-on-focusable.ts │ ├── aria-props.ts │ ├── click-events-have-key-events.ts │ ├── aria-unsupported-elements.ts │ ├── no-autofocus.ts │ ├── mouse-events-have-key-events.ts │ ├── no-distracting-elements.ts │ ├── no-static-element-interactions.ts │ ├── aria-role.ts │ ├── anchor-has-content.ts │ ├── heading-has-content.ts │ ├── media-has-caption.ts │ ├── no-redundant-roles.ts │ ├── role-has-required-aria-props.ts │ ├── form-control-has-label.ts │ ├── alt-text.ts │ ├── interactive-supports-focus.ts │ └── label-has-for.ts ├── configs │ ├── recommended.ts │ ├── flat │ │ └── recommended.ts │ └── rules.ts ├── utils.ts └── index.ts ├── tsconfig.build.json ├── .github ├── dependabot.yml └── workflows │ ├── main.yml │ ├── auto-merge.yml │ └── docs.yml ├── docs ├── rule-overview │ ├── index.md │ ├── RuleTable.vue │ └── rule-overview.data.mts ├── rules │ ├── aria-props.md │ ├── iframe-has-title.md │ ├── mouse-events-have-key-events.md │ ├── click-events-have-key-events.md │ ├── aria-unsupported-elements.md │ ├── tabindex-no-positive.md │ ├── role-has-required-aria-props.md │ ├── no-access-key.md │ ├── no-autofocus.md │ ├── aria-role.md │ ├── no-redundant-roles.md │ ├── no-distracting-elements.md │ ├── no-onchange.md │ ├── form-control-has-label.md │ ├── media-has-caption.md │ ├── no-aria-hidden-on-focusable.md │ ├── no-role-presentation-on-focusable.md │ ├── heading-has-content.md │ ├── anchor-has-content.md │ ├── label-has-for.md │ ├── no-static-element-interactions.md │ ├── alt-text.md │ └── interactive-supports-focus.md ├── .vitepress │ ├── rulesForSidebar.ts │ └── config.mts └── index.md ├── tsconfig.json ├── jest.setup.ts ├── bin └── lint.ts ├── LICENSE ├── README.md ├── package.json ├── CODE_OF_CONDUCT.md └── CHANGELOG.md /.eslintignore: -------------------------------------------------------------------------------- 1 | __fixtures__/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .* 2 | bin/ 3 | docs/ 4 | src/ 5 | jest.setup.ts 6 | -------------------------------------------------------------------------------- /__tests__/integrations/__fixtures__/flat-config/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /__tests__/integrations/__fixtures__/legacy-config/.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.eslintcache 2 | /.husky/ 3 | /.idea/ 4 | /dist/ 5 | node_modules 6 | /docs/.vitepress/dist 7 | /docs/.vitepress/cache 8 | yarn-error.log 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .eslintcache 2 | .eslintignore 3 | .gitignore 4 | .husky 5 | .npmignore 6 | .prettierignore 7 | LICENSE 8 | yarn.lock 9 | dist 10 | __fixtures__ 11 | -------------------------------------------------------------------------------- /src/utils/makeDocsURL.ts: -------------------------------------------------------------------------------- 1 | function makeDocsURL(name: string) { 2 | return `https://vue-a11y.github.io/eslint-plugin-vuejs-accessibility/rules/${name}.html`; 3 | } 4 | 5 | export default makeDocsURL; 6 | -------------------------------------------------------------------------------- /src/utils/makeKebabCase.ts: -------------------------------------------------------------------------------- 1 | function makeKebabCase(value: string) { 2 | return value 3 | .replace(/_/gu, "-") 4 | .replace(/\B([A-Z])/gu, "-$1") 5 | .toLowerCase(); 6 | } 7 | 8 | export default makeKebabCase; 9 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": [ 4 | "bin", 5 | "src/**/__tests__", 6 | "docs/**", 7 | "jest.setup.ts", 8 | "dist", 9 | "__tests__" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /__tests__/integrations/helper.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import path from "path"; 3 | 4 | export function readPackageJson(base: string) { 5 | return JSON.parse( 6 | fs.readFileSync(path.resolve(base, "package.json"), "utf-8") 7 | ); 8 | } 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "npm" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "daily" 11 | -------------------------------------------------------------------------------- /__tests__/integrations/__fixtures__/flat-config/a.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /__tests__/integrations/__fixtures__/legacy-config/a.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | -------------------------------------------------------------------------------- /__tests__/integrations/__fixtures__/flat-config/eslint.config.js: -------------------------------------------------------------------------------- 1 | import plugin from "eslint-plugin-vuejs-accessibility"; 2 | 3 | export default [ 4 | ...plugin.configs["flat/recommended"], 5 | { 6 | rules: { 7 | "vuejs-accessibility/alt-text": "warn" 8 | } 9 | } 10 | ]; 11 | -------------------------------------------------------------------------------- /src/rules/__tests__/no-access-key.test.ts: -------------------------------------------------------------------------------- 1 | import rule from "../no-access-key"; 2 | import makeRuleTester from "./makeRuleTester"; 3 | 4 | makeRuleTester("no-access-key", rule, { 5 | valid: ["
", "
"], 6 | invalid: ["
", "
"] 7 | }); 8 | -------------------------------------------------------------------------------- /docs/rule-overview/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | prev: 3 | text: Getting Started 4 | link: / 5 | next: 6 | text: alt-text 7 | link: /rules/alt-text 8 | --- 9 | 10 | 13 | 14 | # Rule Overview 15 | 16 | 17 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "nodenext", 4 | "module": "nodenext", 5 | "target": "es2019", 6 | "strict": true, 7 | "esModuleInterop": true, 8 | "resolveJsonModule": true, 9 | "declaration": true, 10 | "outDir": "./dist" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/rules/__tests__/no-onchange.test.ts: -------------------------------------------------------------------------------- 1 | import rule from "../no-onchange"; 2 | import makeRuleTester from "./makeRuleTester"; 3 | 4 | makeRuleTester("no-onchange", rule, { 5 | valid: [""], 6 | invalid: [""] 7 | }); 8 | -------------------------------------------------------------------------------- /src/utils/hasOnDirectives.ts: -------------------------------------------------------------------------------- 1 | import type { AST } from "vue-eslint-parser"; 2 | 3 | import hasOnDirective from "./hasOnDirective"; 4 | 5 | function hasOnDirectives(node: AST.VElement, names: string[]) { 6 | return names.some((name) => hasOnDirective(node, name)); 7 | } 8 | 9 | export default hasOnDirectives; 10 | -------------------------------------------------------------------------------- /src/rules/__tests__/iframe-has-title.test.ts: -------------------------------------------------------------------------------- 1 | import rule from "../iframe-has-title"; 2 | import makeRuleTester from "./makeRuleTester"; 3 | 4 | makeRuleTester("iframe-has-title", rule, { 5 | valid: ["