Skip to content

fix(biome_html_analyze): recognize Astro Image component in useAnchorContent#9369

Merged
ematipico merged 3 commits intobiomejs:mainfrom
costajohnt:fix/use-anchor-content-image-component
Mar 12, 2026
Merged

fix(biome_html_analyze): recognize Astro Image component in useAnchorContent#9369
ematipico merged 3 commits intobiomejs:mainfrom
costajohnt:fix/use-anchor-content-image-component

Conversation

@costajohnt
Copy link
Contributor

@costajohnt costajohnt commented Mar 6, 2026

Summary

Fixes #9210

The useAnchorContent rule was reporting a false positive when an Astro <Image> component with an alt attribute was used inside an anchor element. The has_accessible_content() function only recognized <img> (case-insensitive) but not Astro's built-in <Image> component, which renders to <img> at build time.

This PR recognizes Image (case-sensitive PascalCase) as an image element in the self-closing element check, scoped to Astro files only so that a <Image> component in Vue or Svelte doesn't incorrectly bypass the rule.

Test Plan

  • cargo test -p biome_html_analyze -- use_anchor_content — all tests pass
  • Valid case: <a><Image alt="description" /></a> in Astro produces no diagnostic
  • Invalid cases: <a><Image /></a> and <a><Image alt="" /></a> produce expected diagnostics

Docs

No documentation changes needed — this is a bug fix for an existing rule with no new options.

@changeset-bot
Copy link

changeset-bot bot commented Mar 6, 2026

🦋 Changeset detected

Latest commit: 0e3ac96

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 13 packages
Name Type
@biomejs/biome Patch
@biomejs/cli-win32-x64 Patch
@biomejs/cli-win32-arm64 Patch
@biomejs/cli-darwin-x64 Patch
@biomejs/cli-darwin-arm64 Patch
@biomejs/cli-linux-x64 Patch
@biomejs/cli-linux-arm64 Patch
@biomejs/cli-linux-x64-musl Patch
@biomejs/cli-linux-arm64-musl Patch
@biomejs/wasm-web Patch
@biomejs/wasm-bundler Patch
@biomejs/wasm-nodejs Patch
@biomejs/backend-jsonrpc Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions bot added A-Linter Area: linter L-HTML Language: HTML and super languages labels Mar 6, 2026
@costajohnt costajohnt marked this pull request as ready for review March 6, 2026 15:49
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 6, 2026

Walkthrough

This PR fixes a false positive in the useAnchorContent rule for Astro's Image component. The fix introduces an is_astro flag throughout the content-checking logic to recognise Astro Image components as accessible elements when they have non-empty alt attributes. Additionally, the logic now treats custom components (PascalCase elements) as potentially containing accessible content, and all recursive calls have been updated to preserve Astro-aware behaviour. Test coverage has been extended for Astro, Svelte, and Vue frameworks.

Possibly related PRs

