diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md
index c01521666e3e..172393b5f2a4 100644
--- a/packages/eslint-plugin/README.md
+++ b/packages/eslint-plugin/README.md
@@ -135,10 +135,14 @@ Ensures that EUI modal components (`EuiModal`, `EuiFlyout`, `EuiConfirmModal`) h
Ensures that form control components within `EuiFormRow` components have matching `isInvalid` prop values. This maintains consistent validation state between parent form rows and their child form controls, leading to a more predictable and accessible user experience.
-### `@elastic/eui/sr_output_disabled_tooltip`
+### `@elastic/eui/sr-output-disabled-tooltip`
Ensures `disableScreenReaderOutput` is set when `EuiToolTip` content matches `EuiButtonIcon` "aria-label".
+### `@elastic/eui/prefer-eui-icon-tip`
+
+Ensure `EuiIconTip` is used rather than ``, as it provides better accessibility and improved support for assistive technologies.
+
## Testing
### Running unit tests
diff --git a/packages/eslint-plugin/changelogs/CHANGELOG_2025.md b/packages/eslint-plugin/changelogs/CHANGELOG_2025.md
index 598223f66182..b9e03d889aa9 100644
--- a/packages/eslint-plugin/changelogs/CHANGELOG_2025.md
+++ b/packages/eslint-plugin/changelogs/CHANGELOG_2025.md
@@ -1,6 +1,6 @@
## [`v2.1.0`](https://github.com/elastic/eui/releases/v2.1.0)
-- Added new `sr_output_disabled_tooltip` rule. ([#8848](https://github.com/elastic/eui/pull/8848))
+- Added new `sr-output-disabled-tooltip` rule. ([#8848](https://github.com/elastic/eui/pull/8848))
- Added new `consistent-is-invalid-props` rule. ([#8843](https://github.com/elastic/eui/pull/8843))
- Added new `require-aria-label-for-modals` rule. ([#8811](https://github.com/elastic/eui/pull/8811))
diff --git a/packages/eslint-plugin/changelogs/upcoming/8877.md b/packages/eslint-plugin/changelogs/upcoming/8877.md
new file mode 100644
index 000000000000..eb55b0e16fbb
--- /dev/null
+++ b/packages/eslint-plugin/changelogs/upcoming/8877.md
@@ -0,0 +1 @@
+- Added new `prefer-eui-icon-tip` rule.
diff --git a/packages/eslint-plugin/src/index.ts b/packages/eslint-plugin/src/index.ts
index 60e792fc4d9e..fda6edd2d3cb 100644
--- a/packages/eslint-plugin/src/index.ts
+++ b/packages/eslint-plugin/src/index.ts
@@ -24,6 +24,7 @@ import { NoCssColor } from './rules/no_css_color';
import { RequireAriaLabelForModals } from './rules/a11y/require_aria_label_for_modals';
import { ConsistentIsInvalidProps } from './rules/a11y/consistent_is_invalid_props';
import { ScreenReaderOutputDisabledTooltip } from './rules/a11y/sr_output_disabled_tooltip';
+import { PreferEuiIconTip } from './rules/a11y/prefer_eui_icon_tip';
const config = {
rules: {
@@ -32,7 +33,8 @@ const config = {
'no-css-color': NoCssColor,
'require-aria-label-for-modals': RequireAriaLabelForModals,
'consistent-is-invalid-props': ConsistentIsInvalidProps,
- 'sr_output_disabled_tooltip': ScreenReaderOutputDisabledTooltip,
+ 'sr-output-disabled-tooltip': ScreenReaderOutputDisabledTooltip,
+ 'prefer-eui-icon-tip': PreferEuiIconTip,
},
configs: {
recommended: {
@@ -43,7 +45,9 @@ const config = {
'@elastic/eui/no-css-color': 'warn',
'@elastic/eui/require-aria-label-for-modals': 'warn',
'@elastic/eui/consistent-is-invalid-props': 'warn',
- '@elastic/eui/sr_output_disabled_tooltip': 'warn',
+ '@elastic/eui/sr-output-disabled-tooltip': 'warn',
+ '@elastic/eui/no-css_color': 'warn',
+ '@elastic/eui/prefer-eui-icon-tip': 'warn',
},
},
},
diff --git a/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.test.ts b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.test.ts
new file mode 100644
index 000000000000..547f851b0d71
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.test.ts
@@ -0,0 +1,84 @@
+import dedent from 'dedent';
+import { RuleTester } from '@typescript-eslint/rule-tester';
+import { PreferEuiIconTip } from './prefer_eui_icon_tip';
+
+const languageOptions = {
+ parserOptions: {
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+};
+
+const ruleTester = new RuleTester();
+
+ruleTester.run('prefer-eui-icon-tip', PreferEuiIconTip, {
+ valid: [
+ {
+ code: dedent`
+ const MyComponent = () => (
+
+
+
+ )
+ `,
+ languageOptions,
+ },
+ {
+ code: dedent`
+ const MyComponent = () => (
+
+
+ Some text
+
+
+
+ )
+ `,
+ languageOptions,
+ },
+ {
+ code: dedent`
+ const MyComponent = () => (
+
+
+
+ )
+ `,
+ languageOptions,
+ },
+ ],
+ invalid: [
+ {
+ code: dedent`
+ const MyComponent = () => (
+
+
+
+ )
+ `,
+ languageOptions,
+ errors: [
+ {
+ messageId: 'preferEuiIconTip',
+ },
+ ],
+ },
+ {
+ code: dedent`
+ const MyComponent = () => (
+
+
+ Other content
+
+ )
+ `,
+ languageOptions,
+ errors: [
+ {
+ messageId: 'preferEuiIconTip',
+ },
+ ],
+ },
+ ],
+});
diff --git a/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.ts b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.ts
new file mode 100644
index 000000000000..984280fcee54
--- /dev/null
+++ b/packages/eslint-plugin/src/rules/a11y/prefer_eui_icon_tip.ts
@@ -0,0 +1,49 @@
+import { type TSESTree, ESLintUtils } from '@typescript-eslint/utils';
+
+const TOOLTIP_COMPONENT = 'EuiToolTip';
+const ICON_COMPONENT = 'EuiIcon';
+const ICON_TIP_COMPONENT = 'EuiIconTip';
+
+export const PreferEuiIconTip = ESLintUtils.RuleCreator.withoutDocs({
+ create(context) {
+ return {
+ JSXElement(node) {
+ const openingElement = node.openingElement;
+ if (
+ openingElement.name.type !== 'JSXIdentifier' ||
+ openingElement.name.name !== TOOLTIP_COMPONENT
+ ) {
+ return;
+ }
+
+ // Find first JSX child
+ const firstChild = node.children.find(
+ (child): child is TSESTree.JSXElement =>
+ child.type === 'JSXElement'
+ );
+
+ if (
+ firstChild &&
+ firstChild.openingElement.name.type === 'JSXIdentifier' &&
+ firstChild.openingElement.name.name === ICON_COMPONENT
+ ) {
+ context.report({
+ node: firstChild,
+ messageId: 'preferEuiIconTip'
+ });
+ }
+ },
+ };
+ },
+ meta: {
+ type: 'suggestion',
+ docs: {
+ description: `Prefer using ${ICON_TIP_COMPONENT} over <${TOOLTIP_COMPONENT}><${ICON_COMPONENT}/>${TOOLTIP_COMPONENT}>, as it delivers better accessibility and improved support for assistive technologies.`
+ },
+ schema: [],
+ messages: {
+ preferEuiIconTip: `Ensure ${ICON_TIP_COMPONENT} is used rather than <${TOOLTIP_COMPONENT}><${ICON_COMPONENT}/>${TOOLTIP_COMPONENT}>, as it delivers better accessibility and improved support for assistive technologies.`,
+ },
+ },
+ defaultOptions: [],
+});