Skip to content

Commit 44d13f0

Browse files
authored
Merge pull request #149 from DenisValeev/codex/remove-table-of-contents-and-add-navigation-buttons
Replace docs navigation list with pager controls
2 parents 5fe7a24 + bf5981f commit 44d13f0

1 file changed

Lines changed: 111 additions & 145 deletions

File tree

docs/index.html

Lines changed: 111 additions & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
}
6161

6262
.doc-page {
63-
width: min(1220px, 100%);
63+
width: min(900px, 100%);
6464
display: flex;
6565
flex-direction: column;
6666
gap: clamp(20px, 3vw, 32px);
@@ -125,119 +125,72 @@
125125
}
126126

127127
main.doc-main {
128-
display: grid;
129-
grid-template-columns: minmax(220px, 320px) minmax(0, 1fr);
128+
display: flex;
129+
flex-direction: column;
130130
gap: clamp(20px, 3vw, 36px);
131131
}
132132

133-
nav.doc-nav {
133+
section.doc-content {
134134
background: var(--panel-surface);
135-
border-radius: 22px;
135+
border-radius: 24px;
136136
border: 1px solid var(--border-color);
137-
padding: clamp(18px, 2vw, 24px);
137+
padding: clamp(20px, 3vw, 32px);
138+
box-shadow: 0 26px 60px var(--shadow-color);
138139
display: flex;
139140
flex-direction: column;
140-
gap: 18px;
141-
position: relative;
142-
}
143-
144-
nav.doc-nav::after {
145-
content: "";
146-
position: absolute;
147-
inset: 12px 16px 12px auto;
148-
width: 4px;
149-
border-radius: 999px;
150-
background: linear-gradient(180deg, transparent, var(--accent), transparent);
151-
opacity: 0.28;
152-
}
153-
154-
nav.doc-nav h2 {
155-
margin: 0;
156-
font-size: 0.95rem;
157-
text-transform: uppercase;
158-
letter-spacing: 0.12em;
159-
color: var(--text-muted);
141+
gap: 20px;
142+
min-height: 420px;
160143
}
161144

162-
nav.doc-nav ul {
163-
list-style: none;
164-
margin: 0;
165-
padding: 0;
145+
.doc-controls {
166146
display: flex;
167-
flex-direction: column;
168-
gap: 8px;
169-
}
170-
171-
nav.doc-nav a {
172-
display: grid;
173-
grid-template-columns: 1fr auto;
174147
align-items: center;
148+
justify-content: space-between;
175149
gap: 12px;
176-
padding: 12px 16px;
177-
border-radius: 14px;
178-
text-decoration: none;
179-
color: inherit;
180-
background: transparent;
181-
font-weight: 600;
182-
transition: background 0.18s ease, transform 0.18s ease, color 0.18s ease;
183-
position: relative;
184-
overflow: hidden;
150+
flex-wrap: wrap;
185151
}
186152

187-
nav.doc-nav a::before {
188-
content: "";
189-
position: absolute;
190-
inset: 0;
191-
background: var(--accent-soft);
192-
opacity: 0;
193-
transition: opacity 0.18s ease;
194-
pointer-events: none;
153+
.doc-progress {
154+
color: var(--text-muted);
155+
font-size: 0.9rem;
156+
flex: 1 1 180px;
157+
text-align: center;
195158
}
196159

197-
nav.doc-nav a span.badge {
198-
justify-self: end;
199-
padding: 4px 10px;
160+
.doc-nav-button {
161+
display: inline-flex;
162+
align-items: center;
163+
justify-content: center;
164+
gap: 8px;
165+
padding: 10px 16px;
200166
border-radius: 999px;
167+
border: 1px solid var(--border-color);
201168
background: rgba(79, 70, 229, 0.12);
202169
color: var(--accent);
203-
font-size: 0.75rem;
204170
font-weight: 600;
205-
letter-spacing: 0.04em;
206-
}
207-
208-
nav.doc-nav a:hover {
209-
transform: translateX(4px);
210-
color: var(--accent);
171+
letter-spacing: 0.01em;
172+
cursor: pointer;
173+
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease;
211174
}
212175

213-
nav.doc-nav a:hover::before {
214-
opacity: 1;
176+
.doc-nav-button:hover {
177+
transform: translateY(-1px);
178+
box-shadow: 0 12px 24px rgba(79, 70, 229, 0.18);
179+
background: rgba(79, 70, 229, 0.18);
215180
}
216181

217-
nav.doc-nav a:focus-visible {
182+
.doc-nav-button:focus-visible {
218183
outline: none;
219-
color: var(--accent);
220-
box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.35);
221-
}
222-
223-
nav.doc-nav a.is-active {
224-
color: var(--accent);
225-
}
226-
227-
nav.doc-nav a.is-active::before {
228-
opacity: 1;
184+
box-shadow: 0 0 0 3px rgba(129, 140, 248, 0.45);
229185
}
230186

231-
section.doc-content {
232-
background: var(--panel-surface);
233-
border-radius: 24px;
234-
border: 1px solid var(--border-color);
235-
padding: clamp(20px, 3vw, 32px);
236-
box-shadow: 0 26px 60px var(--shadow-color);
237-
display: flex;
238-
flex-direction: column;
239-
gap: 20px;
240-
min-height: 420px;
187+
.doc-nav-button:disabled {
188+
opacity: 0.55;
189+
cursor: not-allowed;
190+
transform: none;
191+
box-shadow: none;
192+
background: rgba(148, 163, 209, 0.18);
193+
color: var(--text-muted);
241194
}
242195

243196
header.content-meta {
@@ -350,16 +303,9 @@
350303
border: none;
351304
}
352305

353-
main.doc-main {
354-
grid-template-columns: 1fr;
355-
}
356-
357-
nav.doc-nav {
358-
order: 2;
359-
}
360-
361-
section.doc-content {
362-
order: 1;
306+
.doc-progress {
307+
order: 3;
308+
flex-basis: 100%;
363309
}
364310
}
365311

@@ -377,10 +323,6 @@
377323
align-self: stretch;
378324
justify-content: center;
379325
}
380-
381-
nav.doc-nav {
382-
padding: 16px;
383-
}
384326
}
385327
</style>
386328
</head>
@@ -394,32 +336,35 @@ <h1>Toolbox knowledge base</h1>
394336
<a class="home-link" href="/" aria-label="Return to the toolbox home">⌂ Home</a>
395337
</header>
396338
<main class="doc-main">
397-
<nav class="doc-nav" aria-label="Documentation navigation">
398-
<h2>Guides</h2>
399-
<ul data-doc-nav></ul>
400-
</nav>
401339
<section class="doc-content" aria-live="polite">
340+
<div class="doc-controls">
341+
<button type="button" class="doc-nav-button" data-doc-prev aria-label="View previous guide">← Previous</button>
342+
<p class="doc-progress" data-doc-progress aria-live="polite"></p>
343+
<button type="button" class="doc-nav-button" data-doc-next aria-label="View next guide">Next →</button>
344+
</div>
402345
<header class="content-meta">
403346
<h2 data-doc-title>Loading…</h2>
404347
<p data-doc-updated hidden></p>
405348
</header>
406349
<p class="doc-status" data-doc-status></p>
407350
<article class="markdown-body" data-doc-content>
408-
<p>Select a guide from the navigation to load documentation.</p>
351+
<p>Use the buttons above to browse the documentation guides.</p>
409352
</article>
410353
</section>
411354
</main>
412355
</div>
413356
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
414357
<script>
415358
(function () {
416-
const docListEl = document.querySelector('[data-doc-nav]');
359+
const prevButton = document.querySelector('[data-doc-prev]');
360+
const nextButton = document.querySelector('[data-doc-next]');
361+
const progressEl = document.querySelector('[data-doc-progress]');
417362
const contentEl = document.querySelector('[data-doc-content]');
418363
const titleEl = document.querySelector('[data-doc-title]');
419364
const updatedEl = document.querySelector('[data-doc-updated]');
420365
const statusEl = document.querySelector('[data-doc-status]');
421366

422-
if (!docListEl || !contentEl || !titleEl || !updatedEl || !statusEl) {
367+
if (!prevButton || !nextButton || !progressEl || !contentEl || !titleEl || !updatedEl || !statusEl) {
423368
return;
424369
}
425370

@@ -435,44 +380,46 @@ <h2 data-doc-title>Loading…</h2>
435380
];
436381

437382
const guideMap = new Map(guides.map((guide) => [guide.id, guide]));
383+
let activeGuideId = '';
384+
385+
function updatePager(id) {
386+
const index = guides.findIndex((guide) => guide.id === id);
387+
const total = guides.length;
388+
if (index === -1) {
389+
prevButton.disabled = true;
390+
nextButton.disabled = true;
391+
prevButton.textContent = '← Previous';
392+
nextButton.textContent = 'Next →';
393+
prevButton.setAttribute('aria-label', 'Previous guide unavailable');
394+
nextButton.setAttribute('aria-label', 'Next guide unavailable');
395+
progressEl.textContent = total > 0 ? `0 of ${total}` : '';
396+
return;
397+
}
438398

439-
function renderNav() {
440-
docListEl.innerHTML = '';
441-
guides.forEach((guide) => {
442-
const item = document.createElement('li');
443-
const link = document.createElement('a');
444-
link.href = `#${guide.id}`;
445-
link.dataset.docId = guide.id;
446-
link.textContent = guide.title;
447-
448-
if (guide.summary) {
449-
const badge = document.createElement('span');
450-
badge.className = 'badge';
451-
badge.textContent = guide.summary;
452-
link.appendChild(badge);
453-
}
399+
const prevGuide = guides[index - 1];
400+
const nextGuide = guides[index + 1];
454401

455-
link.addEventListener('click', (event) => {
456-
event.preventDefault();
457-
loadGuide(guide.id, { pushState: true });
458-
});
402+
if (prevGuide) {
403+
prevButton.disabled = false;
404+
prevButton.textContent = `← ${prevGuide.title}`;
405+
prevButton.setAttribute('aria-label', `View previous guide: ${prevGuide.title}`);
406+
} else {
407+
prevButton.disabled = true;
408+
prevButton.textContent = '← Previous';
409+
prevButton.setAttribute('aria-label', 'No previous guide');
410+
}
459411

460-
item.appendChild(link);
461-
docListEl.appendChild(item);
462-
});
463-
}
412+
if (nextGuide) {
413+
nextButton.disabled = false;
414+
nextButton.textContent = `${nextGuide.title} →`;
415+
nextButton.setAttribute('aria-label', `View next guide: ${nextGuide.title}`);
416+
} else {
417+
nextButton.disabled = true;
418+
nextButton.textContent = 'Next →';
419+
nextButton.setAttribute('aria-label', 'No next guide');
420+
}
464421

465-
function setActive(id) {
466-
const links = docListEl.querySelectorAll('a[data-doc-id]');
467-
links.forEach((link) => {
468-
const isActive = link.dataset.docId === id;
469-
link.classList.toggle('is-active', isActive);
470-
if (isActive) {
471-
link.setAttribute('aria-current', 'page');
472-
} else {
473-
link.removeAttribute('aria-current');
474-
}
475-
});
422+
progressEl.textContent = `Guide ${index + 1} of ${total}`;
476423
}
477424

478425
function formatUpdated(text) {
@@ -496,6 +443,9 @@ <h2 data-doc-title>Loading…</h2>
496443
return;
497444
}
498445

446+
activeGuideId = guide.id;
447+
updatePager(activeGuideId);
448+
499449
if (options.pushState) {
500450
window.history.pushState({ docId: guide.id }, '', `#${guide.id}`);
501451
} else if (window.location.hash.replace('#', '') !== guide.id) {
@@ -519,9 +469,10 @@ <h2 data-doc-title>Loading…</h2>
519469

520470
contentEl.innerHTML = parsed;
521471
titleEl.textContent = guide.title;
522-
setActive(guide.id);
523472
statusEl.textContent = '';
524473

474+
updatePager(guide.id);
475+
525476
const lastModified = response.headers.get('last-modified');
526477
const fallback = formatUpdated(lastModified);
527478
if (fallback) {
@@ -537,7 +488,7 @@ <h2 data-doc-title>Loading…</h2>
537488
contentEl.innerHTML = '<p>Failed to load the requested guide. Please try again.</p>';
538489
titleEl.textContent = guide.title;
539490
statusEl.textContent = 'An error occurred while loading the guide.';
540-
setActive(guide.id);
491+
updatePager(guide.id);
541492
}
542493
}
543494

@@ -565,10 +516,25 @@ <h2 data-doc-title>Loading…</h2>
565516
}
566517
});
567518

568-
renderNav();
519+
prevButton.addEventListener('click', () => {
520+
const index = guides.findIndex((guide) => guide.id === activeGuideId);
521+
if (index > 0) {
522+
loadGuide(guides[index - 1].id, { pushState: true });
523+
}
524+
});
525+
526+
nextButton.addEventListener('click', () => {
527+
const index = guides.findIndex((guide) => guide.id === activeGuideId);
528+
if (index !== -1 && index < guides.length - 1) {
529+
loadGuide(guides[index + 1].id, { pushState: true });
530+
}
531+
});
532+
569533
const initialId = getInitialGuideId();
570534
if (initialId) {
571535
loadGuide(initialId, { pushState: false });
536+
} else if (guides[0]) {
537+
loadGuide(guides[0].id, { pushState: false });
572538
}
573539
})();
574540
</script>

0 commit comments

Comments
 (0)