Give it a try
last updated: 08/04/2026
-
To see your bookmarks bar, press the keys
(Ctrl/Cmd + Shift + B). - Drag the "SpotCheck - Alt Text Checker" 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
Each image gets a colored outline and a badge showing what was found.
The image has clear, descriptive alt text, or is correctly
marked as decorative with an empty alt attribute
alt="".
The image has alt text, but it looks like a filename or starts with a redundant phrase like "image of", worth rewriting to describe the content or purpose instead.
The image has no alt attribute at all, so screen readers may announce the filename or nothing meaningful.
Try it here
Run the check against the sample markup below.
Applicable WCAG 2.2 success criteria
A text alternative is what lets a screen reader convey what an image shows, or tell the user to skip over one that is purely decorative.
-
1.1.1
Non-text Content(Level A)
Every image needs a text alternative: descriptive alt text for meaningful images, or an empty alt for ones that are purely decorative.
Read the source
The bookmarklet contains the TL;DR version of the code. Here's the readable version.
(function () {
var w = window;
var d = document;
var STATE_KEY = "__a11yImageAltChecker";
if (w[STATE_KEY]) {
w[STATE_KEY].clear();
return;
}
var COLORS = {
green: "#1a7d4f",
gold: "#8b6800",
red: "#be412a"
};
var OUTLINES = {
green: "5px solid",
gold: "6px dotted",
red: "5px dashed"
};
var OUTLINE_OFFSET = "3px";
var DOTS = {
green: "#3fbf7f",
gold: "#e0b84f",
red: "#e06a4f"
};
var LEVELS = {
ok: "green",
decorative: "green",
filename: "gold",
redundant: "gold",
missing: "red"
};
var BADGES = {
ok: "Alt text",
decorative: "Decorative (empty alt)",
filename: "Alt text looks like a filename",
redundant: "Redundant phrase in alt text",
missing: "No alt attribute"
};
var QUOTED_KINDS = ["ok", "filename", "redundant"];
var FILENAME_PATTERNS = [
/\.(jpg|jpeg|png|gif|bmp|webp|svg|tiff?)$/i,
/^(img|image|dsc|photo|screenshot)[-_ ]?\d/i
];
var REDUNDANT_PATTERN = /^(image|photo|picture|graphic|icon)\s+of\b/i;
var records = [];
var counts = { green: 0, gold: 0, red: 0 };
var panel = null;
function normalize(value) {
return (value || "").replace(/\s+/g, " ").trim();
}
function isVisible(el) {
var style = getComputedStyle(el);
if (style.display === "none" || style.visibility === "hidden") {
return false;
}
var rect = el.getBoundingClientRect();
return rect.width > 0 && rect.height > 0;
}
function looksLikeFilename(alt) {
var trimmed = alt.trim();
return FILENAME_PATTERNS.some(function (pattern) {
return pattern.test(trimmed);
});
}
function classify(el) {
if (!el.hasAttribute("alt")) {
return "missing";
}
var alt = el.getAttribute("alt");
if (!alt.trim()) {
return "decorative";
}
if (looksLikeFilename(alt)) {
return "filename";
}
if (REDUNDANT_PATTERN.test(alt.trim())) {
return "redundant";
}
return "ok";
}
function labelFor(el, kind) {
if (QUOTED_KINDS.indexOf(kind) === -1) {
return BADGES[kind];
}
return BADGES[kind] + ': "' + normalize(el.getAttribute("alt")) + '"';
}
function makeBadge(el, kind) {
var level = LEVELS[kind];
var badge = d.createElement("div");
badge.setAttribute("data-a11y-alt-check", "badge");
badge.setAttribute("aria-hidden", "true");
badge.textContent = labelFor(el, kind);
badge.style.cssText = [
"position:absolute",
"width:fit-content",
"max-width:min(90vw, 24rem)",
"padding:4px 8px",
"border-radius:4px",
"background:" + COLORS[level],
"color:#fff",
"font:500 16px/1.2 Arial, Helvetica, \"Helvetica Neue\", sans-serif",
"pointer-events:none",
"z-index:2147483646"
].join(";");
return badge;
}
function placeBadge(el, badge) {
var rect = el.getBoundingClientRect();
var badgeRect = badge.getBoundingClientRect();
badge.style.top = rect.top + w.scrollY - badgeRect.height + "px";
badge.style.left = rect.left + w.scrollX + "px";
}
function mark(el) {
var kind = classify(el);
var level = LEVELS[kind];
var record = {
el: el,
outline: el.style.outline,
offset: el.style.outlineOffset,
badge: makeBadge(el, kind)
};
counts[level] += 1;
el.style.outline = OUTLINES[level] + " " + COLORS[level];
el.style.outlineOffset = OUTLINE_OFFSET;
d.body.appendChild(record.badge);
placeBadge(el, record.badge);
records.push(record);
}
function makeCountLine(level, word) {
var line = d.createElement("p");
var dot = d.createElement("span");
line.style.cssText = "margin:0;padding:0";
dot.textContent = "\u25cf";
dot.style.cssText = "color:" + DOTS[level] + ";margin-right:6px";
line.appendChild(dot);
line.appendChild(d.createTextNode(counts[level] + " " + word));
return line;
}
function makePanel() {
var wrapper = d.createElement("div");
var heading = d.createElement("strong");
var close = d.createElement("button");
wrapper.setAttribute("data-a11y-alt-check", "panel");
wrapper.setAttribute("role", "status");
wrapper.style.cssText = [
"position:fixed",
"right:16px",
"bottom:16px",
"max-width:260px",
"padding:12px 32px 12px 16px",
"border-radius:6px",
"background:#1b2430",
"color:#e7eaed",
"font:400 14px/1.5 Arial, Helvetica, \"Helvetica Neue\", sans-serif",
"box-shadow:0 8px 24px rgba(0,0,0,0.35)",
"z-index:2147483647"
].join(";");
heading.textContent = "Image Alt Text Checker";
heading.style.cssText = "display:block;margin-bottom:4px";
wrapper.appendChild(heading);
wrapper.appendChild(makeCountLine("green", "correct"));
wrapper.appendChild(makeCountLine("gold", "flagged"));
wrapper.appendChild(makeCountLine("red", "incorrect"));
close.type = "button";
close.textContent = "\u00d7";
close.setAttribute("aria-label", "Clear the alt text check results");
close.style.cssText = [
"position:absolute",
"top:6px",
"right:6px",
"width:24px",
"height:24px",
"padding:2px 6px",
"border:none",
"background:transparent",
"color:#e7eaed",
"font:16px/1 Arial, Helvetica, \"Helvetica Neue\", sans-serif",
"cursor:pointer"
].join(";");
close.addEventListener("click", clear);
wrapper.appendChild(close);
return wrapper;
}
function clear() {
records.forEach(function (record) {
record.el.style.outline = record.outline;
record.el.style.outlineOffset = record.offset;
if (!normalize(record.el.getAttribute("style"))) {
record.el.removeAttribute("style");
}
if (record.badge.parentNode) {
record.badge.parentNode.removeChild(record.badge);
}
});
records = [];
if (panel && panel.parentNode) {
panel.parentNode.removeChild(panel);
}
panel = null;
delete w[STATE_KEY];
}
Array.prototype.slice
.call(d.querySelectorAll("img"))
.filter(isVisible)
.forEach(mark);
panel = makePanel();
d.body.appendChild(panel);
w[STATE_KEY] = { clear: clear };
})();
FYI
-
An empty
altattributealt=""is one suitable way to mark a decorative image. - Alt text that repeats the filename or starts with "image", "picture", or "photo" adds noise without adding information. Screen readers already announce that it is an image.
-
This check only looks at
<img>elements. CSS background images and SVG icons need their own accessible-name treatment, which this tool does not cover yet.