import type { Element, Root } from "hast"; import { h } from "hastscript"; import type { Plugin } from "unified"; import { visit } from "unist-util-visit"; /** * Rehype plugin that wraps images with figure/figcaption to display alt text on hover. * Skips images without alt text or with generic alt text like "Image". */ export const rehypeImageCaptions: Plugin<[], Root> = () => { return (tree: Root) => { visit(tree, "element", (node: Element, index, parent) => { // Only process img elements that have a parent and an index if (node.tagName !== "img" || !parent || index === undefined) { return; } // Get the alt text const alt = node.properties?.alt; if (!alt || typeof alt !== "string") { return; } // Skip generic or empty alt text const trimmedAlt = alt.trim(); if (!trimmedAlt || trimmedAlt.toLowerCase() === "image") { return; } // Preserve all existing properties including data-zoomable for medium-zoom const imgProperties = { ...node.properties }; // Create the figcaption element with tailwind classes for hover overlay const figcaption = h( "figcaption", { class: [ "pointer-events-none", "absolute", "bottom-0", "left-0", "right-0", "rounded-b-lg", "bg-black/70", "px-3", "py-2", "text-sm", "text-white", "opacity-0", "transition-opacity", "group-hover:opacity-100", ].join(" "), }, [{ type: "text", value: trimmedAlt }], ); // Create the figure wrapper with group class for hover trigger const figure = h( "figure", { class: "image-with-alt group relative", }, [ { type: "element", tagName: "img", properties: imgProperties, children: [], }, figcaption, ], ); // Replace the img with the figure in the parent parent.children[index] = figure; }); }; };