No files matched your search
@@ -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 */
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 47 KiB |
@@ -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);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||

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

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