├── .gitattributes ├── manifest.json ├── background.js └── readme.md /.gitattributes: -------------------------------------------------------------------------------- 1 | .gitattributes text 2 | .gitignore text 3 | *.js text 4 | *.json text 5 | *.md text 6 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "AxSHammer", 4 | "version": "2024.1", 5 | "description": "Hammer an inaccessible website into shape", 6 | "author": "James Teh", 7 | "browser_specific_settings": { 8 | "gecko": { 9 | "id": "axSHammer@jantrid.net" 10 | } 11 | }, 12 | "background": { 13 | "scripts": ["background.js"] 14 | }, 15 | "permissions": ["menus", "activeTab"] 16 | } 17 | -------------------------------------------------------------------------------- /background.js: -------------------------------------------------------------------------------- 1 | /* 2 | * AxSHammer 3 | * Background script 4 | *Author: James Teh 5 | * Copyright 2020-2024 James Teh 6 | * License: GNU General Public License version 2.0 7 | */ 8 | 9 | const topMenu = browser.menus.create({ 10 | contexts: ["all"], 11 | title: "A&xSHammer", 12 | }); 13 | browser.menus.create({ 14 | parentId: topMenu, 15 | title: "Expose completely inaccessible elements", 16 | onclick: exposeCompletelyInaccessibleElements, 17 | }); 18 | browser.menus.create({ 19 | parentId: topMenu, 20 | title: "Kill all aria-&hidden", 21 | onclick: killAllAriaHidden, 22 | }); 23 | browser.menus.create({ 24 | parentId: topMenu, 25 | title: "Kill all ARIA &live regions", 26 | onclick: killAllAriaLive, 27 | }); 28 | browser.menus.create({ 29 | parentId: topMenu, 30 | title: "Kill all ARIA &applications", 31 | onclick: killAllAriaApplication, 32 | }); 33 | browser.menus.create({ 34 | parentId: topMenu, 35 | title: "Kill all aria-la&bel", 36 | onclick: killAllAriaLabel, 37 | }); 38 | browser.menus.create({ 39 | parentId: topMenu, 40 | title: "Kill all ARIA &roles", 41 | onclick: killAllAriaRole, 42 | }); 43 | browser.menus.create({ 44 | parentId: topMenu, 45 | title: "No idea, do all the things", 46 | onclick: runAll, 47 | }); 48 | 49 | function exposeCompletelyInaccessibleElements(info, tab) { 50 | browser.tabs.executeScript(tab.id, { 51 | allFrames: true, 52 | code: ` 53 | for (let el of document.body.querySelectorAll(":empty:not(input):not(textarea):not([aria-label])")) { 54 | el.setAttribute("role", "button"); 55 | let label = typeof el.className == "string" ? el.className : null; 56 | if (label) { 57 | // Strip out useless Font Awesome stuff: 58 | // fa- prefixes, but keep the rest (fa-foo becomes just foo); and 59 | // far and fas classes. 60 | label = label.replace(/\\bfa-|\\bfa[rs]?\\b/g, ""); 61 | } 62 | if (label) { 63 | el.setAttribute("aria-label", label); 64 | } 65 | el.setAttribute("data-axSHammer-exposedCompletelyInaccessibleElement", 66 | "true"); 67 | } 68 | `, 69 | }); 70 | } 71 | 72 | function killAllAriaHidden(info, tab) { 73 | browser.tabs.executeScript(tab.id, { 74 | allFrames: true, 75 | code: ` 76 | for (let el of document.querySelectorAll("[aria-hidden]")) { 77 | el.removeAttribute("aria-hidden"); 78 | } 79 | `, 80 | }); 81 | } 82 | 83 | function killAllAriaLive(info, tab) { 84 | browser.tabs.executeScript(tab.id, { 85 | allFrames: true, 86 | code: ` 87 | for (let el of document.querySelectorAll("[aria-live]")) { 88 | el.removeAttribute("aria-live"); 89 | } 90 | `, 91 | }); 92 | } 93 | 94 | function killAllAriaApplication(info, tab) { 95 | browser.tabs.executeScript(tab.id, { 96 | allFrames: true, 97 | code: ` 98 | for (let el of document.querySelectorAll("[role=application]")) { 99 | el.removeAttribute("role"); 100 | } 101 | `, 102 | }); 103 | } 104 | 105 | function killAllAriaLabel(info, tab) { 106 | browser.tabs.executeScript(tab.id, { 107 | allFrames: true, 108 | code: ` 109 | for (let el of document.querySelectorAll("[aria-label]")) { 110 | el.removeAttribute("aria-label"); 111 | } 112 | `, 113 | }); 114 | } 115 | 116 | function killAllAriaRole(info, tab) { 117 | browser.tabs.executeScript(tab.id, { 118 | allFrames: true, 119 | code: ` 120 | for (let el of document.querySelectorAll("[role]")) { 121 | el.removeAttribute("role"); 122 | } 123 | `, 124 | }); 125 | } 126 | 127 | function runAll(info, tab) { 128 | exposeCompletelyInaccessibleElements(info, tab); 129 | killAllAriaHidden(info, tab); 130 | killAllAriaLive(info, tab); 131 | killAllAriaApplication(info, tab); 132 | killAllAriaLabel(info, tab); 133 | killAllAriaRole(info, tab); 134 | } 135 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # AxSHammer 2 | Sometimes, when something's broken, you have to hammer it into shape. 3 | 4 | - Author: James Teh <jamie@jantrid.net> 5 | - Copyright: 2020-2024 James Teh 6 | - License: GNU General Public License version 2.0 7 | 8 | In an ideal world, websites would all be accessible. 9 | Sadly, the world is far from ideal. 10 | 11 | Sometimes, you might be able to convince the owner to fix their website. 12 | Other times, the accessibility of a particular site can be improved via scripts or assistive technology support written specifically for that site. 13 | For example, [AxSGrease](https://github.com/jcsteh/axSGrease) provides Greasemonkey scripts to improve the accessibility of several individual sites. 14 | 15 | But sometimes, you don't have the ability, time, money, patience or energy for either of these. 16 | You just want some kind of access and you're willing to deal with a bit (or a lot) of ugliness to get it. 17 | 18 | AxSHammer is a Mozilla Firefox add-on which provides a set of tools to try to help in this situation. 19 | They might not work. 20 | They might break things further. 21 | Even if they do work, the access they provide is probably going to be ugly and suboptimal. 22 | But when you're out of options, it might just be enough. 23 | Maybe. 24 | 25 | ## Tools 26 | The tools AxSHammer provides are accessed from the context menu of a web page, inside the AxSHammer menu. 27 | 28 | ### Expose completely inaccessible elements 29 | Websites frequently use elements containing background images or icons. 30 | If these aren't made accessible, they might not be visible to screen readers at all, to the point where a user might not even know they're there. 31 | For example, [my local Pizza store](https://www.pizzacommune.com.au/) allows you to customise the ingredients on your pizza. 32 | Each ingredient has a quantity and two icons (decrease and increase) to adjust the quantity. 33 | The two icons don't show up with a screen reader at all. 34 | Similarly, [some companies](https://www.appveyor.com/docs/macos-images-software/#operating-system) have tables comparing products/services and they use inaccessible icons to show whether a particular product has or doesn't have a given feature. 35 | 36 | This tool looks for elements which contain no text. 37 | It makes each such element into a button so you can find it. 38 | Furthermore, it uses information from the web page code (technically, the class attribute) to deduce a label. 39 | The label might be pretty ugly, but it might be enough to distinguish it or help you figure out what it is. 40 | 41 | Note that this is likely to make a lot of things into buttons which aren't useful, but in some cases, the pros outweigh the cons. 42 | 43 | ### Kill all aria-hidden 44 | aria-hidden allows authors to specify that something should be invisible for accessibility purposes. 45 | Unfortunately, this is sometimes misused by authors, potentially hiding huge parts of a page or even the entire page! 46 | This is particularly common after closing a dialog. 47 | Or it could be as small (yet critical) as a checkout button which has been accidentally hidden for accessibility, as I saw on [Robins Kitchen](https://www.robinskitchen.com.au/). 48 | 49 | This tool removes aria-hidden from everything. 50 | If you suspect that something on a page has been hidden from your assistive technology, give this tool a try. 51 | 52 | ### Kill all ARIA live regions 53 | ARIA live regions allow authors to specify that part of a page should be reported automatically when it is updated. 54 | When misused, particularly by ads, this can be extremely annoying. 55 | This tool disables all live regions on the page. 56 | 57 | ### Kill all ARIA applications 58 | Authors can specify that an area of a page should not be treated as a document at all by marking it as an "application". 59 | This means that screen readers won't use browse mode or equivalent by default. 60 | This is almost always misused. 61 | 62 | This tool removes the application role from all elements. 63 | If your screen reader reports "application" while you're navigating and you think you're missing useful content, this tool might help. 64 | 65 | ### Kill all aria-label 66 | aria-label allows authors to specify a label for accessibility purposes. 67 | On elements such as links and buttons, this overrides the text content of the element. 68 | This is sometimes misused by authors to provide secondary information such as "(opens in a new tab)" or "Go to (some web address)". 69 | This might result in overly long labels in some cases, but worse, it could result in the primary information being completely lost. 70 | 71 | This tool removes aria-label from everything. 72 | If you encounter a site with many links that are missing text you would expect, this tool might help. 73 | 74 | ### Kill all ARIA roles 75 | ARIA roles allow authors to specify the type of a custom element. 76 | For example, an author might use this if they implement a custom check box, menu, etc. 77 | Unfortunately, when ARIA roles are misused, they can make sites difficult or even impossible to use with assistive technology. 78 | This tool removes all ARIA roles. 79 | 80 | ### No idea, do all the things 81 | If you just don't know which tool to use, you can try this, which just runs all of the above, consequences be damned. 82 | For example, when shopping on [Robins Kitchen](https://www.robinskitchen.com.au/), I couldn't find the checkout button. 83 | I assumed it was an inaccessible icon, so I tried "Expose completely inaccessible elements" and was sad when I still couldn't check out. 84 | Upon investigation, I subsequently discovered that the checkout button had been aria-hidden, but I didn't even think to try that tool... because who would explicitly hide a checkout button, right? 85 | --------------------------------------------------------------------------------