Where JS lives
| File | Loaded | Purpose |
|---|---|---|
Inline in baseof.html <script> | every page | Global functions (theme, menu) |
static/js/article.js | article pages via _default/single.html scripts block | Reading progress bar, ToC, copy buttons, image lightbox |
static/js/pagefind-search.js | /posts/ and /tags/ | Pagefind search UI |
static/js/taxonomy.js | /tags/ (after inline POSTS[] data) | Tag filter logic + article grid |
Inline in certs/single.html {{ block "scripts" }} | /certs/* | Accordion toggle |
static/js/ns.js | /posts/linux-namespaces/ | Namespace explorer (data + render) |
baseof.html — global functions
toggleTheme()
function toggleTheme()
Called by: onclick on #themeBtn (desktop) and #themeBtnMob (mobile).
What it does:
- Reads the current
data-themeattribute from<html> - Switches to the opposite (
dark↔light) - Saves to
localStorage.theme - Updates the icon on all
#themeBtn,#themeBtnMobbuttons
CSS link: document.documentElement.setAttribute('data-theme', next) triggers CSS variables [data-theme="light"].
setLang(lang)
function setLang(lang)
// Example call: setLang('ru')
Called by: onclick on .lang-btn buttons in desktop nav and mobile drawer.
What it does: if the page is bilingual (has <meta id="page-lang">) and the selected language differs from the current one — redirects to the data-lang-pair URL. On non-bilingual pages does nothing.
toggleMobMenu()
function toggleMobMenu()
Called by: onclick on #burgerBtn.
What it does: toggles .open class on three elements simultaneously:
#mobDrawer— slides out the drawer#mobOverlay— shows the backdrop#burgerBtn— animates burger into an X
closeMobMenu()
function closeMobMenu()
Called by: onclick on #mobOverlay (clicking the overlay closes the menu).
What it does: removes .open from #mobDrawer, #mobOverlay, #burgerBtn.
applyTheme(theme)
function applyTheme(theme)
Single source of truth for theme state. Called on page load and by toggleTheme().
What it does:
- Sets
data-themeattribute on<html> - Updates icon on all
#themeBtn,#themeBtnMob - Sets
aria-pressedon theme buttons
Called at end of <body> as: applyTheme(localStorage.getItem('theme') || 'dark')
article.js — article page functions
Path: static/js/article.js — loaded on all single article pages.
Reading progress bar
Uses #readingBar — a CSS-styled <div id="readingBar"> injected at page load. Width updated on scroll event: scrollY / (scrollHeight - viewportHeight) * 100%.
ToC builder
Finds all h2, h3 inside #articleBody. Only activates if more than 2 headings exist. Inserts links into #tocAside, adds .has-toc to .page at width ≥ 860px, highlights active heading via IntersectionObserver.
Copy buttons
For each <pre> not inside .code-block, wraps it in .code-block and adds a copy button. The {{< code >}} shortcode handles its own copy button separately.
Code toggle
Activated via document.body.dataset.codeToggle === 'true'. Set in the page scripts block when code_toggle: true in frontmatter.
pagefind-search.js — Pagefind search UI
Path: static/js/pagefind-search.js — loaded on /posts/ and /tags/.
Lazy-loads /pagefind/pagefind.js on first keypress. Renders results into #searchResults (CSS-positioned below .search-wrap). Closes on click outside .search-wrap.
taxonomy.js — tag filter logic
Path: static/js/taxonomy.js — loaded on /tags/ after an inline <script> that defines POSTS[] and currentTag.
Reads POSTS[] (Hugo-generated, EN only) and currentTag (current tag slug from Hugo) from the page. Builds the tag grid, filters articles, manages the active pill UI.
Data contract (set inline in tag.html before this script):
const POSTS = [{ url, title, date, tags, tagLabels, summary }, ...];
const currentTag = "linux"; // or "" if on /tags/ root
certs/single.html — accordion
function toggleTopic(btn) {
const topic = btn.closest('.cert-topic');
const body = topic.querySelector('.cert-topic-body');
if (!body) return; // topics without posts don't expand
const open = topic.classList.toggle('open');
body.style.maxHeight = open ? body.scrollHeight + 'px' : '0';
}
Animation via max-height: CSS animation max-height: 0 → scrollHeight. scrollHeight = actual content height.
Why not height: auto? CSS cannot animate auto. max-height is the standard workaround.
ns.js — Namespace Explorer
Path: static/js/ns.js
Loaded: only on /posts/linux-namespaces/ via <script src="{{ "js/ns.js" | relURL }}">
Contains:
nsData— array of objects with data for all namespaces (name, flag, icon, color, summary, desc…)cheatData— array of commands for the cheatsheet table- Render functions:
buildNsGrid(),buildNsMap(),buildCheatTable() nsToggleCard(el)— expands/collapses a namespace cardIntersectionObserver— progress bar (how many namespaces have been read)buildToc()— builds the table of contents in aside