Hakyll diagram example
2025-12-01
This is a usage example of the hakyll-diagrams library, which renders interactive diagrams. It is included here because GitHub has restrictions on adding inline SVG and JavaScript code to the project README file.
The code below creates an interactive figure that opens a link when clicking on each subpath. The links are embedded within the SVG itself and are created using the Haskell diagrams library. The interactive functionality is achieved by combining custom classes assigned to SVG elements in the Haskell code with JavaScript code that manipulates the SVG DOM.
diagram {
figcaption="An interactive figure. It reacts to mouse hover and each part is a link."
svg:width=400
}
let
hydrogen =
mconcat
[ proton
, electron # moveTo p0
, orbit
]
where
orbit =
circle 2
# svgAttr "pointer-events" "stroke"
# href "https://en.wikipedia.org/wiki/Atomic_orbital"
# svgClass "svg_orbit"
electron =
circle 0.1
# fc blue
# lw 0
# href "https://en.wikipedia.org/wiki/Electron"
# svgClass "svg_electron"
proton =
circle 0.3
# fc red
# lw 0
# href "https://en.wikipedia.org/wiki/Proton"
# svgClass "svg_proton"
p0 = fromJust $ maxTraceP origin (angleV $ 3/8 @@ turn) orbit
background =
roundedRect 5 5 0.1
# lw 0
# fc (sRGB24read "#808080")
# opacity 0.15
in hydrogen <> backgroundHaskell
Javascript code used to animate the figure above:
document.addEventListener("DOMContentLoaded", () => {
const electrons = Array.from(document.querySelectorAll(".svg_electron"));
const orbits = Array.from(document.querySelectorAll(".svg_orbit"));
const protons = Array.from(document.querySelectorAll(".svg_proton"));
const groups = [
{ name: "electron", elements: electrons },
{ name: "orbit", elements: orbits },
{ name: "proton", elements: protons }
];
function fadeOut(list) {
list.forEach(el => {
el.style.transition = "opacity 0.3s ease";
el.style.opacity = "0.2";
});
}
function fadeIn(list) {
list.forEach(el => {
el.style.transition = "opacity 0.3s ease";
el.style.opacity = "1";
});
}
function attachHoverHandlers(group, others) {
group.elements.forEach(el => {
el.addEventListener("mouseenter", () => {
others.forEach(g => fadeOut(g.elements));
});
el.addEventListener("mouseleave", () => {
others.forEach(g => fadeIn(g.elements));
});
});
}
// Attach handlers for each group
groups.forEach((group, _, all) => {
const others = all.filter(g => g !== group);
attachHoverHandlers(group, others);
});
});Javascript