@@ -51,186 +51,207 @@ const description = post.data.subtitle;
5151---
5252
5353<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+ }
102107</Layout >
103108
104109<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+ }
163178 }
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+ });
175189</script >
176190
177191<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 */
235256}
236257</style >
0 commit comments