aria-hidden="true" was sharing the "warning" bucket with real problems
(empty alt, ARIA-name-without-alt) even though it needs no action - the
element is already fully removed from the accessibility tree. It now
gets its own "info"/"Hinweis" kind with a dedicated badge, icon, and
summary count.
Exclusion only makes sense for things you'd want to mark as "reviewed,
intentionally left as-is": errors and real warnings. "success" and the
new "info" kind no longer get a click handler at all, since there is
nothing to exclude - clicking them is now a no-op.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Elements with aria-hidden="true" are already fully removed from the
accessibility tree, equivalent in intent to alt="" - they were
wrongly flagged as errors instead of the same decorative warning
alt="" gets. On role="img" elements with no wrapping link, aria-label,
or (now) aria-hidden exemption, imageIdentifier() also had no way to
build an exclusion key at all, so the exclude click silently did
nothing. Added a className-based fallback identifier as a last
resort so these elements can still be excluded individually.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Labels were inserted as normal DOM siblings with z-index set but
position left as the default "static" - z-index has no effect on
non-positioned elements, so any ancestor that establishes its own
stacking context (e.g. a position:relative logo wrapper with a
hover/link overlay, common in component libraries like Salesforce
LWC/SLDS) could paint over the label and swallow clicks intended for
it. Labels now get a real position:absolute anchored to the target
element's viewport position and are appended directly to
document.body, escaping any ancestor stacking context while still
scrolling correctly with the page.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Some frameworks (e.g. Salesforce LWC) expose a .shadowRoot on elements
whose content is actually the same light-DOM subtree rather than a
real encapsulated tree. deepQueryAll's recursive shadow traversal
revisited that subtree once per nested "shadow" ancestor, so a single
image could get processed and labeled multiple times (observed: one
footer logo produced 5 identical OK badges). deepQueryAll now tracks
visited roots and elements so each is only ever processed once.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Exclusions were keyed only by content identifier (src / role-img
label), so multiple identical images (or repeated icons) shared one
key and excluding one silently excluded all of them. Each occurrence
now gets a per-run instance number appended to the identifier, so
duplicates can be excluded individually while staying stable across
reloads.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
imageIdentifier() only checked currentSrc/src, which don't exist on
inline <svg role="img"> icons (common for social-media links). The
exclude click therefore silently no-op'd for those. Fall back to the
surrounding link's href or the element's aria-label as the identity.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
README is rendered on the repo landing page; the drag-and-drop
installer moves to the pages branch since Codeberg strips
javascript: links from rendered markdown and raw file views.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>