update tree styling to box method
continuous-integration/drone/push Build is passing

This commit is contained in:
inherentlee committed 2026-09-21 15:40:27 -07:00
1 parent 5acda53f58
commit cf26b06e3d
10 files changed
+26 -240

No files matched your search

+26 -23
View File
@@ -51,40 +51,43 @@
top: -1.25rem; top: -1.25rem;
} }
/* my baby my child my `tree` list style */ /* `tree` style toc */
#toc ol, #toc nav > ol {
#toc li { margin-left: 1rem;
margin-block: 0;
list-style: none;
} }
#toc ol { #toc ol {
margin-left: 0; margin: 0;
} }
#toc li { #toc li {
border-left: .095rem solid var(--color-pink); position: relative;
margin-left: 1.5rem; padding-inline-start: 1em;
line-height: 1.15; list-style: none;
text-indent: 1.22rem hanging; margin-block: 0;
margin-left: .35rem;
}
#toc li::before,
#toc li::after {
display: block;
position: absolute;
box-sizing: border-box;
inset-block: 0 auto;
inset-inline: 0 auto;
inline-size: .75em;
border-inline-start: .1rem solid var(--color-pink);
} }
#toc li::before { #toc li::before {
content: '├─' / ""; block-size: .5lh;
color: var(--color-pink); border-block-end: .1rem solid var(--color-pink);
position: relative; content: "";
left: -.34rem;
} }
#toc li:last-child { #toc li:not(:last-child)::after {
border-left-color: transparent; block-size: 100%;
} content: "";
#toc li:last-child::before {
/* Technically, we could use this content for all of them, but doing the full
bar for the non-last-childs keeps the border a *smidge* more consistent for
different levels of magnification */
content: '└─' / "";
} }
/* underlines are unnecessary in this context */ /* underlines are unnecessary in this context */
Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

