Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Skip opacity:0 strip when the element is animating opacity#128

Merged
twschiller merged 2 commits into
mainfrom
fix/hidden-text-strip-transition-opacity
Jun 4, 2026
Merged

Skip opacity:0 strip when the element is animating opacity#128
twschiller merged 2 commits into
mainfrom
fix/hidden-text-strip-transition-opacity

Conversation

@twschiller

Copy link
Copy Markdown
Contributor

Summary

  • hidden-text-strip was the rule hiding GitHub's "Create new issue" modal. Primer's <dialog> backdrop (prc-Dialog-Backdrop-*) renders at opacity: 0 with an opacity transition, then fades to 1 once the dialog opens — the subtree watcher caught the wrapper mid-transition and removed the entire modal subtree before the user ever saw it.
  • New guard skips the opacity-0 strip when the element has an in-flight CSS transition or animation involving opacity (or all). A plain opacity: 0 with no animation is still stripped — the regression test for that case is added explicitly so the guard can't drift permissive.

Why this is safe

An animating opacity: 0 is a transient state — the text will become visible to sighted users when the animation completes, so the asymmetry the rule defends against (text invisible to humans but readable to agents) doesn't hold. The guard requires a non-zero duration to fire, so transition: opacity 0s / unset durations still strip normally.

jsdom note

Real browsers expand the transition/animation shorthand into transitionProperty / transitionDuration / animationName / animationDuration. jsdom keeps only the shorthand. The detector accepts both: longhand is the production path, shorthand fallback keeps the tests honest.

Test plan

  • bun run test — 995 passing, including 4 new cases (transition with opacity, transition with all, keyframe animation, regression guard that plain opacity:0 still strips).
  • bun run check + bun run typecheck.
  • Manual: load extension/dist/ as unpacked, visit github.com/pixiebrix/agent-browser-shield/issues, click New issue — the template chooser modal should appear.

Out of scope

The prc-components-Label-* clip-to-zero strip you also saw in the console (the Select all issues SR-only label) is a separate false positive — Primer's label is a real SR-only element that should be admitted by hasStructuralSrOnlyPattern, but Primer must size it slightly differently than what we recognize. Worth a follow-up issue if it's hurting GitHub a11y output.

Closes #126

🤖 Generated with Claude Code

Primer's <dialog> backdrop renders at opacity:0 with an opacity
transition, then fades to 1 once GitHub's chooser opens. The subtree
watcher caught the wrapper mid-transition and stripped the whole modal
subtree before the user ever saw it.

An animating opacity:0 is a transient state, not the hidden-injection
signal the rule defends against — by definition the text will become
visible to sighted users when the animation completes, so the asymmetry
the rule relies on doesn't hold. The new guard accepts both the
expanded longhand (real browsers populate transitionProperty /
transitionDuration / animationName / animationDuration) and the
shorthand (jsdom keeps only `transition` / `animation`).

A plain opacity:0 with no transition or animation is still stripped.

Closes #126

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@vercel

vercel Bot commented Jun 4, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agent-browser-shield-demo-site Ready Ready Preview, Comment Jun 4, 2026 2:28pm

Request Review

@unblocked unblocked Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found.

About Unblocked

Unblocked has been set up to automatically review your team's pull requests to identify genuine bugs and issues.

📖 Documentation — Learn more in our docs.

💬 Ask questions — Mention @unblocked to request a review or summary, or ask follow-up questions.

👍 Give feedback — React to comments with 👍 or 👎 to help us improve.

⚙️ Customize — Adjust settings in your preferences.

Comment thread extension/src/rules/hidden-text-strip.ts Outdated
Unblocked flagged the original shorthand check on PR #128: the regex
matched every CSS time literal including `0s` / `0ms`, so an attacker
could declare `transition: opacity 0s` and the shorthand block would
short-circuit the strip even though the text stays permanently
invisible. The longhand check on the same path correctly rejected zero
durations, but in a real browser both longhand and shorthand are
populated so execution fell through to the broken shorthand block.

Two changes:

  - Gate the shorthand check behind `!style.transitionProperty` /
    `!style.animationName`. Real browsers always populate the longhand
    (the canonical signal), so the shorthand is only consulted in jsdom
    where the longhand is empty.

  - Parse magnitudes numerically when scanning a shorthand. The
    refactored `shorthandHasNonzeroDuration` walks every duration token
    and returns true only when at least one has a value > 0.

Regression tests cover `transition: opacity 0s`, `animation: fadeIn 0s`,
and `transition: transform 150ms` paired with `opacity: 0` — all three
must still be stripped.

Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
@twschiller twschiller added the bug Something isn't working label Jun 4, 2026
@twschiller twschiller merged commit 153c61a into main Jun 4, 2026
7 checks passed
@twschiller twschiller deleted the fix/hidden-text-strip-transition-opacity branch June 4, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Default rulesets hides New Issue template modal on GitHub

1 participant