Suggested reviewers

  • ematipico
  • dyc3
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main fix: recognising Astro's Image component in the useAnchorContent accessibility rule.
Description check ✅ Passed The description clearly explains the problem (false positive with Astro Image components), the solution (recognising Image in self-closing element check, scoped to Astro), and includes test plan and verification steps.
Linked Issues check ✅ Passed The PR fully addresses #9210 by recognising Astro's Image component as an image element in the accessibility check when an alt attribute is present, scoped appropriately to Astro files.
Out of Scope Changes check ✅ Passed All changes directly support the stated objective: test files document Astro Image handling, the changeset logs the fix, and the implementation adds necessary Astro-scoped logic without unrelated modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.changeset/fix-use-anchor-content-image.md:
- Line 5: Change the changeset line to use the issue-based bug-fix format and
user-facing wording: replace the current sentence about a "false positive" with
a line beginning "Fixed [`#ISSUE_NUMBER`](issue link): ..." that describes the
visible behavior (e.g., "Anchor elements with an Astro Image component that has
an alt attribute are no longer flagged") and include the existing link to
useAnchorContent; update the reference to the Astro Image component and its alt
attribute so the note reads as an end-user fix rather than an internal
diagnostic.

In `@crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs`:
- Around line 215-217: The branch that treats a PascalCase "Image" as exempt
must be limited to Astro files: update has_accessible_content (or its caller) to
accept a source/type flag (e.g., SourceType or is_astro boolean) and change the
match arm Some(name) if name.eq_ignore_ascii_case("img") || name == "Image" to
only allow the "Image" special-case when the source is Astro (e.g., name ==
"Image" && is_astro) so custom components in Vue/Svelte won't be exempt; adjust
all call sites to pass the source info or determine file source earlier and
thread it into has_accessible_content.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dd565063-7e1b-434f-a656-189f0f8e0c09

📥 Commits

Reviewing files that changed from the base of the PR and between 93ea495 and e4814cc.

⛔ Files ignored due to path filters (2)
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • .changeset/fix-use-anchor-content-image.md
  • crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro

Copy link
Member

@ematipico ematipico left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • restore our PR template
  • address the bot suggestions

…Content

Treat the Astro `<Image>` component the same as `<img>` when checking
for accessible content inside anchor elements. An `<Image alt="...">`
inside `<a>` no longer triggers a false positive.

Closes biomejs#9210
@costajohnt costajohnt force-pushed the fix/use-anchor-content-image-component branch from e4814cc to 89dd749 Compare March 6, 2026 18:48
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs (1)

215-217: ⚠️ Potential issue | 🟠 Major

Please keep the Image exemption Astro-only.

This now treats any PascalCase <Image> in non-HTML files as equivalent to <img>, so a custom Vue/Svelte component can slip past the rule if it happens to expose alt. Please thread the source type into has_accessible_content() and only allow this branch for Astro.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs` around lines
215 - 217, The patch currently treats a PascalCase "Image" tag the same as an
HTML <img>, which allows non-Astro frameworks to bypass the rule; update
has_accessible_content(...) to accept (or be passed) the
source/context/source_type (e.g., an enum or &str) and restrict the special-case
branch that checks Some(name) if name.eq_ignore_ascii_case("img") || name ==
"Image" so that the name == "Image" exemption only applies when source_type ==
Astro; leave the img check unchanged for all sources and adjust callers of
has_accessible_content (and its signature) accordingly so only Astro files get
the PascalCase Image exemption.
.changeset/fix-use-anchor-content-image.md (1)

5-5: ⚠️ Potential issue | 🟡 Minor

Use the issue-linked bug-fix template here.

Line 5 still starts with the rule link rather than the issue link, and “false positive” reads a bit too implementation-heavy for a changeset. Please switch it to the Fixed [#9210](...) format and describe the visible user-facing fix.

As per coding guidelines, bug fix changesets should start with 'Fixed [#NUMBER](issue link): ...' format and be written for end users.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.changeset/fix-use-anchor-content-image.md at line 5, Update the changeset
header to the bug-fix template: replace the current rule-link start on line 5
with "Fixed [`#9210`](issue URL): ..." and reword the message to a user-facing
description explaining that the useAnchorContent linter no longer incorrectly
flags anchor elements that contain an Astro Image component with an alt
attribute (i.e., mention useAnchorContent and "Astro Image with alt" so
reviewers can locate the change), ensuring the phrasing is for end users rather
than implementation details.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In @.changeset/fix-use-anchor-content-image.md:
- Line 5: Update the changeset header to the bug-fix template: replace the
current rule-link start on line 5 with "Fixed [`#9210`](issue URL): ..." and
reword the message to a user-facing description explaining that the
useAnchorContent linter no longer incorrectly flags anchor elements that contain
an Astro Image component with an alt attribute (i.e., mention useAnchorContent
and "Astro Image with alt" so reviewers can locate the change), ensuring the
phrasing is for end users rather than implementation details.

In `@crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs`:
- Around line 215-217: The patch currently treats a PascalCase "Image" tag the
same as an HTML <img>, which allows non-Astro frameworks to bypass the rule;
update has_accessible_content(...) to accept (or be passed) the
source/context/source_type (e.g., an enum or &str) and restrict the special-case
branch that checks Some(name) if name.eq_ignore_ascii_case("img") || name ==
"Image" so that the name == "Image" exemption only applies when source_type ==
Astro; leave the img check unchanged for all sources and adjust callers of
has_accessible_content (and its signature) accordingly so only Astro files get
the PascalCase Image exemption.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1ab6b6dc-1b90-4271-b9e3-8554c34fc8dd

📥 Commits

Reviewing files that changed from the base of the PR and between e4814cc and 89dd749.

⛔ Files ignored due to path filters (2)
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (4)
  • .changeset/fix-use-anchor-content-image.md
  • crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/valid.astro
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/astro/invalid.astro

- Only recognize <Image> as an image element in Astro files, not Vue/Svelte
- Pass is_astro flag through has_accessible_content() recursive calls
- Reformat changeset to reference issue biomejs#9210
- Add Vue and Svelte test cases proving Image is not recognized outside Astro
@codspeed-hq
Copy link

codspeed-hq bot commented Mar 7, 2026

Merging this PR will not alter performance

✅ 64 untouched benchmarks
⏩ 152 skipped benchmarks1


Comparing costajohnt:fix/use-anchor-content-image-component (0e3ac96) with main (bc5dd99)

Open in CodSpeed

Footnotes

  1. 152 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

<a><input type="hidden" /></a>

<!-- Image component is not recognized outside Astro -->
<a><Image alt="description" /></a>
Copy link
Member

@ematipico ematipico Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be valid. We can't know if the component will render some accessible content or not, which means we should NOT trigger this

Custom components like <Image> or <CustomIcon> may render accessible
content, so they should not trigger useAnchorContent in Svelte/Vue.

The Astro-specific <Image> + alt check is preserved since Image is a
known built-in that maps to <img>.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte`:
- Around line 20-22: The test currently treats non-Astro components inside
anchors as accessible (lines with <a><Image alt="description" /></a> and
<a><CustomIcon /></a>), which makes the rule too optimistic for Svelte; update
the spec so only Astro-scoped valid cases are blessed: either replace the
non-Astro components (Image, CustomIcon) with concrete accessible HTML (e.g.,
<img alt="..."> or text) or move these cases to the invalid tests so the rule
correctly flags custom component children in <a> as not automatically
accessible. Ensure you edit the entries referencing Image and CustomIcon in the
Svelte test to reflect this change.

In `@crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue`:
- Around line 20-22: The test includes non-Astro Vue component cases (<Image
alt="description" /> and <CustomIcon />) in the `valid` fixture which
incorrectly asserts that arbitrary component children satisfy the
useAnchorContent rule; remove or relocate these Vue component anchor cases from
this `valid.vue` file (lines showing <a><Image ... /></a> and <a><CustomIcon
/></a>) and instead place them in an appropriate Vue-specific fixture or mark
them as invalid/explicit Astro-only tests so `useAnchorContent` only treats
Astro component behavior in this `valid` suite.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 199a2782-1ff8-49c1-8a9a-c6c48a785b3a

📥 Commits

Reviewing files that changed from the base of the PR and between 110d143 and 0e3ac96.

⛔ Files ignored due to path filters (2)
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte.snap is excluded by !**/*.snap and included by **
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/svelte/valid.svelte
  • crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue
🚧 Files skipped from review as they are similar to previous changes (1)
  • crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs

Comment on lines +20 to +22
<!-- Custom components may render accessible content -->
<a><Image alt="description" /></a>
<a><CustomIcon /></a>
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep the non-Astro fixtures out of valid here.

Line 21 and Line 22 codify that Vue custom components satisfy useAnchorContent, which is broader than the Astro-only fix from #9210. That would lock in false negatives for anchors whose component children render no accessible text.

Suggested adjustment
-  <!-- Custom components may render accessible content -->
-  <a><Image alt="description" /></a>
-  <a><CustomIcon /></a>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue`
around lines 20 - 22, The test includes non-Astro Vue component cases (<Image
alt="description" /> and <CustomIcon />) in the `valid` fixture which
incorrectly asserts that arbitrary component children satisfy the
useAnchorContent rule; remove or relocate these Vue component anchor cases from
this `valid.vue` file (lines showing <a><Image ... /></a> and <a><CustomIcon
/></a>) and instead place them in an appropriate Vue-specific fixture or mark
them as invalid/explicit Astro-only tests so `useAnchorContent` only treats
Astro component behavior in this `valid` suite.

Copy link
Member

@ematipico ematipico Mar 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coderabbitai that's the whole point of the test. Since we don't know if a possible component renders something, we add a test to make sure it doesn't trigger a false positive. The previous commit mistakenly emitted a diagnostic

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ematipico, understood — my mistake! Since the linter has no visibility into what a custom component ultimately renders, flagging <a><CustomComponent /></a> as lacking accessible content would indeed be a false positive. Treating PascalCase components as potentially accessible content is the correct conservative approach here.


✏️ Learnings added
Learnt from: ematipico
Repo: biomejs/biome PR: 9369
File: crates/biome_html_analyze/tests/specs/a11y/useAnchorContent/vue/valid.vue:20-22
Timestamp: 2026-03-08T08:18:28.023Z
Learning: In `crates/biome_html_analyze/src/lint/a11y/use_anchor_content.rs` (biomejs/biome), custom components (PascalCase elements) inside `<a>` tags are intentionally treated as potentially accessible content to avoid false positives. The linter cannot statically determine what a custom component renders, so it must not emit a diagnostic for anchors whose only children are custom components. The `valid` fixture files for non-Astro frameworks (e.g., vue/valid.vue, svelte/valid.svelte) correctly include cases like `<a><Image alt="description" /></a>` and `<a><CustomIcon /></a>` as valid to document this behavior.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: dyc3
Repo: biomejs/biome PR: 8291
File: crates/biome_html_formatter/tests/specs/prettier/vue/html-vue/elastic-header.html:10-10
Timestamp: 2025-12-04T13:29:49.287Z
Learning: Files under `crates/biome_html_formatter/tests/specs/prettier` are test fixtures synced from Prettier and should not receive detailed code quality reviews (e.g., HTTP vs HTTPS, formatting suggestions, etc.). These files are test data meant to validate formatter behavior and should be preserved as-is.

Learnt from: ematipico
Repo: biomejs/biome PR: 8537
File: crates/biome_js_analyze/src/lint/nursery/no_leaked_render.rs:167-210
Timestamp: 2025-12-22T09:27:13.161Z
Learning: In crates/biome_analyze/**/*analyze/src/**/*.rs, the `fix_kind` field in `declare_lint_rule!` should only be specified when the rule implements the `action` function. Rules that only emit diagnostics without providing code fixes should not include `fix_kind` in their metadata.

@costajohnt
Copy link
Contributor Author

@ematipico Checking on the status of this PR. Anything else needed from my side right now?

1 similar comment
@costajohnt
Copy link
Contributor Author

@ematipico Checking on the status of this PR. Anything else needed from my side right now?

@ematipico ematipico merged commit b309dde into biomejs:main Mar 12, 2026
18 checks passed
@github-actions github-actions bot mentioned this pull request Mar 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-Linter Area: linter L-HTML Language: HTML and super languages

Projects

None yet

Development

Successfully merging this pull request may close these issues.

💅 useAnchorContent false positive with Astro Image component

2 participants