Fancy Inline Asides

I recently read an article on Thirty-Five about something unrelated and really liked one of the effects they used throughtout the article. They had these little colored and styled icons through the blog that, when you clicked on them, would expand on the content with more deteails in a separate pane — Either directly below or to the side of the text depending on your screen size. That's pretty cool! Let's try to reproduce it.
Ground rules
- The content should be accessible to a screen reader and keyboard navigable
- The component should use the minimum amount of JavaScript possible, and rely on cross-platform web standards when available.
Starting easy
MVP for the feature would be a simple
details
MDN
element. It's web native, expands when you click on it, and even has a mechanism for ensuring only one panel is open at a time, which will be useful later.
Unfortuantely, it has a few major drawbacks. The first is immediately
evident — details are block-level elements. A CSS class with display: inline
can get us part of the way there, but we still have the issue of it not being
phrasing
content to the browser rendering engine, which means it
will close any p
tags, defeating the purpose of making it inline.
For the sake of trying to push this idea through, I'm going to put a pin in that
one for now in order to see how far I can get, but consider this your foreshado-warning.
details
details {
display: inline;
}
baseline-source: first
details {
display: inline;
baseline-source: first;
}
Well, if we dont want something in the flow of the document, we remove it. Good old
position: absolute
details {
display: inline;
baseline-source: first;
&:open > *:not(summary) {
position: absolute;
}
}
(this space is intentionally left blank)
using JS to set a margin-bottom
el.addEventListener("toggle", () => {
const divEl = el.children[1];
el.style.paddingBlockEnd = "0px";
const targetHeight = window.getComputedStyle(divEl).height;
if (el.open) {
el.style.paddingBlockEnd = targetHeight;
}
});
our own
& > summary {
cursor: pointer;
/* Make the details look like our links */
text-decoration: underline;
text-decoration-thickness: 2px;
color: var(--primary-link-color);
text-decoration-color: var(--secondary-link-color);
/*
inline-block is necessary for chrome to not break
the line after the discolsure, and also functions
to turn off the default disclosure triangle so we
can add our own
*/
display: inline-block;
position: relative;
padding-inline-start: 2ch;
}
/*
credit to the wonderfully inspiring folk over at
https://css-tricks.com/how-to-animate-the-details-element/
*/
details.inline > summary::before {
content: "";
border-width: 0.3em 0 0.3em 0.6em;
border-style: solid;
border-color: transparent transparent transparent currentColor;
position: absolute;
top: 0.4em;
left: 0.4ch;
transform: rotate(0);
transform-origin: 0.2rem 50%;
transition: 0.25s transform ease;
}
details.inline[open] > summary:before {
transform: rotate(90deg);
}
display: inline-block;
as
they really did not like to show margin/padding on my inline
summary element.
Dealing with the element in the room
Early browsers made some... questionable decisions that can lead to unexpected behavior when you do things they don't expect. In our case here, the browser attempts to helpfully close paragarph tags when it thinks you are done with them. Since it assumes that the details element is going to be a block element, it closes the currently open P tag when hit. This is not what we want, and I have been avoiding the isuse thus far by not usingp
tags when I knew I was going to include a details element inside of the block
of text. That sucks. It means I have to manually add back the side-wide margins
that I normally get for free by writing semantic HTML and it means that the inclusion
of a details element 'leaks' out and affects how you write surrounding content.
Moreover, a details
element is not Phrasing Contentand thus not technically allowed inside of a P tag even if we could force it.
I am still working on a solution to this :(