Skip to content

fix(a11y/useAnchorContent): don't flag <a> elements used as render props#10220

Open
theBGuy wants to merge 10 commits into
biomejs:mainfrom
theBGuy:fix/use-anchor-content-render-prop
Open

fix(a11y/useAnchorContent): don't flag <a> elements used as render props#10220
theBGuy wants to merge 10 commits into
biomejs:mainfrom
theBGuy:fix/use-anchor-content-render-prop

Conversation

@theBGuy
Copy link
Copy Markdown

@theBGuy theBGuy commented May 3, 2026

Written with assistance from Claude Code (Sonnet 4.6). Code, tests, and the design choices were authored interactively; the AI helped with code generation and explanations along the way.

Summary

  • The useAnchorContent rule was incorrectly flagging <a> elements passed as render prop values (e.g. render={<a href="..." />}), a common pattern in component libraries such as BaseUI and shadcn/ui where the receiving component renders the anchor as a wrapper around its own children.
  • Adds is_render_prop_anchor, which detects this by walking up the CST through JsxTagExpression, JsxElement, and JsParenthesizedExpression until it finds a JsxExpressionAttributeValue. If found, the anchor is skipped; any other ancestor causes an immediate false so normal bare-<a> violations are unaffected.
  • By default only render is treated as a render prop name.
  • Adds three new valid test fixtures covering the self-closing, open/close, and parenthesized forms.

Test plan

  • cargo test -p biome_js_analyze use_anchor_content — all 4 tests pass
  • Existing invalid cases (<a />, <a></a>, whitespace-only, aria-hidden) are still reported
  • New valid cases (render={<a .../>}, render={<a></a>}, render={(<a .../>)}) produce no diagnostic

…op values

When a library like BaseUI or shadcn/ui accepts a JSX element via a render
prop (e.g. `render={<a href="..." />}`), the receiving component wraps its
own children inside that element. The final DOM therefore contains both the
anchor's attributes and visible text content, making the lint diagnostic a
false positive.

Adds `is_render_prop_anchor`, which walks the CST upward through the
transparent wrapper nodes (`JsxTagExpression`, `JsxElement`,
`JsParenthesizedExpression`) and returns `true` when the anchor is the direct
value of a `JsxExpressionAttributeValue`. All other positions continue to be
flagged normally.
Copilot AI review requested due to automatic review settings May 3, 2026 20:45
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 3, 2026

🦋 Changeset detected

Latest commit: 9a0bfc6

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-JavaScript Language: JavaScript and super languages labels May 3, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This patch updates the useAnchorContent accessibility lint to suppress diagnostics when an <a> node is provided as a JSX expression attribute value (render-prop style, e.g. render={<a ... />}) and the receiving element is a custom component. It adds AST-walking helpers that detect JsxExpressionAttributeValue contexts (handling self-closing, explicit, and parenthesised forms) and an early return in the rule to avoid false positives. Tests and a changelog entry were added to cover the valid cases.

Suggested reviewers

  • dyc3
  • ematipico
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarises the main fix: preventing false positives for <a> elements used as render props, which is the core change across all modified files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description clearly relates to the changeset, explaining the fix for useAnchorContent false positives on render-prop anchors with specific implementation details and test coverage.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


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
Copy Markdown
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: 1

