Skip to main content

Images wrapped with <figure> and <figcaption>

Issue No: 142

Category: Semantic HTML

Issue type: Issue

Priority: IMPORTANT

Description

Detect images that are not wrapped in a <figure> element or are wrapped but missing a <figcaption>. Flags images lacking semantic captioning which harms SEO, accessibility, and content discoverability.

How do we capture it

  1. Fetch the page HTML with an HTTP GET (respect robots rules). Use the axios HTTP client and parse the returned HTML with cheerio (exact libraries recommended). Install with:
npm install axios cheerio
  1. Parse the static HTML with cheerio (no JavaScript execution). Extract all <img> elements and all <figure> elements.
  2. For each <img> found:
    • Resolve src to an absolute URL using the page base URL and the HTML <base> tag if present. Remove fragments when resolving.
    • Record alt attribute value (may be empty string or missing).
    • Inspect the DOM parent chain: determine whether the image is contained within a <figure> ancestor (closest <figure>), and whether that <figure> contains a <figcaption> child with non-empty text.
    • If the <img> is inside a <picture> element, still validate the effective <img> element(s) and prefer validating the <img> that will be used by the browser for the default src.
  3. Additional checks:
    • Detect standalone <figure> elements with no <img> children (possible markup errors).
    • Distinguish decorative images: if alt="" (empty string) and the image is purely decorative, do not require a <figcaption>, but still prefer semantic wrapping when an image conveys meaningful content.
  4. Network handling and timeouts:
    • Use a configurable timeout (e.g., 10s) when fetching pages. If the page fetch fails, record the fetch error and skip DOM checks for that URL.
  5. Edge cases & constraints:
    • Do NOT execute JavaScript — images inserted client-side will not be discovered by this static check. Mark such pages as requiring a rendering-capable validation when necessary.
    • CSS background images and images loaded via CSS are out-of-scope for this check.

What to store

FieldTypeComment
urlTEXTPage URL inspected.
http_status INTEGERINTEGERHTTP status code from the page fetch.
img_src TEXTTEXTThe raw src attribute value as found in HTML.
resolved_src TEXTTEXTAbsolute URL resolved for the src (fragments removed).
referrer_url TEXTTEXTThe page URL where the image was discovered.
in_figure BOOLEANBOOLEANTrue if the <img> is inside a <figure> ancestor.
figure_has_figcaption BOOLEANBOOLEANTrue if the containing <figure> has a non-empty <figcaption>.
figcaption_text TEXTTEXTText content of the <figcaption> if present.
image_alt TEXTTEXTThe alt attribute value (may be NULL or empty string).
is_decorative BOOLEANBOOLEANTrue when alt = '' explicitly (decorative image).
is_problem BOOLEANBOOLEANTrue when the check marks the image as problematic per triggers.
problem_type TEXTTEXTShort code for the problem (e.g., no_figure, no_figcaption, missing_alt).
note TEXTTEXTAny crawler notes (e.g., JS_injected_image, decorative_ok).
discovered_on TIMESTAMPTIMESTAMPCrawl timestamp when the image was inspected.

Condition for trigger

  • Trigger Rule 1: Image not wrapped in <figure>

    • Target Field: in_figure
    • Evaluation Logic: in_figure = false AND is_decorative = false
    • Severity: SUGGESTION
    • Diagnostic Message: "Image [resolved_src] on [referrer_url] is not enclosed in a <figure>; consider wrapping images with <figure> for semantic captioning."
  • Trigger Rule 2: <figure> present but missing <figcaption>

    • Target Field: figure_has_figcaption
    • Evaluation Logic: in_figure = true AND figure_has_figcaption = false AND is_decorative = false
    • Severity: WARNING
    • Diagnostic Message: "Image [resolved_src] is inside a <figure> but the <figcaption> is missing or empty; add a descriptive caption for SEO and accessibility."
  • Trigger Rule 3: Image missing alt attribute (non-decorative)

    • Target Field: image_alt, is_decorative
    • Evaluation Logic: image_alt IS NULL OR (image_alt = '' AND is_decorative = false)
    • Severity: CRITICAL
    • Diagnostic Message: "Image [resolved_src] lacks an alt attribute or has an empty alt without being marked decorative; add meaningful alt text or explicitly mark as decorative (alt="")."
  • Trigger Rule 4: <figure> present but contains no <img> children

    • Target Field: in_figure, img_src
    • Evaluation Logic: figure present AND no <img> child
    • Severity: SUGGESTION
    • Diagnostic Message: "A <figure> on [referrer_url] contains no <img> children; check for markup errors."

Sources

Long description

Wrapping images in <figure> with a <figcaption> provides explicit semantic captioning that search engines and assistive technologies can use to understand the image context. Captions improve discoverability, provide descriptive context beyond the alt attribute, and can appear in search previews or image carousels. Missing captions or inconsistent markup reduces SEO signal for image-driven content and worsens the experience for screen-reader users.

Common failure scenarios checked by the crawler:

  • Images used as informative content but not wrapped in <figure>; captions are absent or placed as separate paragraph text that is not semantically tied to the image.
  • <figure> elements included for layout without an <img> child (templating or CMS output errors).
  • Images lacking alt text (critical accessibility issue) or incorrectly marked decorative.
  • Images injected client-side by scripts — static checks will not find them and may require a rendering-capable (headless) follow-up.

ASCII example (good):

<figure>
<img src="/assets/photo.jpg" alt="Team working together">
<figcaption>Company team collaborating on product design.</figcaption>
</figure>

Constraints:

  • The crawler does not execute JavaScript; any images or captions inserted only at runtime will be missed by this check. Mark such pages with note = 'JS_injected_image' for follow-up.
  • Decorative images (alt="") are valid and do not require captions; the check should not force captions for purely decorative assets.

How to Fix

  1. Wrap meaningful images in <figure> and include a concise <figcaption> describing the image context and relevance to the page content.
  2. Ensure alt attributes are present and meaningful for contentful images; use alt="" only for purely decorative images.
  3. If captions are present visually (e.g., adjacent <p>), convert them into <figcaption> inside a <figure> to create an explicit semantic relationship.
  4. Fix template/CMS output that emits empty <figure> wrappers or places captions outside the semantic container.
  5. For client-rendered pages, add a rendering-capable validation step (headless browser run) to verify final DOM and caption presence.