Give it a try
-
To see your bookmarks bar, press the keys
(Ctrl/Cmd + Shift + B). - Drag the "SpotCheck - Link Auditor" link to your bookmarks bar or bookmark folder.
- Open any page, then click the bookmark to run it.
- Click it again or click the close button in the popup to clear the results.
Example of the popup that will appear in the bottom right corner of your browser with a summary of the results.
What the colors mean
Every link on the page gets a colored outline based on its accessible name.
The link has clear, descriptive accessible text.
The link works but its accessible name is very short, worth a second look to confirm it is descriptive.
The link has no accessible name at all, or its text is a generic phrase like "click here" that means nothing out of context.
Try it here
Run the check against the sample markup below without leaving this page.
Applicable WCAG 2.2 success criteria
Link text is often the only clue a screen reader or switch-access user gets about where a link goes, especially when links are scanned out of context.
-
2.4.4
Link Purpose (In Context)(Level A)
The purpose of a link should be clear from its text alone or from text and context together.
-
2.4.9
Link Purpose (Link Only)(Level AAA)
For the strictest conformance level, link text alone, without any surrounding context, should describe its destination.
-
4.1.2
Name, Role, Value(Level A)
Every link needs a programmatically determinable name so assistive technology can announce it at all.
Read the source
The bookmarklet contains the TL;DR version of the code. Here's the readable version.
(function () {
var NS = '__a11yLinkAuditor';
if (window[NS] && window[NS].active) {
window[NS].teardown();
return;
}
var GENERIC = ['click here', 'here', 'read more', 'more', 'link', 'more info', 'learn more', 'this link'];
function getAccessibleName(a) {
var labelledby = a.getAttribute('aria-labelledby');
if (labelledby) {
var text = labelledby.split(/\s+/).map(function (id) {
var ref = document.getElementById(id);
return ref ? ref.textContent.trim() : '';
}).join(' ').trim();
if (text) return text;
}
var label = a.getAttribute('aria-label');
if (label && label.trim()) return label.trim();
var clone = a.cloneNode(true);
Array.prototype.forEach.call(clone.querySelectorAll('[aria-hidden="true"]'), function (n) {
n.parentNode.removeChild(n);
});
var text2 = clone.textContent.replace(/\s+/g, ' ').trim();
if (text2) return text2;
var img = a.querySelector('img[alt]');
if (img && img.getAttribute('alt').trim()) return img.getAttribute('alt').trim();
if (a.title && a.title.trim()) return a.title.trim();
return '';
}
var links = Array.prototype.slice.call(document.querySelectorAll('a'));
var records = [];
var counts = { green: 0, gold: 0, red: 0 };
function mark(el, level, label) {
records.push({ el: el, outline: el.style.outline, outlineOffset: el.style.outlineOffset, position: el.style.position, badge: null });
var color = level === 'green' ? '#1a7d4f' : level === 'gold' ? '#8b6800' : '#be412a';
el.style.outline = '3px solid ' + color;
el.style.outlineOffset = '2px';
el.setAttribute('data-a11y-bm-note', label);
if (getComputedStyle(el).position === 'static') el.style.position = 'relative';
var badge = document.createElement('span');
badge.textContent = label;
badge.setAttribute('aria-hidden', 'true');
badge.style.cssText = [
'position:absolute', 'top:0', 'left:0', 'transform:translateY(-100%)',
'background:' + color, 'color:#fff', 'font:600 14px monospace',
'padding:1px 5px', 'border-radius:3px 3px 0 0', 'z-index:2147483000',
'pointer-events:none', 'white-space:nowrap'
].join(';');
el.insertBefore(badge, el.firstChild);
records[records.length - 1].badge = badge;
counts[level]++;
}
links.forEach(function (a) {
var name = getAccessibleName(a);
if (!name) {
mark(a, 'red', 'No accessible name');
} else if (GENERIC.indexOf(name.toLowerCase().trim()) !== -1) {
mark(a, 'red', 'Generic text out of context: "' + name + '"');
} else if (name.length < 4) {
mark(a, 'gold', 'Very short link text, confirm it is descriptive');
} else {
mark(a, 'green', 'OK: "' + name + '"');
}
});
var panel = document.createElement('div');
panel.setAttribute('data-a11y-bm-panel', 'link-auditor');
panel.setAttribute('role', 'status');
panel.setAttribute('aria-live', 'polite');
panel.style.cssText = [
'position:fixed', 'bottom:16px', 'right:16px', 'z-index:2147483001',
'background:#1b2430', 'color:#e7eaed', 'font:13px/1.5 -apple-system,sans-serif',
'padding:12px 32px 12px 16px', 'border-radius:6px', 'box-shadow:0 4px 16px rgba(0,0,0,.3)',
'max-width:260px'
].join(';');
panel.innerHTML =
'<strong style="display:block;margin-bottom:4px;">Link Accessibility Auditor</strong>' +
'<span style="color:#3fbf7f;">●</span> ' + counts.green + ' correct ' +
'<span style="color:#e0b03f;">●</span> ' + counts.gold + ' flagged ' +
'<span style="color:#e06a4f;">●</span> ' + counts.red + ' incorrect';
var closeBtn = document.createElement('button');
closeBtn.type = 'button';
closeBtn.textContent = '\u00d7';
closeBtn.setAttribute('aria-label', 'Close');
closeBtn.style.cssText = [
'position:absolute', 'top:4px', 'right:6px',
'background:transparent', 'border:none', 'color:#e7eaed',
'font:16px/1 -apple-system,sans-serif', 'cursor:pointer',
'padding:2px 6px', 'border-radius:4px', 'line-height:1'
].join(';');
closeBtn.addEventListener('click', function () { window[NS].teardown(); });
panel.appendChild(closeBtn);
document.body.appendChild(panel);
function teardown() {
records.forEach(function (r) {
r.el.style.outline = r.outline;
r.el.style.outlineOffset = r.outlineOffset;
r.el.style.position = r.position;
r.el.removeAttribute('data-a11y-bm-note');
if (r.badge && r.badge.parentNode) r.badge.parentNode.removeChild(r.badge);
});
if (panel.parentNode) panel.parentNode.removeChild(panel);
}
window[NS] = {
active: true,
teardown: function () {
teardown();
window[NS].active = false;
}
};
})();
FYI
-
An image-only link needs either an
altattribute with the proper text or anaria-labelwith the proper text on the<a>. - "Click here" and similar phrases are flagged even when the surrounding sentence gives context, since many screen reader users browse a links-only list.
- This check looks at accessible name only. It does not verify that the destination itself matches what the link text.