diff options
| author | Dawid Rycerz <dawid@rycerz.xyz> | 2026-01-13 20:21:16 +0100 |
|---|---|---|
| committer | Dawid Rycerz <dawid@rycerz.xyz> | 2026-01-13 20:21:16 +0100 |
| commit | f38a0cb8446201cd6c937d96da33b58b4427c78f (patch) | |
| tree | 20225ce0df0f95e094a01650a3f7477c5032cd2c /src/plugins/rehype-image-captions.ts | |
| parent | b6e440699e9fca474869bf74ce09f2310f05c620 (diff) | |
Add alt texts rendering
Diffstat (limited to 'src/plugins/rehype-image-captions.ts')
| -rw-r--r-- | src/plugins/rehype-image-captions.ts | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/src/plugins/rehype-image-captions.ts b/src/plugins/rehype-image-captions.ts new file mode 100644 index 0000000..61bdd84 --- /dev/null +++ b/src/plugins/rehype-image-captions.ts @@ -0,0 +1,78 @@ +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; + }); + }; +}; |
