/** * French typography: replaces normal spaces with non-breaking spaces * where French typographic rules require them. */ function applyFrenchTypography(elements) { elements.forEach(function (el) { var html = el.innerHTML; // Collapse multiple spaces into one (outside HTML tags) html = html.replace(/([^<>]) {2,}(?=[^<>])/g, '$1 '); // Space before ? ! : ; » html = html.replace(/ ([?!:;»])/g, '\u00A0$1'); // Space after « html = html.replace(/(«) /g, '$1\u00A0'); // "p. 42" → non-breaking space after "p." html = html.replace(/\bp\. /g, 'p.\u00A0'); // "n° 3" → non-breaking space after "n°" html = html.replace(/\bn° /g, 'n°\u00A0'); el.innerHTML = html; }); } document.addEventListener('DOMContentLoaded', function () { applyFrenchTypography(document.querySelectorAll('.post-card h2, .post-card .post-card__title')); // Re-apply on any post-card dynamically added anywhere on the page new MutationObserver(function (mutations) { mutations.forEach(function (m) { m.addedNodes.forEach(function (node) { if (node.nodeType !== 1) return; var targets = node.classList && node.classList.contains('post-card') ? node.querySelectorAll('h2, .post-card__title') : node.querySelectorAll('.post-card h2, .post-card .post-card__title'); applyFrenchTypography(targets); }); }); }).observe(document.body, { childList: true, subtree: true }); });