From ca373ae8ceeb78d685d5adb50539414a0dd6427b Mon Sep 17 00:00:00 2001 From: Nikos Date: Mon, 11 Aug 2025 10:22:38 +0300 Subject: [PATCH 1/4] Center nav and routes within 75rem width --- css/common.css | 7 +++++++ js/components/nav.js | 3 +++ 2 files changed, 10 insertions(+) diff --git a/css/common.css b/css/common.css index eb2c832..8d38097 100644 --- a/css/common.css +++ b/css/common.css @@ -11,6 +11,7 @@ Version: 0.1: added line-height 1.5 for readability and spacing between labels a --p-sm: 0.5rem; --p-md: 1rem; --sm: 600px; + --max-content-width: 75rem; } body { @@ -26,6 +27,12 @@ body { color 0.5s; } +main { + max-width: var(--max-content-width); + margin: 0 auto; + width: 100%; +} + footer { bottom: 0; width: 100%; diff --git a/js/components/nav.js b/js/components/nav.js index 3d537c0..f3e4f61 100644 --- a/js/components/nav.js +++ b/js/components/nav.js @@ -44,6 +44,9 @@ export default (hostComponent) => { flex-direction: column; gap: 0.4rem; padding: 10px 10px; + max-width: var(--max-content-width); + margin: 0 auto; + width: 100%; background-color: var(--nav-background-color); z-index: 10; a { From 4378548db1e7a544cb8b62bfde38c1c3b0b36bbe Mon Sep 17 00:00:00 2001 From: Nikos Date: Mon, 11 Aug 2025 10:41:13 +0300 Subject: [PATCH 2/4] Add developers route with code viewer --- js/components/code-viewer.js | 58 +++++++++++++++++++++++++++++++ js/components/code-viewer.test.js | 42 ++++++++++++++++++++++ js/components/nav.js | 4 +++ js/routes/developers.js | 14 ++++++++ 4 files changed, 118 insertions(+) create mode 100644 js/components/code-viewer.js create mode 100644 js/components/code-viewer.test.js create mode 100644 js/routes/developers.js diff --git a/js/components/code-viewer.js b/js/components/code-viewer.js new file mode 100644 index 0000000..7a7f676 --- /dev/null +++ b/js/components/code-viewer.js @@ -0,0 +1,58 @@ +// File: js/components/code-viewer.js +// Component for displaying code snippets in multiple languages with a selector + +export default (hostComponent) => { + const pres = Array.from(hostComponent.querySelectorAll('pre[data-lang]')); + if (pres.length === 0) return; + + const selector = document.createElement('select'); + pres.forEach((pre, index) => { + const { lang } = pre.dataset; + const code = pre.querySelector('code'); + if (code) { + code.classList.add(`language-${lang}`); + } + const option = document.createElement('option'); + option.value = lang; + option.textContent = lang; + selector.appendChild(option); + pre.style.display = index === 0 ? '' : 'none'; + }); + + selector.addEventListener('change', (event) => { + const lang = event.target.value; + pres.forEach((pre) => { + pre.style.display = pre.dataset.lang === lang ? '' : 'none'; + }); + }); + + hostComponent.insertBefore(selector, hostComponent.firstChild); + + const ensureHighlight = () => + new Promise((resolve) => { + if (window.hljs) { + resolve(); + return; + } + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = + 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/default.min.css'; + document.head.appendChild(link); + const script = document.createElement('script'); + script.src = + 'https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js'; + script.onload = resolve; + document.head.appendChild(script); + }); + + ensureHighlight().then(() => { + pres.forEach((pre) => { + const code = pre.querySelector('code'); + if (code && window.hljs?.highlightElement) { + window.hljs.highlightElement(code); + } + }); + }); +}; + diff --git a/js/components/code-viewer.test.js b/js/components/code-viewer.test.js new file mode 100644 index 0000000..123ea4b --- /dev/null +++ b/js/components/code-viewer.test.js @@ -0,0 +1,42 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import codeViewer from './code-viewer.js'; + +beforeEach(() => { + // Mock hljs to avoid loading external scripts during tests + vi.stubGlobal('hljs', { + highlightElement: vi.fn(), + }); +}); + +describe('code-viewer component', () => { + it('renders selector and shows only first snippet', () => { + const host = document.createElement('div'); + host.innerHTML = ` +
console.log('hi')
+
print('hi')
+ `; + codeViewer(host); + const select = host.querySelector('select'); + expect(select).toBeTruthy(); + const pres = host.querySelectorAll('pre[data-lang]'); + expect(pres[0].style.display).toBe(''); + expect(pres[1].style.display).toBe('none'); + }); + + it('switches visible snippet on selector change', () => { + const host = document.createElement('div'); + host.innerHTML = ` +
console.log('hi')
+
print('hi')
+ `; + codeViewer(host); + const select = host.querySelector('select'); + select.value = 'python'; + select.dispatchEvent(new Event('change')); + const jsPre = host.querySelector('pre[data-lang="js"]'); + const pyPre = host.querySelector('pre[data-lang="python"]'); + expect(jsPre.style.display).toBe('none'); + expect(pyPre.style.display).toBe(''); + }); +}); + diff --git a/js/components/nav.js b/js/components/nav.js index f3e4f61..7dcb624 100644 --- a/js/components/nav.js +++ b/js/components/nav.js @@ -194,6 +194,10 @@ export default (hostComponent) => { 🧮 Web GPU tutorial + + 💻 + Developers + -
+
diff --git a/js/components/nav.js b/js/components/nav.js index 7dcb624..278ca37 100644 --- a/js/components/nav.js +++ b/js/components/nav.js @@ -82,6 +82,12 @@ export default (hostComponent) => { } } } + nav.overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + } .burger-button { position: absolute; right: 0; diff --git a/js/components/router.js b/js/components/router.js index 6578cb7..90775e1 100644 --- a/js/components/router.js +++ b/js/components/router.js @@ -43,6 +43,7 @@ export default async (hostComponent, baseUrl = config.BASE_URL) => { const loadRoute = async (url) => { try { hostComponent.classList.remove('full-width'); + document.querySelector('nav')?.classList.remove('overlay'); const routePath = url === '/' || url === '' ? `${baseUrl}/routes/index.js` : `${baseUrl}/routes${url}.js`; diff --git a/js/routes/heros.js b/js/routes/heros.js index 577b018..e6700c1 100644 --- a/js/routes/heros.js +++ b/js/routes/heros.js @@ -1,5 +1,6 @@ export default (hostComponent) => { hostComponent.classList.add('full-width'); + document.querySelector('nav')?.classList.add('overlay'); // Define HTML structure with data attributes directly in the template const indexHTML = `