---
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
```
We can ignore the ``, ``, ``, and `` components - we're going to focus on the ``s and ``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 ``s and ``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);
}
```

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 ``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.

```css
#toc li::before {
position: relative;
left: -.34rem; /* I fiddled a lot and this is where I landed */
}
```

### 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;
}
```

### 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!

### but what about really, really, really, really, REALLY long headers?
What about line wrapping? It looks pretty bad right now.

(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 */
}
```

## 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: '└─';
}
```