fix: sidenotes use absolute positioning in page margin, not padding

Replace float+padding-left approach (which narrowed article text) with
position:absolute on each sidenote, placing it to the right of 100% of
.e-content so it paints in the existing left page margin. Text width is
unchanged. Requires viewport ≥ 1440px where the margin is 224px (210px
needed). JS sets each sidenote's top relative to its reference span via
getBoundingClientRect, with 8px overlap prevention.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
svemagie
2026-03-27 18:39:32 +01:00
parent 815bf67283
commit 0db48ac6fa
2 changed files with 23 additions and 9 deletions

View File

@@ -1176,22 +1176,25 @@ body[data-indiekit-auth="true"] .share-post-btn:hover {
font-variant-numeric: tabular-nums;
}
/* Wide (xl+): left gutter + floating sidenotes */
@media (min-width: 1280px) {
/* Cancel the overflow:clip BFC so the float can escape the content box
into the padding gutter. The !important overrides Tailwind prose resets. */
/* Wide (1440px+): sidenotes float into the left page margin — text width unchanged */
@media (min-width: 1440px) {
/* Override overflow-x:hidden on .main-content so sidenotes can paint in the margin */
.main-content:has(article.has-sidenotes) {
overflow-x: visible;
}
/* .e-content is the positioning context; cancel overflow:clip so children can escape */
article.has-sidenotes .e-content {
position: relative;
overflow-x: visible !important;
padding-left: 210px !important;
}
article.has-sidenotes .sidenote {
display: block;
float: left;
clear: left;
position: absolute;
right: calc(100% + 20px); /* 20px gap; extends left into page margin */
top: 0; /* overridden by JS */
width: 190px;
margin-left: -210px; /* pull into the 210px padding gutter */
margin-top: 0.15em; /* subtle optical alignment with line */
font-size: 0.8rem;
font-style: italic;
line-height: 1.5;

View File

@@ -587,6 +587,17 @@ export default function (eleventyConfig) {
}
return node;
});
// 5. Inject positioning script before </body>
// Sets each sidenote's top relative to .e-content, with overlap prevention.
const posScript = `(function(){function p(){if(window.innerWidth<1440)return;var a=document.querySelector('article.has-sidenotes');if(!a)return;var e=a.querySelector('.e-content');if(!e)return;var cr=e.getBoundingClientRect();var lb=0;a.querySelectorAll('.sidenote').forEach(function(s){var h=s.parentElement;var hr=h.getBoundingClientRect();var t=Math.max(hr.top-cr.top,lb);s.style.top=t+'px';lb=t+s.offsetHeight+8;});}document.addEventListener('DOMContentLoaded',p);window.addEventListener('load',p);window.addEventListener('resize',p);})();`;
tree.walk(node => {
if (node.tag === "body") {
node.content = node.content || [];
node.content.push({ tag: "script", content: [posScript] });
}
return node;
});
}
},
]).process(content, { sync: false });