Releases: ryersondmp/sa11y
Sa11y 5.0.3
Fix logic issue with HIDDEN_FOCUSABLE test.
Sa11y 5.0.2
- Addresses a serious TypeError in 5.0.1.
Developer changes
These changes consolidate logic and reduce duplication.
- Fix
Lang.sprintf()argument handling and adjust regex behaviour. - Initialize and centralize many constants (stopwords, placeholder sets, link/file type patterns, click/new-window regexes).
- Introduce
pushResulthelper to standardize pushing results toState.resultsand migrate callers to use it. - Refactor all ruleset files to utilize
pushResulthelper, significantly consolidating code. - Improve contrast preview rendering by setting styles on the preview element instead of inline HTML building.
- Change annotation insertion order to prefer link/button targets before
svgfallbacks. - Adds CI tweaks (CodeQL config file and publish workflow permissions).
Sa11y 5.0
This release features a significant refactor focused on XSS prevention, performance optimization, and reduced bundle size. The update enhances existing accessibility checks and includes a first-of-its-kind experimental Language Detection check, powered by the on-device Language Detector API (currently exclusive to Google Chrome).
New experimental checks for language detection
This new ruleset leverages on-device AI to automatically detect and validate the language of your content, ensuring it matches a web page’s language declaration. This feature helps identify issues pertaining to the following WCAG success criteria:
- Language of Page (Level A): Verifies that the primary language declared in the document matches the actual content on the page.
- Language of Parts (Level AA): Scans individual text blocks and image alt text to ensure shifts in language are correctly identified via the lang attribute.
Demo
- Try: the bookmarklet on any webpage.
- View: CodePen demo.
Implementation notes
- This is an experimental browser API and is only available in Chrome at this time.
- For better performance, Sa11y will only run this check once on a page. It will stop scanning remaining element nodes after the first detection. If significant text changes are detected on the page, Sa11y will trigger a fresh scan.
- This feature utilizes a built-in browser Language Detector API. All language analysis is performed locally on your machine, ensuring your page content is never sent to a cloud server or external third party for processing.
Disclaimer: Please note that AI-detection is not 100% accurate and is intended only to flag glaring issues for manual review; results should always be verified by a human.
Enabling this check
This experimental check is off by default. To start using these rules, update your configuration with the following properties:
langOfPartsPlugin: Set totrueto activate the ruleset.langOfPartsCache: Set totrueto enable smarter caching. This prevents redundant scans by storing results for up to 200 URLs and only triggers a fresh analysis if it detects a change in page text, declared language, or previously flagged elements.
Ruleset & logic improvements
-
Page language attribute validation: New ruleset that validates language codes by leveraging the browser’s native Intl.DisplayNames API for improved accuracy over standard string matching. The logic detects and flags invalid declarations, such as those using underscores (e.g., en_US), and provides the normalized string as a recommended suggestion for the user to implement. To ensure stability across all environments, the check includes a regex-based fallback for legacy browsers. New test names include:
META_LANG_VALIDandMETA_LANG_SUGGEST. -
New link checks: The
LINK_UNPRONOUNCEABLErule has been introduced as a standalone check to differentiate links containing only symbols (e.g., ., >, ?) from truly empty tags. This new rule provides a specific tooltip suggesting the link may be a copy-paste error and should either be deleted or given descriptive text. By separating this logic, the audit now offers clearer, more actionable feedback for non-alphanumeric content that screen readers cannot meaningfully process.- The
LINK_MAYBE_BUTTONrule compliments theQA_IN_PAGE_LINKcheck to ensure that broken same page links which resemble a scripted UI control will receive a more appropriate error description. - Improved DOI detection: The
LINK_DOIcheck has been improved to detect link text matching Digital Object Identifiers (DOIs) that lack a prefix. The updated regex identifies strings starting with 10.xxxx, ensuring that academic and technical references are properly captured even when formatted as plain text within a link.
- The
-
Alt text quality checks: Improved detection of nonsensical alt text strings.
- Added untitled image, unnamed, and copy to alt placeholder check for English language files.
- When checking for placeholder stop words, special characters are filtered out to improve accuracy of check (in addition to numbers).
- Alt text that have no spaces, are at least 15 characters, and contain at least 3 dashes/underscores will be flagged as an error.
- New warning-level check that builds upon existing logic for identifying auto-generated alt text. This separate condition flags instances where there is a lower confidence that the text is machine-generated, but it still warrants a manual review. An image will now trigger a warning if its alt text meets any of the following:
- Numeric strings: Consists of 5 or more digits (e.g., 12345).
- Excessive delimiters: Contains more than 3 hyphens (-) or underscores (_).
-
Contrast module improvements:
- Now supports the display-p3 color gamut.
- Contrast logic for gradients has been refined; warnings are now only triggered if a specific color stop fails, significantly reducing false positives.
- Transparent foreground colours and disabled elements are now ignored.
- Performance has also been improved through optimizing the RGBA color conversion logic, reducing computational overhead. Addition of caching and memoization of various functions to prevent redundant DOM traversals for the same elements.
- Added contrast checking support for slotted elements by traversing the flattened DOM tree to accurately detect background colours across Shadow DOM boundaries.
- Contrast check will ignore nodes that only contain non-text characters or visual separators (like / or \ or |).
-
Minimized false positives:
- Minor logic fixes to reduce false positives around fake heading detection.
- Warnings for justified and small text can now be dismissed all.
- Minimize false positives for test
LABELS_ARIA_LABEL_INPUT. - Reduced false positives around fake list detection, and improved detection of potential fake list patterns.
-
Changes to “Good” annotations: The tooltip text for images and links flagged as "Good" will now display "Review" instead. While these elements may technically have alt text or accessible names, the quality of that content still requires human verification. This change prompts developers and authors to ensure the descriptions are actually meaningful, rather than just present.
-
Improved tooltip verbiage for
LINK_IDENTICAL_NAMEandQA_IN_PAGE_LINK -
New test:
TABLES_INVALID_HEADERS_REFevaluates whether the headers attribute within a table contains an invalid reference. -
New test:
ARIA_INPUT_FIELD_NAMEevaluates ARIA-based input or switch fields for an accessible name.
Targeted element exclusion
Developers can now programmatically ignore elements by specific test keys. This provides more granular control, allowing you to exclude specific elements per check, without silencing all other accessibility alerts for that same item. Using the prop ignoreByTest.
For example:
ignoreByTest: {
QA_FAKE_HEADING: 'p.ignore strong',
},Security Improvements
- XSS hardening: Mitigated cross-site scripting by replacing unsafe HTML parsing with secure DOM APIs and literal text rendering.
- Improved data privacy: Hashing is used to hide the values used for dismiss keys, ensuring that data pulled from page elements remains private.
- Code Quality: Integrated CodeQL analysis workflows and custom configuration files for automated security scanning.
Developer changes
Significant refactoring of the main Sa11y class now includes dedicated state management for better stability and cleaner code architecture.
- Utility methods: All utility functions exposed are now exposed as methods for custom implementations.
- Sanitization: Raw text strings passed via custom checks now receive automatic HTML sanitization. However, it’s highly recommended to always use
Lang.sprintf()when passing in tooltip content for custom checks. - New prop: New prop
paragraphIgnore: 'table p',to add exclusions for specific paragraph<p>elements. - Removed functions: Removed
decodeHTML,escapeHTML, andstripHTMLtags. These utilities are now obsolete as Sa11y leverages native DOM APIs and literal text rendering for improved security. - Migration of tippy.js to floating-ui: Tippy.js was archived November 2024. Migration to floating-ui improves performance and reduces total bundle size by almost 10%.
Bug fixes
- Added fallback for
panelPositionprop to ensure proper visual placement of control panel. - Bug fix regarding
altPlaceholderprop. - Adjusted readability thresholds and closed score gaps for more consistent analysis.
- Improvements to accessible name calculation.
- Console error message now includes Sa11y’s configuration to help debug.
- Fixed a memory leak.
- Fallback icon added to the Images panel when an image’s source is invalid, or an SVG, or is less than 1px in width or height.
Translations
New Tamil translation. Many thanks to Jayaseelan Samuel @smartsw33t!
Acknowledgments
Many thanks to @itmaybejj for his contributions to this release!
Sa11y 4.4.1
Bug fixes
- Fixed an issue where SVG colour filters used an incorrect unit for width (
100vh→100vw). Thanks to @LadySolveig! - Replaced the multi-step tritanopia SVG filter with a single color-matrix implementation to prevent Firefox ESR from rasterizing the page and causing text blur.
- Consolidated the
resetColourFilters()function and removed duplicate logic. - Fixed a state restoration bug where toggling Sa11y’s main button while a colour filter was active did not correctly return the page to its normal state.
Sa11y 4.4.0
UX enhancements
- The suggested APCA font-size recommendation (when there is no valid colour combination) within tooltips is now clickable. Clicking copies the recommended value to the clipboard.
Ruleset improvements
- Links whose only text content is a “new window” phrase (e.g., opens new tab) — including phrases passed through
linkIgnoreStringsorlinkIgnoreSpan— are now flagged as non-descriptive. For example,<a href="#">opens new tab</a>will be flagged as non-descript text. - Check for non-descript link check will by default strip away "new tab" or similar phrases by default, improving accuracy of checks. For example,
<a href="#">learn more (opens new tab)</a>will be flagged as non-descript text. - Added the word "test" to placeholder stopword array.
- Added the following placeholder stopwords to English language files:
'hero image', 'hero slide', 'homepage feature image', 'featured image', 'untitled'. - Placeholder word detection improved and accounts for any numbers that may follow a typical placeholder stop word, such as "hero slide 2"
- Existing check for potentially automatically generated or placeholder alt text values, such as
~ai-3a3cb8f0-1554-4e2b-b159has been changed from a warning to an error, given that is unlikely to be prone to false positives. - Adding English
altplaceholder stopwords to all language files, given that some non-English websites have Englishalttext. - Added
opens in a new windowandopens in a new tabto minimize false positives.
Breaking changes
- The
contrastAPCAandcontrastAAAprops have been removed and replaced with a singlecontrastAlgorithmprop, which accepts one of:AA,AAA, orAPCA. linksAdvancedPluginprop is deprecated and removed. Individual checks can be turned off by their respective test name.
Enhancements
Multi-root support
checkRootnow accepts multiple selectors, allowing Sa11y to check several areas of a page.- New
fixedRootsprop enables passing DOM references directly as evaluation targets. linkIgnoreStringsandheaderIgnoreStringsaccepts an array of strings, a comma separated string in addition to regex.
View demo of multi root support.
Readability
- Readability results are now included as issue objects in the main results array, making it compatible with headless testing.
Content scoping
- New
option.ignoreContentOutsideRootsprop: When set totrue, any elements identified by Sa11y will be ignored entirely. For example, the Page Outline excludes headings outside the specified root(s). Previously, all headings under<body>were shown regardless of designated root.
Issue object improvements
- Each issue object now includes a
testkey for clearer rule identification, and to help with server-sided integrations.
Accessible name exclusion controls
- These two new props allow developers to exclude specific elements from accessible-name calculations, improving compatibility with WYSIWYG editors and CMS-generated markup.
option.ignoreAriaOnElementsoption.ignoreTextInElements
Heading level control
- New
initialHeadingLevelprop: Specifies the heading level a section should begin with to prevent false “skipped heading level” errors. This is useful when WYSIWYG content begins with an<h3>but appears after an<h2>from the page template.
Alt text placeholder handling
- New
altPlaceholderprop: Accepts an array of CMS-generated placeholder strings used when alt attributes are empty. Matching values ensure the image is treated as decorative. For example:<img src="/dog.png" alt="This image has an empty alt attribute; its file name is dog.png">
Performance
- Various general performance optimizations.
- Link text module refactored.
Bug fixes
- Fixed a serious bug with the Page Outline panel when
headerIgnoreprop is used. Clicking on the respective heading in Page Outline would set focus on the wrong heading. - Improved accessible name computation for
inputelements. - Placeholder value used as fallback in accessible name computation.
- Fixed an issue regarding
selectorPathprop, where SVGs would cause issues as the computed class name sometimes returns an object. - Fixed a false positive when a link is a single special character, but has a title attribute. Title acts as fallback or accessible name description.
- The tooltip preview for colour contrast warnings now remains “Unknown” when either the foreground or background colour is missing, instead of computing contrast using the fallback #000000.
- Fixed an accessibility issue with contrast-related tooltips, where the visually hidden "Unknown" label indicator would remain in place once a new colour was selected.
- Resolved a bug in the link text module for non-English languages. The regex intended to strip special characters/symbols was inadvertently stripping away non-Latin characters, causing the placeholder stop word logic to fail.
- Resolved a bug where images with
aria-hidden="true"orrole="presentation"within a link with surrounding text were incorrectly flagged as having a missing alt attribute.
Refactoring
- All contrast-checking utilities and readability analysis functions have been refactored for better portability and can now be easily imported into third-party libraries using ES6 modules.
- All SCSS has been migrated to vanilla CSS.
Developer tooling
Sa11y’s development tooling has been significantly modernized and streamlined.
- Replaced legacy build scripts with next-generation tooling, including Vite, LightningCSS, and Biome.
- Development now benefits from instant reloads and zero compile time via Vite’s HMR-enabled dev server.
- Reduced developer dependencies from 27 → 6, simplifying the environment and lowering maintenance overhead.
- Reduced runtime dependencies from 2 → 1. APCA logic is now bundled directly into Sa11y’s source code. Tippy.js pinned to version 6.3.7.
Browser support and compatibility
Sa11y supports most browsers released since mid-2021, with only minor visual inconsistencies in older versions. Sa11y was tested with BrowserStack using the following combinations:
- Tested down to Chrome 88 (January 2021) on Windows 10.
- Tested down to Firefox 84 (December 2020) on Windows 10.
- Tested down to Safari 14.1 (April 2021) on macOS Big Sur.
Acknowledgements
Many thanks to @itmaybejj and @Hackwar
Sa11y 4.3.5
New alt text quality check
This release introduces a new check that detects automatically generated or placeholder alt text values, such as ~ai-3a3cb8f0-1554-4e2b-b159. These are often inserted by AI systems, CMS exports, or image pipelines instead of meaningful human-written descriptions. This check helps identify meaningless placeholders, ensuring that alt text provides useful and descriptive information.
Algorithm explained
The detection logic identifies alt text that appears to be machine-generated using two conditions:
- Single long string: The text is one continuous word exceeding a defined length (default: 15 characters).
- Non-alphabetic composition: The text contains characters outside normal language letters (e.g., digits, underscores, UUID fragments, or symbols).
When both are true, the text is flagged as likely autogenerated and will display a warning.
i18n support
This implementation uses Unicode-aware matching (\p{L}), ensuring it correctly recognizes non-English letters. Words in other languages, such as Donaudampfschifffahrtsgesellschaftskapitän (German) or самовдосконалення (Ukrainian), will not be mistakenly flagged as a warning.
Customizable
You can turn off this check or adjust the minimum string length and issue type (warning or error) through props.
ALT_MAYBE_BAD: {
minLength: 15,
},
LINK_ALT_MAYBE_BAD: {
minLength: 15,
},
Sa11y 4.3.4
Enhancements
Many thanks to François Pradignac for improving the French translation!
Sa11y 4.3.3
Minor enhancements
- Readability analysis now treats any
<p>or<li>that lacks terminal punctuation (., ?, !) as a complete sentence. Some elements appear visually complete due to their block formatting, even without punctuation. This change should improve accuracy of readability score.
Sa11y 4.3.2
Minor enhancements
Both QA_IN_PAGE_LINK and LINK_IDENTICAL_NAME checks will exclude links that have:
- any ARIA role
- are intentionally hidden with
aria-hidden="true"andtabindex="-1" - have a
disabledattribute
This is to minimize excessive content author warnings/false positives.
Sa11y 4.3.1
- Increase default panel width from
400pxto440px. - Increase setting switch size for BG and ES.
