Conversation
- Add LANG_DISPLAY and SITE_FOOTER_LABELS constants for 14 languages - Add generateArticleLanguageSwitcher() and generateSiteFooter() helpers - Update template to include language switcher at top of article - Add "Back to News" link at top (article-top-nav) - Add full site footer matching index.html structure - Add CSS styles for language switcher and top navigation - Add RTL support and responsive styles - Add 21 new tests (129 total in article-template.test.ts) - All 1816 tests pass Co-authored-by: pethers <[email protected]>
Co-authored-by: pethers <[email protected]>
Co-authored-by: pethers <[email protected]>
These generated files should not be versioned. Added to .gitignore. Co-authored-by: pethers <[email protected]>
Signed-off-by: James Pether Sörling <[email protected]>
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
There was a problem hiding this comment.
Pull request overview
This pull request enhances the article template to include consistent site-wide navigation and footer elements that were previously missing from generated articles. Specifically, it adds a language switcher navigation bar, a top "Back to News" link, and a complete site footer matching the structure of index.html. The changes also include proper CSS styling with RTL support and responsive design, plus comprehensive test coverage.
Changes:
- Added language switcher with all 14 supported languages at the top of articles
- Added top "Back to News" navigation for immediate access without scrolling
- Generated complete site footer matching index.html structure with localized content
- Added CSS styles for language switcher, top nav, and RTL support with responsive breakpoints
- Added 21 comprehensive tests covering new functionality
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/article-template.test.ts | Adds 21 comprehensive tests for language switcher, top navigation, site footer, and new exported functions |
| styles.css | Adds CSS for language switcher, article top nav, RTL support, and responsive breakpoints |
| scripts/article-template/template.ts | Integrates language switcher, top nav, and site footer into article HTML generation |
| scripts/article-template/index.ts | Exports new functions generateArticleLanguageSwitcher and generateSiteFooter |
| scripts/article-template/helpers.ts | Implements generateArticleLanguageSwitcher and generateSiteFooter helper functions |
| scripts/article-template/constants.ts | Adds LANG_DISPLAY and SITE_FOOTER_LABELS constants with LangDisplay interface |
| scripts/article-template.ts | Updates barrel exports to include new functions |
| .language-switcher .lang-link:hover, | ||
| .language-switcher .lang-link:focus-visible { | ||
| background: var(--primary-color, #006633); | ||
| color: #fff; | ||
| outline: none; | ||
| } | ||
|
|
||
| .language-switcher .lang-link:focus-visible { | ||
| outline: 2px solid var(--primary-color, #006633); | ||
| outline-offset: 2px; | ||
| } |
There was a problem hiding this comment.
Lines 9537-9540 set outline: none on .language-switcher .lang-link:focus-visible, but then lines 9543-9546 set an outline for the same selector. The first rule makes the second rule redundant and could cause confusion. Consider removing outline: none from lines 9537-9540 since line 9544 explicitly sets the outline for :focus-visible state.
There was a problem hiding this comment.
Removed the redundant outline: none from the hover/focus-visible combined rule. The dedicated :focus-visible rule now solely handles the outline. Fixed in f443dc5.
scripts/article-template/helpers.ts
Outdated
| * Generate the full site footer matching index.html structure. | ||
| * | ||
| * @param lang - The current language | ||
| * @param newsIndexFilename - The news index filename for the language |
There was a problem hiding this comment.
The JSDoc comment mentions a newsIndexFilename parameter that doesn't exist in the function signature. The function only takes lang as a parameter and internally calls getNewsIndexFilename(lang) on line 173. Remove the incorrect parameter documentation.
| * @param newsIndexFilename - The news index filename for the language |
There was a problem hiding this comment.
Removed the incorrect newsIndexFilename parameter from the JSDoc. Fixed in f443dc5.
scripts/article-template/helpers.ts
Outdated
| * All 14 supported language codes in display order. | ||
| */ | ||
| const ALL_LANG_CODES: readonly Language[] = ['en', 'sv', 'da', 'no', 'fi', 'de', 'fr', 'es', 'nl', 'ar', 'he', 'ja', 'ko', 'zh']; |
There was a problem hiding this comment.
The constant ALL_LANG_CODES is duplicated in both this file and in template.ts (line 63). Consider extracting this to a shared constant in constants.ts to maintain a single source of truth and avoid inconsistencies if the language list changes.
| * All 14 supported language codes in display order. | |
| */ | |
| const ALL_LANG_CODES: readonly Language[] = ['en', 'sv', 'da', 'no', 'fi', 'de', 'fr', 'es', 'nl', 'ar', 'he', 'ja', 'ko', 'zh']; | |
| * All supported language codes in display order, derived from LANG_DISPLAY | |
| * to ensure a single source of truth for language configuration. | |
| */ | |
| const ALL_LANG_CODES: readonly Language[] = Object.keys(LANG_DISPLAY) as Language[]; |
There was a problem hiding this comment.
Extracted ALL_LANG_CODES to constants.ts as a shared constant derived from LANG_DISPLAY keys. Both helpers.ts and template.ts now import from the single source of truth. Fixed in f443dc5.
| <div class="footer-section"> | ||
| <h3>${labels.quickLinks}</h3> | ||
| <ul> | ||
| <li><a href="${homePath}">Home</a></li> | ||
| <li><a href="${newsPath}">News</a></li> | ||
| <li><a href="https://www.hack23.com/cia" target="_blank" rel="noopener noreferrer">CIA Platform</a></li> | ||
| <li><a href="https://github.com/Hack23/riksdagsmonitor" target="_blank" rel="noopener noreferrer">GitHub Repository</a></li> | ||
| <li><a href="https://www.riksdagen.se" target="_blank" rel="noopener noreferrer">Sveriges Riksdag</a></li> | ||
| </ul> | ||
| </div> | ||
| <div class="footer-section"> | ||
| <h3>${labels.builtBy}</h3> | ||
| <p>${labels.builtByText}</p> | ||
| <ul> | ||
| <li><a href="https://www.hack23.com" target="_blank" rel="noopener noreferrer">Hack23.com</a></li> | ||
| <li><a href="https://github.com/Hack23/ISMS-PUBLIC" target="_blank" rel="noopener noreferrer">Public ISMS</a></li> | ||
| <li><a href="https://github.com/Hack23/ISMS-PUBLIC/blob/main/Privacy_Policy.md" target="_blank" rel="noopener noreferrer">Privacy Policy</a></li> | ||
| </ul> |
There was a problem hiding this comment.
The generated footer is missing several links present in the main index.html footer (lines 1184-1202):
- Dashboard link (line 1186)
- Company LinkedIn link (line 1198)
- Security Policy link (line 1200)
- Contact Us email link (line 1202)
For consistency across the site, the article footer should include all the same navigation options as the main site footer.
There was a problem hiding this comment.
Added all missing footer links to match index.html: Dashboard (../dashboard/index.html), Company LinkedIn, Security Policy, and Contact Us (mailto:[email protected]). Fixed in f443dc5.
| ${ALL_LANG_CODES.map(l => { | ||
| const display = LANG_DISPLAY[l]; | ||
| const href: string = l === 'en' ? '../index.html' : `../index_${l}.html`; | ||
| return ` <a href="${href}" title="${display.name}"><span aria-hidden="true">${display.flag}</span> ${l.toUpperCase()}</a>`; | ||
| }).join('\n')} |
There was a problem hiding this comment.
The language links in the footer are missing aria-label attributes that are present in the main index.html footer (lines 1209-1222). These attributes provide localized labels for screen readers (e.g., "Switch to English", "Byt till Svenska"). For accessibility consistency, add these aria-label attributes to improve the screen reader experience.
There was a problem hiding this comment.
Added LANG_ARIA_LABELS constant with localized labels (e.g. "Switch to English", "Byt till Svenska") and wired them into the footer language grid links via aria-label attributes. Fixed in f443dc5.
|
@copilot apply changes based on the comments in this thread |
…d missing footer links and aria-labels - Remove redundant outline:none on .lang-link:focus-visible - Remove incorrect newsIndexFilename JSDoc param from generateSiteFooter - Extract ALL_LANG_CODES to constants.ts (single source of truth) - Add Dashboard, Company LinkedIn, Security Policy, Contact Us links to footer - Add LANG_ARIA_LABELS constant and aria-label attributes on language grid links - Add 2 new tests for footer links and aria-labels (1818 total pass) Co-authored-by: pethers <[email protected]>
All 5 review comments addressed in f443dc5:
All 1818 tests pass. |
| <div class="article-top-nav"> | ||
| <a href="${getNewsIndexFilename(lang)}" class="back-to-news"> | ||
| \u2190 ${getFooterLabel(lang, 'backToNews')} | ||
| </a> |
There was a problem hiding this comment.
Top “Back to News” link uses getNewsIndexFilename(lang) directly. When lang is an unknown value (tests pass 'xx' elsewhere), this produces index_xx.html and a broken link. Consider using the same “fallback to English” normalization here (and/or inside getNewsIndexFilename) so the top nav doesn’t generate non-existent index pages.
There was a problem hiding this comment.
Added the same ALL_LANG_CODES.includes() normalization to the top nav's getNewsIndexFilename() call in template.ts, so unknown languages fall back to the English news index. Fixed in 383044f.
scripts/article-template/helpers.ts
Outdated
| const labels = SITE_FOOTER_LABELS[lang as Language] || SITE_FOOTER_LABELS.en; | ||
| const homePath: string = lang === 'en' ? '../index.html' : `../index_${lang}.html`; | ||
| const newsPath: string = getNewsIndexFilename(lang); |
There was a problem hiding this comment.
generateSiteFooter() falls back to English labels for unknown languages, but still builds homePath/newsPath from the unknown lang value (e.g. ../index_xx.html, index_xx.html), which creates broken internal links and is inconsistent with the documented fallback behavior (see test “should fall back to English for unknown language”). Normalize lang to a supported Language (or 'en') before deriving paths / calling getNewsIndexFilename().
| const labels = SITE_FOOTER_LABELS[lang as Language] || SITE_FOOTER_LABELS.en; | |
| const homePath: string = lang === 'en' ? '../index.html' : `../index_${lang}.html`; | |
| const newsPath: string = getNewsIndexFilename(lang); | |
| const normalizedLang: Language = ALL_LANG_CODES.includes(lang as Language) | |
| ? (lang as Language) | |
| : 'en'; | |
| const labels = SITE_FOOTER_LABELS[normalizedLang]; | |
| const homePath: string = normalizedLang === 'en' ? '../index.html' : `../index_${normalizedLang}.html`; | |
| const newsPath: string = getNewsIndexFilename(normalizedLang); |
There was a problem hiding this comment.
Applied the suggested normalization — generateSiteFooter() now normalizes lang via ALL_LANG_CODES.includes() before deriving homePath/newsPath, so unknown languages fall back to English paths instead of producing broken index_xx.html links. Enhanced the existing fallback test to assert not.toContain('index_xx.html'). Fixed in 383044f.
|
@copilot apply changes based on the comments in this thread |
- generateSiteFooter: normalize lang via ALL_LANG_CODES.includes() before deriving homePath/newsPath, preventing broken index_xx.html links - template.ts top nav: same normalization for Back to News link - Enhanced fallback test to assert no index_xx.html paths are generated Co-authored-by: pethers <[email protected]>
Both review comments addressed in 383044f — unknown languages now normalize to English for all path derivation in both the top nav and site footer. All 1818 tests pass. |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
| const active: string = l === currentLang ? ' active' : ''; | ||
| return ` <a href="${baseSlug}-${l}.html" class="lang-link${active}" hreflang="${l}">${display.flag} ${display.name}</a>`; | ||
| }).join('\n'); |
There was a problem hiding this comment.
The active language is only indicated via a CSS class (.active). For better accessibility, add an aria-current attribute (e.g., aria-current="page") on the active language link so assistive technologies can reliably identify the current language/version.
There was a problem hiding this comment.
Added aria-current="page" on the active language link in the switcher. Fixed in 29dcda6.
| ${generateSiteFooter(lang)} | ||
|
|
There was a problem hiding this comment.
While the new top nav normalizes unknown lang values to English for its getNewsIndexFilename() call, the existing “Back to News” link inside the article footer still uses getNewsIndexFilename(lang) without normalization. This means pages rendered with an unknown lang can still output a broken index_xx.html link at the bottom of the article. Consider normalizing lang once (or inside getNewsIndexFilename) and reusing it for both back-to-news links to keep behavior consistent.
There was a problem hiding this comment.
Normalized the bottom footer back-to-news link with the same ALL_LANG_CODES.includes() guard used in the top nav. Fixed in 29dcda6.
scripts/article-template/helpers.ts
Outdated
| const active: string = l === currentLang ? ' active' : ''; | ||
| return ` <a href="${baseSlug}-${l}.html" class="lang-link${active}" hreflang="${l}">${display.flag} ${display.name}</a>`; | ||
| }).join('\n'); | ||
| return ` <nav class="language-switcher" role="navigation" aria-label="Language versions">\n${links}\n </nav>`; |
There was a problem hiding this comment.
generateArticleLanguageSwitcher() hardcodes aria-label="Language versions" in English. On non-English pages (including RTL), screen readers will announce this landmark in the wrong language; elsewhere (e.g., index_sv.html back-to-top) aria-labels are localized. Consider moving this label into a per-language translation table (similar to FOOTER_LABELS / SITE_FOOTER_LABELS) and using the current language (with EN fallback) when rendering the nav’s aria-label.
There was a problem hiding this comment.
Added LANG_SWITCHER_ARIA_LABELS constant with per-language translations (e.g. "Språkversioner" for Swedish, "إصدارات اللغة" for Arabic) and wired it into the nav's aria-label with EN fallback. Fixed in 29dcda6.
|
@copilot apply changes based on the comments in this thread |
…ize bottom back-to-news - Add aria-current="page" on active language link in switcher - Add LANG_SWITCHER_ARIA_LABELS constant for localized nav aria-label - Normalize lang in bottom footer back-to-news link (same as top nav) - Add 2 new tests for aria-current and localized aria-label Co-authored-by: pethers <[email protected]>
All 3 review comments addressed in 29dcda6:
All 1820 tests pass. |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
| <li><a href="${homePath}">Home</a></li> | ||
| <li><a href="${newsPath}">News</a></li> | ||
| <li><a href="../dashboard/index.html">Dashboard</a></li> | ||
| <li><a href="https://www.hack23.com/cia" target="_blank" rel="noopener noreferrer">CIA Platform</a></li> | ||
| <li><a href="https://github.com/Hack23/riksdagsmonitor" target="_blank" rel="noopener noreferrer">GitHub Repository</a></li> |
There was a problem hiding this comment.
The Dashboard link in the generated footer always points to ../dashboard/index.html. The site has per-language dashboard pages (dashboard/index_sv.html, dashboard/index_fr.html, etc.), and the localized homepages link to those language-specific dashboards. Build the dashboard href from normalizedLang (EN -> index.html, others -> index_{lang}.html) so article pages keep users in the same language.
There was a problem hiding this comment.
Dashboard link now uses normalizedLang to build per-language paths (../dashboard/index_sv.html, etc.) and the link text is localized via labels.dashboard (e.g., "Instrumentpanel", "Tableau de bord"). Fixed in c209bfc.
scripts/article-template/helpers.ts
Outdated
| </div> | ||
| </div> | ||
| <div class="footer-bottom"> | ||
| <p>© 2008-<time datetime="2026">2026</time> <a href="https://www.hack23.com" target="_blank" rel="noopener noreferrer">Hack23 AB</a> (Org.nr 5595347807) | Gothenburg, Sweden</p> |
There was a problem hiding this comment.
The footer bottom line is currently hardcoded to "Gothenburg, Sweden" for all languages. In localized homepages (e.g., index_sv.html, index_fr.html) this location text is localized ("Göteborg, Sverige", "Göteborg, Suède"), so article pages will have inconsistent/mixed-language footer content. Consider localizing this string via the same footer label translations used for headings.
| <p>© 2008-<time datetime="2026">2026</time> <a href="https://www.hack23.com" target="_blank" rel="noopener noreferrer">Hack23 AB</a> (Org.nr 5595347807) | Gothenburg, Sweden</p> | |
| <p>© 2008-<time datetime="2026">2026</time> <a href="https://www.hack23.com" target="_blank" rel="noopener noreferrer">Hack23 AB</a> (Org.nr 5595347807) | ${labels.location}</p> |
There was a problem hiding this comment.
Added location field to SITE_FOOTER_LABELS for all 14 languages (e.g., "Göteborg, Sverige", "ヨーテボリ(スウェーデン)", "瑞典哥德堡") and the footer bottom now renders ${labels.location}. Fixed in c209bfc.
| <ul class="footer-stats"> | ||
| <li><strong>349 MPs</strong> tracked</li> | ||
| <li><strong>45 risk rules</strong> active</li> | ||
| <li><strong>14 languages</strong> supported</li> | ||
| <li><strong>50+ years</strong> historical data</li> | ||
| </ul> |
There was a problem hiding this comment.
generateSiteFooter() hardcodes the footer stats text in English (e.g., "349 MPs tracked", "45 risk rules active"). The localized site footers (e.g., index_sv.html, index_fr.html) translate these list items, so articles in non-English languages will show a mixed-language footer. Consider moving these stat strings into the per-language translation tables (similar to SITE_FOOTER_LABELS) and rendering them from normalizedLang.
There was a problem hiding this comment.
Added statMPs, statRiskRules, statLanguages, and statHistoricalData fields to SITE_FOOTER_LABELS with translations sourced from each index_{lang}.html footer. The stats HTML now renders from labels.statMPs etc. Fixed in c209bfc.
|
@copilot apply changes based on the comments in this thread |
…languages - Dashboard link now uses per-language paths (e.g., dashboard/index_sv.html) - Dashboard link text localized (e.g., Instrumentpanel, Tableau de bord) - Footer location localized (e.g., Göteborg, Sverige; 瑞典哥德堡) - Footer stats fully localized for all 14 languages (MPs, risk rules, etc.) - Added dashboard, location, and stat fields to SITE_FOOTER_LABELS type - Added 3 new tests for dashboard, location, and stats localization Co-authored-by: pethers <[email protected]>
All 3 review comments addressed in c209bfc:
All 1823 tests pass. |
🔍 Lighthouse Performance Audit
📥 Download full Lighthouse report Budget Compliance: Performance budgets enforced via |
outline: noneon focus-visiblenewsIndexFilenameparamALL_LANG_CODESto shared constant in constants.tsaria-labelattributes to language grid links in footeraria-current="page"on active language linkaria-labelper language✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.