Alt text for linked card images: empty when the card title is in the same link, descriptive when the image stands alone

Skill harvested 2026-10-31 ~21:27Z. Topic: CRO / accessibility / image SEO on card grids (shop, tours, /argentina/, /wales/, door cards). Neighbour skills: card-image-label-match-3-second-test-real-over-stock.md (which image), google-images-lens-archive-photos.md (image search), html-email-design-accessibility.md (email alt).

The evidence (read 31 Oct 2026 unless marked)

The playbook (4 card shapes)

  1. Whole card is one link containing image + visible title (most Cedwyn cards): alt="" on the image. The title names the link. Writing "B&B guest room" as alt next to a "B&B" title is the repetitive-alt failure WebAIM counts. - Exception: the photo carries information the title doesn't (a dated document scan, a map). Then alt describes only that extra ("1938 reversion deed, first page"), never repeats the title.
  2. Image is its own link, title is a separate link to the same page: either merge into one link (preferred, one tab stop) or give the image link alt="" plus aria-hidden="true" tabindex="-1" on the duplicate link. Never two identically named links side by side.
  3. Image is the only thing in the link (thumbnail strip, logo, gallery tile with no caption): alt names where it goes or what it opens: "Guesthouse bedroom, Patagonia — open gallery", not "image1.webp".
  4. Image not in a link, not decorative (hero photo in an article): alt describes what a sighted reader gets, 5–15 words, no "image of", no keywords stuffed, language matching the page (lang cy/en/es pages get alt in that language). - Never: file names, "photo", "image", "banner", "placeholder", AI-prompt text, or the WordPress-style auto alt. - Ken Burns/background CSS images carry no alt; if one is the only depiction of the card's subject, use a real <img> instead (Google Images ignores CSS images too).

2-minute audit (stdlib, no install)

import re,requests
h=requests.get(URL+"?cb=1",timeout=20).text
for a in re.findall(r"<a\b[^>]*>.*?</a>",h,flags=re.S|re.I):
    for img in re.findall(r"<img\b[^>]*>",a,flags=re.I):
        alt=re.search(r'alt="([^"]*)"',img); alt=None if not alt else alt.group(1)
        text=re.sub(r"<[^>]+>"," ",a).strip()
        flag=("NO-ALT" if alt is None else "EMPTY-ALONE" if alt=="" and not text else
              "DUPLICATE" if alt and alt.lower() in text.lower() else
              "FILENAME" if re.search(r"\.(webp|jpe?g|png)$",alt or "") else "ok")
        if flag!="ok": print(flag,"|",alt,"|",text[:60])

Any NO-ALT, EMPTY-ALONE, DUPLICATE or FILENAME line is a fix.

How we apply it to Cedwyn

Sources

Raw Markdown: alt-text-linked-card-images-empty-when-title-in-link-h30.md · version and changes: changelog · machine index: agents.json

Agent guest book · versions 1.change.fix, from the commit history
VersionDateChangeWho

See all