🤖 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/thick-shoes-jog.md:
- Line 5: Update the changeset note so the referenced rule is linked using the
required website format: replace the plain reference to
lint/a11y/useAnchorContent with the markdown link
[`lint/a11y/useAnchorContent`](https://biomejs.dev/linter/rules/use-anchor-content)
inside .changeset/thick-shoes-jog.md; ensure the link text matches the rule
identifier and the URL points to /linter/rules/use-anchor-content so the
changeset follows the "Include links to rule documentation" guideline.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b6ca24f4-3184-48f9-91d5-0e07409b4853

📥 Commits

Reviewing files that changed from the base of the PR and between edb2367 and 03f062f.

⛔ Files ignored due to path filters (1)
  • crates/biome_js_analyze/tests/specs/a11y/useAnchorContent/valid.jsx.snap is excluded by !**/*.snap and included by **
📒 Files selected for processing (3)
  • .changeset/thick-shoes-jog.md
  • crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs
  • crates/biome_js_analyze/tests/specs/a11y/useAnchorContent/valid.jsx

Comment thread .changeset/thick-shoes-jog.md Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the useAnchorContent accessibility lint rule to avoid reporting certain false positives when an <a> element is passed through a JSX attribute as a render-prop-style value, and it aligns the rule docs/tests/release note with that change.

Changes:

  • Added an is_render_prop_anchor ancestor walk in use_anchor_content.rs and documented the new valid pattern in the rule examples.
  • Added valid JSX fixtures for self-closing, paired, and parenthesized <a> render-prop forms.
  • Updated the snapshot and added a patch changeset entry for the lint-rule fix.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Adds the new attribute-value exemption logic and updates rule documentation.
crates/biome_js_analyze/tests/specs/a11y/useAnchorContent/valid.jsx Adds new valid render-prop anchor fixtures.
crates/biome_js_analyze/tests/specs/a11y/useAnchorContent/valid.jsx.snap Refreshes the valid snapshot to include the new fixtures.
.changeset/thick-shoes-jog.md Adds the release note entry for the patch-level lint fix.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
Comment thread crates/biome_js_analyze/tests/specs/a11y/useAnchorContent/valid.jsx
…allowlist

The previous fix exempted any <a> inside a JSX attribute value, which was too
broad — props like `icon={<a />}` would stop reporting even though the anchor
would be rendered with no content.

Narrows the check to verify the attribute name is in an allowlist. The default
list contains only `"render"`. Users can extend it via the new
`additionalRenderProps` option without replacing the default, e.g.:
```
    { "additionalRenderProps": ["as", "component"] }
```
@dyc3
Copy link
Copy Markdown
Contributor

dyc3 commented May 3, 2026

Please don't request reviews from copilot. its just extra noise, and we have our own clanker that gives better reviews.

also, your PR title was truncated

@theBGuy
Copy link
Copy Markdown
Author

theBGuy commented May 3, 2026

Please don't request reviews from copilot. its just extra noise, and we have our own clanker that gives better reviews.

also, your PR title was truncated

Apologies on that one, it happened automatically when I opened the pr and I couldn't get it to stop.

@dyc3
Copy link
Copy Markdown
Contributor

dyc3 commented May 3, 2026

ah, weird. no worries :)

Comment thread crates/biome_rule_options/src/use_anchor_content.rs Outdated
Comment thread crates/biome_rule_options/src/use_anchor_content.rs Outdated
Comment thread crates/biome_rule_options/src/use_anchor_content.rs Outdated
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
@codspeed-hq
Copy link
Copy Markdown

codspeed-hq Bot commented May 3, 2026

Merging this PR will not alter performance

✅ 58 untouched benchmarks
⏩ 196 skipped benchmarks1


Comparing theBGuy:fix/use-anchor-content-render-prop (9a0bfc6) with main (24b3947)2

Open in CodSpeed

Footnotes

  1. 196 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.

  2. No successful run was found on main (b7f0d02) during the generation of this report, so 24b3947 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

theBGuy added 2 commits May 3, 2026 18:35
…ch release

New options on stable rules require a minor release. Split into two PRs:
this one (patch, targeting main) keeps just the render prop detection;
a follow-up will add the additionalRenderProps option against the next branch.

Also replaces the runtime Vec allocation with a const slice for the
hard-coded "render" prop name, per review feedback.
This reverts commit 6266f0c.

No longer adding the options in this PR
@theBGuy theBGuy changed the title fix(a11y/useAnchorContent): don't flag <a> elements used as render pr… fix(a11y/useAnchorContent): don't flag <a> elements used as render props May 3, 2026
@theBGuy theBGuy requested a review from dyc3 May 3, 2026 23:12
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
…ponents only

- Replace the prop-name allowlist with a component-type check: the rule
  is now suppressed whenever <a> appears as a JSX attribute value on any
  custom component (uppercase or member-expression name), not just props
  named "render". Native HTML elements are unaffected.
- Rewrite the helper as `is_component_attribute`, returning `Option<bool>`
  with the `?` operator instead of the IIFE pattern.
- Move the render-prop explanation out of the inline code comment and into
  prose above the code block in the rule docs.
Copy link
Copy Markdown
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.

🧹 Nitpick comments (1)
crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs (1)

68-74: ⚡ Quick win

Align the wording with the actual exemption scope.

This now skips any <a> used as a JSX attribute value on a custom component, not just render={...}. Keeping “render prop” in the docs and helper name is a bit too specific and will send the next maintainer on a tiny treasure hunt.

✏️ Suggested tidy-up
-/// Returns true when the `<a>` element is the value of a JSX attribute that belongs to a
-/// custom component (render prop pattern).
+/// Returns true when the `<a>` element is the value of a JSX attribute that belongs to a
+/// custom component.
 ///
-/// In this pattern the receiving component renders the anchor element as a wrapper and places
+/// In this pattern the receiving component may render the anchor element as a wrapper and place
 /// its own JSX children inside it, so the final DOM will contain both the anchor's attributes
 /// *and* real text content — making the lint check a false positive.
@@
-fn is_render_prop_anchor(node: &AnyJsxElement) -> bool {
+fn is_custom_component_attribute_anchor(node: &AnyJsxElement) -> bool {

And update the call site at Line 111 to match the rename.

Also applies to: 194-204

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs` around lines 68
- 74, The docs and helper name currently reference "render prop" but the logic
actually exempts any JSX attribute whose value is an anchor element; rename the
helper and references from render-prop-specific terms to a generic name like
is_jsx_attribute_anchor (update the doc comment above UseAnchorContent examples
to remove "render prop" wording and explain the exemption applies to anchor
elements used as JSX attribute values on custom components), and update the call
site that invokes the helper (the call at the location previously noted around
line 111) plus the other occurrences around the 194-204 region to use the new
helper name and wording so names and documentation match the actual scope.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs`:
- Around line 68-74: The docs and helper name currently reference "render prop"
but the logic actually exempts any JSX attribute whose value is an anchor
element; rename the helper and references from render-prop-specific terms to a
generic name like is_jsx_attribute_anchor (update the doc comment above
UseAnchorContent examples to remove "render prop" wording and explain the
exemption applies to anchor elements used as JSX attribute values on custom
components), and update the call site that invokes the helper (the call at the
location previously noted around line 111) plus the other occurrences around the
194-204 region to use the new helper name and wording so names and documentation
match the actual scope.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d27d9b59-1983-452a-b768-4ac939f2af1e

📥 Commits

Reviewing files that changed from the base of the PR and between 4a58772 and c287a25.

📒 Files selected for processing (1)
  • crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs

…on scope

Rename `is_render_prop_anchor` → `is_jsx_attribute_anchor` and remove all
"render prop" wording from docs and comments. The exemption applies to any
JSX attribute on a custom component, not only props named "render".
@theBGuy theBGuy requested a review from ematipico May 4, 2026 22:14
@theBGuy
Copy link
Copy Markdown
Author

theBGuy commented May 5, 2026

Anything else that needs to be done?

Verifies that `<a>` inside a JSX attribute on a custom element (hyphenated
lowercase name) is still flagged — the exemption only applies to uppercase
React components, not web components.
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
Comment thread crates/biome_js_analyze/src/lint/a11y/use_anchor_content.rs Outdated
… review

- Replace manual parent loop with ancestors().skip(1)
- Replace can_cast + unwrap_cast with if let Some(...) = cast(...)
@theBGuy
Copy link
Copy Markdown
Author

theBGuy commented May 11, 2026

@ematipico @dyc3 should I rebase or update with merge commit to resolve the check dependencies action failure, or just not worry about it?

@dyc3
Copy link
Copy Markdown
Contributor

dyc3 commented May 12, 2026

yes, go ahead and update the branch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants