diff --git a/crates/oxc_linter/src/rules.rs b/crates/oxc_linter/src/rules.rs
index eaf480f3d..278fa96c9 100644
--- a/crates/oxc_linter/src/rules.rs
+++ b/crates/oxc_linter/src/rules.rs
@@ -155,6 +155,10 @@ mod unicorn {
pub mod throw_new_error;
}
+mod jsx_a11y {
+ pub mod alt_text;
+}
+
oxc_macros::declare_all_lint_rules! {
deepscan::bad_array_method_on_arguments,
deepscan::bad_bitwise_operator,
@@ -284,4 +288,5 @@ oxc_macros::declare_all_lint_rules! {
import::named,
import::no_cycle,
import::no_self_import,
+ jsx_a11y::alt_text
}
diff --git a/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs
new file mode 100644
index 000000000..ec80a839e
--- /dev/null
+++ b/crates/oxc_linter/src/rules/jsx_a11y/alt_text.rs
@@ -0,0 +1,546 @@
+use oxc_ast::{
+ ast::{
+ JSXAttributeItem, JSXAttributeValue, JSXChild, JSXElement, JSXElementName, JSXExpression,
+ JSXExpressionContainer, JSXOpeningElement,
+ },
+ AstKind,
+};
+use oxc_diagnostics::{
+ miette::{self, Diagnostic},
+ thiserror::Error,
+};
+use oxc_macros::declare_oxc_lint;
+use oxc_span::Span;
+
+use crate::utils::has_jsx_prop_lowercase;
+use crate::{context::LintContext, rule::Rule, AstNode};
+
+#[derive(Debug, Error, Diagnostic)]
+enum AltTextDiagnostic {
+ //
+ #[error("eslint-plugin-jsx-a11y(alt-text): Missing `alt` attribute.")]
+ #[diagnostic(severity(warning), help("Must have `alt` prop, either with meaningful text, or an empty string for decorative images."))]
+ MissingAltProp(#[label] Span),
+
+ #[error("eslint-plugin-jsx-a11y(alt-text): Invalid `alt` value.")]
+ #[diagnostic(
+ severity(warning),
+ help("Must have meaningful value for `alt` prop. Use alt=\"\" for presentational images.")
+ )]
+ MissingAltValue(#[label] Span),
+
+ #[error("eslint-plugin-jsx-a11y(alt-text): Missing value for aria-label attribute.")]
+ #[diagnostic(severity(warning), help("The aria-label attribute must have a value. The alt attribute is preferred over aria-label for images."))]
+ AriaLabelValue(#[label] Span),
+
+ #[error("eslint-plugin-jsx-a11y(alt-text): Missing value for aria-labelledby attribute.")]
+ #[diagnostic(
+ severity(warning),
+ help("The alt attribute is preferred over aria-labelledby for images.")
+ )]
+ AriaLabelledByValue(#[label] Span),
+
+ #[error("eslint-plugin-jsx-a11y(alt-text): ARIA used where native HTML could suffice.")]
+ #[diagnostic(severity(warning), help("Prefer alt=\"\" over presentational role. Native HTML attributes should be preferred for accessibility before resorting to ARIA attributes."))]
+ PreferAlt(#[label] Span),
+
+ //