@@ -51,186 +51,207 @@ const description = post.data.subtitle;
51
51
---
52
52
53
53
<Layout title ={ title } description ={ description } toc ={ post .data .toc } >
54
-
55
- { post .data .toc ?
56
- <TwoCols >
57
- <Fragment slot = " content" >
58
- <Headline id = " contents" as = " h4" title = " Table of contents" />
59
- <div id = " toc" class = " mb-6 last:mb-0 rounded-lg shadow-md bg-white p-6" ></div >
60
- </Fragment >
61
- <Fragment slot = " sidebar" >
62
- <Prose class = " pb-20" >
63
- <Content
64
- components = { {
65
- ButtonLink ,
66
- Map ,
67
- MapSprints ,
68
- YouTube ,
69
- BenefitItem ,
70
- BenefitsList ,
71
- HighlightCard ,
72
- HighlightItem ,
73
- Note ,
74
- SponsorTiers ,
75
- hr: Separator ,
76
- Accordion ,
77
- }}
78
- />
79
- </Prose >
80
- </Fragment >
81
- </TwoCols >
82
- :
83
- <Prose class = " pb-20" >
84
- <Content
85
- components = { {
86
- ButtonLink ,
87
- Map ,
88
- MapSprints ,
89
- YouTube ,
90
- BenefitItem ,
91
- BenefitsList ,
92
- HighlightCard ,
93
- HighlightItem ,
94
- Note ,
95
- SponsorTiers ,
96
- hr: Separator ,
97
- Accordion ,
98
- }}
99
- />
100
- </Prose >
101
- }
54
+ {
55
+ post .data .toc ? (
56
+ <TwoCols >
57
+ <Fragment slot = " content" >
58
+ <div
59
+ id = " toc"
60
+ class = " mt-24 mb-6 last:mb-0 rounded-lg shadow-md bg-white p-6"
61
+ >
62
+ <span class = " text-lg font-bold" >Table of contents</span >
63
+ </div >
64
+ </Fragment >
65
+ <Fragment slot = " sidebar" >
66
+ <Prose class = " pb-20" >
67
+ <Content
68
+ components = { {
69
+ ButtonLink ,
70
+ Map ,
71
+ MapSprints ,
72
+ YouTube ,
73
+ BenefitItem ,
74
+ BenefitsList ,
75
+ HighlightCard ,
76
+ HighlightItem ,
77
+ Note ,
78
+ SponsorTiers ,
79
+ hr: Separator ,
80
+ Accordion ,
81
+ }}
82
+ />
83
+ </Prose >
84
+ </Fragment >
85
+ </TwoCols >
86
+ ) : (
87
+ <Prose class = " pb-20" >
88
+ <Content
89
+ components = { {
90
+ ButtonLink ,
91
+ Map ,
92
+ MapSprints ,
93
+ YouTube ,
94
+ BenefitItem ,
95
+ BenefitsList ,
96
+ HighlightCard ,
97
+ HighlightItem ,
98
+ Note ,
99
+ SponsorTiers ,
100
+ hr: Separator ,
101
+ Accordion ,
102
+ }}
103
+ />
104
+ </Prose >
105
+ )
106
+ }
102
107
</Layout >
103
108
104
109
<script is:inline >
105
- document.addEventListener("DOMContentLoaded", () => {
106
- const tocContainer = document.getElementById("toc");
107
- if (!tocContainer) return;
108
-
109
- const headings = Array.from(document.querySelectorAll("article h2, article h3, article h4, article summary span:nth-child(1)"));
110
- if (!headings.length) return;
111
-
112
- const rootUl = document.createElement("ul");
113
- let currentUl = rootUl;
114
- let lastLevel = 2;
115
- const parents = [rootUl];
116
-
117
- headings.forEach((heading, index) => {
118
- if (!heading.id) {
119
- heading.id = `heading-${index}`;
120
- }
121
-
122
- const level = parseInt(heading.tagName[1], 10);
123
- const li = document.createElement("li");
124
- const a = document.createElement("a");
125
- a.href = `#${heading.id}`;
126
- a.textContent = heading.textContent;
127
- li.appendChild(a);
128
-
129
- if (level > lastLevel) {
130
- const newUl = document.createElement("ul");
131
- parents[parents.length - 1].lastElementChild?.appendChild(newUl);
132
- parents.push(newUl);
133
- } else if (level < lastLevel) {
134
- parents.splice(-(lastLevel - level));
135
- }
136
-
137
- currentUl = parents[parents.length - 1];
138
- currentUl.appendChild(li);
139
- lastLevel = level;
140
- });
141
-
142
- // Add "Back to top" link
143
- const backToTop = document.createElement("li");
144
- const topLink = document.createElement("a");
145
- topLink.href = "#";
146
- topLink.textContent = "↑ Back to top";
147
- topLink.style.marginTop = "1em";
148
- backToTop.appendChild(topLink);
149
- rootUl.appendChild(backToTop);
150
-
151
- tocContainer.appendChild(rootUl);
152
-
153
- // Scroll spy: highlight active link
154
- const observer = new IntersectionObserver(
155
- entries => {
156
- entries.forEach(entry => {
157
- const id = entry.target.id;
158
- const tocLink = tocContainer.querySelector(`a[href="#${id}"]`);
159
- if (tocLink) {
160
- if (entry.isIntersecting) {
161
- tocContainer.querySelectorAll("a").forEach(a => a.classList.remove("active"));
162
- tocLink.classList.add("active");
110
+ document.addEventListener("DOMContentLoaded", () => {
111
+ const tocContainer = document.getElementById("toc");
112
+ if (!tocContainer) return;
113
+
114
+ const headings = Array.from(
115
+ document.querySelectorAll(
116
+ "article h2, article h3, article h4, article summary span:nth-child(1)",
117
+ ),
118
+ );
119
+ if (!headings.length) return;
120
+
121
+ const rootUl = document.createElement("ul");
122
+ let currentUl = rootUl;
123
+ let lastLevel = 2;
124
+ const parents = [rootUl];
125
+
126
+ headings.forEach((heading, index) => {
127
+ if (!heading.id) {
128
+ heading.id = `heading-${index}`;
129
+ }
130
+
131
+ const level = parseInt(heading.tagName[1], 10);
132
+ const li = document.createElement("li");
133
+ const a = document.createElement("a");
134
+ a.href = `#${heading.id}`;
135
+ a.textContent = heading.textContent;
136
+ li.appendChild(a);
137
+
138
+ if (level > lastLevel) {
139
+ const newUl = document.createElement("ul");
140
+ parents[parents.length - 1].lastElementChild?.appendChild(newUl);
141
+ parents.push(newUl);
142
+ } else if (level < lastLevel) {
143
+ parents.splice(-(lastLevel - level));
144
+ }
145
+
146
+ currentUl = parents[parents.length - 1];
147
+ currentUl.appendChild(li);
148
+ lastLevel = level;
149
+ });
150
+
151
+ // Add "Back to top" link
152
+ const backToTop = document.createElement("li");
153
+ const topLink = document.createElement("a");
154
+ topLink.href = "#";
155
+ topLink.textContent = "↑ Back to top";
156
+
157
+ topLink.classList.add("hidden");
158
+ topLink.classList.add("lg:block");
159
+ topLink.style.marginTop = "1em";
160
+ backToTop.appendChild(topLink);
161
+ rootUl.appendChild(backToTop);
162
+
163
+ tocContainer.appendChild(rootUl);
164
+
165
+ // Scroll spy: highlight active link
166
+ const observer = new IntersectionObserver(
167
+ (entries) => {
168
+ entries.forEach((entry) => {
169
+ const id = entry.target.id;
170
+ const tocLink = tocContainer.querySelector(`a[href="#${id}"]`);
171
+ if (tocLink) {
172
+ if (entry.isIntersecting) {
173
+ tocContainer
174
+ .querySelectorAll("a")
175
+ .forEach((a) => a.classList.remove("active"));
176
+ tocLink.classList.add("active");
177
+ }
163
178
}
164
- }
165
- });
166
- },
167
- {
168
- rootMargin: "-30% 0px -60% 0px",
169
- threshold: 0,
170
- }
171
- );
172
-
173
- headings.forEach(heading => observer.observe(heading));
174
- });
179
+ });
180
+ },
181
+ {
182
+ rootMargin: "-10% 0px -60% 0px",
183
+ threshold: 0,
184
+ },
185
+ );
186
+
187
+ headings.forEach((heading) => observer.observe(heading));
188
+ });
175
189
</script >
176
190
177
191
<style is:global >
178
-
179
- #toc {
180
- font-size: 0.95rem;
181
- line-height: 1.5;
182
- padding: 1rem;
183
- border-left: 1px solid #e0e0e0;
184
- position: sticky;
185
- top: 1rem;
186
- }
187
-
188
- #toc ul {
189
- list-style: none;
190
- padding-left: 0;
191
- margin: 0;
192
- }
193
-
194
- #toc li {
195
- margin: 0.25em 0;
196
- }
197
-
198
- #toc li ul {
199
- margin-left: 1em;
200
- border-left: 1px dashed #ddd;
201
- padding-left: 0.75em;
202
- }
203
-
204
- #toc a {
205
- text-decoration: none;
206
- color: #333;
207
- display: block;
208
- padding: 0.25em 0.5em;
209
- /*border-radius: 4px;*/
210
- transition: background 0.2s ease, color 0.2s ease;
211
- }
212
-
213
- #toc a:hover {
214
- background: #f0f0f0;
215
- color: #007acc;
216
- }
217
-
218
- #toc a.active {
219
- font-weight: 600;
220
- color: #007acc;
221
- border-left: 3px solid #007acc;
222
- background: rgba(0, 122, 204, 0.1);
223
- padding-left: 0.5em;
224
- }
225
-
226
- #toc a[href="#"] {
227
- font-size: 0.9em;
228
- color: #555;
229
- margin-top: 0.5em;
230
- }
231
-
232
- #toc a[href="#"]:hover {
233
- color: #007acc;
234
- text-decoration: underline;
192
+ #toc {
193
+ font-size: 0.95rem;
194
+ line-height: 1.5;
195
+ padding: 1rem;
196
+ border-left: 1px solid #e0e0e0;
197
+ position: sticky;
198
+ top: 1rem;
199
+ }
200
+
201
+ #toc ul {
202
+ list-style: none;
203
+ padding-left: 0;
204
+ margin: 0;
205
+ }
206
+
207
+ #toc li {
208
+ margin: 0.25em 0;
209
+ }
210
+
211
+ #toc li ul {
212
+ margin-left: 1em;
213
+ border-left: 1px dashed #ddd;
214
+ padding-left: 0.75em;
215
+ }
216
+
217
+ #toc a {
218
+ text-decoration: none;
219
+ color: #333;
220
+ padding: 0.25em 0.5em;
221
+ transition:
222
+ background 0.2s ease,
223
+ color 0.2s ease;
224
+ }
225
+
226
+ #toc a:hover {
227
+ background: #f0f0f0;
228
+ color: #007acc;
229
+ }
230
+
231
+ #toc a.active {
232
+ font-weight: 600;
233
+ color: #007acc;
234
+ border-left: 3px solid #007acc;
235
+ background: rgba(0, 122, 204, 0.1);
236
+ padding-left: 0.5em;
237
+ }
238
+
239
+ #toc a[href="#"] {
240
+ font-size: 0.9em;
241
+ color: #555;
242
+ margin-top: 0.5em;
243
+ }
244
+
245
+ #toc a[href="#"]:hover {
246
+ color: #007acc;
247
+ text-decoration: underline;
248
+ }
249
+
250
+
251
+ article h2,
252
+ article h3,
253
+ article h4,
254
+ article summary span:nth-child(1) {
255
+ scroll-margin-top: 120px; /* Same offset as used in JS */
235
256
}
236
257
</style >
0 commit comments