summaryrefslogtreecommitdiff
path: root/src/plugins/rehype-image-captions.ts
blob: 61bdd84067992e93b45ddf7fe7b1ac5b7cfd7913 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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;
		});
	};
};