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
- Fetch the page HTML with an HTTP
GET(respect robots rules). Use theaxiosHTTP client and parse the returned HTML withcheerio(exact libraries recommended). Install with:
npm install axios cheerio
- Parse the static HTML with
cheerio(no JavaScript execution). Extract all<img>elements and all<figure>elements. - For each
<img>found:- Resolve
srcto an absolute URL using the page base URL and the HTML<base>tag if present. Remove fragments when resolving. - Record
altattribute 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.
- Resolve
- 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.
- Detect standalone
- 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.
- 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
| Field | Type | Comment |
|---|---|---|
| url | TEXT | Page URL inspected. |
| http_status INTEGER | INTEGER | HTTP status code from the page fetch. |
| img_src TEXT | TEXT | The raw src attribute value as found in HTML. |
| resolved_src TEXT | TEXT | Absolute URL resolved for the src (fragments removed). |
| referrer_url TEXT | TEXT | The page URL where the image was discovered. |
| in_figure BOOLEAN | BOOLEAN | True if the <img> is inside a <figure> ancestor. |
| figure_has_figcaption BOOLEAN | BOOLEAN | True if the containing <figure> has a non-empty <figcaption>. |
| figcaption_text TEXT | TEXT | Text content of the <figcaption> if present. |
| image_alt TEXT | TEXT | The alt attribute value (may be NULL or empty string). |
| is_decorative BOOLEAN | BOOLEAN | True when alt = '' explicitly (decorative image). |
| is_problem BOOLEAN | BOOLEAN | True when the check marks the image as problematic per triggers. |
| problem_type TEXT | TEXT | Short code for the problem (e.g., no_figure, no_figcaption, missing_alt). |
| note TEXT | TEXT | Any crawler notes (e.g., JS_injected_image, decorative_ok). |
| discovered_on TIMESTAMP | TIMESTAMP | Crawl 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."
- Target Field:
-
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."
- Target Field:
-
Trigger Rule 3: Image missing
altattribute (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
altattribute or has an emptyaltwithout being marked decorative; add meaningful alt text or explicitly mark as decorative (alt="")."
- Target Field:
-
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."
- Target Field:
Sources
- WHATWG HTML Living Standard — The
<figure>and<figcaption>elements: https://html.spec.whatwg.org/multipage/grouping-content.html#the-figure-element - MDN Web Docs —
<figure>: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figure - MDN Web Docs —
<figcaption>: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/figcaption
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
alttext (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
- Wrap meaningful images in
<figure>and include a concise<figcaption>describing the image context and relevance to the page content. - Ensure
altattributes are present and meaningful for contentful images; usealt=""only for purely decorative images. - If captions are present visually (e.g., adjacent
<p>), convert them into<figcaption>inside a<figure>to create an explicit semantic relationship. - Fix template/CMS output that emits empty
<figure>wrappers or places captions outside the semantic container. - For client-rendered pages, add a rendering-capable validation step (headless browser run) to verify final DOM and caption presence.