-217
View File
@@ -1,217 +0,0 @@
---
title: tree-style table of contents
layout: post-with-toc.njk
image:
src: 2026/tree-styling-0.png
alt: "screenshot of the table of contents for a blog post (this blog post, in fact). it's been styled so that the output resembles the hierarchical file system output from the shell program 'tree.'"
tags:
- reference
- software
---
I have a shiny new Table of Contents (hereafter ToC) section for some of my posts (like this one)! And for fun and profit, it's styled like the output of [`tree`](https://en.wikipedia.org/wiki/Tree_(command)). I want to lay out the CSS I used for this. Fair warning, there's some hyper-specific border widths and positioning that *probably* should be a `calc()` function of some kind.
I drew inspiration from this [StackOverflow issue regarding tree styling for lists](https://stackoverflow.com/questions/14423671/best-method-for-displaying-a-tree-list-using-css-only-no-images-or-js-example).
## `tree`
Just for reference, here's some sample `tree` output:
```txt
.
├── footer.njk
├── head-content.njk
├── header.njk
├── layouts
│   ├── base.njk
│   ├── page-lists.njk
│   ├── page.njk
│   ├── post.njk
│   ├── post-with-toc.njk
│   └── tag-redirect.njk
├── pagination.njk
├── postlist.njk
└── webring.njk
```
(You'll note that there are gaps between the verticals of each line. That's due to the line-height on the page, but in a standard terminal those vertical lines would be contiguous.)
## HTML
Our ToC uses the following HTML structure:
```html
<aside id="toc">
<details>
<summary><h2>table of contents</h2></summary>
<nav>
<ol>
<li><a>h2 1</a></li>
<li><a>h2 2</a>
<ol>
<li><a>h3 2a</a></li>
<li><a>h3 2b</a></li>
</ol>
</li>
<li><a>h2 3</a></li>
<!-- etc -->
</ol>
</nav>
</details>
</aside>
```
We can ignore the `<aside>`, `<details>`, `<summary>`, and `<nav>` components - we're going to focus on the `<ol>`s and `<li>`s.
## CSS
Despite referencing a StackOverflow issue above, I never like to just copy-paste a chunk of StackOverflow code into place. I don't like extraneous CSS, and sometimes there's stuff mixed in there that just isn't necessary. Let's build from the ground up.
### remove styling that will be in the way
By default, my lists have a lot of space around them for legibility when reading longer list items. We'll be accomplishing the vertical bars with borders here, so it's critical we not have any block-oriented spacing on `<ol>`s and `<li>`s. We're also going to take the numeric `list-style` off.
```css
#toc ol,
#toc li {
margin-block: 0;
list-style: none;
}
```
### start the structure with borders
The `tree`-like structure will be accomplished with both symbols (stolen directly from the tree output) and borders. Let's throw some borders on.
```css
#toc li {
border-left: .095rem solid var(--color-pink);
}
```
![Phase 1. There are thin pink vertical left-side borders for the list item elements, create long contiguous vertical lines. They are directly next to the list items with no spacing.](/img/2026/tree-styling-1.png)
Those are super off. First, they're tabbed too far in to the left; second, there's *no* space between the border and the list item. We're going to fix the first thing by removing left margins on the `<ol>`s. The second is where we pull in the most critical piece of infrastructure.
```css
#toc ol {
margin-left: 0;
}
```
### `li::before`
We're going to use a character (technically two) from the `tree` output and place it before our list items.
```css
#toc li::before {
content: '├─' / "";
color: var(--color-pink);
}
```
Notably, without additional positioning, the bars will be spaced away from the border. We want them to overlap perfectly.
![Phase 2, shown with the new characters before any positioning adjustment. There are tree line leaders before each list item, but they are a few pixels off from overlapping with the border.](/img/2026/tree-styling-2.png)
```css
#toc li::before {
position: relative;
left: -.34rem; /* I fiddled a lot and this is where I landed */
}
```
![Phase 3. The tree line leaders now perfectly overlap the border and it looks like a contiguous vertical line with small tabs inwards.](/img/2026/tree-styling-3.png)
### adjust left margins
Right now, the vertical lines don't nest nicely underneath the level above. Let's change our margins. I also adjusted the line height very slightly, because I noticed on some levels of magnification that the tree line leader characters weren't actually meeting.
```css
#toc li {
margin-left: 1.5rem;
line-height: 1.15;
}
```
![Phase 4. The vertical border comes down directly below the first character of the parent level.](/img/2026/tree-styling-4.png)
### fix those pesky last children
You've probably noticed that the last children in the tree output had a bend rather than the T-shape we've been using. We'll want to remove the border from the last children and change the character we're using.
```css
#toc li:last-child {
border-left-color: transparent;
}
#toc li:last-child::before {
/* Technically, we could use this content for all of them, but doing the full
bar for the non-last-childs keeps the border a *smidge* more consistent for
different levels of magnification */
content: '└─' / "";
}
```
With that, it starts to look complete!
![Phase 5, almost done. The last children no longer have a border that continues onward as though there will be more children.](/img/2026/tree-styling-5.png)
### but what about really, really, really, really, REALLY long headers?
What about line wrapping? It looks pretty bad right now.
![Table of contents now shown with the above header. When the line wraps, instead of lining up with the beginning of the first character on the previous line, it nests under the tree line leader and right in line with the vertical border.](/img/2026/tree-styling-6.png)
(And for the record, this happens for middle children too, it's not just a last child issue.)
We can fix this with a hanging indent.
```css
#toc li {
text-indent: 1.22rem hanging; /* Again, I fiddled */
}
```
![Final version with the hanging indent implemented. Now the second line of text for this header lines up nicely with the start of the text above it.](/img/2026/tree-styling-7.png)
## summary
Here's the final collected CSS:
```css
#toc ol,
#toc li {
margin-block: 0;
list-style: none;
}
#toc ol {
margin-left: 0;
}
#toc li {
border-left: .095rem solid var(--color-pink);
margin-left: 1.5rem;
line-height: 1.15;
text-indent: 1.22rem hanging;
}
#toc li::before {
content: '├─' / "";
color: var(--color-pink);
position: relative;
left: -.34rem;
}
#toc li:last-child {
border-left-color: transparent;
}
#toc li:last-child::before {
/* Technically, we could use this content for all of them, but doing the full
bar for the non-last-childs keeps the border a *smidge* more consistent for
different levels of magnification */
content: '└─' / "";
}
```