wip sidenotes post

This commit is contained in:
Joseph Montanaro 2023-08-15 11:48:07 -07:00
parent 7d5c696fa7
commit adc582116b

View File

@ -25,7 +25,7 @@ draft: true
.sidenote-absolute {
position: absolute;
left: calc(50vw + var(--content-width) / 2 + 1rem);
left: calc(50% + var(--content-width) / 2 + 1rem);
max-width: 12rem;
font-size: 0.75rem;
}
@ -77,18 +77,33 @@ My first approach was something like this:
```css
.sidenote {
position: absolute;
/* 50vw takes us to the midpoint of the page,
/* 50% takes us to the midpoint of the page,
half of content-width gets out out to the gutter,
and the extra 1rem gives us some breathing room. */
left: calc(50vw + var(--content-width) / 2 + 1rem);
left: calc(50% + var(--content-width) / 2 + 1rem);
max-width: 12rem;
font-size: 0.75rem;
}
```
And it worked! Sort of. Here's an example.<span class="counter"></span><span class="sidenote-absolute">My initial take on sidenotes. Seems to be working, right?</span> Unfortunately it has a couple of flaws. For one, it will overflow the screen as soon as the viewport gets too narow, which is easy enough to solve (just a matter of a sufficiently complex `calc()` expression) but definitely needs doing. More importantly, however, there's no facility for dealing with overlaps. So if you have multiple sidenotes\*<span class="sidenote-absolute">Like this one.</span> too close\*<span class="sidenote-absolute" style="transform: translateY(0.2rem)">And this one, which I've moved down just a smidge to make the overlap more apparent.</span> together, they will overlap because absolute positioning Just Doesn't Care.
And it worked! Sort of. Here's an example.<span class="counter"></span><span class="sidenote-absolute">My initial take on sidenotes. Seems to be working, right?</span> Unfortunately it has a couple of flaws. For one, it will overflow the screen as soon as the viewport gets too narow, which is easy enough to solve (just a matter of a sufficiently complex `calc()` expression) but definitely needs doing. More importantly, however, there's no facility for dealing with overlaps. So if you have multiple sidenotes<span class="counter"></span><span class="sidenote-absolute">Like this one.</span><span class="counter"><span class="sidenote-absolute" style="transform: translateY(0.2rem)">And this one, which I've moved down just a smidge to make the overlap more apparent.</span> too close together, they will overlap because absolute positioning Just Doesn't Care.
Obviously, the blunt-instrument solution to this is Javascript, but it's less than ideal for a variety of reasons:
* I wanted to write this as a Svelte component, which means that's the obvious place to put this logic. But because there are many instances of the component and I only want to run the collision-detection logic once, it has to be coordinated across multiple instances of the same component, which is always painful.
* Because we have to wait for the sidenote elements to _have_ concrete positions before we can detect whether they collide, we can't do this until they are mounted (i.e. inserted into the DOM). I was concerned that this would cause [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content)-like problems, although in retrospect I don't actually recall it happening.<Sidenote>Possibly it was mitigated by the way Svelte batches DOM updates.</Sidenote>However, since I was always planning on static-rendering the site and letting SvelteKit do client-side hydration on page load, I don't think the possibility could ever be ruled out entirely.
* Anything that triggered a reflow would cause sidenote positions to get positionally out of sync with their references, and might even cause collisions again. [There are a lot of things that can cause a reflow](https://gist.github.com/paulirish/5d52fb081b3570c81e3a), and I'd have to listen to all of them if I wanted this to be a fully general solution. Sure, you could argue that since it's my site, I could just be aware of this problem and avoid using reflow-causing events where possible--but I wanted the freedom to be able to add as much interactivity as I felt like to any given blog post without having to worry.
https://scripter.co/sidenotes-using-only-css/
None of these problems are _completely_ inaddressible, but it was all going to be very fiddly to fix properly, so I decided to do a bit more research before throwing in the towel. And boy am I glad that I did, because it turns out that with enough...
## CSS Wizardry
...anything is possible.
Eventually I ran across [this post](https://scripter.co/sidenotes-using-only-css/), which describes a shockingly elegant solution to the problem with very few downsides.<Sidenote>It's worth noting that this same approach seems to be used by [Tufte CSS](https://edwardtufte.github.io/tufte-css/), which I had looked at previously but had failed to comprehend, possibly because it doesn't really go into detail about its sidenote mechanism.</Sidenote> The basic idea is extremely straightforward:
1. Give your sidenotes a float, so that they are removed from the regular document flow _but_ (and this is crucual) _other text still makes space for them._
2. Give them a fixed width, and then:
3. Give them a negative margin equal to the max-width, so that they are pulled out of the body of the text and hang out in the gutter.
4. Lastly, give them a `clear` value so that when multiple sidenotes try to occupy the same space the later ones get pushed down below the earlier ones. I'll be honest: this is the bit that I understand the least. I know the basic idea of what `clear` does--it forces the parent of a floated element to expand so that it contains it--but I don't know why it causes multiple floated elements to stack on top of each other like this.