目次
↑これが完成品です。
トグル見出しのCSSを考えて試行錯誤していたところ、detailsとsummaryを使えばJavaScript無しでアコーディオンを作れると知りました。
作りたい目次の形は前々から考えていたので、覚えた知識を早速使って作ってみました。
完成した目次の確認用に見出し多めでお届けします。
JavaScript不使用
CSSだけで作れるのでそうします。JavaScriptを使った目次はサイドバーに搭載したいと考えている「現在地を追従してハイライトする目次」で使えばいいので、今回はとにかく軽くてシンプルにします。
JavaScriptにまだまだ自信がないので避けているだけではありません。
目次
目次は同期ブロックで全記事の最初に挿入してあるので今更感ありますね。デフォルトでは開いており、クリック(タップ)で閉じます。
こだわったと言うほどでもないことですが、端まで伸ばし切ってあります。
見出しレベルに合わせてインデントを設定してありますが、テキストだけに当たり判定があるとカーソル移動が面倒くさいため、雑にマウスを動かしてもどれが選ばれているのか分かるようになっています。
そして折り返すほど長い見出し文章だと右で折り返すのですが、折り返す際の端の隙間は見出しレベルに関係なく一定です。
こういうのって作らないと意識しないので面白いですね。
目次の開閉に応じてsummaryに下線を足しているのですが、その1pxが悪さをしているのか、開閉時に1pxのガタツキがありました。
打ち消すにはpaddingだろうと思って同じ1pxを追加したら上手くいきました。
微動だにしません。
src\components\notion-blocks\TableOfContents.astro
---
import * as interfaces from '../../lib/interfaces.ts'
import { buildHeadingId } from '../../lib/blog-helpers.ts'
import { snakeToKebab } from '../../lib/style-helpers.ts'
import '../../styles/notion-color.css'
import { Icon } from 'astro-icon'
export interface Props {
block: interfaces.Block
headings: interfaces.Block[]
}
const { block, headings } = Astro.props
---
<details class="toggle" open>
<summary>
目次
<Icon name="ph:plus" />
<Icon name="ph:minus" />
</summary>
<div class="table-of-contents">
{
headings.map((headingBlock: interfaces.Block) => {
const heading =
headingBlock.Heading1 ||
headingBlock.Heading2 ||
headingBlock.Heading3
let indentClass = ''
if (headingBlock.Type === 'heading_2') {
indentClass = 'indent-1'
} else if (headingBlock.Type === 'heading_3') {
indentClass = 'indent-2'
}
return (
<a
href={`#${buildHeadingId(heading)}`}
class={`table-of-contents ${snakeToKebab(
block.TableOfContents.Color
)} ${indentClass}`}
>
{heading.RichTexts.map(
(richText: interfaces.RichText) => richText.PlainText
).join('')}
</a>
)
})
}
</div>
</details>
<style>
.toggle {
display: block;
width: 80%;
margin-inline-start: auto;
margin-inline-end: auto;
margin-block-end: 1rem;
border-radius: 0.5rem;
border: 1px solid grey;
}
@media (max-width: 844px) {
.toggle {
width: 100%;
}
}
.toggle > summary {
font-size: 1rem;
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
justify-content: center;
height: 4rem;
position: relative;
&::-webkit-details-marker {
display: none;
}
}
@media (hover: hover) {
.toggle > summary:hover {
text-decoration: underline;
}
}
.toggle[open] > summary {
border-block-end: 1px solid grey;
padding-block-start: 1px;
}
.toggle > summary > [astro-icon] {
position: absolute;
height: 2rem;
left: 100%;
translate: -120%;
&:first-child {
display: block;
}
&:not(:first-child) {
display: none;
}
}
.toggle[open] > summary > [astro-icon] {
&:first-child {
display: none;
}
&:not(:first-child) {
display: block;
}
}
.table-of-contents {
margin-block-start: 1rem;
margin-block-end: 1rem;
}
.table-of-contents > a {
display: block;
font-size: 1rem;
color: var(--fg);
font-weight: 600;
line-height: 1.8rem;
margin: 0;
padding-inline-start: 1rem;
padding-inline-end: 1rem;
}
@media (hover: hover) {
.table-of-contents > a:hover {
text-decoration: underline;
text-underline-offset: 0.1rem;
}
}
.table-of-contents > a.indent-1 {
font-size: 0.9rem;
line-height: 1.6rem;
font-weight: 400;
padding-inline-start: 1.5rem;
}
.table-of-contents > a.indent-2 {
font-size: 0.8rem;
line-height: 1.4rem;
font-weight: normal;
padding-inline-start: 3rem;
}
</style>
HTML部分は殆ど変更することなく作り変えることができました。