From b25ddd6bf482371116d451f530890fcd74bc2881 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:29:53 +0000 Subject: [PATCH 1/2] Initial plan From e481c3d11b817c082365de292438d4031ca4c9c0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 17 Feb 2026 00:38:37 +0000 Subject: [PATCH 2/2] Add template-no-autofocus-attribute rule implementation, tests, and docs Co-authored-by: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> --- README.md | 6 ++ docs/rules/template-no-autofocus-attribute.md | 48 ++++++++++++++++ lib/rules/template-no-autofocus-attribute.js | 46 +++++++++++++++ package.json | 1 + .../rules/template-no-autofocus-attribute.js | 57 +++++++++++++++++++ 5 files changed, 158 insertions(+) create mode 100644 docs/rules/template-no-autofocus-attribute.md create mode 100644 lib/rules/template-no-autofocus-attribute.js create mode 100644 tests/lib/rules/template-no-autofocus-attribute.js diff --git a/README.md b/README.md index 1997fcd7ea..a11681a5a7 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,12 @@ rules in templates can be disabled with eslint directives with mustache or html 🔧 Automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/user-guide/command-line-interface#--fix).\ 💡 Manually fixable by [editor suggestions](https://eslint.org/docs/latest/use/core-concepts#rule-suggestions). +### Accessibility + +| Name | Description | 💼 | 🔧 | 💡 | +| :------------------------------------------------------------------------------- | :--------------------------- | :- | :- | :- | +| [template-no-autofocus-attribute](docs/rules/template-no-autofocus-attribute.md) | disallow autofocus attribute | | 🔧 | | + ### Best Practices | Name | Description | 💼 | 🔧 | 💡 | diff --git a/docs/rules/template-no-autofocus-attribute.md b/docs/rules/template-no-autofocus-attribute.md new file mode 100644 index 0000000000..00dc7eae4f --- /dev/null +++ b/docs/rules/template-no-autofocus-attribute.md @@ -0,0 +1,48 @@ +# ember/template-no-autofocus-attribute + +🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + + + +Disallows the use of `autofocus` attribute on elements. + +The `autofocus` attribute can cause usability issues for both sighted and non-sighted users by disrupting expected behavior and screen reader announcements. + +## Examples + +Examples of **incorrect** code for this rule: + +```gjs + +``` + +```gjs + +``` + +Examples of **correct** code for this rule: + +```gjs + +``` + +```gjs + +``` + +## When Not To Use It + +If you need to autofocus for specific accessibility or UX requirements and have thoroughly tested with assistive technologies, you may disable this rule for those specific cases. + +## References + +- [ember-template-lint no-autofocus-attribute](https://github.com/ember-template-lint/ember-template-lint/blob/master/docs/rule/no-autofocus-attribute.md) +- [MDN autofocus attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autofocus) diff --git a/lib/rules/template-no-autofocus-attribute.js b/lib/rules/template-no-autofocus-attribute.js new file mode 100644 index 0000000000..c3d274a00f --- /dev/null +++ b/lib/rules/template-no-autofocus-attribute.js @@ -0,0 +1,46 @@ +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'suggestion', + docs: { + description: 'disallow autofocus attribute', + category: 'Accessibility', + url: 'https://github.com/ember-cli/eslint-plugin-ember/tree/master/docs/rules/template-no-autofocus-attribute.md', + }, + fixable: 'code', + schema: [], + messages: { + noAutofocus: + 'Avoid using autofocus attribute. Autofocusing elements can cause usability issues for sighted and non-sighted users.', + }, + }, + + create(context) { + return { + GlimmerElementNode(node) { + const autofocusAttr = node.attributes?.find((attr) => attr.name === 'autofocus'); + + if (autofocusAttr) { + context.report({ + node: autofocusAttr, + messageId: 'noAutofocus', + fix(fixer) { + // Remove the attribute including preceding whitespace + const sourceCode = context.sourceCode; + const text = sourceCode.getText(); + const attrStart = autofocusAttr.range[0]; + const attrEnd = autofocusAttr.range[1]; + + let removeStart = attrStart; + while (removeStart > 0 && /\s/.test(text[removeStart - 1])) { + removeStart--; + } + + return fixer.removeRange([removeStart, attrEnd]); + }, + }); + } + }, + }; + }, +}; diff --git a/package.json b/package.json index 58134e9d03..f625498f7b 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ }, "dependencies": { "@ember-data/rfc395-data": "^0.0.4", + "@glimmer/env": "^0.1.7", "css-tree": "^3.0.1", "ember-eslint-parser": "^0.5.9", "ember-rfc176-data": "^0.3.18", diff --git a/tests/lib/rules/template-no-autofocus-attribute.js b/tests/lib/rules/template-no-autofocus-attribute.js new file mode 100644 index 0000000000..afee5842d2 --- /dev/null +++ b/tests/lib/rules/template-no-autofocus-attribute.js @@ -0,0 +1,57 @@ +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +const rule = require('../../../lib/rules/template-no-autofocus-attribute'); +const RuleTester = require('eslint').RuleTester; + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +const ruleTester = new RuleTester({ + parser: require.resolve('ember-eslint-parser'), + parserOptions: { ecmaVersion: 2022, sourceType: 'module' }, +}); + +ruleTester.run('template-no-autofocus-attribute', rule, { + valid: [ + ``, + ``, + ], + + invalid: [ + { + code: ``, + output: ``, + errors: [ + { + message: + 'Avoid using autofocus attribute. Autofocusing elements can cause usability issues for sighted and non-sighted users.', + type: 'GlimmerAttrNode', + }, + ], + }, + { + code: ``, + output: ``, + errors: [ + { + type: 'GlimmerAttrNode', + }, + ], + }, + ], +});