0.10.2
September 16th, 2026
Security
A finding's pointer was drawn as HTML in the control panel, and a heading
could carry a script. Every finding names the part of the page to go and
fix: a duplicate heading's text, an image's src, a link's words. That is
page content, and the panel drew it with a component that renders through
innerHTML. A heading written on a page as <img src=/x.png onerror=...> is
escaped on the front end and was never a risk there, but it reaches the panel
decoded, and the panel ran it in the control panel of whoever pressed Check on
that entry or was refused a save of it. Anyone who can put a heading on a
gated entry could plant it, and it fired for an admin. Confirmed on a live
control panel, not reasoned from the code. Every string the panel draws now
goes through Vue's escaping, page-derived or not, so the same heading draws as
the characters it is.
What to do about it: upgrade, and republish the addon's assets if your
deploy does not (php artisan vendor:publish --tag=statamic-a11y-gate --force, or php artisan statamic:install). 0.10.1 and earlier are affected.
Fixed
A refused save now takes the author to the panel. Below the width where Statamic folds the sidebar into its own tab (tablets and small laptops), an author who pressed Save & Publish on the main tab got only the corner toast "The given data was invalid", while the refusal itself was drawn in full on a tab they were not looking at. The panel now asks Statamic's own reveal to switch to it and scroll it into view, the same call the save pipeline makes. Guarded, so a build without the reveal draws the alert exactly where it did before.
The Tools page drew its lists without bullets. It carried list-disc, and
the control panel's compiled stylesheet does not contain that class, so it was
dropped without a word and the lists drew flat. The bullets are an inline
style now, and a test reads the build and checks every class the addon writes
against it, so a class the panel cannot draw fails before it ships.
Two instructions that were wrong when followed. The 0.8.0 note to
republish assets named --tag=laravel-assets, which publishes nothing for
this addon; it names the addon's own tag now. And the README said a stock site
cannot re-save its home page for want of an h1, which stopped being true
when statamic/statamic 6.5 redesigned the welcome page; it now says which
sites it is about.
Changed
The Tools page no longer says "Anything that would fail WCAG 2.2 AA stops the publish". Read on its own that is the completeness claim this addon refuses to make everywhere else, two paragraphs above the line saying colour contrast is not checked. It says "Anything it finds" now. Render-failure messages no longer read "not found..": Laravel's own message ends in a full stop and every quote added its own.
The auto-placed Accessibility panel is no longer offered as a column on the
entries listing. The field stores nothing, so the column could only ever be
empty. It says listable: false now.
Added
The README says how to install the addon and where to report a problem. An
## Installing section with the composer require line, the Statamic and PHP
floors, what ext-intl adds, the config publish tag, and the two files an
uninstall can leave behind; and a ## Support section naming the public issue
tracker as the one channel, answered by one person with no response-time
promise. A support key in composer.json so Packagist and the Marketplace
show the same.
0.10.1
September 15th, 2026
Changed, in the control panel only
The addon is "A11y Gate" everywhere it names itself. The Addons page, the Tools nav, the utility page header, the report-mode message and the settings copy said "Accessibility Gate", while the Marketplace listing, the README and A11y Docs beside it in the same list already said "A11y". One name now. Handles, permissions, config keys and the package name are unchanged, so nothing has to be edited and nobody loses access.
0.10.0
September 15th, 2026
Changed
A page that answers with a redirect is no longer refused. A page for
signed-in visitors opens with {{ redirect }} when nobody is, and the gate is
nobody. Since 0.9.2 the gate said so accurately ("the page came back as HTTP
302, sending visitors to /login") and refused the save anyway, as a check that
could not run. That made a learning site's account page unpublishable, over a
page that had done exactly what it was written to do.
The page is now saved, and the panel says "Not checked: this page sends visitors to /login, so there was no page to check". The site scan lists it under a new "Not checked" heading and does not fail the build on it. In both places the wording says what a signed-in visitor sees has not been checked, because it has not, and a report that dropped the page would be claiming coverage it did not have.
A redirect back to the page's own address is a loop, which is a broken template rather than a sign-in wall. That is still refused and still fails the build, and has its own test so the two cannot drift into one.
This is a refusal turned into a pass-through, which is the one kind of change this addon is most suspicious of. The reasoning and the alternatives turned down are in the decision log.
0.9.2
September 15th, 2026
Fixed
A page that redirects is reported as a redirect, not as a crash. A page
for signed-in visitors that opens with {{ redirect }} sends the scan, which
is nobody, to the login page. Statamic's tag redirects by throwing the response,
and the renderer caught that with everything else and reported "the page threw
while rendering: HttpResponseException". The page had not crashed; it had done
exactly what it was written to do, and the line named nothing a site owner
could act on. It now reads "the page came back as HTTP 302, sending visitors
to /login". The page is still listed as one the scan could not read, and a save
of it is still refused, because neither has changed: nobody has checked that
page. What changed is that the report now says why.
0.9.1
September 15th, 2026
Changed
Runs on PHP 8.2 and newer. The manifest said 8.4 and nothing in the code needed it: the floor was the version the project started on, not one it used. The real floor is Laravel's, 8.2 on Laravel 12, and that is now what is declared. A job in CI installs the oldest version of everything the manifest allows, on 8.2, and runs the suite there, so the floor is a tested fact rather than a number in a file.
PHP 8.2 leaves security support at the end of December 2026. That is the date to raise this to 8.3, and it is in the decision log so nobody has to remember.
0.9.0
September 10th, 2026
Added
A panel block may name a refusalKey. Statamic puts a 422's errors onto
the publish container exactly as they were sent, and hard-codes the toast to
"The given data was invalid", so an addon that refuses a save has nowhere of
its own to say why. Keying the refusal to a blueprint field puts it under that
field, where it reads as a fault in the field: A11y Docs was keying "this entry
links to a document nobody can read" to title. A block that names a key has
whatever is under that key drawn in the block instead, in place of its standing
lines and above its link. The key has to be shaped like a handle.
PanelExtensions::supports() answers whether this version draws a given
capability, today only refusalKey. A provider ships on its own release cycle,
and naming a key at a gate too old to read one sends the refusal somewhere
nothing draws without failing: the save is still refused and the author is
never told why.
0.8.1
September 10th, 2026
Fixed, where the addon was saying something untrue
The all-clear badge now says what it was an all-clear about. It reads
"Nothing to fix in the page itself". This gate checks the rendered HTML and
cannot open a linked document, so a PanelExtensions block underneath it can
report a PDF a screen reader cannot open on the same screen. A flat "Nothing to
fix" above that was two verdicts with nothing to tell them apart. The wording
does not depend on what else is installed: a badge that means one thing alone
and another in company would be harder to trust, not easier.
0.8.0
September 7th, 2026
Added
A link under each finding to the W3C's page on the criterion it cites.
"WCAG 1.1.1 Non-text Content" under an image finding opens the Understanding
page for it, in a new tab that is announced. Underlined and coloured for both
themes, because the control panel resets anchors to look like text. A house
rule such as "Heading structure" cites no criterion and has no link. The
check endpoint sends the reference as reference on each finding, null for
a house rule.
A block in the panel may carry a mark. PanelExtensions blocks take an
optional mark of a url and an alt, drawn above the block's heading at
about the height of a line of text, so a companion addon's block can be
recognised as the site owner's own. The gate holds no branding of its own and
gains no setting: it draws what a provider hands it. A mark with no words is
left out, and so is an address that is not a path, an http or https URL,
or a data:image/ value. A block reporting a problem wears no mark.
What to do about it: republish the addon's assets if your deploy does
not (php artisan vendor:publish --tag=laravel-assets --force).
0.7.0
September 2nd, 2026
One addition and nothing removed.
A seam in the panel for another addon. PanelExtensions::register() takes a callable that, given the entry, returns a block to draw beneath the gate's own result on the entry screen: a heading, plain lines, an optional link. Nothing about the checks or the refusal changes: a provider cannot add a finding, alter one, or affect a save, and one that throws is drawn as a warning rather than dropped. Accessibility Report uses it to show a page's open issues from the last scan.
What to do about it: nothing. Without a provider registered the panel is what it was.
0.6.0
August 19th, 2026
Removed, and it can break a site
ReadingLevel::grade() is gone, along with the three private helpers behind
it.
It is very unlikely you were calling it. It was never called by the addon
itself, in any commit, and it was named in no README line, no config comment and
no decision entry, so the only way to find it was to read src/. There is no
Antlers tag or modifier for it, so no template could have reached it.
If you were calling it, from a listener or a service that stamps
data-a11y-reading-grade on your plain-language summaries, that call is now a
fatal error. Compute the grade yourself instead. The formula the threshold is
calibrated against is Flesch-Kincaid:
0.39 x (words per sentence) + 11.8 x (syllables per word) - 15.59
ReadingLevel::PLAIN_MAX_GRADE stays, and is still the number the check
compares your stamped grade against.
Why it was removed rather than documented: its syllable count stripped every
character outside a-z, so a word in Japanese, Arabic or Russian counted as one
syllable and the grade collapsed into a function of sentence length alone. Real
passages in those scripts scored 0.0 and 0.1 against a threshold of 9, which is
a confident-looking pass for text the formula has no opinion about. Publishing
that as a supported API was not something this addon could do honestly, and
leaving it in place unused and undeclared was the ambiguity that got it noticed.
There is deliberately no deprecation shim. A method that still returns 0.1 for Arabic is worse kept than removed.
Fixed, where the addon was saying something untrue
- A page whose only media was a
figurewas told to confirm that its video had captions. It has no video. A page that did carry the markup for captions, transcripts, figure text and footnotes was told that captions "are only checked on sites that mark them up, and this one does not", in the same report as a finding only that markup could have produced. - The Accessibility Gate page under Tools said there were three exceptions that do not stop a publish. There are four: link text that is vague rather than plainly wrong was missing. The same page said the entry "is not saved" without mentioning that report mode is a setting on the screen it sends you to, gave 24 pixels as the touch target minimum without saying a config file raises it to 44, and said "embedded videos" where the rule checks every frame, maps and forms included.
- In report mode the log said "This entry was not saved" about an entry that was saved.
- The site-wide scan counted a page once per finding rather than once. A page with three identical "Read more" links reported as three pages, stopped naming the page it was on, and on a small site could claim "every page", which is the line that sends somebody to rewrite a template.
Changed, with no difference to what is reported
- Severity now lives with the rule rather than being passed at each of the eighteen places a rule is raised. A rule can no longer be raised at two different severities, and a typo can no longer turn a refusal into a warning.
- The opt-in setting is read once, by the checker. Every check now always answers how much of the page it could see, so a check can no longer drop out of the count and make "4 of 5 checks ran" read as "4 of 4".
AccessibilityStandardis a backed enum. It used to be three fields anyone could set, so a label claiming AAA could sit over a minimum of 7 pixels.- The corpus refuses to run against a fixture it cannot read, and pins its cases by name rather than by counting them.
Verified across all 28 corpus fixtures, before and after, including every message, call to action and pointer: no finding changed.
0.5.4
November 29th, -0001
- N/A Changelog not available.
0.5.3
November 29th, -0001
- N/A Changelog not available.