diff --git a/.changeset/fix-use-anchor-content-image.md b/.changeset/fix-use-anchor-content-image.md
new file mode 100644
index 000000000000..970a40daa00c
--- /dev/null
+++ b/.changeset/fix-use-anchor-content-image.md
@@ -0,0 +1,5 @@
+---
+"@biomejs/biome": patch
+---
+
+Fixed [#9210](https://github.com/biomejs/biome/issues/9210): [`useAnchorContent`](https://biomejs.dev/linter/rules/use-anchor-content/) no longer reports an accessibility error for Astro `Image` components inside links when they provide non-empty `alt` text.
diff --git a/crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs b/crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs
index 524a8f58a051..1f8d9cc3f27b 100644
--- a/crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs
+++ b/crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs
@@ -143,7 +143,8 @@ impl Rule for UseAnchorContent {
}
// Check if the anchor has accessible content
- if has_accessible_content(&html_element.children()) {
+ let is_astro = source_type.is_astro();
+ if has_accessible_content(&html_element.children(), is_astro) {
return None;
}
@@ -190,14 +191,14 @@ impl Rule for UseAnchorContent {
}
/// Checks if `HtmlElementList` contains accessible content (non-empty text or visible elements).
-fn has_accessible_content(html_child_list: &HtmlElementList) -> bool {
+fn has_accessible_content(html_child_list: &HtmlElementList, is_astro: bool) -> bool {
html_child_list.into_iter().any(|child| match &child {
AnyHtmlElement::AnyHtmlContent(content) => is_accessible_text_content(content),
AnyHtmlElement::HtmlElement(element) => {
if html_element_has_truthy_aria_hidden(element) {
false
} else {
- has_accessible_content(&element.children())
+ has_accessible_content(&element.children(), is_astro)
}
}
AnyHtmlElement::HtmlSelfClosingElement(element) => {
@@ -212,7 +213,10 @@ fn has_accessible_content(html_child_list: &HtmlElementList) -> bool {
let tag_text = element.name().ok().and_then(|n| n.token_text_trimmed());
match tag_text.as_ref().map(|t| t.as_ref()) {
- Some(name) if name.eq_ignore_ascii_case("img") => {
+ Some(name)
+ if name.eq_ignore_ascii_case("img")
+ || (is_astro && name == "Image") =>
+ {
html_self_closing_element_has_non_empty_attribute(element, "alt")
}
Some(name)
@@ -235,6 +239,8 @@ fn has_accessible_content(html_child_list: &HtmlElementList) -> bool {
});
!is_hidden
}
+ // Custom components (PascalCase) may render accessible content
+ Some(name) if name.starts_with(|c: char| c.is_uppercase()) => true,
_ => false,
}
}
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro
index ef06ca900117..4c4a9fa9ab8c 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro
@@ -16,6 +16,12 @@
+
+
+
+
+
+
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro.snap b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro.snap
index 3a5cf1873e3e..e48117c9cae9 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro.snap
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro.snap
@@ -1,5 +1,6 @@
---
source: crates/biome_html_analyze/tests/spec_tests.rs
+assertion_line: 83
expression: invalid.astro
---
# Input
@@ -22,6 +23,12 @@ expression: invalid.astro
+
+
+
+
+
+
@@ -181,7 +188,7 @@ invalid.astro:17:1 lint/a11y/useAnchorContent ━━━━━━━━━━━
> 17 │
│ ^^^^^^^^^^^^^^^^^^^^^
18 │
- 19 │
+ 19 │
i All links on a page should have content that is accessible to screen readers.
@@ -199,11 +206,55 @@ invalid.astro:20:1 lint/a11y/useAnchorContent ━━━━━━━━━━━
× Provide screen reader accessible content when using a elements.
- 19 │
- > 20 │
+ 19 │
+ > 20 │
+ │ ^^^^^^^^^^^^^^^^
+ 21 │
+ 22 │
+
+ i All links on a page should have content that is accessible to screen readers.
+
+ i Accessible content refers to digital content that is designed and structured in a way that makes it easy for people with disabilities to access, understand, and interact with using assistive technologies.
+
+ i Follow these links for more information,
+ WCAG 2.4.4
+ WCAG 4.1.2
+
+
+```
+
+```
+invalid.astro:23:1 lint/a11y/useAnchorContent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Provide screen reader accessible content when using a elements.
+
+ 22 │
+ > 23 │
+ │ ^^^^^^^^^^^^^^^^^^^^^^^
+ 24 │
+ 25 │
+
+ i All links on a page should have content that is accessible to screen readers.
+
+ i Accessible content refers to digital content that is designed and structured in a way that makes it easy for people with disabilities to access, understand, and interact with using assistive technologies.
+
+ i Follow these links for more information,
+ WCAG 2.4.4
+ WCAG 4.1.2
+
+
+```
+
+```
+invalid.astro:26:1 lint/a11y/useAnchorContent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ × Provide screen reader accessible content when using a elements.
+
+ 25 │
+ > 26 │
│ ^^^^^^^^^^^^^
- 21 │
- 22 │
+ 27 │
+ 28 │
i All links on a page should have content that is accessible to screen readers.
@@ -217,16 +268,16 @@ invalid.astro:20:1 lint/a11y/useAnchorContent ━━━━━━━━━━━
```
```
-invalid.astro:21:1 lint/a11y/useAnchorContent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+invalid.astro:27:1 lint/a11y/useAnchorContent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× Provide screen reader accessible content when using a elements.
- 19 │
- 20 │
- > 21 │
+ 25 │
+ 26 │
+ > 27 │
│ ^^^^^^^^^^^^^
- 22 │
- 23 │
+ 28 │
+ 29 │
i All links on a page should have content that is accessible to screen readers.
@@ -240,14 +291,14 @@ invalid.astro:21:1 lint/a11y/useAnchorContent ━━━━━━━━━━━
```
```
-invalid.astro:24:1 lint/a11y/useAnchorContent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+invalid.astro:30:1 lint/a11y/useAnchorContent ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
× Provide screen reader accessible content when using a elements.
- 23 │
- > 24 │
+ 29 │
+ > 30 │
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- 25 │
+ 31 │
i All links on a page should have content that is accessible to screen readers.
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro
index 3b35fb09bc71..4a805afa0664 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro
@@ -15,6 +15,7 @@
content
content
+
Home
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro.snap b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro.snap
index f31816c90732..ca412d20f6a9 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro.snap
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro.snap
@@ -1,5 +1,6 @@
---
source: crates/biome_html_analyze/tests/spec_tests.rs
+assertion_line: 83
expression: valid.astro
---
# Input
@@ -21,6 +22,7 @@ expression: valid.astro
content
content
+
Home
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte
index 001fb72ddd66..e8fb306d85f5 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte
@@ -16,3 +16,7 @@
+
+
+
+
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte.snap b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte.snap
index 883b33c5036e..679d7053efa0 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte.snap
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte.snap
@@ -23,4 +23,8 @@ expression: valid.svelte
+
+
+
+
```
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue
index 49d641203266..03630f7be33f 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue
@@ -16,4 +16,8 @@
+
+
+
+
diff --git a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue.snap b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue.snap
index be8f531c5fe2..5f1378d9c59d 100644
--- a/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue.snap
+++ b/crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue.snap
@@ -22,6 +22,10 @@ expression: valid.vue
+
+
+
+
```