I audited 25 stock photos on a live client site. Four had to be destroyed.
Three had tiled watermarks. One was a torii gate on a beach, keyed "solar-farm-rows". Every automated check passed all four.
- images
- qa
- astro
A solar installer’s website was carrying a photograph of a Japanese torii gate. Live, in two places. A torii gate, on a beach. No solar panels anywhere in the frame. It was keyed solar-farm-rows.
I found it because I opened it. That is the entire mechanism. There was no clever check.
The audit
Camelphat Energy & Solar Solutions is a solar installer in Cape Town. Twelve pages, Astro 5, Tailwind CSS 4, vanilla JavaScript, deployed to Cloud Run: camelphat-solar-503426587396.europe-west1.run.app
I went through all 25 stock images, one at a time, at full size. Four had to be destroyed.
Three carried tiled “rawpixel” watermarks — repeated across the whole frame. At thumbnail size in a file list, a tiled watermark reads as texture. At 100% it reads as a licence violation printed over your client’s homepage.
The fourth was the torii gate.
A fifth image was wrong in a quieter way. The Free Assessment page hero was the International Space Station’s array wings. Technically photovoltaic. Also in orbit, and not a roof in Cape Town.
Every automated check passed all four
This is the part worth sitting with. Not one thing in the pipeline objected.
The key said solar-farm-rows. astro:assets and sharp processed it without complaint and optimised it. The build passed. The alt attribute was present and well-formed, which is all any check of alt text can confirm — nothing can compare the words to the pixels. My own Puppeteer pass confirmed no horizontal overflow at 320, 390, 768 and 1440. Everything was green.
A watermark is pixels. A torii gate is pixels. None of the tooling has an opinion about what a photograph is of. Every check that is not “look at the pixels” will pass a watermarked image of the wrong subject, cheerfully, and then compress it for you.
The only check that catches this is a person opening the file at full size. There is no second one. Build the workflow around that fact instead of hoping something upstream will catch it.
The alt text was lying twice
Seven alt descriptions described scenes that were not in the frame.
That is worse than a wrong photo. A sighted visitor sees the torii gate and files it as an odd stock choice. A screen reader user gets told, with total confidence, about a scene that is not in the frame, and has no way to verify it. Alt text written from the key inherits the key’s error and then launders it into an authoritative-sounding description. I rewrote all seven.
While I was in there
Two other things fell out of the same pass.
3.6MB of images were being bundled and referenced by no page at all. Dead weight in the build output, carried along for nothing.
One file was 8.2MB. It came out at 739KB.
What sits on top of the image also has to be measured
Breadcrumbs sat over the hero image on seven pages. Measured against the actual rendered background, they came out at 1.49:1. Not “a bit low” — invisible. They are now 5.04:1. I got that number by driving headless Chrome and pixel-sampling the real rendered background, not by reading the CSS and assuming.
The same site has a video hero, and it made the point harder. Contrast over a video is not one number, it is a number per frame. I measured against the worst frame in the clip, not an average one: bright aluminium rails sweep through and drop the lead paragraph to 1.07:1. A brightness floor plus a scrim that changes axis at 1024px fixed it, ending at 11.2:1 for the headline and 6.8:1 for the lead on mobile.
The routine I run now
- Open every image at 100%, full size, before it is registered in the project. Not the thumbnail. Not the search-result grid. The actual file.
- Make the key match what you can see in it. A key is a claim someone else made, usually an algorithm. Once you have looked, make it true.
- Write the alt text with the image on screen. If you are writing it from the key, you are writing fiction.
- After the build, list every image in the output and grep the source for a reference to each. Anything with zero references gets deleted.
- Check the weight of the biggest three files. An 8.2MB hero is not a design decision, it is an oversight.
- Sample the rendered pixels for the contrast of anything overlaid on an image, and for video, sample the worst frame.
Steps 3 to 6 can be scripted. Steps 1 and 2 cannot, and they are the ones that caught the torii gate.
Twenty-five images, and one of them was a beach in Japan doing duty as a solar farm in two places. It was live. The point of hiring someone is that they looked. The pixels are the only evidence there is.