UI/UX Component Spec¶
Single point of reference for the theme's front-end. Every UI feature is catalogued with its purpose, source files, public API surface, and test coverage β so each component can be defined, tested, and improved from one place.
This document is generated from a full sweep of _sass/**, _includes/**, _layouts/**, assets/js/**, _plugins/**, and test/**. It covers 111 components across 8 clusters, plus the test harness.
How to use this doc¶
- Quick scan: the Component Index lists every component with its primary source file, test, and coverage status.
- Deep dive: each cluster section gives per-component Purpose / Capabilities / Source / API surface / Tests / Gaps.
- Test: the Testing Infrastructure section explains how to run each tier and lists the biggest coverage gaps.
- Improve: the Coverage Gaps & Roadmap section aggregates every "improvement idea" into one backlog.
Coverage legend: π’ good (behavior + a11y asserted) Β· π‘ partial (some assertions) Β· π΄ none (no automated test).
Architecture at a glance¶
The design framework is a layered SCSS + Liquid + ES-module system. assets/css/main.scss is the single stylesheet assembly manifest; _sass/custom.scss is a thin back-compat barrel.
Design tokens _sass/tokens/* --zer0-* CSS custom properties (color, spacing,
typography, shadow, motion, breakpoints, layers)
Theme layer _sass/theme/* color modes (light/dark/wizard), 9 skins (mixin),
--bd-* docs-heritage colors, SVG backgrounds
Core scaffolding _sass/core/* navbar, nav-tree, sidebar, TOC, docs-layout,
docs-code-examples, obsidian, syntax, code-copy
Components _sass/components/* cookie-banner, notes, callout, footer, search-modal,
content-tables, theme-preview, ui-enhancements, β¦
Layouts _sass/layouts/* landing, section, navbar-extras, global-chrome
Markup _includes/**, _layouts/** Liquid partials + page layouts (root β default β β¦)
Behavior assets/js/** ES modules under assets/js/modules/navigation|theme/,
plus per-feature scripts (search, code-copy, obsidian, β¦)
Server _plugins/** obsidian links, search/sitemap, statistics
Runtime theming flows: compiled --zer0-* defaults β _includes/core/tokens-inline.html (site theme_color + Appearance localStorage) β per-skin [data-theme-skin] overrides. Tests: a platform-independent Playwright smoke tier (runs on macOS) guards DOM/CSS/computed-token behavior; a Linux-baselined snapshots tier guards the 9 skins' pixels in CI.
Component Index¶
Every catalogued component, its primary implementation file, primary test, and coverage. Jump to the cluster section for full detail.
Global Chrome & Primary Navigation¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| Header / Site Shell | _includes/core/header.html |
core/styling.spec.js, core/responsive.spec.js, core/accessibility.spec.js |
π‘ partial |
| Branding (title/subtitle) | _includes/core/branding.html |
features/navbar.spec.js, core/styling.spec.js |
π‘ partial |
| Primary Navbar (menubar + dropdowns) | _includes/navigation/navbar.html |
features/navbar.spec.js, core/styling.spec.js |
π‘ partial |
| Navbar Mobile Quicklinks (tablet chips) | _includes/navigation/navbar-mobile-quicklinks.html |
features/navbar.spec.js |
π‘ partial |
| Head (document head / asset pipeline) | _includes/core/head.html |
core/styling.spec.js, test/test_quality.sh |
π‘ partial |
| Footer | _includes/core/footer.html |
core/layout-chrome.spec.js, core/responsive.spec.js |
π‘ partial |
| Breadcrumbs | _includes/navigation/breadcrumbs.html |
features/layouts.spec.js |
π‘ partial |
| Back-to-Top FAB | _includes/core/footer.html |
β | π΄ none |
| Auto-Hide Navbar | assets/js/auto-hide-nav.js |
β | π΄ none |
| Nanobar (scroll/load progress bar) | _includes/components/nanobar.html |
β | π΄ none |
| Offcanvas Sidebars & Unified Drawer | _includes/navigation/sidebar-left.html |
features/content-enhancements.spec.js |
π‘ partial |
| Navbar Extras / FAB Stacking | _sass/layouts/_navbar-extras.scss |
features/content-enhancements.spec.js |
π‘ partial |
| Navigation Orchestrator (index.js + config.js) | assets/js/modules/navigation/index.js |
core/styling.spec.js, features/layouts.spec.js |
π‘ partial |
| Navbar Module (dropdowns/keyboard/tooltips) | assets/js/modules/navigation/navbar.js |
β | π΄ none |
| Scroll-Spy Module | assets/js/modules/navigation/scroll-spy.js |
β | π΄ none |
| Smooth-Scroll Module | assets/js/modules/navigation/smooth-scroll.js |
β | π΄ none |
| Keyboard Shortcuts Module | assets/js/modules/navigation/keyboard.js |
features/layouts.spec.js |
π‘ partial |
| Swipe Gestures Module | assets/js/modules/navigation/gestures.js |
β | π΄ none |
| Focus Manager Module | assets/js/modules/navigation/focus.js |
β | π΄ none |
| Sidebar State Module | assets/js/modules/navigation/sidebar-state.js |
β | π΄ none |
| Sidebar Visibility Module | assets/js/modules/navigation/sidebar-visibility.js |
features/content-enhancements.spec.js |
π‘ partial |
| TOC Visibility Module | assets/js/modules/navigation/toc-visibility.js |
features/content-enhancements.spec.js |
π‘ partial |
Sidebar, Table of Contents & Docs Layout¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| Docs layout shell (.bd-layout/.bd-sidebar/.bd-main/.bd-toc) | _sass/core/_docs-layout.scss |
core/styling.spec.js, features/content-enhancements.spec.js |
π‘ partial |
| Nav-tree sidebar (YAML tree mode) | _includes/navigation/nav-tree.html |
β | π΄ none |
| Sidebar categories (categories mode) | _includes/navigation/sidebar-categories.html |
β | π΄ none |
| Sidebar folders (auto mode) | _includes/navigation/sidebar-folders.html |
β | π΄ none |
| Section sidebar (topic navigation) | _includes/navigation/section-sidebar.html |
features/layouts.spec.js |
π‘ partial |
| Table of Contents (Liquid parser + sidebar-right) | _includes/content/toc.html |
features/content-enhancements.spec.js |
π‘ partial |
| TOC FAB (mobile trigger) | _includes/navigation/toc-fab.html |
features/content-enhancements.spec.js |
π‘ partial |
| TOC visibility toggle + persistence | assets/js/modules/navigation/toc-visibility.js |
features/content-enhancements.spec.js |
π‘ partial |
| Scroll-spy (active heading highlight) | assets/js/modules/navigation/scroll-spy.js |
β | π΄ none |
| Page intro header (.bd-intro family) | _includes/content/intro.html |
features/layouts.spec.js |
π‘ partial |
| Docs code-example chrome (.bd-example/.bd-clipboard) | _sass/core/_docs-code-examples.scss |
β | π΄ none |
| Content tables (styling + CSV copy) | _sass/components/_content-tables.scss |
features/content-enhancements.spec.js |
π‘ partial |
Landing, Home & Component Polish¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| Landing layout | _layouts/landing.html |
features/layouts.spec.js |
π‘ partial |
| Landing quick-links bar | _includes/landing/landing-quick-links.html |
β | π΄ none |
| Landing install cards | _includes/landing/landing-install-cards.html |
β | π΄ none |
| Home layout | _layouts/home.html |
features/layouts.spec.js |
π‘ partial |
| Index layout | _layouts/index.html |
β | π΄ none |
| Welcome layout | _layouts/welcome.html |
features/layouts.spec.js |
π‘ partial |
| Section include (components/section.html) | _includes/components/section.html |
β | π΄ none |
| Feature card include (components/feature-card.html) | _includes/components/feature-card.html |
features/category-badges.spec.js |
π‘ partial |
| CTA button include (components/cta-button.html) | _includes/components/cta-button.html |
features/layouts.spec.js |
π‘ partial |
| Info section / settings offcanvas (components/info-section.html) | _includes/components/info-section.html |
β | π΄ none |
| Bootstrap component polish (UI enhancements) | _sass/components/_ui-enhancements.scss |
features/content-enhancements.spec.js, features/layouts.spec.js |
π‘ partial |
| Share actions (LinkedIn/copy) | assets/js/share-actions.js |
β | π΄ none |
| Skeleton loader | _sass/components/_skeleton.scss |
β | π΄ none |
| Particles hero background | assets/js/particles.js |
β | π΄ none |
Theming: Tokens, Color Modes, Skins & Customizers¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| Design Tokens (--zer0-* layer) | _sass/tokens/_index.scss |
core/styling.spec.js |
π‘ partial |
| Runtime Token Injection (tokens-inline.html) | _includes/core/tokens-inline.html |
β | π΄ none |
| Color Modes (light/dark/wizard) | _sass/theme/_color-modes.scss |
β | π΄ none |
| Named Skins (9 data-theme-skin palettes) | _sass/theme/_skins.scss |
features/appearance.spec.js |
π’ good |
| Appearance Panel (appearance.js) | assets/js/modules/theme/appearance.js |
β | π΄ none |
| Background Customizer (zer0Bg API + panels) | assets/js/background-customizer.js |
features/appearance.spec.js, features/appearance.spec.js |
π’ good |
| Palette Generator (palette-generator.js) | assets/js/palette-generator.js |
features/appearance.spec.js |
π‘ partial |
| Skin Editor (skin-editor.js) | assets/js/skin-editor.js |
β | π΄ none |
| Theme Customizer & Preview Gallery (admin UI) | _includes/components/theme-customizer.html |
features/appearance.spec.js |
π‘ partial |
Content & Collections¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| Note layout | _layouts/note.html |
β | π΄ none |
| Notebook layout | _layouts/notebook.html |
β | π΄ none |
| Notes & Notebooks index grids + difficulty badges | pages/notes.md |
β | π΄ none |
| Callout | _includes/components/callout.html |
β | π΄ none |
| Post navigation (prev/next cards) | _sass/components/_post-navigation.scss |
β | π΄ none |
| Code copy button | assets/js/code-copy.js |
features/content-enhancements.spec.js, core/accessibility.spec.js |
π‘ partial |
| Syntax highlighting | _sass/core/_syntax.scss |
β | π΄ none |
| Author card | _includes/components/author-card.html |
β | π΄ none |
| Author E-E-A-T block | _includes/components/author-eeat.html |
β | π΄ none |
| Post card | _includes/components/post-card.html |
β | π΄ none |
| Post-type badge | _includes/components/post-type-badge.html |
β | π΄ none |
| Feature card | _includes/components/feature-card.html |
β | π΄ none |
| Preview image | _includes/components/preview-image.html |
β | π΄ none |
| Comments (Giscus) | _includes/content/giscus.html |
β | π΄ none |
| Share actions (LinkedIn enhancement) | assets/js/share-actions.js |
β | π΄ none |
| Posts pagination | assets/js/posts-pagination.js |
features/layouts.spec.js |
π‘ partial |
| Article layout | _layouts/article.html |
features/layouts.spec.js |
π‘ partial |
| Collection layout | _layouts/collection.html |
β | π΄ none |
| News layout | _layouts/news.html |
β | π΄ none |
| Tag layout | _layouts/tag.html |
β | π΄ none |
Obsidian & Knowledge-Graph Features¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| Wiki-Links ([[Page]]) | _plugins/obsidian_links.rb |
test/test_obsidian.sh, test/test_ruby_converter.rb, test/test_resolver.js |
π‘ partial |
| Embeds & Transclusion (![[β¦]]) | _plugins/obsidian_links.rb |
test/test_obsidian.sh, test/test_ruby_converter.rb, test/test_resolver.js |
π‘ partial |
| Callouts (> [!type]) | _plugins/obsidian_links.rb |
test/test_obsidian.sh, test/test_ruby_converter.rb, test/test_resolver.js |
π‘ partial |
| Inline Tags (#tag) | _plugins/obsidian_links.rb |
test/test_obsidian.sh, test/test_ruby_converter.rb, test/test_resolver.js |
π‘ partial |
| Wiki Index (wiki-index.json) | assets/data/wiki-index.json |
test/test_obsidian.sh |
π‘ partial |
| Full Knowledge Graph (graph page) | assets/js/obsidian-graph.js |
β | π΄ none |
| Local Graph (sidebar panel + FAB) | assets/js/obsidian-local-graph.js |
β | π΄ none |
| Backlinks Panel (Linked mentions) | _includes/content/backlinks.html |
β | π΄ none |
Admin Tools & Dashboards¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| Admin Shell (layout + sidebar nav + tabs) | _layouts/admin.html |
features/admin.spec.js, core/accessibility.spec.js, test/test_plugins.rb |
π’ good |
| Config Viewer | _includes/components/config-viewer.html |
features/admin.spec.js, core/security.spec.js |
π‘ partial |
| Config Editor | _includes/components/config-editor.html |
features/admin.spec.js |
π‘ partial |
| Raw YAML / Config Sanitization | pages/_about/settings/config.md |
core/security.spec.js |
π‘ partial |
| Environment Dashboard | _includes/components/env-dashboard.html |
features/admin.spec.js, core/accessibility.spec.js |
π’ good |
| Environment Switcher | _includes/components/env-switcher.html |
β | π΄ none |
| Env-Var Helper (zer0-env-var) | _includes/components/zer0-env-var.html |
β | π΄ none |
| Navigation Editor | _includes/components/nav-editor.html |
features/admin.spec.js, core/accessibility.spec.js |
π‘ partial |
| Collection Manager | _includes/components/collection-manager.html |
features/admin.spec.js, core/accessibility.spec.js |
π‘ partial |
| Analytics Dashboard | _includes/components/analytics-dashboard.html |
features/admin.spec.js, core/accessibility.spec.js |
π‘ partial |
| Statistics Dashboard | _layouts/stats.html |
features/admin.spec.js, core/accessibility.spec.js, test/test_plugins.rb |
π‘ partial |
| Setup Wizard | _layouts/setup.html |
β | π΄ none |
| Setup Banner & Setup Check | _includes/components/setup-banner.html |
β | π΄ none |
| Dev Shortcuts | _includes/components/dev-shortcuts.html |
β | π΄ none |
Widgets, Search & Integrations¶
| Component | Primary source | Test | Coverage |
|---|---|---|---|
| AI Chat Assistant | _includes/components/ai-chat.html |
core/security.spec.js |
π΄ none |
| Site Search (modal + index) | _includes/components/search-modal.html |
β | π΄ none |
| Cookie Consent | _includes/components/cookie-consent.html |
β | π΄ none |
| PostHog Analytics | _includes/analytics/posthog.html |
core/security.spec.js, test/test_quality.sh |
π‘ partial |
| Google Analytics & Tag Manager | _includes/analytics/google-analytics.html |
core/security.spec.js |
π΄ none |
| Mermaid Diagrams | _includes/components/mermaid.html |
β | π΄ none |
| Keyboard Shortcuts | assets/js/modules/navigation/keyboard.js |
β | π΄ none |
| Shortcuts Cheatsheet Modal | _includes/components/shortcuts-modal.html |
β | π΄ none |
| cheetsheet.js (Bootstrap demos) | assets/js/cheetsheet.js |
β | π΄ none |
| Halfmoon Theme Switcher | _includes/components/halfmoon.html |
core/styling.spec.js |
π‘ partial |
| Misc Widgets (powered-by, showcase, js-cdn, svg) | _includes/components/powered-by.html |
core/styling.spec.js |
π‘ partial |
Global Chrome & Primary Navigation¶
The fixed top header (brand, primary menubar, utility controls, mobile/tablet shortcuts), the document <head>, the rich footer with FAB stack, breadcrumbs, scroll-progress nanobar, back-to-top, the offcanvas sidebars/drawers, and the ES-module navigation orchestrator that wires hover dropdowns, scroll-spy, keyboard shortcuts, gestures, focus management, and persisted sidebar/TOC visibility together form the persistent UI shell that appears on (nearly) every page.
Header / Site Shell¶
- Purpose: The fixed-top
<header id="navbar">banner that hosts the sidebar toggle, brand cluster, branding, tablet quicklinks, primary menubar, utility controls (Search/Settings), mobile menu toggle, and the optional in-navbar nanobar mount. It is the structural skeleton every other chrome component plugs into. - Capabilities:
fixed-topBootstrap navbar; skip-to-content link (#main-content); responsive lg+ 3-column CSS grid[brand | nav | utilities]vs.<lgflex; conditional left-sidebar toggle (omitted whenpage.sidebar: falseor no sidebar content); unified-drawer vs. legacy-offcanvas target switching viasite.navigation.unified_mobile_drawer; conditional#top-progress-targetnanobar mount whensite.nanobar.position == "navbar"; container-query namenavbar-mainfor progressive degradation. - Source:
- SCSS:
_sass/core/_navbar.scss(the#navbargrid, fixed/auto-hide, brand degradation, mobile layout blocks at ln 865+);_sass/layouts/_global-chrome.scss(.bottom-shadow, sticky helpers, resets) - Markup:
_includes/core/header.html,_layouts/default.html(consumer) - JS:
assets/js/auto-hide-nav.js(sets body padding to#navbarheight) - API surface: ids
#navbar,#main-content,#top-progress-target; classes.navbar-main,.navbar-main-start,.navbar-brand-group,.navbar-home-links,.navbar-utility-controls,.bottom-shadow,.bd-navbar-toggle,.navbar-hidden; containercontainer-name: navbar-main; data-attrdata-bs-target(toggles offcanvas/drawer); CSS vars--bs-body-bg,--zer0-bp-* - Tests:
test/visual/core/styling.spec.jsβ "desktop header and navbar render" (assertsheader#navbar,.navbar.navbar-expand-lg,nav.navbar-mainvisible; brand link + img present); "mobile main navigation toggle is visible".test/visual/core/responsive.spec.jsβ "main landmarks visible at {viewport}" assertsheader#navbarvisible across all 5 viewports.test/visual/core/accessibility.spec.jsβ "skip link is focusable and targets main content" assertsa[href="#main-content"].visually-hidden-focusableis attached + focusable. - Gaps / improvement ideas: No test asserts the lg+ 3-column grid actually keeps the menubar from painting over Search/Settings (the stated reason for the grid). The nanobar
#top-progress-targetmount path is untested. Body padding fromauto-hide-nav.jsis computed in JS and never asserted, so a regression that drops it (content hidden under the fixed header) would pass CI.
Branding (site title / subtitle)¶
- Purpose: Renders the clickable site title (and optional subtitle) inside the brand cluster, with responsive icon and ellipsis behavior.
- Capabilities: Title links to
/; optional title icon shown only at smβlg (d-none d-sm-inline d-lg-none); subtitle shown only at lg+ and only whensite.subtitleis non-empty;site-title-textellipsis at viewport breakpoints (60vw β 50vw β 40vw). - Source:
- SCSS:
_sass/core/_navbar.scss(.site-title-textresponsivemax-width,#navbar .site-title/.site-subtitleellipsis in the lg grid) - Markup:
_includes/core/branding.html - API surface: classes
.site-title,.site-subtitle,.site-title-text,.site-subtitle-text,.navbar-brand; configsite.title,site.subtitle,site.title_icon,site.default_icon - Tests:
test/visual/features/navbar.spec.jsβ "mid desktop brand logo and title do not overlap" (asserts.site-title-textsits right of the logo, no overlap); "mobile shows site title and offcanvas toggler" (asserts.site-title-textvisible).test/visual/core/styling.spec.jschecks thea.navbar-brandlogo image exists. - Gaps / improvement ideas: Subtitle rendering/visibility (lg+ only, suppressed when empty) is untested. The title-icon breakpoint window (smβlg only) is untested. There is a stale
d-lg-noneicon class string that duplicatessite.default_icon({{ site.default_icon }} {{ site.default_icon }}-...) β likely a typo that should be{{ site.default_icon }} bi-{{ site.title_icon }}; worth verifying the icon actually renders.
Primary Navbar (menubar + dropdowns)¶
- Purpose: The primary menubar: inline at lg+, collapsing into the
#bdNavbaroffcanvas below lg, with split-button dropdowns, data-driven or auto-generated nav items, and mobile-only Home/Search/Settings rows. - Capabilities: Data-driven from
_data/navigation/main.yml, else auto-generated fromsite.collections(skipspages, honorsnav_exclude); split dropdown (parent link navigates, chevron button toggles submenu);dropdown-menu-endalignment for last-two items; icon+label with container-query density tiers (full labels β₯38rem β icon-only <38rem); active state viaaria-current="page"with an underline pseudo-element; mobile offcanvas adds Home/Search/Settings entries; tooltips on compact desktop (992β1199px). - Source:
- SCSS:
_sass/core/_navbar.scss(split toggle,@container bd-navdensity tiers, desktop dropdown animation, mobile accordion dropdowns, active states,.nav-tooltip) - Markup:
_includes/navigation/navbar.html - JS:
assets/js/modules/navigation/navbar.js - Plugin/data:
_data/navigation/main.yml - API surface: id
#bdNavbar; classes.nav-hover-dropdown,.dropdown-toggle-split,.nav-link-text,.bd-navbar-nav-viewport,.nav-tooltip,.navbar-nav; data-attrdata-search-toggle,data-bs-toggle="offcanvas",data-bs-target="#info-section"; containerbd-nav(container-type: inline-size); ARIAaria-current,aria-expanded,aria-haspopup; CSS vars--bs-tertiary-bg,--bs-primary,--zer0-space-* - Tests:
test/visual/features/navbar.spec.jsβ "wide desktop shows full nav labels without ellipsis" (no...suffix; "Quick Start" visible); "mobile shows site title and offcanvas toggler".test/visual/core/styling.spec.jsβ "mobile main navigation toggle is visible" asserts.bd-navbar-toggle.d-lg-none button.navbar-toggler[data-bs-target="#bdNavbar"]. - Gaps / improvement ideas: Dropdown open/close (chevron click toggling
.show), keyboard arrow/Home/End/Escape navigation, outside-click-close, and the icon-only<38remtier are all untested behaviorally. Tooltip show/hide on compact desktop is untested. No test exercises the auto-generated (nomain.yml) nav fallback path.
Navbar Mobile Quicklinks (tablet chips)¶
- Purpose: Horizontal icon+label chip shortcuts shown only between md and lg (768β991px), filling the center band while the full menubar stays in the offcanvas.
- Capabilities: Renders only when
site.data.navigation.mainexists; shows first 5 top-level links; horizontal scroll with hidden scrollbars;aria-current="page"styling; label ellipsis at 5.5rem. - Source:
- SCSS:
_sass/layouts/_navbar-extras.scss(.navbar-mobile-quicklinksand__list/__chip/__label) - Markup:
_includes/navigation/navbar-mobile-quicklinks.html - Plugin/data:
_data/navigation/main.yml - API surface: classes
.navbar-mobile-quicklinks,.navbar-mobile-quicklinks__list,.navbar-mobile-quicklinks__chip,.navbar-mobile-quicklinks__label; responsived-none d-md-flex d-lg-none; CSS vars--bs-primary,--bs-primary-bg-subtle,--bs-border-color-translucent - Tests:
test/visual/features/navbar.spec.jsβ "tablet shows mobile quicklink chips between md and lg" asserts.navbar-mobile-quicklinksvisible and firsta.navbar-mobile-quicklinks__chipvisible at the tablet viewport. - Gaps / improvement ideas: The
limit: 5truncation and the horizontal-scroll overflow behavior with many links are untested. No assertion that the chips are hidden at mdβ or lg+ (only the in-window case is checked).
Head (document head / asset pipeline)¶
- Purpose: Assembles the entire
<head>: deferred theme scripts, conditional Mermaid/MathJax, nanobar include, SEO/structured-data, analytics (production-only), Bootstrap + Bootstrap Icons +main.css, and inline design-token overrides. - Capabilities: Loads
auto-hide-nav.js,back-to-top.js,halfmoon.js,side-bar-folders.js,code-copy.js,table-copy.js,ui-enhancements.js(alldefer); GTM/GA gated onjekyll.environment == "production"; conditional Mermaid (page.mermaid), MathJax (page.mathjax), stats CSS; vendored (no-CDN) Bootstrap 5.3.3 + icons;tokens-inline.htmlemitted aftermain.cssso configtheme_colorwins; optionaluser-overrides.cssviasite.user_overrides. - Source:
- SCSS: β (links the compiled
assets/css/main.css) - Markup:
_includes/core/head.html,_includes/core/tokens-inline.html,_layouts/root.html(host) - JS: deferred theme scripts (above);
_includes/components/nanobar.html,_includes/components/mermaid.html - API surface:
<link rel="stylesheet">toassets/vendor/bootstrap/...,assets/vendor/bootstrap-icons/...,assets/css/main.css; configsite.user_overrides,site.google_analytics,page.mermaid,page.mathjax;window.MathJaxconfig object - Tests:
test/visual/styling.spec.jsβ "same-origin CSS assets return 200"; "HTML references compiled main stylesheet" (amain*.csslink); "Bootstrap exposes CSS variables on :root"; "homepage does not reference common third-party CDNs for core assets" (bans jsDelivr/jQuery/Font Awesome/etc.).test/test_quality.shchecks semantic-HTML presence broadly. - Gaps / improvement ideas: No test verifies the token-override cascade order (
tokens-inlineaftermain.css,user-overrides.csslast). Production-only gating of GTM/GA is not asserted in any spec. The MathJax/Mermaid conditional loads are untested.
Footer¶
- Purpose: The rich site footer: powered-by credits row with Info offcanvas trigger, a dark four-column block (site info / quick links / latest posts / social+RSS), a placeholder subscribe form, policy links, copyright, and the host for the FAB stack + back-to-top + local-graph panel.
- Capabilities: Powered-by links from
site.powered_by(with trailing Info trigger to#info-section); quick links resolved fromsite.footer_quick_linksor auto-detected against existing pages (avoids 404s); latest 3 posts (hidden when none); social links fromsite.links+ RSS feed; disabled placeholder subscribe form; conditional Privacy/Terms links (only when pages exist) + Cookie Preferences modal trigger; FAB/back-to-top/local-graph excluded on a list of root-only layouts. - Source:
- SCSS:
_sass/components/_footer.scss - Markup:
_includes/core/footer.html - Plugin/data:
site.powered_by,site.links,site.data.ui-text - API surface: classes
.bd-footer,.footer-powered-by,.footer-powered-by-links,.powered-by-link,.footer-dark-block,.footer-nav-columns,.footer-latest-posts,.footer-subscribe-form,.footer-policy-links; data-attrdata-bs-toggle="offcanvas"/"modal"(#info-section,#cookieSettingsModal); CSS vars--zer0-space-*,--bs-secondary-color - Tests:
test/visual/core/layout-chrome.spec.jsβ "powered-by credits are real links" (hrefs truthy, not#); "footer nav columns use equal width at tablet".test/visual/core/responsive.spec.js"main landmarks visible at {viewport}" assertsfooter.bd-footervisible across viewports. - Gaps / improvement ideas: The quick-links auto-detection (skip-when-page-missing) and conditional policy-link rendering are untested β a regression that surfaces 404 links would pass. The disabled subscribe form's accessibility (the
disabledbutton + hint association) is unverified. Latest-posts hide-when-empty is untested.
Breadcrumbs¶
- Purpose: Accessible breadcrumb trail with Schema.org
BreadcrumbListmicrodata for rich results, rendered on non-home pages whensite.breadcrumbsis enabled. - Capabilities: Rendered only when
page.url != "/"andsite.breadcrumbs; i18n root label fromsite.data.ui-text; special handling for known sections (posts,notebooks,notes,docs) to avoid linking intermediate dirs lacking index pages; folder icon kept outside the<ol>for HTML validity;aria-current="page"on the leaf;itemprop/itemscopemicrodata throughout. - Source:
- SCSS: β (uses Bootstrap
.breadcrumb) - Markup:
_includes/navigation/breadcrumbs.html - Plugin/data:
site.data.ui-text(breadcrumbs_root,breadcrumbs_aria) - API surface: classes
.breadcrumbs,.breadcrumb,.breadcrumb-item; ARIAaria-label,aria-current="page"; microdatahttps://schema.org/BreadcrumbList/ListItem,itemprop="position|name|item" - Tests:
test/visual/layouts.spec.jsβ "breadcrumbs<nav>exposes aria-label" (on/about/); "breadcrumbs are<ol>with<li>children (no orphan<i>)" (asserts the folder icon is not a direct<ol>child). - Gaps / improvement ideas: The Schema.org microdata (
itemprop/positionsequencing) is not validated. The known-section special path (/posts/YYYY/...collapsing to Home βΊ Posts βΊ leaf) is untested. No test confirms breadcrumbs are suppressed on/or whensite.breadcrumbsis off.
Back-to-Top FAB¶
- Purpose: A floating button that appears after scrolling 200px and smooth-scrolls to the top of the page.
- Capabilities: Hidden by default (
display:none); shown whenscrollY > 200; smoothscrollTo({top:0}); sits at the bottom of the FAB stack (lowest z-index of the three FABs); hover lifts and brightens; uses unified--zer0-layer-*/--zer0-space-fab-*tokens. - Source:
- SCSS:
_sass/components/_back-to-top.scss; FAB tokens in_sass/tokens/_layers.scss,_spacing.scss,_shadow.scss - Markup:
_includes/core/footer.html(#backToTopBtn) - JS:
assets/js/back-to-top.js - API surface: id
#backToTopBtn; CSS vars--zer0-layer-fab-back-to-top(1050),--zer0-space-fab-offset,--zer0-shadow-fab,--zer0-color-primary,--zer0-motion-duration-fast - Tests: No automated tests.
- Gaps / improvement ideas: The 200px show/hide threshold, the smooth-scroll action, and the FAB stacking order (back-to-top below TOC/chat FABs) are entirely untested. The button uses inline
style="display:none"toggled toblockin JS β a no-JS or JS-error state leaves it permanently hidden with no fallback assertion.
Auto-Hide Navbar¶
- Purpose: Hides the fixed header on scroll-down and reveals it on scroll-up to maximize reading space, while reserving body padding equal to the header height.
- Capabilities: Threshold 80px to hide, 3px delta to trigger, always-shown within 50px of top;
requestAnimationFrame-throttled scroll; respectsprefers-reduced-motion(disables transform transition); setsbody { padding-top }to header height (debounced on resize); pauses (re-shows + unbinds scroll) while the#bdNavbaroffcanvas is open; injects its own#navbar.navbar-hidden { translateY(-100%) }style if absent. - Source:
- SCSS:
_sass/core/_navbar.scss(#navbar.navbar-hidden,@media (prefers-reduced-motion)block) - Markup: β (operates on
#navbarfromheader.html) - JS:
assets/js/auto-hide-nav.js - API surface: id
#navbar; class.navbar-hidden; injected style id#navbar-autohide-styles; events listened:show.bs.offcanvas/hidden.bs.offcanvason#bdNavbar - Tests: No automated tests.
- Gaps / improvement ideas: Hide-on-down / show-on-up, the body-padding compensation, reduced-motion handling, and the offcanvas-open pause are all untested β a meaningful surface for regressions (content jump, header overlapping content). There is duplicate transition/
navbar-hiddenlogic split between this JS and_navbar.scss; consolidating would reduce drift risk.
Nanobar (scroll/load progress bar)¶
- Purpose: A thin config-driven page-load progress bar that animates through configured percentage steps, mountable at top, bottom, or inline under the navbar.
- Capabilities: Fully config-driven via
site.nanobar.*(color, background, height, position, z-index, steps, step delay, classname, id, target); three placement modes (top/bottom/navbar); injects CSS custom properties; bridges config to JS viawindow.zer0Nanobar; vendored library (nanobar.min.js) +nanobar-init.js; navbar mode mounts into#top-progress-target. - Source:
- SCSS: inline
<style id="nanobar-theme">in the include (.nanobar,.nanobar--bottom,.nanobar-mount,.nanobar--navbar) - Markup:
_includes/components/nanobar.html; mount point in_includes/core/header.html - JS:
assets/js/nanobar-init.js,assets/js/nanobar.min.js - API surface:
window.zer0Nanobar(classname,id,position,target,steps,stepDelay); ids#top-progress-bar,#top-progress-target; classes.nanobar,.nanobar--bottom,.nanobar--navbar,.nanobar-mount; CSS vars--nanobar-color,--nanobar-bg,--nanobar-height,--nanobar-z; configsite.nanobar.* - Tests: No automated tests.
- Gaps / improvement ideas: No spec confirms the bar renders, animates steps, or honors the three position modes / the
#top-progress-targetmount. Bridging-config-to-JS (window.zer0Nanobar) and theNanobarlibrary presence guard are untested β silent failure (no progress bar) would not be caught.
Offcanvas Sidebars & Unified Drawer¶
- Purpose: The mobile/offcanvas panels: left docs sidebar (
#bdSidebar), right TOC (#tocContents), and the optional consolidated tabbed drawer (#zer0UnifiedDrawer) that merges Browse/Menu/Settings into one offcanvas. - Capabilities: Left sidebar has 3 content modes (
autofolder scan,categories, YAMLtree); per-panel FOUC guard scripts apply hidden-preference classes pre-paint (bd-sidebar-pref-hidden/bd-toc-pref-hidden); desktop visibility toggles in panel headers; TOC parser invoked only when the page has h2βh4; unified drawer (opt-in vianavigation.unified_mobile_drawer) uses Bootstrap nav-pills tabs and reuses the same nav data; shared offcanvas header chrome (48px close target, focus-visible outline) across sidebar/TOC/local-graph panels. - Source:
- SCSS:
_sass/core/_offcanvas-panels.scss(shared header/title/close chrome);_sass/core/_navbar.scss(#bdNavbaroffcanvasβinline at lg+) - Markup:
_includes/navigation/sidebar-left.html,sidebar-right.html,unified-drawer.html,nav-tree.html,sidebar-folders.html,sidebar-categories.html - JS:
assets/js/modules/navigation/sidebar-visibility.js,toc-visibility.js,sidebar-state.js - API surface: ids
#bdSidebar,#tocContents,#zer0UnifiedDrawer,#TableOfContents,#info-section; classes.offcanvas-lg,.bd-sidebar-visibility-toggle,.bd-toc-visibility-toggle,.nav-tree,.zer0-unified-drawer; html classesbd-sidebar-pref-hidden/bd-toc-pref-hidden; data-attrdata-bs-toggle="offcanvas"/"tab",data-nav-tree; configpage.sidebar.nav,site.navigation.unified_mobile_drawer - Tests:
test/visual/features/content-enhancements.spec.jsβ "docs article exposes ToC on desktop" (asserts.bd-toc, #tocContentsattached;.bd-sidebar-visibility-toggle/.bd-toc-visibility-toggleattached). The Bootstrap offcanvas open/close itself is exercised indirectly via the toggler tests incore/styling.spec.js. - Gaps / improvement ideas: The unified-drawer tab switching, the three sidebar nav modes (auto/categories/tree), and the FOUC-guard hidden-preference path are untested. No spec asserts the desktop visibility toggle actually collapses the column (that lives in JS modules below). The TOC "only parse when headings exist" branch is untested.
Navbar Extras / FAB Stacking¶
- Purpose: Centralizes the floating action button stack (TOC FAB, left-sidebar FAB) positioning and z-index so the FABs and back-to-top never overlap on small viewports.
- Capabilities: TOC FAB and sidebar FAB mirror each other on opposite edges; mobile stacking math lifts them above back-to-top (
offset + size + gap); restore-mode FABs (--restore/html.bd-*-pref-hidden) shown only on mobile (desktop uses the in-header rail toggle); 3.5rem touch targets with hover lift; also styles the tablet quicklink chips. - Source:
- SCSS:
_sass/layouts/_navbar-extras.scss; tokens in_sass/tokens/_layers.scss(--zer0-layer-fab-*),_spacing.scss(--zer0-space-fab-*) - Markup:
_includes/navigation/toc-fab.html,local-graph-fab.html(rendered fromfooter.html) - JS: visibility driven by
toc-visibility.js/sidebar-visibility.js(toggle--restoreclasses) - API surface: classes
.bd-toc-fab,.bd-sidebar-fab,.bd-toc-toggle,.bd-sidebar-toggle,.bd-toc-fab--restore,.bd-sidebar-fab--restore; ids#tocFab,#sidebarFab; CSS vars--zer0-layer-fab-toc(1055),--zer0-layer-fab-back-to-top(1050),--zer0-layer-fab-local-graph(1060),--zer0-space-fab-offset/size/gap - Tests:
test/visual/features/content-enhancements.spec.jsβ "mobile exposes sidebar FAB and ToC FAB" (asserts#sidebarFab, .bd-sidebar-fabvisible and#tocFabattached at the mobile viewport). - Gaps / improvement ideas: The actual stacking (FABs not overlapping back-to-top) is computed via CSS
calc()but never asserted with bounding-box overlap checks βfixtures.jsalready providesboxesOverlap/assertStackedVerticallyhelpers that could verify it. The restore-mode visibility transitions and the local-graph FAB are untested.
Navigation Orchestrator (modules/navigation/index.js + config.js)¶
- Purpose: The ES-module entry point that constructs
window.zer0Navigation, syncs breakpoints from CSS tokens, conditionally instantiates each sub-module, and exposes a public API (scrollTo,expandTo,expandAll/collapseAll,getShortcuts,getModule,destroy). - Capabilities: Auto-inits on DOM-ready (waits for Bootstrap
loadif absent);syncBreakpointsFromCss()reads--zer0-bp-*so SCSS token overrides propagate to JS; instantiates TOC modules only if a#TableOfContentsexists, sidebar-visibility only if a left sidebar + docs layout exist; dispatchesnavigation:ready/navigation:destroyed; loaded via_includes/components/js-cdn.htmlastype="module". - Capabilities (config): centralizes selectors, scroll-spy margins, smooth-scroll offset, keyboard key map, gesture thresholds, localStorage prefix
zer0-nav-, and breakpoints; exportsisBelowBreakpoint/isAtOrAboveBreakpoint. - Source:
- SCSS: β (consumes
--zer0-bp-*from_sass/tokens/_breakpoints.scss) - Markup:
_includes/components/js-cdn.html(module<script>loader) - JS:
assets/js/modules/navigation/index.js,assets/js/modules/navigation/config.js - API surface:
window.zer0Navigation(.init(),.getModule(name),.getConfig(),.scrollTo(),.expandTo(),.expandAll(),.collapseAll(),.getShortcuts(),.destroy()); eventsnavigation:ready,navigation:destroyed; exportedconfig,syncBreakpointsFromCss,isBelowBreakpoint,isAtOrAboveBreakpoint; CSS vars--zer0-bp-sm/md/lg/xl/xxl - Tests: Indirect:
test/visual/styling.spec.jsandfeatures/layouts.spec.jsboth assert--zer0-bp-lgresolves to a pixel value (the tokensyncBreakpointsFromCssreads). No test assertswindow.zer0Navigationexists or thatnavigation:readyfires. - Gaps / improvement ideas: No direct assertion that the orchestrator initializes (
window.zer0Navigation._initialized) or that conditional module gating works. The Bootstrap-not-loaded fallback path is untested. Public API methods (scrollTo,expandTo) have no coverage.
Navbar Module (hover dropdowns / mobile menu / focus trap / tooltips)¶
- Purpose: Sub-module ported from the legacy
navigation.jsIIFE that wires all#bdNavbarinteractions: split-toggle dropdowns, keyboard nav, outside-click close, offcanvas link-close + reset, manual tooltips, focus trap, and responsive reset. - Capabilities: Click-only dropdown toggle (hover-to-open deliberately disabled); full keyboard menu nav (Enter/Space/Arrow/Home/End/Escape/Tab); closes others on open; outside-click + offcanvas-hide reset; manual Bootstrap tooltips shown only on compact desktop (992β1199px) with 400/100ms delay; focus-first on offcanvas shown; debounced resize cleanup; listener bookkeeping for
destroy(). - Source:
- SCSS:
_sass/core/_navbar.scss - Markup:
_includes/navigation/navbar.html - JS:
assets/js/modules/navigation/navbar.js - API surface: class
Navbar(.init(),.destroy()); operates on#bdNavbar,.nav-hover-dropdown,.dropdown-toggle-split,.dropdown-menu; toggles.show+aria-expanded; tooltipcustomClass: 'nav-tooltip'; breakpoints fromconfig.breakpoints.lg/xl - Tests: No automated tests (behaviorally). Static markup that it drives is touched by the navbar render/toggle tests in
core/styling.spec.js. - Gaps / improvement ideas: None of the rich keyboard/dropdown/tooltip behavior is exercised by Playwright β a high-value gap given accessibility commitments.
_setupDropdownHoverDelay()is a dead no-op left in place.destroy()only removes listeners tracked via_on, but the mobile/outside-click handlers are registered through_on, so this is mostly fine β still untested.
Scroll-Spy Module¶
- Purpose: IntersectionObserver-based section tracking that highlights the corresponding TOC link and auto-scrolls the TOC to keep it visible.
- Capabilities: Observes headings referenced by
#TableOfContents a[href^="#"]; activates the most-visible heading's link (.active); rootMargin-80pxfor the fixed header; auto-scrolls TOC container; dispatchesnavigation:sectionChange;setActiveById/getActivehelpers; cleandestroy(). - Source:
- JS:
assets/js/modules/navigation/scroll-spy.js(config inconfig.js) - Markup:
_includes/navigation/sidebar-right.html(#TableOfContents) - API surface: class
ScrollSpy(.setActiveById(),.getActive(),.destroy()); eventnavigation:sectionChange; toggles.activeon TOC links; configscrollSpy.rootMargin,scrollSpy.threshold, selectorstoc,tocLinks,tocContainer - Tests: No automated tests.
- Gaps / improvement ideas: Active-link highlighting on scroll is untested. Note this is the theme's custom scroll-spy;
fixtures.gotoBeforeScrollSpydisables Bootstrap's native ScrollSpy for admin pages, but the custom one has no positive coverage.
Smooth-Scroll Module¶
- Purpose: Intercepts in-page TOC anchor clicks to smooth-scroll with a fixed-header offset, update the URL hash without a jump, and close the mobile TOC offcanvas.
- Capabilities: Offset of 80px;
history.pushStatehash update; focus management (tabindex=-1+focus({preventScroll})) for a11y; closes#tocContentsoffcanvas below lg; dispatchesnavigation:scroll;scrollToElement/scrollToIdpublic methods. - Source:
- JS:
assets/js/modules/navigation/smooth-scroll.js - Markup:
_includes/navigation/sidebar-right.html - API surface: class
SmoothScroll(.scrollToElement(),.scrollToId(),.destroy()); eventnavigation:scroll; configsmoothScroll.offset,smoothScroll.behavior - Tests: No automated tests.
- Gaps / improvement ideas: The offset scroll, hash update, and mobile-offcanvas-close are untested.
destroy()is a documented no-op (handlers aren't stored), so hot-reload/SPA cleanup would leak listeners β acceptable for full-page Jekyll but worth noting.
Keyboard Shortcuts Module¶
- Purpose: Global keyboard navigation: prev/next section (
[/]), focus search (/), toggle sidebar (b), toggle TOC (t), and open the shortcuts help modal (?). - Capabilities: Ignores keystrokes in inputs/textareas/contenteditable (with a
typeof matchesguard against Document-target TypeErrors);?checked before/fallback so Shift+/ opens the modal not search; routes sidebar/TOC toggles through the visibility modules when present, else Bootstrap offcanvas; dispatchesnavigation:keyboardNav/searchRequest/sidebarToggle;getShortcuts()for the help modal. - Source:
- JS:
assets/js/modules/navigation/keyboard.js - Markup:
_includes/components/shortcuts-modal.html(#zer0-shortcuts-modal) - API surface: class
KeyboardShortcuts(.getShortcuts(),.destroy()); eventsnavigation:keyboardNav,navigation:searchRequest,navigation:sidebarToggle; id#zer0-shortcuts-modal; configkeyboard.keys([,],/,b,t) - Tests:
test/visual/layouts.spec.jsβ "pressing ? opens the shortcuts modal" (dispatches a synthetic?keydown and asserts#zer0-shortcuts-modalgets.show). - Gaps / improvement ideas: Only the
?shortcut is tested.[/]section nav,/search focus, andb/ttoggles are untested. The input-field guard (don't hijackb/twhile typing) is also unverified.
Swipe Gestures Module¶
- Purpose: Touch gesture support: swipe from the left edge to open the docs sidebar, swipe from the right edge to open the TOC, on mobile only.
- Capabilities: 50px swipe threshold + 50px edge zone; horizontal-dominant swipe detection; only below lg; opens
#bdSidebar/#tocContentsvia Bootstrap offcanvas; dispatchesnavigation:swipe; passive listeners; cleandestroy(). - Source:
- JS:
assets/js/modules/navigation/gestures.js - API surface: class
SwipeGestures(.destroy()); eventnavigation:swipe(detail.direction,detail.sidebar); configgestures.enabled,gestures.threshold,gestures.edgeZone; selectorsleftSidebar(#bdSidebar),rightSidebar(#tocContents) - Tests: No automated tests.
- Gaps / improvement ideas: Edge-swipe opening of either sidebar is untested (Playwright can synthesize touch events). No guard differentiates an edge-swipe from a horizontal content scroll/swipe-carousel, which could cause accidental opens β worth a behavioral test.
Focus Manager Module¶
- Purpose: Accessibility focus management: returns focus to the trigger when an offcanvas closes and adds a keyboard-navigation body class for focus-visible styling.
- Capabilities: On
hidden.bs.offcanvas, finds the[data-bs-target]/[href]trigger and refocuses it (rAF-deferred);keyboard-navbody class toggled on Tab vs. mousedown;focusFirst/focusLast/trapFocusutilities (trapFocus returns a cleanup fn). - Source:
- JS:
assets/js/modules/navigation/focus.js - API surface: class
FocusManager(.returnFocus(),.focusFirst(),.focusLast(),.trapFocus(),.destroy()); body classkeyboard-nav; selectoroffcanvas(.offcanvas) - Tests: No automated tests (the generic skip-link focus test in
core/accessibility.spec.jsis unrelated). - Gaps / improvement ideas: Focus-return-to-trigger on offcanvas close (a real a11y requirement) is untested. The
keyboard-navbody class toggle andtrapFocusare unused-by-default utilities with no coverage.
Sidebar State Module¶
- Purpose: Persists expanded/collapsed state of sidebar tree nodes across page loads via localStorage, and exposes expand/collapse helpers.
- Capabilities: Persists expanded node ids under
zer0-nav-expanded-nodes; listens to Bootstrapshow/hide.bs.collapse(only for nodes inside.bd-sidebar/.nav-tree); restores state on load (no animation);expandAll/collapseAll/expandPathTo/isExpanded/getExpandedNodes/clearState; dispatchesnavigation:toggle/expandAll/collapseAll/stateCleared. - Source:
- JS:
assets/js/modules/navigation/sidebar-state.js - Markup:
_includes/navigation/nav-tree.html,sidebar-folders.html(collapse nodes) - API surface: class
SidebarState(.setExpanded(),.isExpanded(),.expandAll(),.collapseAll(),.expandPathTo(),.getExpandedNodes(),.clearState(),.destroy()); eventsnavigation:toggle/expandAll/collapseAll/stateCleared; storage keyzer0-nav-expanded-nodes - Tests: No automated tests.
- Gaps / improvement ideas: Persistence across reloads, restore-on-load, and
expandPathTo(reveal a deep node) are untested β a regression that loses sidebar state would be invisible to CI.
Sidebar Visibility Module¶
- Purpose: Collapses/restores the left docs sidebar column on desktop (and toggles the offcanvas on mobile), persisting the preference.
- Capabilities: Preference under
zer0-nav-sidebar-visible; toggles layout classesbd-layout--sidebar-collapsed,bd-sidebar--hidden,bd-sidebar-fab--restore, andhtml.bd-sidebar-pref-hidden(FOUC-guarded); updates ARIA labels/aria-expandedon the in-header toggle and the restore FAB; mobile path opens/toggles the Bootstrap offcanvas; dispatchesnavigation:sidebarVisibility. Only instantiated when a#bdSidebar+.bd-layout(not--no-sidebar) exist. - Source:
- SCSS:
_sass/layouts/_navbar-extras.scss(.bd-sidebar-fab--restore);_sass/core/_docs-layout.scss(collapsed-column rules) - Markup:
_includes/navigation/sidebar-left.html(toggles + FOUC script) - JS:
assets/js/modules/navigation/sidebar-visibility.js - API surface: class
SidebarVisibility(.isVisible(),.setVisible(),.toggle(),.destroy()); eventnavigation:sidebarVisibility; classesbd-layout--sidebar-collapsed,bd-sidebar--hidden,bd-sidebar-fab--restore, htmlbd-sidebar-pref-hidden; storage keyzer0-nav-sidebar-visible; selectors.bd-sidebar-visibility-toggle,.bd-sidebar-fab - Tests:
test/visual/features/content-enhancements.spec.jsβ "docs article exposes ToC on desktop" asserts.bd-sidebar-visibility-toggleis attached (presence only, not toggle behavior). - Gaps / improvement ideas: The collapse/restore action, preference persistence, FOUC-guard class, and the mobile offcanvas branch are untested. The
bkeyboard shortcut routes here but its end-to-end effect is unverified.
TOC Visibility Module¶
- Purpose: Collapses/restores the right TOC column on desktop (and toggles the offcanvas on mobile), persisting the preference β the mirror of Sidebar Visibility.
- Capabilities: Preference under
zer0-nav-toc-visible; togglesbd-main--no-toc,bd-toc--hidden,bd-toc-fab--restore,html.bd-toc-pref-hidden; updates ARIA on the in-header toggle and restore FAB; mobile opens/toggles#tocContents; dispatchesnavigation:tocVisibility. Only instantiated when#tocContentsexists. - Source:
- SCSS:
_sass/core/_toc.scss,_sass/layouts/_navbar-extras.scss(.bd-toc-fab--restore) - Markup:
_includes/navigation/sidebar-right.html(toggles + FOUC script) - JS:
assets/js/modules/navigation/toc-visibility.js - API surface: class
TocVisibility(.isVisible(),.setVisible(),.toggle(),.destroy()); eventnavigation:tocVisibility; classesbd-main--no-toc,bd-toc--hidden,bd-toc-fab--restore, htmlbd-toc-pref-hidden; storage keyzer0-nav-toc-visible; selectors.bd-toc-visibility-toggle,.bd-toc-fab - Tests:
test/visual/features/content-enhancements.spec.jsβ "docs article exposes ToC on desktop" asserts.bd-toc-visibility-toggleis attached (presence only). - Gaps / improvement ideas: Same as Sidebar Visibility β the actual hide/restore, persistence, FOUC class, and mobile branch (plus the
tshortcut effect) are untested.
Sidebar, Table of Contents & Docs Layout¶
The reading-page shell for content/docs pages: a CSS-grid .bd-layout (left .bd-sidebar nav + .bd-main holding .bd-intro hero, right .bd-toc, and .bd-content body), four interchangeable left-nav renderers (YAML tree, auto folders, categories, section topics), a Liquid-parsed sticky Table of Contents with scroll-spy + collapsible rails persisted to localStorage, and Bootstrap-docs-derived chrome for code examples and content tables.
Docs layout shell (.bd-layout / .bd-sidebar / .bd-main / .bd-toc)¶
- Purpose: The responsive grid scaffold for every default-layout page: a left documentation sidebar, a main column, a right table-of-contents column, and the content body. Collapses to offcanvas drawers + FABs on mobile and to slim "rail" columns when the user hides a sidebar.
- Capabilities: CSS-grid areas
sidebar main(desktop) andintro/toc/contentinside.bd-main; sticky sidebar + TOC (top: 5rem,height: calc(100vh - β¦)); width grows past Bootstrap's 1320px cap on docs pages via--zer0-layout-max-width-xl/xxl;--no-sidebarmodifier when no left nav content; collapsed/rail states (bd-layout--sidebar-collapsed,bd-main--no-toc, and pre-painthtml.bd-sidebar-pref-hidden/html.bd-toc-pref-hiddenFOUC guards) that animategrid-template-columnsand fade out everything except the toggle-hosting headers;prefers-reduced-motiondisables the transitions; mobile FABs (#sidebarFab/.bd-sidebar-fab,#tocFab/.bd-toc-fab). - Source:
- SCSS:
_sass/core/_docs-layout.scss - Markup:
_layouts/default.html(.bd-layout/.bd-main/.bd-content+ sidebar FAB),_includes/navigation/sidebar-left.html(#bdSidebaroffcanvas-lg,.bd-sidebar-desktop-header),_includes/navigation/sidebar-right.html(#tocContentsoffcanvas-lg +#TableOfContents) - JS:
assets/js/modules/navigation/toc-visibility.jsandsidebar-visibility.jstoggle the--no-toc/rail state classes (config inassets/js/modules/navigation/config.js) - API surface: classes
.bd-layout,.bd-layout--no-sidebar,.bd-layout--sidebar-collapsed,.bd-sidebar,.bd-sidebar-desktop-header,.bd-sidebar-fab,.bd-main,.bd-main--no-toc,.bd-toc,.bd-content,.bd-gutter; ids#bdSidebar,#tocContents,#sidebarFab,#tocFab; html classesbd-sidebar-pref-hidden,bd-toc-pref-hidden; data-attrs on<main>:data-bs-spy="scroll",data-bs-target="#TableOfContents",data-bs-offset="100",data-bs-smooth-scroll; CSS vars--zer0-sidebar-width(17rem),--zer0-sidebar-toc-width(12rem),--zer0-sidebar-rail-width/--zer0-sidebar-toc-rail-width(fallback 2.25rem only β undefined as tokens),--zer0-layout-max-width-xl/xxl,--zer0-motion-duration-base/fast,--bd-sidebar-link-bg - Tests:
test/visual/core/styling.spec.jsβ "default layout page exposes docs-layout regions" assertsmain.bd-mainand.bd-contentare visible.test/visual/features/content-enhancements.spec.jsβ "docs article exposes ToC on desktop" asserts.bd-toc, #tocContentsattached and the visibility toggles attached; "mobile exposes sidebar FAB and ToC FAB" asserts#sidebarFab/.bd-sidebar-fabvisible and#tocFabattached. No test exercises the collapsed-rail transition or the FOUC pref guards. - Gaps / improvement ideas:
--zer0-sidebar-rail-widthand--zer0-sidebar-toc-rail-widthare consumed only via inline fallbacks and never defined in_sass/tokens/β promote them to real tokens for fork override. No automated coverage of the hide/show rail collapse, thebd-*-pref-hiddenpre-paint guard, or keyboard reachability of the rail toggle when collapsed..bd-sidebar/.bd-tocusearia-controlson offcanvas-lg containers that are static on desktop β consider verifying screen-reader semantics in the rail state where most content isvisibility: hidden.
Nav-tree sidebar (YAML "tree" mode)¶
- Purpose: Renders a hierarchical left-sidebar nav (up to 3 levels) from a
_data/navigation/*.ymlfile selected bypage.sidebar.nav. Used in the docs sidebar's "tree" mode. - Capabilities: Bootstrap collapse-based expand/collapse with chevron rotation; per-item
icon,url,expandeddefault state, andchildren; active-link highlighting viapage.url == item.url; depth-based font weight/size styling (data-depth="0|1|2"); leaf items without URLs render as muted text; graceful "navigation not found" fallback; keyboard-focus outline under.keyboard-nav; dark-mode hover/active tints. - Source:
- SCSS:
_sass/core/_nav-tree.scss - Markup:
_includes/navigation/nav-tree.html(wrapped by_includes/navigation/sidebar-left.htmlin anav.nav-tree[data-nav-tree]);_includes/navigation/nav_list.htmlis a deprecated thin wrapper that just delegates to nav-tree - Plugin/data:
_data/navigation/*.yml - API surface: classes
.nav-tree,.nav-tree-root,.nav-tree-item,.nav-tree-link,.nav-tree-toggle,.nav-tree-text,.nav-tree-children,.nav-tree-chevron,.active,.collapsed; data-attrsdata-depth="0|1|2",data-nav-tree; Bootstrapdata-bs-toggle="collapse"/data-bs-target/aria-expanded/aria-controls; CSS vars--bs-body-color,--bs-primary,--bs-primary-bg-subtle,--bs-tertiary-bg,--bs-border-color,--bs-primary-rgb - Tests: No automated tests (no spec renders a tree-mode sidebar or asserts expand/collapse, active state, or chevron rotation).
- Gaps / improvement ideas: No coverage of collapse toggling,
aria-expandedsyncing, active-link resolution, or the "not found" fallback. The Level-1-with-children branch omits thearia-controls/aria-labelthat the root toggle has on its icon-only button (the root variant labels "Toggle β¦ submenu"); align ARIA across depths. Slugifieditem_idcollisions are possible if two items share a title β consider namespacing by depth/parent.
Sidebar categories (categories mode)¶
- Purpose: Groups posts/pages by Jekyll category into collapsible "ghost-pill" headers in the left sidebar, each revealing its post links. Used in the docs sidebar's "categories" mode.
- Capabilities: Collapsible category groups (Bootstrap collapse, default collapsed); accent-bar ghost-pill header with hover/expanded tinting via
color-mix; 2-line clamped post-title links; active link whenpage.url == post.url; reuses.nav-tree/.nav-tree-linkstructure scoped to.nav-tree--categories; dark-mode softer surfaces; uses--zer0-*design tokens throughout. - Source:
- SCSS:
_sass/core/_sidebar-categories.scss, plus active-state shim in_sass/core/_sidebar-extras.scss - Markup:
_includes/navigation/sidebar-categories.html(invoked bysidebar-left.htmlwithcategories=site.categories | sort) - API surface: classes
.nav-tree--categories,.sidebar-categories,.sidebar-categories-heading,.sidebar-categories-group,.sidebar-categories-toggle(+__icon/__label/__chevron),.sidebar-categories-posts,.sidebar-categories-link(+__icon/__text),.active; ids#{cat-slug}-list;data-bs-toggle="collapse"/aria-expanded/aria-controls; CSS vars--zer0-color-ink,--zer0-color-ink-muted,--zer0-color-primary,--zer0-color-bg-elevated,--zer0-color-border-translucent,--zer0-text-sm,--zer0-space-*,--zer0-font-weight-*,--zer0-leading-* - Tests: No automated tests.
- Gaps / improvement ideas: No coverage of expand/collapse or active-state. Heading is an
<h2>even when nested under the sidebar's own<h2>"Browse docs" β heading-order/landmark check advisable. The-webkit-line-clamp2-line truncation has no non-WebKit fallback (acceptable but worth a fade). Active-state is defined in two places (_sidebar-categories.scssand the_sidebar-extras.scssshim) with slightly different backgrounds β consolidate to avoid drift.
Sidebar folders (collection mode)¶
- Purpose: Live left-sidebar document tree of a collection, grouped by sub-folder into collapsible sections. Used by the sidebar's
collectionmode (and byautowhen no curated_data/navigation/<collection>.ymlexists). - Capabilities: Filters
sidebar_exclude: truedocs; sorts bypath/title/date(+reverse); groups viagroup_by_expon the doc path relative to the collection folder; humanizes folder names; a folder'sindex.*doc becomes the folder link (link + separate collapse toggle, mirroringnav-tree.html); group containing the current page starts expanded (expand: trueexpands all);aria-current="page"on the active link; heading from collectiontitle/iconmetadata; "no collection found" fallback. - Source:
- SCSS: shared nav-tree styles in
_sass/core/_nav-tree.scss - Markup:
_includes/navigation/sidebar-folders.html(invoked viasidebar-nav.html, wrapped in.nav-tree#sidebar-content[data-nav-tree]) - JS: none β Bootstrap collapse handles disclosure (
assets/js/side-bar-folders.jstargeted the pre-2026.folder/.filemarkup and is now inert) - API surface: classes
.sidebar-collection,.sidebar-collection-heading,.sidebar-collection-group,.nav-tree-*family,.active; ids#sidebar-coll-{group-slug}; attrsdata-bs-toggle="collapse",aria-expanded,aria-controls,aria-current; front-matter configpage.sidebar.{collection,sort,reverse,expand}, per-docsidebar_exclude - Tests: No automated tests.
- Gaps / improvement ideas: No behavioral coverage of expand/collapse or active-trail expansion.
assets/js/side-bar-folders.jsis now dead code and can be removed once no fork consumes it. Deeply nested sub-folders flatten to "parent / child" group labels rather than true nesting.
Section sidebar (topic navigation)¶
- Purpose: A self-contained sticky topic sidebar for section/archive pages: a desktop card of topics (with article counts) plus a mobile offcanvas drawer, with in-page smooth-scroll and scroll-spy highlighting. Used by
_layouts/section.html. - Capabilities: Desktop sticky card (
top: 80px) listing "All Articles" + up to 15 tag-derived topics with per-topic counts and a "View All Tags" footer when >15; section stats (total/topics/featured); mobile offcanvas variant; inline<style>+ IIFE<script>providing smooth-scroll to#anchorsections and an IntersectionObserver scroll-spy (rootMargin: -20% 0px -80% 0px) toggling.active. - Source:
- SCSS: inline
<style>block in the include (.sidebar-nav .nav-link*); no shared partial - Markup:
_includes/navigation/section-sidebar.html(paramssection_posts,sub_categories,page_title,sidebar_id) - JS: inline IIFE in the include
- API surface: classes
.section-sidebar-desktop,.sidebar-nav,.nav-link/.active,.nav-text,.offcanvas; ids#{sidebar_id}(defaultsectionSidebar),#all-posts,#{tag-slug}; data-attrsdata-section,data-bs-dismiss="offcanvas"; CSS vars--bs-primary-rgb,--bs-body-color - Tests:
test/visual/features/layouts.spec.jsβ "news section page loads with layout containment" asserts the section sidebar sits left of#all-posts(guards.section-layout-sidebar/#all-posts), but does not exercise this include's scroll-spy or topic counts directly. - Gaps / improvement ideas: Ships its own inline CSS and JS rather than reusing
_sass/assets/js/modulesβ duplicates the scroll-spy logic already inmodules/navigation/scroll-spy.js. Smooth-scroll and scroll-spy here are not covered by any behavioral assertion. The active topic uses<a>withoutaria-current; add it for assistive tech. Hard-codedtop: 80pxshould reference the navbar-height token.
Table of Contents (Liquid TOC parser + sidebar-right)¶
- Purpose: Builds an "On this page" outline by parsing the rendered page HTML for
h1βh6and emitting a nested list, rendered in the sticky right column / offcanvas. Only runs when the page actually contains<h2>/<h3>/<h4>. - Capabilities: allejo/jekyll-toc Liquid parser with
h_min/h_max,sanitize,class,item_class,anchor_class,skip_no_ids, ordered/unordered,no_tocskip, base_url; here invoked withh_min=1 h_max=3 sanitize=true skip_no_ids=true class="list-group-flush" item_class="list-group-item"; "No sections on this page" fallback; sticky desktop column with scrollable overflow; active-link border-left accent. - Source:
- SCSS:
_sass/core/_toc.scss(#TableOfContents,.toc,.toc-toggle,.toc-collapse), plus.bd-toc nav β¦rules in_sass/core/_docs-layout.scssand the active shim in_sass/core/_sidebar-extras.scss - Markup:
_includes/content/toc.html(parser),_includes/navigation/sidebar-right.html(#tocContentsoffcanvas +nav#TableOfContentswrapper + toggle button) - JS: scroll-spy via
assets/js/modules/navigation/scroll-spy.js - API surface: ids
#TableOfContents,#tocContents,#tocLabel; classes.bd-toc,.toc,.toc-collapse,.bd-toc-collapse,.list-group-flush,.list-group-item,.bd-toc-visibility-toggle,a.active;role="navigation",aria-label="Table of Contents"; CSS vars--bd-toc-color,--bs-tertiary-bg,--bs-border-color,--bd-violet/--bd-violet-rgb - Tests:
test/visual/features/content-enhancements.spec.jsβ "docs article exposes ToC on desktop" asserts.bd-toc, #tocContentsis attached (presence only). No spec asserts the TOC parser output (heading nesting, anchor hrefs, the no-sections fallback) or active-link highlighting. - Gaps / improvement ideas: Both
_sass/core/_toc.scss(.toc/#TableOfContents) and_docs-layout.scss(.bd-toc) style the TOC, with overlapping/legacy.tocrules that the current markup (.bd-toc) doesn't use β prune the dead.toc/.toc-collapseruleset or confirm a consumer. No behavioral test of the heading parser despite it being intricate Liquid. Consider asserting that TOC anchorhrefs resolve to real heading ids (the scroll-spy depends on this).
TOC FAB (mobile trigger)¶
- Purpose: A fixed circular button on small screens that opens the TOC offcanvas (or restores the desktop-hidden TOC). Rendered from the footer so it shares the footer stacking context.
- Capabilities: Shown only when the page has an effective sidebar (
page.sidebar != false, or non-featured/breaking default);d-lg-none; toggles the#tocContentsoffcanvas viaTocVisibility;aria-controls="tocContents", dynamicaria-expanded/aria-label. - Source:
- SCSS: FAB stacking in
_sass/layouts/_navbar-extras.scss(referenced);_sass/core/_sidebar-extras.scssnotes the move - Markup:
_includes/navigation/toc-fab.html(included from_includes/core/footer.html) - JS:
assets/js/modules/navigation/toc-visibility.js(_fab,_fabToggle,bd-toc-fab--restore) - API surface: id
#tocFab; classes.bd-toc-fab,.bd-toc-toggle,.bd-toc-fab--restore; attrsaria-controls="tocContents",aria-expanded,aria-label - Tests:
test/visual/features/content-enhancements.spec.jsβ "mobile exposes sidebar FAB and ToC FAB" asserts#tocFabis attached at mobile viewport (presence only, behind a skip guard). - Gaps / improvement ideas: No test that clicking the FAB opens/restores the TOC or that
aria-expandedflips. The FAB's effective-sidebar logic is duplicated betweentoc-fab.htmlanddefault.htmlβ extract to a shared include/variable.
TOC visibility toggle (hide/show + persistence)¶
- Purpose: Lets desktop users collapse the right TOC column to a slim rail (and restore it), persisting the choice in localStorage; on mobile, toggles the TOC offcanvas. Paired with a pre-paint FOUC guard.
- Capabilities: Reads/writes
localStorage['zer0-nav-toc-visible']; appliesbd-main--no-toc,bd-toc--hidden,bd-toc-fab--restore, andhtml.bd-toc-pref-hidden; updatesaria-expanded/aria-label/titleon all toggles; mobile path uses Bootstrap Offcanvas; dispatchesnavigation:tocVisibilityCustomEvent; breakpoint-aware viaisBelowBreakpoint('lg');setVisible()/toggle()/isVisible()API; focus management on restore. - Source:
- SCSS: rail/fade states in
_sass/core/_docs-layout.scss - Markup:
.bd-toc-visibility-togglebuttons in_includes/navigation/sidebar-right.html; FOUC guard<script>inline there - JS:
assets/js/modules/navigation/toc-visibility.js(+ selectors inconfig.js) - API surface: classes
.bd-toc-visibility-toggle; html classbd-toc-pref-hidden; localStorage keyzer0-nav-toc-visible; eventsnavigation:tocVisibility(detail{visible}); selectors from config (tocWrapper,tocFab,mainArea,rightSidebar,tocVisibilityToggle) - Tests:
test/visual/features/content-enhancements.spec.jsβ "docs article exposes ToC on desktop" asserts the toggle is attached. No spec clicks the toggle, verifies persistence across reload, or checks thearia-expandedflip. - Gaps / improvement ideas: No behavioral/persistence test for the core feature (toggle β reload β state retained). Console
console.logleft in production init. Consider asserting thenavigation:tocVisibilityevent and that focus lands correctly after collapse/restore.
Scroll-spy (active heading highlight)¶
- Purpose: Highlights the TOC link for the currently-visible heading using an IntersectionObserver, and auto-scrolls the TOC to keep the active link in view.
- Capabilities: Maps each
#TableOfContents a[href^="#"]to its heading by id; observes headings withrootMargin: -80px 0px -80px 0px,threshold: [0,0.25,0.5,0.75,1]; picks the most-visible heading and toggles.active; auto-scrolls TOC container;setActiveById()/getActive()/destroy(); dispatchesnavigation:sectionChange; safe getters that warn-and-continue. (Note: a second, independent scroll-spy exists inline insection-sidebar.htmlfor section pages.) - Source:
- SCSS: active styling via
.bd-toc nav a.active(_docs-layout.scss) and.bd-toc nav a.activeshim (_sidebar-extras.scss) - JS:
assets/js/modules/navigation/scroll-spy.js(+config.js); Bootstrap's own scrollspy also wired viadata-bs-spy="scroll"on<main>indefault.html - API surface: events
navigation:sectionChange(detail{link, href}); classes.active; CSS selectors#TableOfContents a,.bd-toc .offcanvas-body(configtocLinks/tocContainer);data-bs-spy="scroll"onmain.bd-main - Tests: No automated tests (no spec scrolls a docs page and asserts the active TOC link changes).
- Gaps / improvement ideas: Two competing active-link drivers β Bootstrap's
data-bs-spy="scroll"on<main>and the custom IntersectionObserver β may fight over.active; verify/dedupe. No behavioral test of active-link tracking, the most-visible selection, or TOC auto-scroll. Console logging in init.
Page intro header (.bd-intro family)¶
- Purpose: The full-bleed hero at the top of default-layout pages: title/subtitle/description over a darkened preview image, plus a frosted metadata footer (author, dates, category, difficulty, tags, reading time, source) and an action cluster (Share, Copilot Agent prompts, Edit on GitHub).
- Capabilities: Preview-image resolution with
assets_prefixauto-prepend; author name/URL from_data/authors.ymlorsite.author; published vs. updated date logic (only shows "Updated" when it differs); most-specific category badge with link; difficulty/level badge with color variants (beginner/intermediate/advanced/expert); up to 5 tag chips + "+N"; reading-time estimate (number_of_words / 200); Share dropdown (Reddit/LinkedIn/X/Copy Link withdata-copy+js-linkedin-share); Copilot Agent prompt dropdown that prefills a GitHub issue from_data/prompts.ymlwith page context + environment tables; Edit-on-GitHub link; frosted-glass footer withbackdrop-filter+@supportsfallback. - Source:
- SCSS:
_sass/core/_docs-layout.scss(.bd-intro*,.bd-intro-meta-*,.bd-intro-badge*,.bd-intro-actions,.copilot-agent-*) - Markup:
_includes/content/intro.html(included by_layouts/default.html, skipped for article/note layouts andhide_intro) - JS:
assets/js/ui-helpers.js(data-copy,.js-linkedin-share) β referenced, not in this cluster - Plugin/data:
_data/authors.yml,_data/prompts.yml - API surface: classes
.bd-intro,.bd-intro-inner/-content/-title/-subtitle/-description,.bd-intro-meta-footer/-row/-item/-icon/-link/-bottom/-meta,.bd-intro-meta-item--tags/--source,.bd-intro-badge(+--level-*,--tag,--more),.bd-intro-tag-list,.bd-intro-actions,.bd-intro-action-link,.copilot-agent-menu/-item*,.js-linkedin-share; attrsaria-label="Page metadata",data-copy,data-share-url/-title/-description; CSS vars--bd-intro-min-height,--bd-intro-meta-footer-bg/-border,--bd-intro-meta-gap/-separator, many--zer0-text-*/--zer0-space-*/--zer0-font-weight-* - Tests:
test/visual/features/layouts.spec.jsβ "meta footer wraps actions and stacks below description" asserts.bd-intro-meta-footer,.bd-intro-actions,.bd-intro-meta-row[aria-label]visible, description stacked above footer, and β₯2 action buttons; "intro action buttons share consistent height" asserts non-dropdown.bd-intro-actions .btnheights differ β€14px; homepage/article single-h1 checks indirectly guard against duplicate intro H1s.test/visual/core/accessibility.spec.jsβ "intro metadata row has aria-label when present" asserts the row'saria-labelis non-empty. - Gaps / improvement ideas: No test of date logic (published vs. updated dedupe), category/difficulty badge resolution, tag overflow (+N), or reading-time computation. The hero uses a hardcoded inline
backgroundgradient with#ffftext on an arbitrary preview image β color-contrast is unverified (the axe scans disablecolor-contrast). Copilot-Agent issue bodies embed full page+env context; no test asserts the generatedissue.newURL encodes correctly.
Docs code-example chrome (.bd-example / .bd-code-snippet / clipboard)¶
- Purpose: Bootstrap-docs-style framing for live component examples and their code snippets, plus AnchorJS heading links and ClipboardJS "Copy" buttons injected over highlighted code. Primarily used by the cheatsheet/style page.
- Capabilities: Bordered/rounded example + snippet containers with responsive bleed; spacing normalizers for nested components; example variants (row/cols/cssgrid/flex/ratios/offcanvas/zindex/placeholder); custom tooltip/popover demos;
.bd-clipboard/.btn-clipboard+.bd-edit/.btn-edit(desktop-only) over.highlight;.bd-placeholder-img(-lg);scroll-margin-top: 80pxon focusable/heading targets.docs.min.jsbundles AnchorJS (anchors on.bd-content > h2..h5), ClipboardJS (injects.bd-clipboardbefore eachdiv.highlight, copies sibling, Bootstrap tooltip "Copied!"), Bootstrap tooltip/popover/toast/modal demo wiring, and an Algolia docs-search binding. - Source:
- SCSS:
_sass/core/_docs-code-examples.scss - Markup:
_includes/docs/bootstrap-docs.html(thebd-cheatsheetwith.bd-example/.bd-example-snippet/.bd-heading) - JS:
assets/js/docs.min.js - API surface: classes
.bd-code-snippet,.bd-example,.bd-example-snippet,.bd-example-{row,cols,cssgrid,flex,ratios,offcanvas,zindex-levels,border-utils,β¦},.bd-clipboard,.btn-clipboard,.bd-edit,.btn-edit,.highlight-toolbar,.bd-placeholder-img(-lg),.bd-heading; JS globalswindow.anchors(AnchorJS),ClipboardJS; CSS vars--bd-example-padding,--bd-violet/--bd-violet-rgb,--bd-pre-bg,--bs-tooltip-*/--bs-popover-* - Tests: No automated tests target
.bd-example/.bd-clipboard/AnchorJS. (Thecode-blocksspecs infeatures/content-enhancements.spec.jstest a different system β.code-block-header/.code-line-numbersfrom the rouge enhancer, notdocs.min.js's.bd-clipboard.) - Gaps / improvement ideas:
docs.min.jsstill carries an Algolia "bootstrap" docs-search binding (apiKey,indexName:'bootstrap', getbootstrap.com URL rewrites) that is dead/irrelevant to this theme β strip it. Two parallel code-copy systems coexist (docs.min.js.btn-clipboardfor.bd-examplepages vs. the rouge.code-block-header .copyused in real content) β document which applies where, and add a behavioral copy test for at least the content path..bd-example::after { content: null }is invalid CSS (should be""/none). No anchor-link or copy-button behavioral coverage.
Content tables (markdown/HTML table styling + CSV copy)¶
- Purpose: Styles bare Kramdown and Bootstrap
.tablemarkup inside reading areas with a card-like, sticky-header, striped, hover-highlight look, and injects a per-table "Copy CSV" toolbar button. - Capabilities: Token-driven striping/hover/sticky-header via
color-mix; rounded card corners; horizontal scroll on mobile (min-width: 36rem);code/link/strongstyling inside cells; scoped to.bd-content,.landing-content-body,.post-content,.note-content,.notebook-content,.page-contentand excludes#sitemapTable/#admin-content; JS wraps each eligible table in.content-table-wrapperwith a.content-table-toolbar+.table-copy-csvbutton that serializes thead/tbody/tfoot to CSV (proper quoting), copies via Clipboard API withexecCommandfallback, shows azer0UI.showToastand a transient "Copied!"/"Copy failed" state. - Source:
- SCSS:
_sass/components/_content-tables.scss - Markup: generated by JS (
.content-table-wrapper/.content-table-toolbar);_includes/components/quick-index.htmlis a separate small collection-index list (<ul>of pages in the current collection + optional#categoriesmount) - JS:
assets/js/table-copy.js(loadeddeferfrom_includes/core/head.html) - API surface: classes
.content-table-wrapper,.content-table-toolbar,.table-copy-csv(+.copied),.table-responsive; ids excluded#sitemapTable, scope#admin-content; JS depwindow.zer0UI.showToast; CSS vars--zer0-table-stripe/-hover/-header-bg/-header-border,--zer0-color-*,--zer0-space-*,--zer0-shadow-focus,--zer0-motion-duration-fast - Tests:
test/visual/features/content-enhancements.spec.jsβ "landing comparison table has toolbar and distinct header" waits for.table-copy-csv, asserts.content-table-wrapper .table-copy-csvis visible, and that thead vs. tbody background colors differ. Does not verify the actual CSV output or clipboard write. - Gaps / improvement ideas: No test of CSV correctness (cell quoting, colspan handling β note
getTableMatrixignorescolspan/rowspan, so merged cells produce misaligned CSV), theexecCommandfallback path, or the failure/empty-table toast.quick-index.htmlis unstyled (text-decoration/active-state) and its<p>Quick Index</p>sits illegally inside a<ul>β fix the markup. Consider keyboard/aria-liveannouncement parity between toolbar copy and toast.
Landing, Home & Component Polish¶
The marketing-facing surface of the theme: the data-driven landing layout (hero, feature cards, quick-links bar, install cards, get-started), the minimal home/index/welcome layouts, the reusable section/feature-card/cta-button/info-section includes, plus the cross-cutting Bootstrap "polish" layer (button ripples, card hover lift, hero/stagger animations, table/badge/link/form refinements) driven by ui-enhancements.js, ui-helpers.js, and share-actions.js, with a token-aware skeleton loader and an orphaned particles.js hero background.
Landing layout¶
- Purpose: The data-driven marketing homepage shell β a hero with CTAs and image, a quick-links bar, the rendered Markdown body, a features grid, a get-started install section, and an author/E-E-A-T block. Gives first-time visitors a polished first impression and a clear path to install.
- Capabilities: Hero with
page.title/page.descriptionand up to three CTAs (primary/secondary/tertiary, tertiary URL falls back tosite.resources.github_repo); inlineaspect-ratio+max-widthreserve the hero-image box pre-CSS to avoid layout jerk; eager/high-priority hero<img>with JS fade-in; placeholderbi-code-squarecard when nohero_image; data-driven features section (rendered only iflanding.features.items); data-driven get-started section with install cards; suppression of a duplicate first body<h1>via SCSS (README-as-homepage pattern). - Source:
- SCSS:
_sass/layouts/_landing.scss,_sass/layouts/_global-chrome.scss(.min-vh-50,.landing-hero-mediashim),_sass/components/_ui-enhancements.scss(hero/stagger animations,.bg-darkquick-links) - Markup:
_layouts/landing.html,_includes/landing/landing-quick-links.html,_includes/landing/landing-install-cards.html,_includes/components/cta-button.html,_includes/components/author-eeat.html - JS:
assets/js/ui-helpers.js(bindHeroImages),assets/js/ui-enhancements.js - Plugin/data:
_data/landing.yml(hero CTAs, features heading/lead/items, get_started) - API surface: classes
.landing-hero,.landing-hero-copy,.landing-hero-media,.landing-hero-img,.is-loaded,.landing-content-body,.landing-feature-card,.landing-feature-icon,.zer0-bg-hero,.zer0-section,.min-vh-50, ids#features/#get-started; CSS vars--zer0-space-section,--zer0-color-bg-elevated,--zer0-color-border,--zer0-shadow-md,--zer0-color-primary,--zer0-color-primary-rgb,--zer0-motion-duration-base,--zer0-motion-ease-standard; no custom data-attributes or events - Tests:
test/visual/features/layouts.spec.jsβ "homepage exposes exactly one accessible h1" (getByRole level 1 == 1, validates the.landing-content-body > h1:first-of-typedisplay:none suppression), "features section renders from _data/landing.yml" (skips if no#features; asserts#featuresvisible and.landing-feature-cardcount β₯ 1), "hero CTA buttons expose accessible names" (every.landing-hero a.btnhas text or aria-label), "homepage axe scan completes" (< 25 violations).test/visual/features/content-enhancements.spec.jsβ "landing comparison table has toolbar and distinct header" exercises.landing-content-body table. Axe advisory scans across all viewports. - Gaps / improvement ideas: No test asserts the hero image actually gains
.is-loaded(the anti-jerk fade-in is unverified). The tertiary-CTA GitHub fallback and the no-hero_imageplaceholder branch are untested. The duplicate-H1 suppression relies ondisplay:noneon:first-of-typewhich would wrongly hide a legitimately-first body heading that is not a title repeat. Feature cards have no entrance-animation reduced-motion test. Consider extracting the inline hero style attributes into a class to satisfy stricter CSP.
Landing quick-links bar¶
- Purpose: A dark full-width bar of four outline-light buttons (GitHub, RubyGems, Docker Hub, Fork Project) rendered directly under the hero for fast outbound navigation.
- Capabilities: Four equal-width responsive columns (
col-6 col-md-3), each aw-100outline-light button with a Bootstrap icon; URLs sourced fromsite.resources.*withsite.github.repository_urlfallbacks; slide-down entrance animation and lift-on-hover from the polish layer. - Source:
- SCSS:
_sass/components/_ui-enhancements.scss(.bg-darkslideDown +.btn-outline-lighthover),_sass/layouts/_landing.scss(.landing-quick-linkshover lift β note: the include does not currently emit the.landing-quick-linksclass) - Markup:
_includes/landing/landing-quick-links.html - JS: β (styling-only)
- Plugin/data:
site.resourcesconfig - API surface: classes
.bg-dark,.btn-outline-light,.btn-sm,.w-100; the SCSS.landing-quick-linksselector exists but no markup applies it; CSS vars--zer0-color-bg-elevated,--zer0-color-border,--zer0-motion-duration-fast - Tests: No automated tests (no spec targets the quick-links bar specifically).
- Gaps / improvement ideas: Dead/mismatched selector β
.landing-quick-linksstyling in_landing.scssnever applies because the include emits.bg-darkinstead; either add the class to the markup or remove the orphan rule. All four links open in the same tab semantics viatarget="_blank" rel="noopener"but there is novisually-hidden"opens in new tab" cue (unlikecta-button.html). No test verifies the four links resolve to non-#hrefs.
Landing install cards¶
- Purpose: Three side-by-side install-method cards (Ruby Gem, Docker Image, Fork & Deploy) plus a Contributing-guide CTA, shown in the get-started section to convert visitors into users.
- Capabilities: Color-coded card headers (primary/info/secondary); copy-ready
<pre><code>install snippets; smart fork-URL builder that appends/forkonly when absent; ordered fork checklist; outbound buttons to RubyGems/Docker Hub/GitHub withsite.resources.*fallbacks; top-border accent on hover via the polish layer. - Source:
- SCSS:
_sass/components/_ui-enhancements.scss(#get-started .cardtop-border hover accent) - Markup:
_includes/landing/landing-install-cards.html - JS: β (the snippets are not wired to the
data-copycopy helper) - Plugin/data:
site.resources.rubygems,site.resources.docker,site.resources.github_fork,site.github.repository_url,site.resources.contributing - API surface: classes
.card,.card-header,.bg-primary/.bg-info/.bg-secondary,.btn-outline-primary/-info/-secondary, id#get-started; CSS vars consumed via Bootstrap (--bs-primary,--bs-info,--bs-secondary) - Tests: No automated tests (the get-started/install cards are not asserted by any spec).
- Gaps / improvement ideas: Install snippets are plain
<pre>with no copy button despiteui-helpers.jsproviding a readydata-copybinding β a clear UX win going untested/unused. The#get-started .card &.card-header.bg-primary ~ .card-bodySCSS selector is malformed (a.cardis never also a.card-header), so those~ .card-bodyborder rules never match. No test verifies the/forkURL-builder logic.
Home layout¶
- Purpose: A minimal homepage container β optional
<h1>, the page content, and an RSS subscribe link β for clean landing/showcase pages that supply their own structure. - Capabilities: Optional title via
page.titlewithhide_title: trueto keep SEO title but suppress the visible<h1>; opt-out RSS link viarss_subscribe: false; bypasses sidebar for a distraction-free presentation. - Source:
- SCSS: β (uses
.page-heading,.rss-subscribefrom global typography) - Markup:
_layouts/home.html(inheritsroot.html) - JS: β
- API surface: classes
.home,.page-heading,.rss-subscribe; front mattertitle,hide_title,rss_subscribe - Tests: Partially β
features/layouts.spec.js"homepage exposes exactly one accessible h1" applies when/uses this layout; no test exerciseshide_title/rss_subscribetoggles. - Gaps / improvement ideas: The
hide_titleandrss_subscribe:falsebranches are untested. No automated check that the RSS link resolves to a valid/feed.xml.
Index layout¶
- Purpose: A full-width fluid container intended for search/index/archive pages that need edge-to-edge content without the sidebar.
- Capabilities:
container-fluidwith responsive top/bottom padding; semantic#search-indexwrapper; minimal structure to host search forms/results. - Source:
- SCSS: β
- Markup:
_layouts/index.html(inheritsroot.html) - JS: β
- API surface: id
#search-index; classes.container-fluid,.pt-5,.py-5 - Tests: No automated tests target the index layout directly.
- Gaps / improvement ideas: Documented as a search-index layout but ships no search wiring of its own (search lives in the modal). The duplicate
pt-5 py-5padding is redundant. Consider documenting/removing if effectively unused.
Welcome layout¶
- Purpose: The first-run onboarding experience for freshly-installed remote-theme sites β shows a hero, a 3-file starter accordion, a config wizard, and next-step cards until the site is configured, then renders the user's content.
- Capabilities: Gating via
components/setup-check.html(site_needs_setup); hero with "required/optional" file checklist and badges; Bootstrap accordion of_config.yml/Gemfile/index.mdstarters with syntax-highlighted snippets; embedded setup wizard (setup/wizard.html); next-steps cards; falls through to a centered content container oncesite_configured. - Source:
- SCSS: β (Bootstrap accordion/card/badge/alert utilities)
- Markup:
_layouts/welcome.html,_includes/components/setup-check.html,_includes/setup/wizard.html - JS: β (Bootstrap collapse/accordion bundle)
- API surface: ids
#minimal-starter,#setup-wizard,#starterAccordion,#starter-config/-gemfile/-index; classes.accordion,.accordion-item,.text-bg-*,.badge.rounded-pill; configsite_configured, plugin varsite_needs_setup - Tests:
test/visual/layouts.spec.jsβ "welcome page has an h1 when present" (skips if/welcome/404s; asserts β₯ 1 h1). Only the configured-or-unconfigured h1 presence is checked. - Gaps / improvement ideas: The accordion interaction, wizard rendering, and the
site_needs_setupgating branch are untested behaviorally. Next-step card links point to hardcoded GitHub README anchors that can rot. No reduced-motion or accessibility assertion on the accordion.
Section include (components/section.html)¶
- Purpose: A reusable, token-aware section wrapper that standardizes vertical rhythm, container width, optional heading/lead block, and ARIA labelling across landing/content bands.
- Capabilities: Variants
default/muted/inverse; spacingtight/normal/loosemapped to--zer0-space-section; configurable container class and heading level (2β6); auto-generatedid+-headingandaria-labelledby(oraria_labeloverride); heading/lead block skipped entirely when omitted so it can wrap pure content. - Source:
- SCSS:
_sass/layouts/_landing.scss(.zer0-section,--spacing-tight,--spacing-loose) - Markup:
_includes/components/section.html - JS: β
- API surface: classes
.zer0-section,.zer0-section--spacing-tight,.zer0-section--spacing-loose,.bg-body-tertiary(muted),.bg-primary.text-white(inverse); paramsid,variant,container,spacing,heading,heading_level,lead,aria_label,content; CSS var--zer0-space-section - Tests: No automated tests (no spec invokes the section component directly; the landing layout hand-rolls equivalent markup rather than calling this include).
- Gaps / improvement ideas: The landing layout does NOT use this component (its features/get-started sections inline the same structure), so the include is effectively unexercised β consider refactoring
landing.htmlto call it, which would also bring test coverage. No test for the heading-level or aria-label override logic.
Feature card include (components/feature-card.html)¶
- Purpose: Renders a single feature object from
_data/features.ymlas a Bootstrap card with icon, title, description, sub-feature list, id/version/tag badges, and optional docs/demo footer. Used by the features page and other registries β distinct from the landing layout's inline.landing-feature-card. - Capabilities: Configurable border
style, icon + icon color,show_refs,compact(hides sub-features),features_limit; rendersid/version/tag badges; conditionalcard-footerwith Documentation and Demo buttons. - Source:
- SCSS:
_sass/components/_ui-enhancements.scss(.cardhover lift,.card-body i/.rounded-circleicon animation,.feature-categories .badge) - Markup:
_includes/components/feature-card.html - JS: β
- Plugin/data:
_data/features.yml - API surface: classes
.card,.card-title,.card-text,.badge.bg-primary/.bg-secondary/.bg-light,.card-footer,.btn-outline-*; paramsfeature,style,icon,icon_color,show_refs,compact,features_limit - Tests:
test/visual/features/category-badges.spec.jsβ "feature category badges link to in-page anchors" exercises.feature-categories a.badge[href^="#"]on/features/(asserts the anchor target exists). The feature-card component's own structure/footer is not directly asserted. - Gaps / improvement ideas: Note the naming collision: this
feature-card.htmlis unrelated to the landing layout's.landing-feature-card(which is hand-coded). Thereferencesrendering branch (show_refs) andcompactmode are untested. Demo-link guardf.link != "/"is brittle.
CTA button include (components/cta-button.html)¶
- Purpose: A themed call-to-action
<a>button used by the landing hero, normalizing variant/size, icon, relative-URL handling, and accessible external-link semantics. - Capabilities: Variants
primary/secondary/outline/light; sizessm/md/lg; optional leading Bootstrap icon; smart URL handling (leaves absolute/mailto:/tel:/#anchoruntouched, runs others throughrelative_url);externalopens new tab withrel="noopener noreferrer"and avisually-hidden"(opens in a new tab)" cue;aria_labeloverride. - Source:
- SCSS: β (Bootstrap
.btn-*+ the.btnripple/hover polish;.zer0-ctahas no dedicated rule) - Markup:
_includes/components/cta-button.html - JS:
assets/js/ui-enhancements.js(ripple),assets/js/ui-enhancements.jsscroll-spy.activefor#anchorCTAs - API surface: classes
.btn,.zer0-cta(marker only, unstyled),.btn-primary/-secondary/-outline-light/-light,.btn-lg/-sm; paramslabel,url,variant,size,icon,external,aria_label - Tests:
test/visual/layouts.spec.jsβ "hero CTA buttons expose accessible names" asserts every.landing-hero a.btnhas a non-empty accessible name (covers this component's output indirectly). No test targets.zer0-ctaor the external-link cue. - Gaps / improvement ideas:
.zer0-ctais emitted but never styled β either a hook for future theming or dead markup; document or use it. The URL-normalization branches and theexternalvisually-hidden cue are not directly asserted. Variantoutlinemaps only tobtn-outline-light(no dark/colored outline option).
Info section / Settings offcanvas (components/info-section.html)¶
- Purpose: A unified right-side settings offcanvas with three tabs β Appearance (color mode, theme skin, backgrounds, primary color), Site (environment, theme & build info, admin links) and Developer (page location, page metadata, source shortcuts).
- Capabilities: Bootstrap offcanvas + nav-tabs with three panes; Site tab shows a Prod/Dev badge from
is_production; admin quick-links render only when the target admin pages exist in the build (pure-Liquid scan incomponents/admin-links.htmlunderinclude_cachedβ local_pluginsnever load under the github-pages gem, so a plugin-computed lookup would be dead); page-metadata table; delegated copy-to-clipboard for URL fields (data-zer0-copy/data-zer0-copy-target); embedshalfmoon,background-settings,env-switcher,theme-info,admin-links,breadcrumbs,dev-shortcuts; hosts the appearance.js primary-color picker in#zer0AppearanceSlot. - Source:
- SCSS:
_sass/core/_navbar.scss(tab underline styles + compact xs tabs) - Markup:
_includes/components/info-section.html(+env-detect,env-switcher,theme-info,halfmoon,admin-links,dev-shortcuts,background-settings,breadcrumbs) - JS: Bootstrap bundle (offcanvas/tab), inline delegated copy handler,
assets/js/modules/theme/appearance.js(slot mount) - API surface: ids
#info-section,#infoTabs,#appearance-pane/#site-pane/#developer-pane,#zer0AppearanceSlot; classes.offcanvas-end,.nav-tabs,.tab-pane,.list-group-flush,.table-sm.table-hover; data-attributesdata-bs-toggle="tab|offcanvas",data-bs-target,data-zer0-copy,data-zer0-copy-target - Tests:
test/visual/settings-panel.spec.js(smoke) β tab wiring, one-of-each appearance controls, color-mode flipsdata-bs-theme, admin links resolve 200, breadcrumb gating, no horizontal scroll at 320px. Evidence:test/visual/evidence/settings-panel/. - Gaps / improvement ideas: Tab roving-tabindex accessibility is asserted only indirectly (Bootstrap's implementation). The copy buttons rely on
navigator.clipboard(silently no-op on insecure origins other than localhost).
Bootstrap component polish (UI enhancements)¶
- Purpose: A site-wide interaction/animation layer that elevates plain Bootstrap components β button ripple + hover lift, card hover lift with icon animation, hero/quick-links/feature-card entrance animations, and table/badge/link/form/code-block refinements.
- Capabilities:
.btnripple (CSS::beforegrow + JS-injected.ripplespan), button translateY hover/active +.btn-lgsizing;.cardtranslateY(-8px) hover with shadow and iconscale/rotate; herofadeInUp/fadeInRightand image drop-shadow;slideDownquick-links; staggeredfadeInUpfor feature cards (nth-child(1..3), matching the 3 shipped items); install-card top-border accent; table-row hover tint; badge hover scale + feature-category badge focus ring; link underline-on-hover; form-control focus glow + translateY; focus-visible outlines; smooth scroll with 80px navbar offset andscroll-margin-top; mobile/touch hover suppression + 44px tap targets; print optimizations. - Source:
- SCSS:
_sass/components/_ui-enhancements.scss,_sass/layouts/_global-chrome.scss - Markup: applies globally to Bootstrap classes in all layouts/includes
- JS:
assets/js/ui-enhancements.js(ripples, scroll animations, image loading, scroll-spy),assets/js/ui-helpers.js(toast, clipboard,data-copy, hero fade-in) - API surface: classes
.btn,.ripple,.card,.badge,.feature-categories,.table,.shadow-sm/.shadow/.shadow-lg(overridden),.animate-on-scroll,.nav-link.active/.btn.active,.loaded; data-attributesdata-copy; JSwindow.zer0UI.showToast(message, {variant,duration}),window.zer0UI.copyToClipboard(text); keyframesfadeInUp/fadeInRight/slideDown/ripple-animation; CSS vars--bs-primary(-rgb),--bs-dark,--zer0-space-2,--zer0-layer-toast - Tests:
test/visual/features/layouts.spec.jsβ "intro action buttons share consistent height".test/visual/features/content-enhancements.spec.jsβ "landing comparison table has toolbar and distinct header" (thead bg β tbody bg) and the code-block header/copy tests.test/visual/core/layout-chrome.spec.jsβ footer-link checks.test/visual/core/accessibility.spec.js/test/visual/core/responsive.spec.jsβ axe advisory scans per viewport. No spec asserts the ripple element, card hover transform, badge hover, or thewindow.zer0UItoast/clipboard API. - Gaps / improvement ideas: Heavy use of hardcoded
rgba(0,0,0,β¦)shadows andrgba(255,255,255,β¦)ripple/gradient values instead of--zer0-*tokens β these do not adapt to dark mode (the rest of the system is token-driven). The global.btn::before/.card:hovertransforms are unverified by tests and the JS ripple listener attaches to every.btnon load (no delegation; dynamically-added buttons miss it).window.zer0UI.showToast/copyToClipboardhave no unit/behavioral test.scroll-padding-top: 80pxis a magic number duplicated across rules.
Share actions (LinkedIn/copy share)¶
- Purpose: Enhances LinkedIn share links and copy-share buttons by building a cleaned, de-duplicated article summary (title + description + excerpt + URL), copying it to the clipboard, and opening the LinkedIn share window β so users can paste a polished post.
- Capabilities: Extracts an excerpt from
[itemprop="articleBody"]/.bd-content/main, normalizes whitespace, dedupes sections, truncates to a sentence β€ 420 chars; async clipboard with graceful failure; opens share window then navigates; accessiblerole="status"toast notifications; binds via.js-linkedin-shareand.js-copy-share-linkwith idempotency guards. - Source:
- SCSS:
_sass/components/_notes-index.scss(.share-buttons .btn-sharehover scale β note: separate Markup path) - Markup:
_includes/content/intro.html(share dropdown),_layouts/note.html(.btn-share.js-linkedin-share) - JS:
assets/js/share-actions.js(loaded non-deferred from_includes/components/js-cdn.html) - API surface: classes
.js-linkedin-share,.js-copy-share-link,.btn-share,.share-buttons; data-attributesdata-share-title,data-share-description,data-share-url,data-copy-text,data-copy-success,data-linkedin-share-bound,data-copy-bound; no exported window namespace - Tests: No automated tests (no spec triggers a
.js-linkedin-share/.js-copy-share-linkclick or asserts clipboard/excerpt behavior). - Gaps / improvement ideas: Excerpt extraction, dedupe, and sentence truncation are non-trivial pure functions with zero unit coverage β ideal candidates for a fast jsdom/unit test. Uses its own
notify()toast (z-index:1085, hardcoded) instead of the sharedwindow.zer0UI.showToastβ duplicate notification systems should be consolidated.openShareWindowis called with two args but only accepts one (the'_blank'is ignored). The.btn-shareSCSS border color#dee2e6is hardcoded, not dark-mode-safe.
Skeleton loader¶
- Purpose: A token-aware shimmer placeholder for loading states that stays visible in both light and dark color modes.
- Capabilities: Animated 200%-width gradient between elevated and muted surface tokens; infinite 1.5s
zer0-skeleton-shimmer; rounded corners and amin-height: 1emso empty placeholders have size. - Source:
- SCSS:
_sass/components/_skeleton.scss - Markup: β (no include/layout currently emits
.skeleton) - JS: β
- API surface: class
.skeleton; keyframeszer0-skeleton-shimmer; CSS vars--zer0-color-bg-elevated,--zer0-color-bg-muted - Tests: No automated tests.
- Gaps / improvement ideas: The
.skeletonclass is defined but used by no markup or JS in the repo β it is an orphaned utility shipped for consumers. Either document it as a public utility (with usage examples) or wire it into a real loading state (e.g., search results, lazy images) to justify its inclusion. No reduced-motion guard on the infinite shimmer animation (relies on the global motion reset).
Particles hero background¶
- Purpose: An interactive canvas particle animation (particles.js fork) intended as a decorative hero/landing background.
- Capabilities:
particlesJS.load('particles-js', '/assets/particles.json', cb)loader; full particles.js engine (circle/edge/triangle/polygon/star/image shapes, line-linking, hover grab/repulse/bubble, click push/remove, retina detection, density auto-particles, resize handling). - Source:
- SCSS: β
- Markup: β (no layout/include renders
id="particles-js"; only referenced in_layouts/README.mddocs and_data/features.yml) - JS:
assets/js/particles.js(loader call),assets/js/particles-source.js(engine; exposeswindow.particlesJS,window.pJSDom) - Plugin/data:
assets/particles.json(config),_data/features.yml(documented feature entry) - API surface: id
#particles-js(expected container, not present); JSwindow.particlesJS(id, config),window.particlesJS.load(id, jsonPath, cb),window.pJSDom,Object.deepExtend,window.requestAnimFrame; no CSS vars - Tests: No automated tests.
- Gaps / improvement ideas: Fully orphaned in the current theme β neither script is loaded by any layout/include (
js-cdn.html/head.htmldo not reference them), and no element withid="particles-js"exists, soparticles.jswould throw on anullcontainer if ever loaded. The config (particles.json) and engine are dead weight unless re-wired. The fork has no reduced-motion respect (continuous animation) and the hover/click listeners run unconditionally β accessibility and battery concerns if revived. Decide: remove, or formally wire behind an opt-in landing flag with a reduced-motion guard.
Theming: Tokens, Color Modes, Skins & Customizers¶
The theme's appearance is a four-layer system: a --zer0-* design-token base (aliased onto Bootstrap --bs-* / accent --bd-* vars), three data-bs-theme color modes (light/dark/wizard), nine named data-theme-skin palettes that re-wire Bootstrap components and SVG backgrounds, and a set of admin-page customizers (appearance panel, background customizer, palette generator, skin editor, theme/preview galleries) that mutate CSS variables at runtime and export YAML for _config.yml.
Design Tokens (--zer0-* layer)¶
- Purpose: Single source of truth for color, spacing, typography, shadow, motion, breakpoint, and z-index decisions, exposed as
--zer0-*CSS custom properties that default to Bootstrap/--bd-*values so existing styling keeps working. - Capabilities: Semantic color roles (primary/secondary/accent/state/surfaces/ink/border/code/link) with
color-mixtranslucent border; Bootstrap-mirrored spacing scale 0β5 plus FAB/sidebar/layout vars; fluidclamp()heading scale + font families/weights/line-heights; purpose-named shadow scale incl.--zer0-shadow-fab/--zer0-shadow-focus; motion durations/easings that collapse to0.01msunderprefers-reduced-motion; breakpoint px values for JS/inline use (not usable in@media); ordered z-index/layer scale for FABs, overlays, toasts, skip-link. - Source:
- SCSS:
_sass/tokens/_index.scss(barrel),_color.scss,_spacing.scss,_typography.scss,_shadow.scss,_motion.scss,_breakpoints.scss,_layers.scss - Markup: β (consumed by all
_includes/_layoutsvia compiledassets/css/main.css) - JS: β (read at runtime by appearance/customizer modules)
- API surface: CSS vars
--zer0-color-{primary,primary-rgb,secondary,accent,success,info,warning,danger,bg,bg-elevated,bg-muted,ink,ink-muted,border,border-translucent,code-bg,code-ink,link,link-hover};--zer0-space-{0..5,section,container-x,fab-offset,fab-size,fab-gap},--zer0-sidebar-width,--zer0-sidebar-toc-width,--zer0-layout-max-width-{xl,xxl};--zer0-font-{sans,mono,weight-*},--zer0-text-{base,sm,lg,h1..h6},--zer0-leading-{tight,normal,loose};--zer0-shadow-{xs,sm,md,lg,fab,focus};--zer0-motion-{duration-fast/base/slow,ease-standard/in/out};--zer0-bp-{sm,md,lg,xl,xxl};--zer0-layer-{base,elevated,sticky,header,backdrop,fab-*,offcanvas,modal,popover,tooltip,toast,cookie-banner,skip-link} - Tests:
test/visual/styling.spec.jsβ "--zer0-color-primaryresolves on:root" (non-empty computed value) and "--zer0-bp-lgresolves to a pixel value" (matches/^\d+px$/); "Bootstrap exposes CSS variables on :root" asserts--bs-primarynon-empty. Only color-primary and bp-lg of the whole token set are asserted; spacing/typography/shadow/motion/layer tokens untested. - Gaps / improvement ideas: No test asserts
--bd-*β--zer0-*aliasing actually resolves (e.g.--zer0-color-code-ink), nor that motion tokens collapse under reduced-motion. Breakpoint tokens duplicate--bs-breakpoint-*and the JS nav config β drift risk with no synchronization test. Consider a contract test enumerating every documented--zer0-*token and asserting it resolves on:root.
Runtime Token Injection (tokens-inline.html)¶
- Purpose: Emits an in-
<head><style>+ inline script that overrides--zer0-color-*tokens from_config.ymltheme_colorat compile time and restores user Appearance-panel overrides fromlocalStoragebefore first paint (no flash of default palette). - Capabilities: Conditionally emits only the
theme_colorkeys present (mainβprimary, secondary, redβdanger, yellowβwarning, greenβsuccess, tealβinfo, blueβlink, purpleβaccent); pre-paint script readslocalStorage["zer0-appearance"]JSON and sets--zer0-color-{primary,secondary,accent}ondocumentElement; wrapped in try/catch for private-mode/quota safety. - Source:
- SCSS: β
- Markup:
_includes/core/tokens-inline.html - JS: inline
<script>in the include (pairs withassets/js/modules/theme/appearance.js) - Plugin/data:
_config.ymltheme_color - API surface:
<style id="zer0-tokens-inline">; localStorage keyzer0-appearance({primary,secondary,accent}); writes CSS vars--zer0-color-{primary,secondary,accent}; Liquidsite.theme_color.{main,secondary,red,yellow,green,teal,blue,purple} - Tests: No automated tests. (
features/appearance.spec.jscovers the customizer YAML, not this configβtoken bridge or the pre-paint restore.) - Gaps / improvement ideas: No test verifies that a configured
theme_color.mainactually overrides--zer0-color-primary, nor that the pre-paint localStorage restore runs before main.css (the core anti-flash guarantee). Key-name mapping (redβdanger etc.) is undocumented in the UI and easy to mis-set. JSON-parse failure is silently swallowed β no console signal for debugging a corrupt pref.
Color Modes (light / dark / wizard)¶
- Purpose: Bootstrap
data-bs-themecolor-scheme switching that retones--bs-*/--bd-*accent tokens for light, dark, and a custom blue "wizard" mode; user-selectable via the halfmoon dropdown (light/dark/auto), with wizard set via config/programmatically. - Capabilities:
--bd-*accent tokens (violet/purple/accent/toc/sidebar/callout/pre-bg) differ per light vs dark; wizard mode overrides body bg/color to blue + retones dropdowns and.btn-secondaryusing Sass color math; halfmoon toggle persistslocalStorage["theme"], honorsprefers-color-schemefor auto, updates active icon +aria-pressed+aria-label; Mermaid treatswizardlikedark. - Source:
- SCSS:
_sass/theme/_color-modes.scss(barrel),_css-variables.scss(--bd-*light/dark),_wizard-mode.scss - Markup:
_includes/components/halfmoon.html(light/dark/auto segmented control),_includes/components/mermaid.html(wizardβdark) - JS:
assets/js/halfmoon.js,assets/js/modules/theme/appearance.js(renders its own light/dark/auto group only on explicit[data-appearance-panel-host]mounts; in the settings panel it renders the primary-color picker only) - API surface: attributes
data-bs-theme="light|dark|wizard",data-bs-theme-value; localStoragetheme; class.bd-theme-switch; CSS vars--bd-{purple,violet,accent,violet-rgb,accent-rgb,pink-rgb,teal-rgb,violet-bg,toc-color,sidebar-link-bg,callout-link,callout-code-color,pre-bg}, plus--bs-body-*/--bs-dropdown-*/--bs-btn-*under wizard - Tests:
test/visual/settings-panel.spec.jsasserts clicking light/dark buttons flipsdata-bs-themeand syncs.active/aria-pressed;features/appearance.spec.jsand the dark/contrast snapshots exercise dark surfaces. - Gaps / improvement ideas: Wizard mode is defined in SCSS and read by Mermaid but is not selectable from any UI (halfmoon offers only light/dark/auto) β either expose it or document it as config-only.
--bd-callout-linkis an RGB triple in light/dark but consumed inconsistently.
Named Skins (the 9 data-theme-skin palettes)¶
- Purpose: Nine named palettes (air/aqua/dirt/neon/mint/plum/sunrise + dark/contrast for backgrounds) that re-wire Bootstrap components and
--zer0-color-*tokens plus SVG backgrounds, applied via adata-theme-skinattribute on<html>. - Capabilities:
zer0-skin-paletteSass mixin sets primary/accent/link tokens with WCAG-AA-tuned light & dark link colors,color-mixbutton hover/active and elevated-surface tints, focus ring, and component overrides (.btn-primary/.btn-outline-primary/.alert-primary/.nav-tabs/.card/.list-group/form-controls/pagination/breadcrumb/progress/pre/navbar/.text-primary/.border-primary/.link-primary); dark-mode link overrides nested under&[data-bs-theme="dark"]; runtime switching + localStorage persistence (see Background Customizer JS). - Source:
- SCSS:
_sass/theme/_skins.scss(mixin + 7 palettes),_sass/theme/_backgrounds.scss(9 background sets) - Markup: applied on
<html data-theme-skin>(server-rendered fromsite.theme_skin) - JS:
assets/js/background-customizer.js(zer0Bg.setSkin) - Plugin/data:
_data/theme_skins.yml(order + default),_data/theme_backgrounds.yml(per-skin colors/assets) - API surface: attribute
data-theme-skin="air|aqua|contrast|dark|dirt|neon|mint|plum|sunrise"; CSS vars--zer0-color-{primary,primary-rgb,accent,border,link,link-hover,bg-elevated,bg-muted},--bs-{primary,primary-rgb,link-color,link-hover-color},--zer0-shadow-focus; SCSS@mixin zer0-skin-palette($brand,$brand-rgb,$accent,$accent-rgb,$link-light,$link-light-hover,$link-dark,$link-dark-hover) - Tests:
test/visual/skins.spec.jsβ per-skin (all 9 fromfixtures.SKINS): "setsdata-theme-skinattribute", "persists skin to localStorage", "skin restores after navigation" (poll on/faq/), "homepage visual snapshot" (homepage-<skin>.png,maxDiffPixels:150); plus "zer0Bg.currentSkin()returns active skin" and "skin-change event fires with correct detail". Snapshots committed inskins.spec.js-snapshots/homepage-*-chromium-linux.png. Asserts attribute/persistence/visual but not per-component token values or contrast. - Gaps / improvement ideas: Inconsistency:
_skins.scssdefines UI palettes for only 7 skins (nocontrast, nodark), yet_backgrounds.scssand the customizer offer 9 βcontrast/darkget SVG backgrounds but inherit default Bootstrap component colors. Default-skin drift:theme_skins.ymldefault isairand lists 7 skins, butbackground-customizer.js/theme-customizer.jsfall back todark, and the customizer/background includes hardcode a 9-string list β three sources of truth. No automated WCAG-contrast assertion despite carefully annotated AA ratios in the SCSS comments. Snapshots are linux-only (CI), so local runs can't compare baselines.
Appearance Panel (appearance.js)¶
- Purpose: Opt-in runtime panel (inside the Settings offcanvas) for choosing color mode (light/dark/auto) and a custom primary color that overrides
--zer0-color-primarylive across the whole theme. - Capabilities: Color-mode button group writing
data-bs-theme+localStorage["theme"](halfmoon-compatible); debounced<input type=color>primary picker persisting tolocalStorage["zer0-appearance"]and live-setting the token; Reset-to-defaults; robust hex coercion of arbitrary CSS colors (3/6-digit hex, rgb/rgba via a probe element); mounts into[data-appearance-panel-host]or#info-section .offcanvas-body; XSS-safe (never interpolates stored data into innerHTML). - Source:
- SCSS:
_sass/components/_theme-preview.scss(.zer0-appearance-panelreset inside preview host) - Markup: injected into Settings offcanvas; gated by
site.appearance_panel - JS:
assets/js/modules/theme/appearance.js - API surface: classes
.zer0-appearance-panel; ids#zer0-appearance-heading,#zer0-appearance-primary; data-attrsdata-mode,data-appearance-panel-host,data-appearance-reset; localStoragezer0-appearance({primary,secondary,accent}) +theme; CSS var--zer0-color-primary;aria-pressed/aria-labelledby/aria-describedby - Tests: No automated tests. (No spec mounts the appearance panel or asserts primary-color live override / mode persistence.)
- Gaps / improvement ideas: Entirely untested despite touching global tokens and localStorage.
writePrefsstoressecondary/accent(andtokens-inline.htmlrestores them) but the panel UI only exposesprimaryβ dead capability or missing controls. No visible feedback when localStorage is unavailable. Should have a behavioral test for: picker β--zer0-color-primarychange, reset clears it, mode buttons syncaria-pressed.
Background Customizer (zer0Bg API + panels)¶
- Purpose: Runtime engine and offcanvas/tab UI for switching the active skin, toggling fffuel-style layered SVG backgrounds on/off, and tuning gradient/texture/pattern opacity β all persisted to localStorage.
- Capabilities:
zer0Bg.setSkin/toggle/setOpacity/currentSkinglobal API; restores skin + bg-enabled on load from localStorage (falls back to server attr, defaultdark); dispatcheszer0:skin-change/zer0:bg-toggleevents; CSS-driven layered::before/::aftergradient+noise+pattern with per-skin asset URLs andmix-blend-mode;[data-zer0-bg="off"]kill switch; reduced-motion guard; two UI surfaces (floating-button offcanvas + Settings-tab variant) with skin buttons, enable switch, 3 opacity sliders, reset; config-driven opacity/blend override viasvg-background.html. - Source:
- SCSS:
_sass/theme/_backgrounds.scss,_sass/theme/_background-mixins.scss - Markup:
_includes/components/background-customizer.html,background-settings.html,svg-background.html - JS:
assets/js/background-customizer.js - Plugin/data:
_data/theme_backgrounds.yml,_config.ymltheme_background - API surface:
window.zer0Bg.{setSkin(name),toggle(force?),setOpacity(layer,value),currentSkin()}; eventszer0:skin-change,zer0:bg-toggle; attributesdata-theme-skin,data-zer0-bg="on|off",data-skin; localStoragezer0-theme-skin,zer0-bg-enabled; CSS vars--zer0-bg-{gradient,pattern,noise,gradient-opacity,texture-opacity,pattern-opacity,blend,pattern-size,enabled}; SCSS mixinszer0-bg-{gradient,noise,pattern,layered}; classes.zer0-bg-{hero,body,surface,footer}; ids#zer0BgCustomizer,#zer0SkinButtons,#zer0BgToggle,#zer0{Gradient,Texture,Pattern}Opacity,#zer0BgReset - Tests:
test/visual/backgrounds.spec.jsβ "toggle off/on setsdata-zer0-bg", "toggle persists to localStorage" (zer0-bg-enabled), "bg-toggle event fires with correct detail", three "setOpacity(...) updates CSS variable" (gradient/texture/pattern,toBeCloseTo), "background state persists across navigation" (/faq/). Skin persistence covered infeatures/appearance.spec.js. Tests assert API/attribute/var/persistence/events; the offcanvas/slider DOM wiring and visual layering are untested. - Gaps / improvement ideas: The two near-identical include scripts (
background-customizer.htmlvsbackground-settings.html) duplicate ~50 lines of slider/reset logic and both bind#zer0SkinButtons/#zer0BgToggleby shared ids β rendering both on one page would double-bind.setOpacityaccepts the public layer nametexturebut no validation/logging when an unknown layer is passed.--zer0-bg-enabledCSS var exists but is unused (toggle uses the[data-zer0-bg]attribute). No test for the include UI controls or thesvg-background.htmlconfig override.
Palette Generator (palette-generator.js)¶
- Purpose: chroma.js-powered color-harmony generator and live Bootstrap CSS-variable editor on the Theme Customizer admin page, with WCAG contrast badges and combined YAML export.
- Capabilities: Six harmony algorithms (complementary/analogous/triadic/split-complementary/tetradic/monochromatic) + base-color scale; click-to-copy swatches with contrast ratio/AA-AAA labels; random base color; live editor for ~17
--bs-*vars (colors + sizing/typography ranges) writing todocumentElementwith auto-rgbvariants; "apply palette" maps generated colors to semantic Bootstrap vars; reset-live;window.rebuildFullYaml()builds quotedtheme_skin+theme_colorYAML; re-reads computed styles ondata-bs-themechange via MutationObserver; per-field stable ids with<label for>for a11y. - Source:
- SCSS: β
- Markup: rendered into the Theme Customizer page (
/about/settings/theme/),_includes/components/theme-preview-gallery.htmladjacent - JS:
assets/js/palette-generator.js(requires globalchroma) - API surface:
window.rebuildFullYaml(); data-attrsdata-palette-color,data-live-var,data-live-text,data-live-val,data-unit; ids#palette-swatches,#palette-base-color,#palette-base-text,#palette-harmony,#palette-random,#palette-apply,#live-editor-fields,#live-reset,#theme-yaml-output; live-edited vars--bs-{primary,secondary,success,info,warning,danger,(+-rgb),body-bg,body-color,tertiary-bg,border-color,link-color,link-hover-color,border-radius,border-width,body-font-size,body-font-weight,body-line-height} - Tests:
test/visual/theme-colors.spec.jsβ "page loads 200", "color picker inputs have valid #RRGGBB", "color picker change updates paired text input" (Color Editor tab), "YAML export quotes hex color values" (T-008 regression: every hex line must be quote-wrapped). Harmony algorithms, live--bs-*application, apply/reset, and contrast badges are not directly asserted (tests are tolerant/skip when elements absent). - Gaps / improvement ideas: Harmony output and the live-editorβ
--bs-*application are untested β only YAML quoting and pickerβtext sync are.rebuildFullYamlemits layout overrides as commented-out lines (# border_radius:), so range edits never round-trip into usable config. Hard dependency on globalchroma; failure path onlyconsole.warns. Two YAML builders (theme-customizer.jsfallback and this) must stay quote-consistent β covered by one regression test but fragile.
Skin Editor (skin-editor.js)¶
- Purpose: Colorffy-inspired editor on the Theme Customizer page for editing the 9 built-in skins or authoring custom ones by adjusting 3 gradient stops + SVG turbulence filter params, with live preview, auto-generated palettes, and SVG/CSS export.
- Capabilities: Built-in skin defs (stops + feTurbulence freq/oct/seed/scale/opacity + patternSize); live gradient/pattern SVG generation applied to
--zer0-bg-*; chroma.js palettes (gradient scale, per-stop tints, surface, tonal surface, semantic success/warning/danger/info) with WCAG badges; save/delete custom skins tolocalStorage["zer0-custom-skins"]; random skin; reset to built-in (viazer0Bg.setSkin); export SVG files / copy CSS; toast feedback; re-syncs onzer0:skin-change; exposeswindow.skinEditor. - Source:
- SCSS: β (uses inline styles + Bootstrap utilities)
- Markup:
pages/_about/settings/theme.md(#pane-skin-editor,#skin-editor-*hosts) - JS:
assets/js/skin-editor.js(requires globalchroma; integrateszer0Bg) - API surface:
window.skinEditor.{applyLive,resetLive,getState,BUILTIN_SKINS}; ids#pane-skin-editor,#skin-editor-{select,stops,preview,palettes,filters,save,delete,random,reset,apply,export-svg,export-css,toast},#stop-{color,text,hex,preview}-{0..2},#filter-{freq,oct,seed,scale,opacity,patternSize}; localStoragezer0-custom-skins; CSS vars--zer0-bg-{gradient,pattern,pattern-size}; listenszer0:skin-change - Tests: No automated tests. (No spec loads
#pane-skin-editor; the skins specs use the runtimezer0BgAPI, not this editor.) - Gaps / improvement ideas: Entirely untested (largest theming JS file, ~28 KB). Builds swatch/scale HTML with inline
onclick="navigator.clipboard.writeText('<hex>')"β brittle and CSP-unfriendly; prefer delegated listeners. Custom skins only override--zer0-bg-*(backgrounds), not the--zer0-color-*component palette, so a saved custom skin won't retint buttons/links like the built-ins.BUILTIN_SKINSstops are a 4th copy of skin colors (alongside_skins.scss,_backgrounds.scss,theme_backgrounds.yml). Save usesprompt()/confirm()(no UI test hook). No persistence of custom skins into the livezer0Bgskin list.
Theme Customizer & Preview Gallery (admin UI)¶
- Purpose: The admin-page UI shell β a skin-card preview grid, a quick-select/mode controls bar, a full component preview gallery (style guide), and a compact theme/build info panel β that ties the runtime APIs together and drives YAML export.
- Capabilities:
theme-customizer.htmlskin-card grid (gradient swatch + 3 color dots, keyboard-activatable) driven bytheme-customizer.js(card/quick-bar click βzer0Bg.setSkin, highlight sync, YAML rebuild, copy/downloadtheme-config.yml);theme-controls-bar.htmlembeds halfmoon mode toggle +#quickSkinBar;theme-preview-gallery.htmlrenders Bootstrap components (typography/buttons/alerts/forms/etc.) for live skin/mode visual testing with section TOC + status readout viatheme-preview.js;theme-info.htmlshows theme/Jekyll/env/build/repo + quick links. - Source:
- SCSS:
_sass/components/_theme-preview.scss - Markup:
_includes/components/theme-customizer.html,theme-controls-bar.html,theme-preview-gallery.html,theme-info.html;pages/_about/settings/theme.md,.../theme-preview.md - JS:
assets/js/theme-customizer.js,assets/js/theme-preview.js - Plugin/data:
_data/theme_skins.yml,_data/theme_backgrounds.yml - API surface: classes
.skin-card,.theme-preview-{gallery,section,heading,toc,navbar,footer},.theme-controls-bar,.theme-info-compact; data-attrsdata-skin,data-quick-skin,data-color-key,data-color-text,data-bs-theme-value; ids#skin-grid,#quickSkinBar,#theme-yaml-output,#theme-copy-yaml,#theme-download-yaml,#theme-preview-active-{skin,mode},#theme-preview-gallery; eventszer0:skin-change; CSS vars consumed--zer0-space-5,--zer0-color-{border,bg-muted} - Tests:
test/visual/theme-colors.spec.jsexercises this page (/about/settings/theme/) β load 200, color pickers valid, pickerβtext sync, YAML quoting;theme-customizer.js'srebuildYamlfallback is the quoting code under test (T-008).ADMIN_PAGESinfixtures.jslists the Theme Customizer/Preview routes (used by admin-nav specs elsewhere). The card-grid clickβskin apply, gallery rendering, and copy/download buttons are not directly asserted. - Gaps / improvement ideas: Skin-source inconsistency surfaces here:
theme-customizer.htmliteratestheme_skins.yml.order(7), buttheme-customizer.js/theme-preview.jsfall back to'dark'(not in that list) β UI can show a skin with no card.theme-customizer.jsupdateSkinCardUIrewrites footer viaouterHTML(drops listeners/ids) β fragile. Copy/download buttons and keyboard activation of cards are untested. The gallery's note that Appearance/theme_coloroverrides may beat skin tokens is real precedence complexity with no test. Consider a single canonical skin manifest consumed by SCSS, JS, includes, and tests.
Content & Collections¶
The components that render long-form and reference content in zer0-mistakes β note/notebook/article/collection/news/tag layouts, their index grids and difficulty badges, plus the supporting building blocks (callouts, post navigation, code copy + syntax highlighting, author/post/feature cards, preview images, comments, share actions, and client-side pagination).
Note layout¶
- Purpose: Displays quick notes / cheatsheets / TILs with a compact, scannable header and metadata, related-by-tag notes, and prev/next navigation. Targets
_notescollection items. - Capabilities: compact header (author, date, reading-time computed at 200 wpm, last-modified); difficulty badge (
bg-success/bg-warning/bg-danger); tag + category badge links; lead description; share buttons (X / LinkedIn via share-actions.js / email); related notes (up to 3, tag-overlap match); Giscus comments (gatedcomments != false and site.giscus.enabled); backlinks panel (content/backlinks.html); date-sorted prev/next pagination; Schema.orgArticleJSON-LD + microdata + microformats (h-entry,p-name,dt-published). - Source:
- SCSS:
_sass/components/_notes.scss, difficulty badges in_sass/components/_notes-index.scss - Markup:
_layouts/note.html(inherits_layouts/default.htmlβroot.html) - JS:
assets/js/share-actions.js(LinkedIn enhancement),assets/js/code-copy.js,assets/js/table-copy.js - Plugin/data:
_includes/content/backlinks.html,_includes/content/giscus.html,_data/authors.yml - API surface: classes
.note-article,.note-header,.note-title,.note-meta,.note-description,.note-content,.note-footer,.related-notes,.note-navigation,.reading-time;.js-linkedin-sharewithdata-share-url/data-share-title/data-share-description; CSS vars--zer0-color-border,--zer0-color-ink-muted,--zer0-text-h2,--zer0-space-*,--zer0-shadow-md,--zer0-motion-*. Front matter:difficulty,share,comments,lastmod,tags,categories. - Tests: No automated tests directly target
note.html(no/notes/<slug>spec).test/visual/accessibility.spec.js"code copy buttons are keyboard focusable" / "table CSV export button has accessible name" cover the.note-content-scoped enhancements only via generic content scopes. No shell suite. - Gaps / improvement ideas: Untested layout entirely (single-H1, related-notes correctness, prev/next ordering).
.note-navigation .nav-link-notestyles exist in SCSS but the layout actually renders Bootstrap.pagination/.page-linkmarkup β dead/mismatched CSS. Related-notes loop iteratessite.notesin collection order while only checkingrelated_count < 3inside the tag-match branch, so ordering is non-deterministic. Difficulty values map to color via inline{% case %}in the layout but.badge-beginner/intermediate/advancedclasses (from_notes-index.scss) are never applied here β inconsistent with the index grid.
Notebook layout¶
- Purpose: Renders Jupyter notebooks (
_notebookscollection) converted to HTML, with kernel metadata, MathJax math, a download-original link, and related-by-tag notebooks. - Capabilities: header (author, date, reading time, last-modified, "Jupyter Notebook" kernel marker when
jupyter_metadatapresent); tag + category links; download original.ipynb(raw.githubusercontent URL built fromsite.repository/site.branch/page.slug); share buttons (X / LinkedIn / email β note: plain links, no share-actions enhancement); related notebooks (up to 3); Giscus comments; prev/next pagination; print + dark-mode styles; Schema.orgTechArticleJSON-LD. - Source:
- SCSS:
_sass/notebooks.scss(root partial, not under components) - Markup:
_layouts/notebook.html - JS:
assets/js/code-copy.js, MathJax (loaded in_includes/core/head.html) - Plugin/data:
_includes/content/giscus.html,_data/authors.yml - API surface: classes
.notebook-article,.notebook-header/-title/-meta/-description/-content/-footer/-navigation,.download-notebook,.related-notebooks, plus Jupyter hooks.jp-OutputArea/.output-area/.cell-output,.jp-Cell/.cell,.input-prompt/.output-prompt(CSS::before/::after"In [β¦]:" labels),.MathJax_Display. Mostly Bootstrap--bs-*vars (not--zer0-*tokens). - Tests: No automated tests. Not in
UI_ROUTES; no spec visits a notebook page. - Gaps / improvement ideas: Styling uses raw
--bs-*and hardcodedrgba(0,0,0,.1)shadows /prefers-color-schememedia query instead of the--zer0-*token system +[data-bs-theme="dark"]used elsewhere β dark mode won't follow the manual theme toggle, only OS preference..input-prompt/.output-prompt/.jp-*classes assume nbconvert output that Jekyll's markdown pipeline does not emit, so most of this CSS is likely inert. Download link hardcodes thepages/_notebooks/path. LinkedIn share lacks the cleaned-summary enhancement that note/article get.
Notes & Notebooks index grids + difficulty badges¶
- Purpose: Card-grid landing pages listing all notes (
/notes/) and notebooks (/notebooks/) with client-side tag/difficulty filtering and difficulty badges. - Capabilities: responsive
row-colscard grid; filter button bar (notes: by tag; notebooks: by difficulty) togglingdisplayvia inline<script>; difficulty badge (inline Liquid color map); hover lift (translateY(-4px)+ shadow); notebooks grid cards have a blueborder-left; fade-out transition whendisplay:none. - Source:
- SCSS:
_sass/components/_notes-index.scss(#notes-grid,#notebooks-grid,.badge-beginner/intermediate/advanced,.share-buttons) - Markup:
pages/notes.md,pages/notebooks.md(bothlayout: default, grids + filter JS inline) - API surface: ids
#notes-grid,#notebooks-grid; classes.note-card,.notebook-card,.badge-beginner,.badge-intermediate,.badge-advanced; data-attributesdata-filter,data-tags(notes),data-difficulty(notebooks); CSS vars--zer0-color-success/warning/danger(with Bootstrap hex fallbacks). Difficulty defaultintermediate(notebooks only). - Tests: No automated tests.
UI_ROUTES.notes(/notes/) is defined in fixtures but no spec asserts against the grid, filters, or badges. - Gaps / improvement ideas: Filtering logic is duplicated inline in two pages instead of a shared module; no debounce/empty-state-after-filter, no
aria-pressed/aria-controlson filter buttons (a11y gap), and filtered-out cards remain in the tab/AX tree (display:noneis used, which is fine, but no announce)..badge-beginneretc. classes are defined but the index markup usesbg-success/warning/dangerdirectly β the dedicated badge classes are effectively unused. Notes filter does substringtags.includes(filter)which can mis-match overlapping tag names. No tests guard the difficulty color mapping or filter behavior.
Callout¶
- Purpose: Bootstrap-docs-style info/note/warning/tip/danger aside block for use inside Markdown via
{% include %}, with semantic colors driven by design tokens. - Capabilities: five types (
notedefault,tip,info,warning,danger) each mapping to an icon + token color; optional title; icon override; grid layout (icon + body); per-type color injected inline as--zer0-callout-color;role="note"aside; dark-mode-safe via tokens (no per-type CSS needed). - Source:
- SCSS:
_sass/components/_callout.scss - Markup:
_includes/components/callout.html - API surface: classes
.zer0-callout,.zer0-callout--{type},.zer0-callout__icon,.zer0-callout__body,.zer0-callout__title,.zer0-callout__content; CSS var--zer0-callout-color(set inline tovar(--zer0-color-success|info|warning|danger|primary)); include paramstype,title,icon,content. Renders<aside role="note">. - Tests: No automated tests.
- Gaps / improvement ideas: No visual or a11y spec exercises the five color variants or token wiring.
contentmust be pre-rendered markup (Liquidcapture), which is awkward for authors; no Markdown-native syntax (the Obsidian callout path is separate). Icon isaria-hiddenbut the type itself (e.g. "warning") is not announced β consider a visually-hidden type label. No test that--zer0-callout-colorresolves per type.
Post navigation (prev/next cards)¶
- Purpose: Previous/next article navigation rendered as two elevated, hover-lifting cards at the foot of an article. Pairs with the
post-navigationblock inarticle.html. - Capabilities: card affordance with token shadow/border; hover elevation (
translateY(-2px)+ primary-tinted border);:focus-visiblering; disabled state (--disabled, dimmed,pointer-events:none); right-aligned next card; truncated titles (ellipsis); dark-mode shadow overrides; hover suppressed on coarse pointers. - Source:
- SCSS:
_sass/components/_post-navigation.scss - Markup:
_layouts/article.html(nav.post-navigation, usespage.previous/page.next) - API surface: classes
.post-navigation,.post-nav-card,.post-nav-card--prev,.post-nav-card--next,.post-nav-card--disabled,.post-nav-card__body/__label/__title; CSS vars--zer0-color-bg-elevated,--zer0-color-border,--zer0-shadow-sm/md/focus,--zer0-color-primary(-rgb),--zer0-motion-*.aria-disabled="true"on disabled cards. - Tests: No spec targets the prev/next cards specifically.
test/visual/layouts.spec.js"article post has exactly one h1" navigates to an article but only counts H1s; it does not assert post-navigation markup, hover, focus, or disabled state. - Gaps / improvement ideas: Hover/focus elevation and the disabled affordance are untested. Note layout has a parallel but different prev/next implementation (Bootstrap
.pagination) β two divergent patterns for the same need; could be unified. No keyboard-focus-ring visual regression test.
Code copy button¶
- Purpose: Progressive-enhancement script that adds a copy-to-clipboard button, a language header bar, and a line-number gutter to every code block, and makes blocks keyboard-focusable scroll regions.
- Capabilities: detects Rouge (
pre.highlight) and standalonepre code; injects line-number gutter (separate DOM node, never copied); language label fromlanguage-*class (withshellβbash,plaintextβtextremap); Copy button with clipboard write, "Copied!" 2s feedback, failure state; strips#-comment lines from copied text; WCAG 2.1.1 fix β addstabindex="0",role="region",aria-labelto scrollable<pre>; header-mounted button for Rouge blocks vs. absolute overlay for standalone; single-line variant centering. - Source:
- SCSS:
_sass/core/code-copy.scss - JS:
assets/js/code-copy.js(loadeddeferin_includes/core/head.html) - API surface: classes
.copy,.copy.copied,.copy-code,.code-block-header,.code-block-lang,.code-block-body,.code-block-body--single-line/--standalone,.code-line-numbers,.code-block--single-line,.has-copy-button,.has-code-header,.has-line-numbers; CSS vars--zer0-code-copy-width,--zer0-code-header-height,--zer0-code-accent-width,--zer0-code-gutter-width; usesnavigator.clipboard.writeText. No global JS API/events. - Tests:
test/visual/features/content-enhancements.spec.js"rouge blocks get header bar and line gutter after JS" (asserts.code-block-headervisible +.code-line-numbersattached + header copy button visible) and "single-line rouge blocks place copy button in header bar".test/visual/core/accessibility.spec.js"code copy buttons are keyboard focusable" (focus βtoBeFocused). All run againstUI_ROUTES.codeCopy=/docs/features/code-copy/. - Gaps / improvement ideas: No test of the actual copy action / clipboard content, the
#-comment stripping, the "Copied!"βreset transition, or the failure path.getCopyableCodesilently drops every line starting with#, which corrupts copied YAML/Python/shell-comment-bearing snippets β a correctness bug worth a regression test.aria-labelis static "Copy code to clipboard"; copied-state change isn't announced (noaria-live).
Syntax highlighting¶
- Purpose: Rouge token color theme for fenced code, with a GitHub-Light palette in light mode and a Material-Dark base16 palette under
[data-bs-theme="dark"]. - Capabilities: full Rouge token class coverage (comments, keywords, strings, names, generic diff
gi/gdwith background tints, numbers, operators); WCAG-AA-tuned light palette on#f8f9fa; dark overrides keyed off the manual theme attribute;.hllline-highlight; gist table reset. - Source:
- SCSS:
_sass/core/_syntax.scss - API surface: Rouge token classes under
.highlight(.c/.k/.s/.n/.o/.na/.nf/.gi/.gd/.hll, β¦); dark variants under[data-bs-theme="dark"] .highlight; Sass$base00..$base0fpalette vars (compile-time only). - Tests: No spec asserts token colors or light/dark palette swaps.
test/visual/styling.spec.jscontains no syntax/code assertions. - Gaps / improvement ideas: Palette is hardcoded hex rather than
--zer0-*tokens, so it won't follow custom skins (only the binary light/dark split). Comment is dated ("--bd-pre-bg") β references a retired variable name. No visual-regression snapshot of a highlighted block in either mode to catch palette regressions.
Author card¶
- Purpose: Reusable author profile display resolving an author key against
_data/authors.yml(falling back to a plain name string), with three density styles. - Capabilities: styles
inline(avatar 24px + name),compact(48px + role + optional bio),full(80px card + bio + social buttons); avatar fallback to a primary-circlebi-person; social links (GitHub, X, LinkedIn, website, email) in full style;show_bio/show_socialflags with per-style defaults. - Source:
- Markup:
_includes/components/author-card.html - Plugin/data:
_data/authors.yml - API surface: classes
.author-inline,.author-card-compact,.author-card,.author-name,.author-social; include paramsauthor,style(inline|compact|full),show_bio,show_social. No JS, no dedicated CSS partial (relies on Bootstrap utilities). - Tests: No automated tests.
- Gaps / improvement ideas:
article.htmlre-implements the "full" author card inline (the About-the-Author block) instead of{% include author-card.html style="full" %}β duplicated markup that can drift. Avatarsrcuses{{ site.baseurl }}/{{ site.public_folder }}{{ avatar }}(different convention frompreview-image.html's assets-prefix logic) β two image-path conventions. Noloading="lazy"on avatars. X social link missingaria-labelin some branches (present in full, but inline/compact have no social).
Author E-E-A-T block¶
- Purpose: Visible author-credibility block (Experience/Expertise/Authority/Trust) emitting Schema.org
Personmicrodata for AI-engine optimization and SEO. - Capabilities: two styles β
banner(full-width bordered strip) andcard; avatar with primary border + fallback; name/role/bio withitempropname/jobTitle/description; social buttons withitemprop="sameAs"/url; defaults author tobamr87. - Source:
- Markup:
_includes/components/author-eeat.html - Plugin/data:
_data/authors.yml - API surface: Bootstrap utility classes only (
.bg-body-secondary,.card,.rounded-circle, β¦); microdataitemscope itemtype="https://schema.org/Person"+itempropname/jobTitle/description/image/sameAs/url; include paramsauthor_key,style(banner|card). No--zer0-*vars, no JS. - Tests: No automated tests (microdata not validated by any spec).
- Gaps / improvement ideas: Overlaps heavily with
author-card.html(style="full") and the inline article author block β three author renderers. Hardcoded defaultbamr87baked into the component. No test that thePersonJSON-LD/microdata is well-formed. Avatar lacksloading="lazy".
Post card¶
- Purpose: Canonical reusable blog-post card used across news, tag, section, and archive pages for consistent rendering.
- Capabilities: breaking badge (red, top-left), featured badge (gold star, top-right), contextual post-type badge (bottom-left), preview image with fallback, category badge linking to
/news/<cat>/, title (stretched-link), subtitle, excerpt (truncate 120), author + date footer, reading time (estimated_reading_timeor "2 min" fallback), up to 3 tag badges + "+N" overflow; toggle flags for each section. - Source:
- Markup:
_includes/components/post-card.html(usespost-type-badge.html+preview-image.html) - Plugin/data:
site.teaserfallback image - API surface: classes
.post-card, plus Bootstrap.card h-100 border-0 shadow-sm,.stretched-link, badge positioning utilities,.z-1; include paramspost(required),show_category,show_excerpt,show_author,show_reading_time,show_post_type,card_class. No dedicated SCSS (.post-cardhas no rules of its own in this cluster), no JS. - Tests: No spec instantiates or asserts post-card structure/badges. (Used on
/tags/and/news/which appear only indirectly infeatures/layouts.spec.jssection/grid checks that don't reach card internals.) - Gaps / improvement ideas: Reading-time fallback is a hardcoded "2 min" string when
estimated_reading_timeis absent β misleading.stretched-linkon the title combined with the separate image<a>and category<a>creates nested/competing click targets (the stretched-link will swallow the others) β an interaction bug..post-cardclass exists but has no styling hook. No automated coverage of badge precedence (breaking vs featured vs post_type).
Post-type badge¶
- Purpose: Maps a
post_typevalue to a colored Bootstrap badge with icon; the single source of truth for article-type chips. - Capabilities: types
featured(gold star),breaking(red lightning),opinion(gray),review(blue half-star),tutorial(green book),listicle(primary list),interview(dark mic);standardrenders nothing. - Source:
- Markup:
_includes/components/post-type-badge.html - API surface: Bootstrap badge classes (
.badge bg-warning/danger/secondary/info/success/primary/dark); include parampost_type. No JS/CSS/vars. - Tests: No automated tests.
- Gaps / improvement ideas: No fallback/
{% else %}for unknown post_type values (silently renders nothing).bg-info/bg-warningbadges usetext-darkbutbg-secondary/bg-successrely on default contrast β verify AA in all skins. No test of the case mapping. Callers re-checkpost_type != "standard"everywhere because the component emits empty output for standard β could centralize.
Feature card¶
- Purpose: Data-driven card rendering a single feature from
_data/features.yml(id, version, description, sub-features, references, docs/demo links). - Capabilities: optional colored border (
style), icon + icon-color, sub-feature list (limit configurable), references list (handles nested file arrays), id/version/tag badges, docs + demo footer buttons; compact mode hides sub-features. - Source:
- Markup:
_includes/components/feature-card.html - Plugin/data:
_data/features.yml - API surface: Bootstrap
.card h-100 border-{style},.badge,.btn btn-outline-*; include paramsfeature(required),style,icon,icon_color,show_refs,compact,features_limit. No JS/CSS vars. - Tests:
test/visual/features/category-badges.spec.js"feature category badges link to in-page anchors" tests the features page's.feature-categories a.badge(the page-level category nav), not this card component's internals. So effectively no direct coverage of feature-card. - Gaps / improvement ideas: No test of the references-list nested-array branch or the docs/demo button conditionals. Tag badges use
.bg-light text-dark(low contrast in dark mode). No--zer0-*token usage. Component assumesf.version/f.idalways present.
Preview image¶
- Purpose: Single consistent
<img>renderer for preview/teaser images that auto-prepends the configured assets prefix and falls back tosite.teaser. - Capabilities: external-URL passthrough (
://); auto-prefix/assetswhenauto_prefix(skips if already prefixed);relative_urlnormalization;loading="lazy"default; alt escaping; optional inlinestyle. - Source:
- Markup:
_includes/components/preview-image.html - Data:
site.preview_images.*config,site.teaser - API surface: include params
src,alt,class(defaultcard-img-top),style,loading; config keyspreview_images.assets_prefix,preview_images.auto_prefix. No CSS/JS. - Tests: No automated tests of the include's path logic.
- Gaps / improvement ideas: No
width/heightattributes β cumulative-layout-shift risk and no intrinsic ratio. Nodecoding="async". Generation of the images themselves lives in the Python engine (scripts/lib/preview_generator.py, driven byscripts/generate-preview-images.sh).
Comments (Giscus)¶
- Purpose: GitHub-Discussions-backed comment thread embedded at the foot of articles/notes/notebooks, themed to the reader's color scheme.
- Capabilities: loads
giscus.app/client.jsasync; pathname-based strict thread mapping; reactions enabled; top input position;preferred_color_schemetheming; English; configured fromsite.repository+site.giscus.{data-repo-id,data-category-id}. Gated per-layout (comments != falseandsite.giscus/site.giscus.enabled). - Source:
- Markup:
_includes/content/giscus.html - Plugin/data:
site.giscusconfig in_config.yml - API surface:
<script data-repo data-repo-id data-category-id data-mapping="pathname" data-strict="1" data-reactions-enabled="1" data-theme="preferred_color_scheme" β¦>. No classes/vars. - Tests: No automated tests (third-party iframe; specs don't load it).
- Gaps / improvement ideas:
data-theme="preferred_color_scheme"follows OS preference, not the site's manual[data-bs-theme]toggle β comments mismatch the chosen theme. Inconsistent gating:article.htmlcheckssite.giscustruthiness while note/notebook checksite.giscus.enabledβ three layouts, two conditions. No graceful fallback/placeholder if Discussions is disabled. Noloading/visibility deferral until scrolled into view.
Share actions (LinkedIn enhancement)¶
- Purpose: Progressive enhancement for LinkedIn share links that copies a cleaned, de-duplicated article summary (title + description + excerpt + URL) to the clipboard before opening LinkedIn's share dialog.
- Capabilities: binds
.js-linkedin-shareand.js-copy-share-link; extracts excerpt from[itemprop="articleBody"]/.bd-content/main(paragraphs >40 chars, dedup vs description, truncate to sentence at 420 chars); opens share window first (popup-blocker friendly) then sets location; toast notification (role="status", auto-dismiss 4s) on copy success/failure; idempotent binding viadata-*-bound flags. - Source:
- JS:
assets/js/share-actions.js(loaded non-defer in_includes/components/js-cdn.html) - Markup consumer:
_layouts/note.html(.js-linkedin-share) - API surface: classes/hooks
.js-linkedin-share,.js-copy-share-link; data-attributesdata-share-url,data-share-title,data-share-description,data-copy-text,data-copy-success,data-linkedin-share-bound,data-copy-bound; usesnavigator.clipboard.writeText. No global API/events. - Tests: No automated tests (no spec asserts the copy-then-open flow or toast).
- Gaps / improvement ideas: Only
note.htmluses.js-linkedin-share;article.htmlandnotebook.htmlLinkedIn buttons are plain links that skip this enhancement β inconsistent. The toast has no close button and isn't focus-managed. Excerpt extraction can leak nav/footer text ifarticleBody/.bd-contentaren't present (falls back tomain/body). No test of dedup or the 420-char sentence truncation.
Posts pagination¶
- Purpose: Client-side, hash-driven pagination for the posts archive β shows/hides
.post-itemslices and renders an accessible Bootstrap pager without server round-trips. - Capabilities: reads
data-per-page/data-total; URL-hash page state (#page=N) withhashchange+history.replaceState; condensed page range with ellipsis for >7 pages; prev/next witharia-disabled/tabindex=-1at bounds; active page exposesaria-current="page"; "Showing XβY of N" + "Page X of Y" status text; smooth-scrolls grid into view on change. - Source:
- JS:
assets/js/posts-pagination.js(loadeddeferin_includes/components/js-cdn.html) - API surface: root attr
data-posts-archivewithdata-per-page,data-total; required ids#posts-grid,#pagination-info,#posts-info,#pagination-controls; item class.post-item; pagerdata-pageattrs;aria-current="page"on active. No global API/events. - Tests:
test/visual/layouts.spec.js"Posts archive (/pages/) β pagination β active page exposes aria-current='page'" (visits/pages/, asserts#pagination-controls .page-item.active .page-linkhasaria-current="page"; skips if no archive/single page). - Gaps / improvement ideas: Only the
aria-currentattribute is tested β the ellipsis range builder, prev/next disabling, hash navigation, and "Showing XβY" math are untested. Pager<a href="#">items can jump the scroll position beforepreventDefaultin edge cases. No "no results" handling. Page slicing relies on DOM order matching server sort.
Article layout¶
- Purpose: Primary blog-post layout with eight
post_typevariations, rich metadata, related posts, prev/next cards, author bio, and comments. Mapsfeatured/breakingbooleans to post types. - Capabilities: post-type variants (standard/featured/breaking/opinion/review/tutorial/listicle/interview) each with a tailored banner/box (breaking alert, featured hero image + display-4 title, opinion author box, review rating stars + pros/cons + verdict, tutorial outcomes, listicle quick-nav, interview notice); per-type sidebar resolution (featured/breaking β no sidebar unless overridden); reading time; category + tag badge links; inline About-the-Author block; related posts (tag overlap, max 3, excludes index/self); prev/next
.post-nav-cards; Giscus; Schema.orgBlogPosting+ microformats. Review stars userole="img"+aria-label+visually-hiddenscore for a11y. - Source:
- SCSS:
_sass/components/_post-navigation.scss(nav cards); type-variant styles elsewhere in_sass - Markup:
_layouts/article.html; componentspost-type-badge.html,preview-image.html,content/giscus.html - Plugin/data:
_data/authors.yml - API surface: classes
.post,.post-header,.post-title,.post-meta,.post-content,.post-navigation+.post-nav-card*,.featured-hero,.opinion-author-box,.review-summary,.rating-score/-stars,.review-pros-cons,.tutorial-info,.listicle-nav,.interview-notice,.author-section; front matterpost_type,sidebar,featured,breaking,rating,verdict,pros,cons,learning_outcomes,interviewee,sub-title,comments. - Tests:
test/visual/layouts.spec.js"Article layout β single H1" (follows first/posts/link, asserts exactly one H1). No spec exercises the post_type variants, rating math, pros/cons, or related-posts logic. - Gaps / improvement ideas:
{% if post_type == "review" and page.pros or page.cons %}mixesand/orwithout grouping (same precedence trap the file fixed elsewhere) β pros/cons box can render for non-review types whenconsis set. About-the-Author block is duplicated fromauthor-card.html(style=full). Rating star math (divided_by: 2,modulo: 2) only yields full/half stars to 5 β no empty-star track. Only single-H1 is tested across all eight variants.
Collection layout¶
- Purpose: Generic card-grid index for any Jekyll collection, driven by
page.collectionandpage.sort_order. - Capabilities: renders page content, then a 3-up responsive card grid of collection entries; preview image (fallback
site.teaser); excerpt (truncate 160); last-modified footer;itempropheadline/description microdata; supportssort_orderwith optionalreverse. - Source:
- Markup:
_layouts/collection.html(usespreview-image.html) - API surface: Bootstrap
.row row-cols-1 row-cols-md-3 g-4,.card h-100,.card-link,.post-meta; id#index-collection; front mattercollection,sort_order(reversespecial-cased);itemprop="headline|description". - Tests: No automated tests.
- Gaps / improvement ideas: Heading reads literal "Collection Index - {{ page.collection }}" (un-prettified key). Does not use
post-card.htmlβ a fourth bespoke card variant.sort_orderreverse handling is brittle (sort: nilthen conditional reverse). No empty-state. Cards have no badges/tags/author unlikepost-card.html. Nowidth/heighton images (CLS).
News layout¶
- Purpose: Magazine-style section front page for the
newscollection with hero, section nav, featured grid, per-category blocks, latest list, and footer widgets; supportsmagazine(default),grid, andliststyles. - Capabilities: hero (first breaking, else first featured) with badge + meta + CTA + preview image; section navigation; featured section (one large + four secondary); per-category
section-postsblocks;grid-section/list-sectionalternates; latest section; page content; footer widgets + newsletter section; reusespost-type-badge.html+preview-image.htmlthroughout; inline<style>for hero image responsiveness. - Source:
- Markup:
_layouts/news.html; componentspost-type-badge.html,preview-image.html - Plugin/data:
site.og_image/site.teaser - API surface: classes
.hero-section,.section-nav,.featured-section,.section-posts,.grid-section,.list-section,.latest-section,.page-content,.footer-widgets,.newsletter-section; front mattersection_style(magazine|grid|list),category, post flagsbreaking/featured/post_type. - Tests:
test/visual/features/layouts.spec.js"Section archive β news section page loads with layout containment" visitsUI_ROUTES.newsSection(/news/business/) and asserts.section-layout/#all-postscontainment β but that targets the section.html layout, notnews.html;news.htmlitself has no direct spec. - Gaps / improvement ideas: Large (660+ lines) with inline
<style>and inline share<script>rather than partials β does not reusepost-card.html, re-implementing cards in 4+ places (drift + maintenance cost). Hero/featured image heights are inlinestyle="height:...". No tests for the breakingβfeatured hero fallback, the three section_styles, or the newsletter form. Share buttons here skip the share-actions.js LinkedIn enhancement.
Tag layout¶
- Purpose: Tag-archive page listing all posts carrying
page.tag, with breadcrumbs, count, post-card grid, and a related-tags cloud. - Capabilities: breadcrumb nav; tag icon + title + pluralized count; optional description + page content; 3-up
post-card.htmlgrid; empty state with "Browse all tags" CTA; related-tags discovery (co-occurring tags, limit 12). - Source:
- Markup:
_layouts/tag.html(usespost-card.html) - API surface: classes
.tag-header,.tagged-posts,.related-tags, Bootstrap.breadcrumb,.row row-cols-*; front mattertag(required),title,description; tag links/tags/#<slug>. - Tests: No direct spec. (Uses
post-card.html; no spec visits a/tags/<tag>/page.) - Gaps / improvement ideas: Related-tags links point to
/tags/#<slug>anchors that depend on a tags index page rendering those anchors β fragile cross-page contract, untested. Breadcrumb uses<ol class="breadcrumb">but lacks thenav.breadcrumbs+aria-labelpattern the layouts spec checks for on other pages (a11y inconsistency). No automated coverage of count/pluralize or empty state.
Obsidian & Knowledge-Graph Features¶
This cluster brings Obsidian-flavored Markdown (wiki-links, embeds/transclusion, callouts, inline tags) and a force-directed knowledge graph to the theme, via a dual-path design: a build-time Ruby plugin for vanilla-Jekyll forks and a client-side JS resolver/graph for the default GitHub-Pages remote_theme build, both consuming a single generated wiki-index.json.
Wiki-Links ([[Page]])¶
- Purpose: Renders Obsidian
[[Page Title]]references as resolved internal links so authors can cross-link content the way they do in Obsidian. Unresolved targets degrade to a visible "broken link" affordance instead of dead text. - Capabilities: alias display
[[Page|Alias]]; heading anchors[[Page#Heading]](anchorized, shown asPage βΊ Heading); block refs[[Page^block-id]]degraded to heading-style anchor; case-insensitive lookup by title / basename / front-matteraliases(normalized lowercase + collapsed whitespace, first-registration-wins); skips matches inside fenced/inline/indented code;aria-current="page"self-link marking; broken state styled distinctly; dual server (Ruby) + client (JS DOM-rewrite) paths producing identical HTML. - Source:
- SCSS:
_sass/core/_obsidian.scss(.wiki-link,.wiki-link-broken) - Markup: rewritten in-body (no dedicated include); JS wired via
_includes/components/js-cdn.html - JS:
assets/js/obsidian-wiki-links.js - Plugin/data:
_plugins/obsidian_links.rb,assets/data/wiki-index.json - API surface: classes
.wiki-link,.wiki-link-broken; data-attributesdata-wiki-target; attributearia-current="page"; JSwindow.ObsidianResolver.{rewriteHtml,rewriteContainer,buildIndex,normalize},window.__OBSIDIAN_INDEX__, opt-out flagwindow.__OBSIDIAN_DISABLE_CLIENT__, configwindow.OBSIDIAN_CONFIG.{wikiIndexUrl,attachmentsPath,tagBase}; eventobsidian:ready(document-level,detail.count/detail.calloutCount); CSS vars--bs-primary-rgb,--bs-danger,--bs-danger-rgb - Tests:
test/test_obsidian.shorchestrates:test/test_ruby_converter.rbβtest_resolved_wiki_link,test_alias_wiki_link,test_alias_lookup_via_aliases,test_header_anchor,test_unresolved_wiki_link,test_wiki_link_inside_code_block_is_preserved,test_wiki_link_inside_inline_code_is_preserved,test_plain_markdown_unchanged;test/test_resolver.js(JSDOM-free shim) mirrors these (resolved class+href, alias text, alias key, header anchor, broken class). No Playwright/visual coverage. - Gaps / improvement ideas: No end-to-end browser test asserting the JS DOM-rewrite path actually fires on a rendered page (only the shim is tested); broken links use
href="#"(clickable no-op, scrolls to top) rather than a<span>oraria-disabled; no a11y test that broken links are keyboard-discernible; thecursor: helpbroken state has noaria-describedby/tooltip for non-mouse users.
Embeds & Transclusion (![[β¦]])¶
- Purpose: Renders
![[image.png]]as an<img>and![[Note Title]]as an inline excerpt of another note, letting authors compose pages from reusable fragments. - Capabilities: image embeds with width hint
![[img.png|400]]or alt-text override; absolute (/path) vs. attachments-path-relative resolution; note transclusion (Ruby path emits a Liquidtransclude.htmlinclude rendering a card with header/date/800-char body; JS path renders a header+excerpt block from the index); broken-embed warning alert; transclusion loop guarded (converter deliberately not re-run on embedded body); image extensions.png .jpg .jpeg .gif .svg .webp .avif .bmp. - Source:
- SCSS:
_sass/core/_obsidian.scss(.obsidian-embed-image,.obsidian-embed-note,.obsidian-embed-broken) - Markup:
_includes/content/transclude.html - JS:
assets/js/obsidian-wiki-links.js(renderImageEmbed,renderNoteEmbed) - Plugin/data:
_plugins/obsidian_links.rb(render_image_embed,render_note_embed),assets/data/wiki-index.json(excerptfield) - API surface: classes
.obsidian-embed,.obsidian-embed-image,.obsidian-embed-note,.obsidian-embed-header/.obsidian-embed-excerpt(JS) /.obsidian-embed-source/.obsidian-embed-body/card(Ruby include),.obsidian-embed-broken; img attrsloading="lazy", optionalwidth; Liquid include paramstarget,url; CSS vars--bs-primary,--bs-light-rgb,--bs-dark-rgb - Tests:
test/test_ruby_converter.rbβtest_image_embed_with_width,test_image_embed_default_attachments_path,test_note_embed_resolved(assertstransclude.htmlinclude + url),test_note_embed_missing.test/test_resolver.jsβ image embed<img>+width, note embed β.obsidian-embed-noteblock, missing note embed β broken alert. No Playwright coverage; thetransclude.htmlLiquid include itself is not unit-tested (only that the converter emits the include tag). - Gaps / improvement ideas: Markup divergence between the two paths is untested β Ruby produces a
<aside class="card">with truncated 800-charmarkdownifybody, JS produces a flat 240-charexcerptdiv; no test asserts visual/structural parity. No test that the Ruby transclude include correctly resolves vs. falls back to the broken alert. Image embeds lack width/height pairing (onlywidth) so CLS is possible; no responsivesrcset.
Callouts (> [!type])¶
- Purpose: Converts Obsidian callout blockquotes (
> [!note] Title) into themed Bootstrap alert cards with an icon, title, and body, giving authors admonition boxes. - Capabilities: ~30 mapped types (note/abstract/summary/tldr/info/todo/tip/hint/important/success/check/done/question/help/faq/warning/caution/attention/failure/fail/missing/danger/error/bug/example/quote/cite) each mapped to a Bootstrap alert color + Bootstrap Icon; unknown type falls back to
note/alert-primary; optional custom title (defaults to capitalized type); fold markers[!type]+/[!type]-with collapsed state; inner Markdown preserved (Ruby usesmarkdown="1"body span; JS moves blockquote children into the body div); Ruby path operates on raw markdown pre-kramdown, JS path rewrites the post-kramdown<blockquote>; works even when the wiki-index fetch fails (callouts need no index). - Source:
- SCSS:
_sass/core/_obsidian.scss(.obsidian-callout,.obsidian-callout-title,.obsidian-callout-body) - JS:
assets/js/obsidian-wiki-links.js(rewriteCallouts,CALLOUT_TYPES,CALLOUT_HEAD_RE) - Plugin/data:
_plugins/obsidian_links.rb(transform_callouts,CALLOUT_TYPES) - API surface: classes
.alert.alert-{color},.obsidian-callout,.obsidian-callout-{type},.obsidian-callout-title,.obsidian-callout-body; data-attributesdata-obsidian-callout(type, JS only / dedupe guard),data-collapsed="true";role="alert"; iconsbi bi-*(aria-hidden="true"); configobsidian.callout_class_prefix(Ruby, defaultobsidian-callout) - Tests:
test/test_ruby_converter.rbβtest_callout_note(class +alert-primary+ title +role="alert"),test_callout_warning_with_fold_marker,test_callout_collapsed(data-collapsed="true"),test_callout_unknown_type_falls_back_to_note.test/test_resolver.jsβrewriteCalloutsreturns 1,alert-warning obsidian-callout-warning, title/body preserved,role="alert", non-callout blockquote untouched. No Playwright coverage; collapse/fold has no interactive (click-to-expand) toggle test β and notably no JS toggles the collapsed state at all. - Gaps / improvement ideas:
data-collapsed="true"only hides the body via CSS (display:none) β there is no toggle UI/JS to expand it, so foldable callouts authored with[!type]-are permanently collapsed and their content is hidden from keyboard/SR users with no control.role="alert"on every callout is questionable a11y (asserts assertive live-region semantics for static prose); considerrole="note"/region. No test covers the icon-onlyaria-hiddentitle accessibility. The two CALLOUT_TYPES maps (Ruby + JS) are duplicated and could drift.
Inline Tags (#tag)¶
- Purpose: Turns Obsidian-style inline
#tagmentions into linked tag badges pointing at the site's tag index. - Capabilities: supports nested/slashed tags
#fixture/example; 1β64 char limit; requires a leading letter; skips tags inside code spans/fences and (Ruby) avoids markdown-heading#and link-internal hashes via masking; slugifies the tag for the anchor; links to configurable tag base (/tags/#slug). - Source:
- SCSS:
_sass/core/_obsidian.scss(.obsidian-tag) - JS:
assets/js/obsidian-wiki-links.js(TAG_RE, tag branch ofrewriteHtml) - Plugin/data:
_plugins/obsidian_links.rb(transform_inline_tags,INLINE_TAG_RE) - API surface: classes
.obsidian-tag; configobsidian.tag_base_url(Ruby) /window.OBSIDIAN_CONFIG.tagBase(JS, default/tags/); CSS vars--bs-secondary,--bs-secondary-rgb,--bs-primary - Tests:
test/test_ruby_converter.rbβtest_inline_tag(.obsidian-tag>#obsidian,#fixture/example),test_tag_inside_code_skipped.test/test_resolver.jsβ inline tag β.obsidian-tag, "hash followed by space is NOT a tag". No Playwright coverage. - Gaps / improvement ideas: Tag links go to
/tags/#slugbut there is no test that the tags page anchor actually exists / resolves; risk of dangling fragment links. No a11y/contrast test for the badge. Ruby vs. JS tag regexes differ subtly (INLINE_TAG_RElookbehind vs. JS leading-char capture) and are not cross-validated against the same fixtures.
Wiki Index (wiki-index.json)¶
- Purpose: Build-time generated JSON map of every renderable doc/page (title, basename, url, collection, tags, categories, aliases, outgoing wiki-links, excerpt) that is the single source of truth feeding the client resolver, both graph views, and the backlinks logic.
- Capabilities: Liquid-generated at
/assets/data/wiki-index.json(layout: null,sitemap: false); iterates all collection docs +output_ext == .htmlpages; strips fenced + inline code before extracting[[β¦]]targets; masks![[β¦]]embeds so they aren't double-counted; heuristically discards operator-heavy/Bash[[ β¦ ]]test targets ($,==,&&,-eq, braces, quotes, leading-); de-dupes outgoing per page; 240-char excerpt;generated_at+count. Lookup keys normalized identically across Ruby/JS (lowercase().trim().replace(/\s+/g,' ')). - Source:
- Markup:
assets/data/wiki-index.json(Liquid template, front-matter + body) - JS consumers:
obsidian-wiki-links.js,obsidian-graph.js,obsidian-local-graph.js - Plugin/data:
_plugins/obsidian_links.rbalso builds an equivalent in-memoryIndex(server path);_includes/content/backlinks.htmlre-derives links independently in Liquid - API surface: JSON schema
{ generated_at, count, entries[] }, entry{ title, basename, url, collection, tags, categories, aliases, outgoing, excerpt }; fetched viaOBSIDIAN_CONFIG.wikiIndexUrl/OBSIDIAN_WIKI_INDEX_URL/data-index-url; cacheforce-cache(resolver) - Tests:
test/test_obsidian.shLayer 3 β runsjekyll buildand validates the generated file with a Python schema check: top-level object, requiredcount/entrieskeys,entriesis an array,count == len(entries),count > 0, and sample entry hastitle/basename/url. No assertion onoutgoing/aliases/excerptcorrectness or the code-stripping heuristic. - Gaps / improvement ideas: The Bash-operator exclusion heuristic in the Liquid template is fragile and untested β false positives/negatives in
outgoingdirectly corrupt both graphs; add fixtures asserting specific outgoing edges. The RubyIndex(server path), the Liquid index (client path), and the Liquidbacklinks.htmlmatcher are three independent implementations of "what links to what" that can drift; no cross-consistency test. Excerpt strips HTML but not Liquid/front-matter edge cases.
Full Knowledge Graph (graph page)¶
- Purpose: A force-directed, interactive site-wide map (cytoscape.js) rendered on the
/docs/obsidian/graph/page showing every page as a node and every[[wiki-link]]as a directed edge, including dangling links as red broken nodes. - Capabilities: cose force layout; per-collection node colors (posts/docs/notes/notebooks/quickstart/about/hobbies/news/services + fallback page gray); node size mapped to degree; hub nodes (degree β₯ 12) stay labeled when zoomed out, others reveal labels past zoom 1.25 / on hover; hover highlights closed neighborhood and fades the rest; broken targets become dashed red nodes/edges; tap to navigate (Cmd/Ctrl-click β new tab); search box filters+fits matching nodes with live status; "Reset view" fit button; "Show orphans" toggle (hidden by default, re-runs layout); stats badges (pages/links/broken); light/dark theme resolution; cytoscape loaded from CDN only on this page; graceful failure alerts if cytoscape or data fail to load.
- Source:
- SCSS: inline
<style>in_includes/obsidian/full-graph.html(scoped#obsidian-graph, toolbar, legend, tips) - Markup:
_includes/obsidian/full-graph.html(page body atpages/_docs/obsidian/graph.md) - JS:
assets/js/obsidian-graph.js - Plugin/data:
assets/data/wiki-index.json - API surface: ids
#obsidian-graph,#obsidian-graph-stats,#obsidian-graph-search,#obsidian-graph-fit,#obsidian-graph-orphans,#obsidian-graph-status; classes.obsidian-graph-toolbar,.obsidian-graph-legend,.obsidian-graph-tips; cytoscape classes.faded,.highlighted; JSwindow.ObsidianGraph.{cy,byKey,entries},window.OBSIDIAN_WIKI_INDEX_URL;role="img"/aria-live="polite"/role="status"; CSS vars--bs-border-color(-rgb),--bs-body-bg(-rgb),--bs-tertiary-bg,--bs-primary,--bs-secondary-color,--bs-border-radius(-lg) - Tests: No automated tests. Not referenced by any
test/visual/*.spec.js,test_obsidian.sh, or unit suite β the graph rendering, search, orphans toggle, theming, and broken-node logic are entirely untested. - Gaps / improvement ideas: Zero coverage of a fairly complex interactive component β add Playwright behavioral tests (graph mounts, stats populate, search filters, orphans toggle hides/shows, broken nodes render dashed/red) and a visual snapshot. Cytoscape canvas graph is fundamentally inaccessible (a single
role="img"with a generic label, no text/table fallback or keyboard navigation of nodes). External CDN + SRI dependency is a single point of failure with only a generic error alert. Hardcoded collection color palette duplicated across full graph, local graph, and the page legend.
Local Graph (sidebar panel + FAB)¶
- Purpose: Obsidian-style page-scoped "local graph" β a collapsible offcanvas side panel triggered by a floating action button that shows the current page plus its immediate wiki-link neighbors.
- Capabilities: BFS subgraph (default depth 1, configurable via
local_graph_depthfront matter /data-depth) following both outgoing and incoming links; current-page node highlighted ("you are here", orange border); current page matched by URL then title/basename/alias fallback (handles permalink/baseurl quirks); broken neighbor nodes; lazy single-load of cytoscape from CDN with SRI (shared with full graph page); panel auto-hides if the page isn't in the wiki-index or index fails to load; per-page node/link status text; "Full graph" link; resizes on offcanvas show + window resize; opt-out vialocal_graph: false; auto-disabled when sidebar is off (featured/breaking posts). - Source:
- SCSS:
_sass/core/_obsidian.scss(.obsidian-local-graph-fab,.obsidian-local-graph-toggle,.obsidian-local-graph-panel,.obsidian-local-graph-widget,#obsidian-local-graph) - Markup:
_includes/navigation/local-graph.html(panel),_includes/navigation/local-graph-fab.html(FAB) β both included from_includes/core/footer.html - JS:
assets/js/obsidian-local-graph.js - Plugin/data:
assets/data/wiki-index.json - API surface: id
#obsidian-local-graph; data-attributesdata-obsidian-local-graph-panel,data-obsidian-local-graph-toggle,data-obsidian-local-graph-status,data-depth,data-index-url; Bootstrap offcanvas attrsdata-bs-toggle="offcanvas"/data-bs-target="#obsidianLocalGraphPanel"; element propcontainer.__obsidianLocalGraph(cy instance); globalswindow.__obsidianCytoscapeLoading; eventsshown.bs.offcanvas,resize; front matterlocal_graph,local_graph_depth,sidebar; CSS vars--zer0-space-fab-offset,--zer0-space-fab-size,--zer0-shadow-fab,--zer0-layer-fab-local-graph,--zer0-layer-offcanvas,--bs-offcanvas-*,--bs-tertiary-bg,--bs-border-color,--bs-border-radius-lg;role="img"/role="status" - Tests: No automated tests. The include, FAB, offcanvas wiring, BFS subgraph, current-page detection, and depth config have no unit, shell, or Playwright coverage.
- Gaps / improvement ideas: Entirely untested despite nontrivial BFS + permalink-fallback logic; add a JS unit test for
buildSubgraph/findCurrentEntryand a Playwright test that the FAB opens the panel and the graph mounts. Same cytoscape inaccessibility as the full graph (canvas,role="img"). FAB z-index/offset coordination with other FABs (back-to-top, navbar-extras) relies on shared--zer0-*tokens but has no visual regression test guarding overlap. DuplicatedreadTheme/collectionColor/buildLookup/normalizelogic across the two graph scripts and the resolver β candidate for a shared ES module underassets/js/modules/.
Backlinks Panel (Linked mentions)¶
- Purpose: Renders an Obsidian-style "Linked mentions" section listing every page that wiki-links or permalink-references the current page, computed entirely at build time in Liquid.
- Capabilities: scans all collection docs + HTML pages; matches the current page's permalink in body, or its title/basename inside
[[β¦]](case-insensitive, whitespace-collapsed); skips self-refs and (unlessshow_drafts) drafts/unpublished; renders title, collection badge, and truncated description; count badge; on by default innotelayout, opt-in (backlinks: true) indefaultlayout, suppressible per page (backlinks: false). - Source:
- SCSS:
_sass/core/_obsidian.scss(.obsidian-backlinks,.obsidian-backlinks-list,.obsidian-backlink,.obsidian-backlink-link,.obsidian-backlink-excerpt) - Markup:
_includes/content/backlinks.html, included from_layouts/note.htmland conditionally_layouts/default.html - Plugin/data: independent Liquid matcher (does not consume
wiki-index.json) - API surface: classes
.obsidian-backlinks,.obsidian-backlinks-list,.obsidian-backlink,.obsidian-backlink-link,.obsidian-backlink-excerpt; ids#backlinks-heading;aria-labelledby="backlinks-heading"; front matterbacklinks(true/false),site.show_drafts; iconbi-link-45deg; CSS vars--bs-primary(-rgb) - Tests: No automated tests. Not exercised by
test_obsidian.sh, the unit suites, or Playwright β the matching logic (permalink +[[title]]/[[basename]]) and draft filtering are unverified. - Gaps / improvement ideas: Substring matching (
_body contains _self_url,[[title]]) is prone to false positives (partial-title collisions, permalink-as-substring of another URL) and false negatives (aliases are not matched, unlike the wiki-index/plugin which do match aliases) β add fixtures. This is a third independent link-resolution implementation distinct from the RubyIndexand the JSON index; consider unifying onwiki-index.json'soutgoingto guarantee parity with the graphs. O(docs Γ content) Liquid scan on every page render has build-cost implications on large sites and is untested for performance.
Admin Tools & Dashboards¶
A suite of dev-facing admin pages (all under /about/ on the admin/stats/setup layouts) that surface, browse, edit, and export Jekyll configuration, navigation, collections, analytics, environment, and content statistics β plus a first-run setup wizard/banner. All rendering is build-time Liquid reading site.* / site.data.*; client JS adds search/filter, tabbed YAML export, copy-to-clipboard, and download. Secrets are sanitized before reaching the DOM. Most components are wired into pages in pages/_about/settings/*.md and exercised by test/visual/*.spec.js against the live Docker-served site.
Admin Shell (layout + sidebar nav + tabs)¶
- Purpose: Dashboard-style chrome for every admin/settings page: breadcrumbs, icon+title header, optional action buttons, and a sticky/offcanvas data-driven sidebar. Gives config pages a non-article presentation.
- Capabilities: front-matter-driven (
icon,admin_nav,admin_section,admin_actions); desktop sticky sidebar (col-lg-3) vs. mobile offcanvas toggle (#adminSidebar); active-link detection by URL oradmin_section; dynamic sidebar badges (collections count, analytics On/Off, env Prod/Dev); external-link separator<li><hr></li>; placeholder-token resolution ({github_user},{repository_name}); reusableadmin-tabs.htmlrenders a Bootstrap tablist from a pipe/colon-encodedtabsstring;admin_page_urls.rbprecomputes a pipe-delimited string of/about/page URLs once per build for cheapcontainschecks. - Source:
- SCSS: β (uses Bootstrap utilities +
nav.admin-sidebar; no dedicated partial found) - Markup:
_layouts/admin.html,_includes/navigation/admin-nav.html,_includes/components/admin-tabs.html - JS: β (Bootstrap offcanvas/tab/collapse only)
- Plugin/data:
_plugins/admin_page_urls.rb,_data/navigation/admin.yml - API surface: classes
.admin-sidebar,.nav.nav-pills.flex-column,.nav-link.active,.nav-tabs; ids#adminSidebar,#admin-content,#{tab_prefix}Tabs,#tab-<id>/#pane-<id>; data-attrsdata-bs-toggle="offcanvas|tab",data-bs-target; front mattericon/admin_nav/admin_section/admin_actions; site datasite.data.admin_page_urls,site.data.navigation.admin. No JS globals/events/CSS vars. - Tests:
test/visual/admin-layout.spec.jsβ per-page (8 ADMIN_PAGES): 200 status, header+h1.h3title equalspageTitle, breadcrumb β₯3 items with active matching title, header iconi.fs-2visible,#admin-contentvisible, desktop sidebar visible + active link href matches URL, mobile toggle visible / desktop sidebar hidden / offcanvas opens and shows.nav.nav-pills, and no console errors.test/visual/admin-nav.spec.jsβ sidebar renders β₯ADMIN_PAGES links, each internal link returns 200, external links havetarget="_blank"+rel~="noopener", exactly one active link per page, clicking "Theme Customizer" navigates.test/visual/accessibility.spec.jsβ every admin page passes axe WCAG 2.1 AA;nav.admin-sidebar[aria-label]present; tabs exposearia-controlsβexisting panel;<hr>is not a direct child of<ul>.admin_page_urls.rbunit-tested intest/test_plugins.rb(sorted pipe-delimited output, non-HTML exclusion, empty case), run bytest/test_core.sh. - Gaps / improvement ideas:
admin-tabs.htmlinclude exists but the real config/navigation pages hand-roll their tablists instead of using it β either adopt it or mark it deprecated. Theadmin-tabscolon-split breaks if a tab label contains a colon. No test assertsadmin_actionsbuttons render or that dynamic sidebar badges (collections/analytics/env) show correct values.
Config Viewer¶
- Purpose: Read-only accordion browser of the live
_config.yml(site.*) with per-value/per-section/full-config copy and instant search. The default "View Config" tab on/about/config/. - Capabilities: accordion sections (Site Identity, GitHub, URLs, Personalization, Analytics, Collections, Plugins, Build & Markdown, Theme Colors swatches, Powered By); live search filtering rows + auto-hiding empty sections; expand-all/collapse-all; copy single value, copy section (rebuilt as
key: valuelines), copy full config (reads hidden#cfg-full-yaml); clear-search button; color swatches and tech badges. - Source:
- SCSS: β
- Markup:
_includes/components/config-viewer.html(wired bypages/_about/settings/config.md) - JS:
assets/js/config-utility.js(initSearch,initCopyButtons,initSectionCopy,initFullCopy,initExpandCollapse) - Plugin/data: reads
site.*; full-YAML/raw element sanitized inconfig.mdvia Liquid +sanitize_config_filter.rb - API surface: classes
.cfg-row,.cfg-section,.cfg-copy-val,.cfg-copy-section; ids#cfg-search,#cfg-search-clear,#cfg-expand-all,#cfg-collapse-all,#cfg-copy-full,#cfg-full-yaml,#configAccordion; data-attrsdata-key,data-value,data-section. No JS globals/events/CSS vars. - Tests:
test/visual/config-viewer.spec.jsβ 200 status, accordion/cards render (>0),#cfg-searchfill keeps content visible, no-results search doesn't crash; onetest.fixme(section-copy hex-quoting regression, disabled).test/visual/security.spec.jsβ#cfg-full-yaml(if present) contains noapi_key:/secret:/password:/token:/phc_patterns. - Gaps / improvement ideas: Search/copy success paths are only smoke-tested β no assertion that filtering actually hides non-matching
.cfg-rows, that section/full copy writes correct text to clipboard, or that expand/collapse toggles.show. The section-copy YAML does not quote special values (hex colors become YAML comments) β the regression test isfixme'd and unfixed. Per-value copy buttons (.cfg-copy-val) have icon-only content with only atitleβ verify accessible name.
Config Editor¶
- Purpose: Form-based
_config.ymlbuilder with a live YAML preview pane plus copy/download. The "Edit & Export" tab on/about/config/, pre-populated fromsite.*. - Capabilities: grouped form cards (Identity, GitHub, URLs/Deployment, Personalization, Analytics) with text/email/url/number/select/checkbox fields; live YAML rebuild on every input/change (
buildEditorYAMLwithyamlEscape/padalignment); description char-counter (160 max); theme-skin dropdown (9 skins); copy YAML; download_config.ymlvia Blob; emits a fixed plugins/build block. - Source:
- SCSS: β
- Markup:
_includes/components/config-editor.html(wired bypages/_about/settings/config.md) - JS:
assets/js/config-utility.js(initEditor,buildEditorYAML,yamlEscape,initCharCounters) - Plugin/data: reads
site.*to seed field values - API surface: classes
.edit-field; ids#configEditor,#editor-yaml-preview,#editor-copy,#editor-download,#edit-title/#edit-founder/#edit-description/#edit-theme-skin/#edit-posthog-enabledetc.; data-attrsdata-key(dotted forposthog.enabled/posthog.api_key),data-char-counter. No globals/events/CSS vars. - Tests:
test/visual/config-editor.spec.jsβ Edit/Export tab activates and shows a panel, title field pre-populated (length>0), changing title updates YAML preview to contain "Test Site Title", skin dropdown has >1 option, a download button exists. Note: several locators (input#cfg-title,select#cfg-skin) do NOT match the actual ids (#edit-title,#edit-theme-skin), so those assertions hit thetest.skip()fallback and silently pass without truly testing the editor. - Gaps / improvement ideas: Spec selector drift (
#cfg-title/#cfg-skinvs real#edit-*ids) means the editor's pre-population, live-preview, and skin-dropdown tests effectively no-op β fix the locators. No test of the download Blob content or copy action.buildEditorYAMLquotes values viayamlEscape(good) but hardcodesremote_themedefault tobamr87/zer0-mistakes; the email field usesyamlEscapeeven though@triggers quoting β verify generated YAML round-trips.
Raw YAML / Config Sanitization¶
- Purpose: Shows the full
_config.ymltext in a copyable Raw-YAML tab and feeds the viewer's hidden copy element, with secrets redacted before they ever reach the DOM (defense for GitHub Pages where plugins are no-ops). - Capabilities: pure-Liquid line filter in
config.mdredacts lines containingapi_key/secret/password/token/phc_to# [redacted];sanitize_config_filter.rbadds asanitize_config_yamlLiquid filter (key-name regex β[REDACTED],phc_β¦value masking) as plugin-side defense-in-depth; same sanitized capture reused for visible#cfg-raw-yamland hidden#cfg-full-yaml; raw-tab copy button. - Source:
- SCSS: β
- Markup:
pages/_about/settings/config.md(capture + Raw tab +#cfg-full-yaml) - JS: inline
cfg-copy-rawhandler inconfig.md - Plugin/data:
_plugins/sanitize_config_filter.rb - API surface: ids
#cfg-raw-yaml(<code>),#cfg-full-yaml(hidden<pre>),#cfg-copy-raw; Liquid filtersanitize_config_yaml; constantsSENSITIVE_KEY_RE,PHC_VALUE_RE. - Tests:
test/visual/security.spec.jsβcode#cfg-raw-yamlmust exist (count 1) and contain noapi_key:/secret:/password:/token:/phc_β¦/sk_β¦/ghp_β¦patterns, and must includeremote_theme|theme_skin(proves non-empty render); hidden#cfg-full-yamllikewise scrubbed; page HTML free ofghp_{36}/sk_live_patterns. - Gaps / improvement ideas:
sanitize_config_filter.rbhas no direct Ruby unit test (only end-to-end DOM assertion) β add one for edge cases (indented keys, inline anchors like&github_user, multi-secret lines). The two redaction implementations (Liquid inconfig.mdand the Ruby filter) duplicate the secret pattern list and can drift apart; consider centralizing the pattern set.
Environment Dashboard¶
- Purpose: Full-page environment/build readout for
/about/settings/environment/: Jekyll/Ruby versions, environment, build time, site config, theme/repository info, active plugins, and dev/prod URL comparison. - Capabilities: four overview cards (Jekyll version, Ruby version, environment with success/warning styling, build time); Site Configuration and Theme & Repository tables; active-plugins grid (or empty state); URL-configuration table (
site.url,site.baseurl,jekyll.environment, full base path); quick links to config + analytics dashboards. - Source:
- SCSS: β
- Markup:
_includes/components/env-dashboard.html(wired bypages/_about/settings/environment.md) - JS: β
- Plugin/data: reads
jekyll.version,jekyll.environment,site.*(ruby_versionis a config key, not introspected) - API surface: Bootstrap card/table classes only; conditional
border-success/border-warningon environment card. No ids/data-attrs/globals/events/CSS vars. - Tests:
test/visual/env-dashboard.spec.jsβ 200 status; overview cards render (>0); Jekyll version matches\d+\.\d+\.\d+and content has no "undefined"; noundefined/NaN/nulltokens in#admin-content; active-plugins list non-empty (or content mentions "plugin"); onetest.fixmefor Ruby version (blocked by github-pages safe mode). Also covered by admin-layout/admin-nav/accessibility per-page suites. - Gaps / improvement ideas: Ruby version comes from a config key
site.ruby_version(shows "?" if unset) and the version test isfixme'd β wire a safe-mode-compatible source or remove the card. No assertion that the URL-comparison table or theme/repository values are correct, only that no error tokens appear.
Environment Switcher¶
- Purpose: Inline (info-panel) widget showing current environment, current page URL, and quick Prod/Dev/Source links with copy buttons. Used inside
info-section.html, not a standalone admin page. - Capabilities: env status card (Prod success / Dev warning) using
is_productionheuristic (treats localhost/127.0.0.1/0.0.0.0 URLs as dev even whenJEKYLL_ENV=production); readonly current-URL input with copy; build-info tiles (build time, Jekyll version, rawJEKYLL_ENV); quick-links list (canonical prod origin viasite.domain_url/production_url, devlocalhost:port, GitHub source) with open-in-tab + copy; dismissible env tip alert; self-containedcopyToClipboard/copyUrl/showCopyFeedbackscript. - Source:
- SCSS: β
- Markup:
_includes/components/env-switcher.html, depends on_includes/components/env-detect.html(setsbuild_env,is_local_url,is_production) - JS: inline (defines global
copyToClipboard(inputId, btn),copyUrl(url, btn),showCopyFeedback) - Plugin/data: reads
jekyll.environment/jekyll.version,site.domain_url/production_url/url/baseurl/port/repository/branch - API surface: ids
#currentUrlInput; JS globalswindow.copyToClipboard,window.copyUrl,window.showCopyFeedback;onclickhandlers; CSS vars none. - Tests: No automated tests (not directly asserted by any spec; only indirectly via pages that include
info-section.html). - Gaps / improvement ideas: Defines a global
copyToClipboardthat collides in name (different signature) with the module-scoped one inconfig-utility.jsβ risk if both ever load on one page. Inline<script>per-include can duplicate if the include renders twice. No test coverage of the prod/dev URL derivation or the local-URL-as-dev heuristic. Inline scripts also conflict with strict CSP viaonclick.
Env-Var Helper (zer0-env-var)¶
- Purpose: Standalone interactive table to define shell environment variables (GITHOME, GHUSER, etc.) and emit
exportstatements; an onboarding/setup helper. Documented in_includes/README.mdbut not wired into any current page. - Capabilities: editable key/value table with add/remove rows; Submit writes values to
sessionStorage, builds anexport β¦code block, and updates a#repo-linkanchor to the user's GitHub repo. - Source:
- SCSS: β
- Markup:
_includes/components/zer0-env-var.html - JS: inline (addRow/removeRow/submit handlers)
- Plugin/data: none (client-only)
- API surface: ids
#envTable,#addRow,#submit,#codeBlock,#repo-link(external dependency β not defined in this include); classes.removeRow,.copy. No globals/events/CSS vars. - Tests: No automated tests.
- Gaps / improvement ideas: References
#repo-linkwhich this include never renders β submit will throw if the host page lacks it (no null guard), a latent JS error. Inputs lack<label>/aria-label(a11y). BuildscodeBlock.innerHTMLfrom user input as HTML spans (minor XSS/escaping smell). Appears orphaned (no page includes it) β confirm whether to wire into setup or remove.
Navigation Editor¶
- Purpose: Read-only viewer + exporter for all
_data/navigation/*.ymlmenu files on/about/settings/navigation/. Three tabs: Overview (tree), Edit Menus (selector stub), Export YAML. - Capabilities: summary cards (file count, total top-level items, source dir); per-file accordion tree (
nav-editor.html) with icons, urls, external badges, sub-item counts, and "not found" badges; overview cards (nav-overview.html) listing first 5 items per file + "N more"; YAML export blocks (nav-export.html) with file selector + copy; editor tab is a guided stub (no real editing β directs users to edit YAML in the repo);nav-editor.jscopies the export, populates a placeholder YAML on file-select, and expands the matching accordion. - Source:
- SCSS: β
- Markup:
_includes/components/nav-editor.html,_includes/components/nav-overview.html,_includes/components/nav-export.html(pagepages/_about/settings/navigation.md) - JS:
assets/js/nav-editor.js(+ inlineshowNavExport/copyNavExportin nav-export.html) - Plugin/data:
_data/navigation/{main,home,about,docs,posts,quickstart,admin}.yml - API surface: ids
#navAccordion,#navAcc-<file>,#nav-file-select,#nav-copy-yaml,#nav-yaml-output,#exportNavSelect,#export-<name>,#export-pre-<name>; classes.nav-export-block; JS globalswindow.showNavExport,window.copyNavExport; usesbootstrap.Collapse.getOrCreateInstance. No events/CSS vars. - Tests: No dedicated spec; covered only by the per-page admin-layout/admin-nav/accessibility suites (200, layout, WCAG, tab ARIA roles). The "Edit Menus" tab and export/copy logic are untested.
- Gaps / improvement ideas: The "Edit Menus" tab is a non-functional stub (only an info alert) β either build real editing or relabel to avoid implying it edits.
nav-yaml-outputis populated with a placeholder comment bynav-editor.jsrather than the real YAML rendered innav-export.html(two parallel export mechanisms β#nav-yaml-outputvs.nav-export-block); consolidate. The hardcodednav_fileslist is duplicated across three includes β extract to one source. No assertion that copy writes the rendered YAML.
Collection Manager¶
- Purpose: Jekyll collections overview on
/about/settings/collections/: counts, output status, directories, permalinks, and per-collection defaults/recent docs. - Capabilities: four summary cards (total collections, total documents, with-output count, collections dir); collections table (label, doc count badge, output check icon, directory, permalink or "default"); per-collection defaults accordion with output/directory/permalink + up to 5 recent document links and "N more".
- Source:
- SCSS: β
- Markup:
_includes/components/collection-manager.html(wired bypages/_about/settings/collections.md) - JS: β
- Plugin/data: reads
site.collections - API surface: ids
#collDefaultsAccordion,#collDef-<slug>; Bootstrap table/card/accordion/badge classes. No data-attrs/globals/events/CSS vars. - Tests: No dedicated spec; covered by per-page admin-layout/admin-nav/accessibility suites (200, layout, sidebar badge shows collections count, WCAG). Table/accordion content not asserted.
- Gaps / improvement ideas: Name says "Manager" but it is read-only (no create/edit/delete) β relabel or extend. No test that the collections count card matches the sidebar dynamic badge or the actual
site.collectionssize. Recent-docs links are not verified to resolve.
Analytics Dashboard¶
- Purpose: PostHog/tracking configuration overview on
/about/settings/analytics/: enablement, privacy/compliance settings, custom-event toggles, and links out to PostHog. - Capabilities: four status cards (enabled, respect DNT, session recording, secure cookies); PostHog Configuration table (api_host, person_profiles, autocapture, pageview/pageleave capture, persistence); Privacy & Compliance table (DNT, session recording, cookies, cross-subdomain, mask text/inputs, IP anonymization); custom-event cards (downloads, external links, search, scroll depth) when configured; external links to PostHog dashboard + full config.
- Source:
- SCSS: β
- Markup:
_includes/components/analytics-dashboard.html(wired bypages/_about/settings/analytics.md) - JS: β
- Plugin/data: reads
site.posthog.* - API surface: Bootstrap card/table/badge classes; conditional
border-success/border-warning. No ids/data-attrs/globals/events/CSS vars. - Tests: No dedicated spec; covered by per-page admin-layout/admin-nav/accessibility suites (200, layout, sidebar shows On/Off analytics badge, WCAG). PostHog field rendering not directly asserted.
- Gaps / improvement ideas: Read-only config mirror β no live PostHog data (impressions/events), despite "Dashboard" naming. The status card shows raw
ph.enabled | default: falsetext (prints "true"/"false"/"false") inconsistently with the badge styling below; normalize. No test that secret-shaped PostHog values (api_key) are absent here (they're not rendered, but worth a guard test).
Statistics Dashboard¶
- Purpose: Full content-analytics portal at
/about/stats/(layoutstats): overview metrics, top categories/tags, tag cloud, quick facts/insights, and a no-data onboarding state. Data is regenerated on every build. - Capabilities: header with rotating icon + last-updated/refresh; quick-jump button group (overview/categories/tags/metrics); four overview cards (posts, total content, categories, words with delimiter); categories & tags lists capped at 15 with "show N more" expand toggles (
stats.js), activity/usage labels, summary footers (total/most/avg); tag cloud with size tiers (fs-xl/lg/md/sm) + tooltips; metrics column (content overview, top performers with progress bars, data-health completeness score + help modal); print/refresh actions; no-data state with generation instructions/troubleshooting; build-time JS animates progress bars, smooth-scroll, fade-in/IntersectionObserver;content_statistics_generator.rbrunsgenerate_statistics.rbafter init and reloadssite.data.content_statistics(disable viacontent_statistics.auto_generate: false). - Source:
- SCSS:
assets/css/stats.css(loaded only whenpage.layout == 'stats') - Markup:
_layouts/stats.html,_includes/stats/{stats-header,stats-overview,stats-categories,stats-tags,stats-metrics,stats-no-data}.html - JS:
assets/js/stats.js(toggleAllCategories/toggleAllTags/StatsDashboard), plus inline init script instats.html - Plugin/data:
_plugins/content_statistics_generator.rb,_data/content_statistics.yml,_data/generate_statistics.rb - API surface: ids
#overview/#categories/#tags/#metrics/#helpModal; classes.stats-card,.stats-taxonomy-list,.stats-taxonomy-link,.stats-category-extra,.stats-tag-extra,.stats-progress,.tag-cloud .fs-xl/lg/md/sm,.stats-icon-rotate,.fade-in/.slide-up; data-attrsdata-width,data-hidden-count,aria-expanded; JS globalswindow.toggleAllCategories,window.toggleAllTags,window.StatsDashboard.VISIBLE_LIMIT(=15); CSS uses--bs-*tokens (no--zer0-*). - Tests: No dedicated
test/visual/stats*.spec.js; the stats page IS in ADMIN_PAGES so admin-layout/admin-nav/accessibility cover it (200, header title "Site Statistics Portal", breadcrumb, WCAG, console errors).content_statistics_generator.rbhelpergenerator_scriptunit-tested intest/test_plugins.rb(finds in source, falls back to theme root, nil when missing); run bytest/test_core.sh. The expand toggles, tag-cloud sizing, completeness scoring, and no-data state have no behavioral assertions. - Gaps / improvement ideas:
stats.cssis large with duplicated@keyframes shimmer, two@media printand twoprefers-reduced-motionblocks, and many unused classes (.stats-shimmer,.tag-shimmer,.fade-in-up,.bg-outline-success) β dedupe/prune. Thestats.jsscript tag is conditionally injected from BOTH stats-categories and stats-tags (guarded by category-count) β fragile loading; load it once in the layout. No test exercisestoggleAllCategories/Tags, the progress-bardata-widthanimation, or the no-data branch.data-widthwidth is only applied via JS, so progress bars are 0% without JS (no-JS fallback gap). Categories/tagsslugifyanchors to/categories///tags/aren't verified to exist.
Setup Wizard¶
- Purpose: Multi-step interactive
_config.ymlgenerator (dev-only) on thesetuplayout: collect identity/URLs/collections/analytics, preview the generated YAML, then copy/download. - Capabilities: 5 pill-tab steps (Identity, URLs, Collections, Analytics, Review) with Next/Back buttons that switch Bootstrap tabs; collection toggles; PostHog enable + key; social links; description char counter (160);
buildYAMLemits a full config (identity, GitHub with YAML anchors&github_user/&github_repository, URLs, collections + permalinks, defaults, build, plugins, analytics, social, exclude) withyamlValuequoting/padalignment; live preview updates on tab change; download_config.ymlvia Blob; copy YAML. - Source:
- SCSS: β
- Markup:
_layouts/setup.html,_includes/setup/wizard.html - JS:
assets/js/setup-wizard.js(buildYAML,yamlValue,collectionPermalink, download/copy/preview) - Plugin/data: none (client-only)
- API surface: ids
#setup-wizard,#wizardTabs,#tab-identity/urls/collections/analytics/review,#step-*,#yaml-preview,#btn-download,#btn-copy/#btn-copy-full,#desc-count,#cfg-titleβ¦; classes.cfg-field,.cfg-collection,.btn-next,.btn-prev; data-attrsdata-key(dotted forposthog.*),data-col,data-next/data-prev; layout front mattersetup_step/setup_totaldrive a progress bar. No globals/events/CSS vars. - Tests: No automated tests. (Not in ADMIN_PAGES; no spec targets the wizard, the setup layout, or
setup-wizard.js.) - Gaps / improvement ideas: Entirely untested despite generating a config users paste into their repo β add tests for step navigation, the description counter, collection toggles affecting
collections:/defaults:, PostHog/social conditionals, and thatyamlValuequotes special chars so the output is valid YAML (the README claims dev-only guarding but the include itself isn't guarded β verify). Step tabs aren't gated by validation (can skip required fields). Loadssetup-wizard.jsvia a non-deferred<script>at include end.
Setup Banner & Setup Check¶
- Purpose: Detect an unconfigured site (still on theme defaults) and show a dismissible "Almost there!" banner linking to the setup wizard; provides shared
site_needs_setup/site_is_user_repoLiquid vars. - Capabilities:
setup-check.htmlsetssite_needs_setupvia explicitsite.site_configuredflag or heuristics (no owner founder/author/email, or title matches placeholder list likezer0-mistakes/My Awesome Site/empty) andsite_is_user_repo(empty baseurl +.github.iourl);setup-banner.htmlfast-exits whensite.site_configuredand otherwise renderssetup-banner-inner.htmlviainclude_cached(one render reused across the whole build); inner banner is a dismissible top alert linking to#setup-wizard; thesetuplayout also shows a "Setup required" alert + progress indicator using the same check. - Source:
- SCSS: β
- Markup:
_includes/components/setup-banner.html,_includes/components/setup-banner-inner.html,_includes/components/setup-check.html, consumed by_layouts/setup.html - JS: β (Bootstrap alert dismiss)
- Plugin/data: reads
site.site_configured/title/founder/author/email/baseurl/url - API surface: ids
#setup-banner; Liquid varssite_needs_setup,site_is_user_repo; classes.alert.alert-primary.alert-dismissible; data-attrdata-bs-dismiss="alert". No globals/events/CSS vars. - Tests: No automated tests (no spec drives an unconfigured-site state; the homepage/admin WCAG runs occur on the already-configured repo where the banner is suppressed).
- Gaps / improvement ideas: The setup-layout "setup guide" link points at
/404.html(placeholder) β fix to a real guide. Heuristic placeholder-title list will misfire if a real site legitimately uses one of those titles. No test covers the unconfigured branch β add a fixture build with default config to assert the banner renders and that a configured site suppresses it (theinclude_cachedperf path especially). Dismissal isn't persisted, so the banner reappears every page load.
Dev Shortcuts¶
- Purpose: Source-code quick-access links (View on GitHub, GitHub.dev, local VS Code, site config) shown in the info panel for the current page. Used inside
info-section.html. - Capabilities: GitHub blob link, GitHub.dev editor link, and a local
vscode://fileβ¦link (only when not production andsite.local_gitis set), plus a_config.ymlGitHub link; tooltips; computes the on-disk path fromsite.local_git/repository_name/page.path/collections_dir; gracefully degrades to an instructional message whensite.repository/site.branchare unset (prevents brokengithub.com//blob//URLs). - Source:
- SCSS: β
- Markup:
_includes/components/dev-shortcuts.html(consumed by_includes/components/info-section.html) - JS: β (Bootstrap tooltips;
is_productionfromenv-detect.html) - Plugin/data: reads
site.repository/branch/collections_dir/local_git/repository_name,page.collection/page.path - API surface: classes
.dev-shortcuts,.btn-group-vertical; data-attrsdata-bs-toggle="tooltip",data-bs-placement; depends on outeris_production. No ids/globals/events/CSS vars. - Tests: No automated tests.
- Gaps / improvement ideas: No coverage of the bare-site degradation branch or the production-gated
vscode://link. The doublereplace: "//"path normalization is brittle for nested paths (won't collapse 3+ consecutive slashes). Relies on a parent-scopeis_productionvariable being set (coupling toinfo-section.html) β document or guard.
Widgets, Search & Integrations¶
Floating/embedded widgets and third-party integrations layered onto the theme: the Claude-powered AI chat assistant, site search (modal + index generator), consent-gated analytics (PostHog, GA, GTM), Mermaid diagrams, keyboard shortcuts and their cheatsheet, the Halfmoon theme switcher, and assorted small includes (cookie consent, powered-by, component showcase, vendor JS loader, SVG sprite). Most are wired into _layouts/root.html and _includes/core/head.html.
AI Chat Assistant¶
- Purpose: Floating Claude-powered chat assistant grounded in the current page's content, with optional GitHub actions (open issue / propose PR) and a dev-only local page-edit mode, exposed to the model via Claude tool use.
- Capabilities: FAB toggle + slide-in panel (ARIA dialog, Escape/outside-click close, focus management); streaming SSE responses from the Claude Messages API in proxy mode (key server-side) or direct mode (key in page, dev only); strict page-grounding with configurable out-of-scope message; quick-action chips (report issue / suggest improvement / edit page); agentic tool loop (max 5 rounds, 40-message history cap) with tools
get_page_source,create_github_issue,create_pull_request(proxy mode only),update_page_content(local edit); inline confirmation card before any create/edit; result/link/reload cards; send cooldown (1s); status-code error messages; conditional render (only when enabled AND a usable auth path exists); reduced-motion + mobile responsive. - Source:
- SCSS: β (scoped
<style>block inside_includes/components/ai-chat.html; uses--zer0-space-fab-*and--zer0-layer-fab-chattokens from_sass/tokens/_spacing.scss,_layers.scss) - Markup:
_includes/components/ai-chat.html(included in_layouts/root.html:103) - JS:
assets/js/ai-chat.js - Plugin/data: proxy
templates/deploy/chat-proxy/(Cloudflareworker.js, Nodedev-proxy.mjs,page-store.mjs); config viasite.ai_chatin_config.yml/_config_dev.yml - API surface: IDs
#aiChatToggle,#aiChatPanel,#aiChatMessages,#aiChatForm,#aiChatInput,#aiChatSend,#aiChatClose,#aiChatConfig/#aiChatPageContext/#aiChatPageContent(JSON/text config blocks); classes.ai-chat-toggle,.ai-chat-panel,.ai-chat-panel--open,.ai-chat-message--user/--assistant,.ai-chat-bubble,.ai-chat-chip,.ai-chat-action-card(--resolved),.ai-chat-typing; data-attributesdata-prompt; CSS vars--zer0-space-fab-offset/-size/-gap,--zer0-layer-fab-chat; no globalwindow.*export (IIFE); toolsget_page_source/create_github_issue/create_pull_request/update_page_content - Tests:
test/visual/security.spec.jsβ "page source does not contain common secret patterns" and "raw YAML tab does not expose API keys" assert noghp_/sk_live_token patterns leak into the DOM (relevant to direct-mode key exposure), but do not exercise the chat widget itself. No behavioral test for the toggle, streaming, tool confirmations, or grounding. No CSP-header test despite scope wording. - Gaps / improvement ideas: No automated coverage of the widget at all β add Playwright tests for toggle open/close, Escape, focus trap,
aria-hidden/aria-expandedsync, the inline confirmation card, and out-of-scope handling.renderAssistantMarkdownbuilds HTML via regex on escaped text (links forced tohttps?+rel=noopener) β worth a unit test for injection edge cases. Direct mode embeds the Anthropic key in page source (documented as dev-only) β a build-time guard could fail production builds that shipauthMode: directwith a non-empty key. The aria-liverole="log"region is not asserted anywhere. No focus-trap (Tab can leave the open panel).
Site Search (modal + index)¶
- Purpose: Site-wide search exposed as a Bootstrap modal that fetches a client-side
/search.jsonindex and renders ranked, highlighted results; falls back to a/sitemap/query page for "view all". - Capabilities: Opens via
/or Cmd/Ctrl+K shortcut,navigation:searchRequestevent, or[data-search-toggle]buttons; mutually exclusive with the cookie settings modal and the#info-sectionoffcanvas (closes them first to avoid stacked backdrops); debounced input (200ms); substring match over title/description/content;<mark>highlight with regex-escaped query; snippet extraction around the match; capped at 8 results + "View all results" link; focus + select on open, clears on close; prevents empty submissions; lazy-loads + caches the index (graceful empty-array fallback on fetch error). Thesearchbar.htmlinclude is a separate empty#searchbox/#hitscontainer (legacy InstantSearch-style stub, no JS bound in this cluster). - Source:
- SCSS:
_sass/components/_search-modal.scss - Markup:
_includes/components/search-modal.html(#siteSearchModal, in_layouts/root.html:70),_includes/components/searchbar.html,_includes/search-data.json,_layouts/search.html - JS:
assets/js/search-modal.js(loaded in_includes/components/js-cdn.html) - Plugin/data:
_plugins/search_and_sitemap_generator.rb(auto-generates/search.jsonvia thesearchlayout and/sitemap/);_data/ui-text.ymlforsearch_placeholder_text - API surface: IDs
#siteSearchModal,#site-search-input; classes.search-modal,.search-results,.list-group-item,.nested-list-group(.show),.folder; data-attributesdata-search-form,data-search-input,data-search-results,data-search-empty,data-search-toggle; events listens tonavigation:searchRequest; index URL/search.json; form action/sitemap/?q=; configsite.search.generate_index,site.search.content_length,site.sitemap_page.generate - Tests: No automated tests covering search behavior (open, debounce, highlight, result rendering, or modal exclusivity).
test/visual/styling.spec.jsindirectly verifies same-origin assets load but does not touch search. - Gaps / improvement ideas: Bug:
_includes/navigation/unified-drawer.html:100opensdata-bs-target="#search-modal", but the modal id issiteSearchModalβ that drawer Search button is dead (works only viadata-search-toggle/shortcut). Add Playwright coverage for/and Cmd/Ctrl+K opening, result highlighting, the empty/"no results" states, and the cookie-modal/offcanvas exclusivity logic. Thesearchbar.htmlstub renders unused#searchbox/#hitswith no client code β confirm it is intentional or remove. Search is plain substring matching (no fuzzy/relevance ranking); consider weighting title matches. Noaria-liveannouncement of result counts for screen readers.
Cookie Consent¶
- Purpose: GDPR/CCPA-style consent banner + preferences modal that records granular consent (essential/analytics/marketing) in localStorage and drives PostHog opt-in/opt-out.
- Capabilities: Slide-up banner after a 1s delay for first-time visitors; "Accept All" / "Reject All" / "Manage Cookies" actions; preferences modal with toggles for analytics and marketing (essential always-on), provider disclosure
<details>; 365-day consent expiry with re-prompt; persists tolocalStorage(zer0-cookie-consent); callsposthog.opt_in_capturing()/opt_out_capturing(); dispatchescookieConsentChanged; exposeswindow.cookieManager; transition-aware hide with reduced-motion/timeout fallback. - Source:
- SCSS:
_sass/components/_cookie-banner.scss(banner); modal helper styles inline in the include - Markup:
_includes/components/cookie-consent.html(in_layouts/root.html:100) - JS: inline
<script>in the include - API surface: IDs
#cookieConsent,#cookieSettingsModal,#acceptAllCookies,#rejectAllCookies,#rejectAllModal,#saveCookiePreferences,#analyticsCookies,#marketingCookies; classes.cookie-consent-banner,.cookie-banner-visible,.cookie-category,.cursor-pointer; events dispatchescookieConsentChanged; globalswindow.cookieManager(getConsent,setConsent,showBanner,hideBanner,hasConsent),window.cookieConsent; CSS vars--zer0-layer-cookie-banner,--zer0-color-bg-elevated/-muted/-ink/-border,--zer0-motion-duration-slow/-base,--zer0-motion-ease-standard; storage keyzer0-cookie-consent - Tests: No automated tests for consent flow, persistence, expiry, or PostHog opt-in/out wiring.
- Gaps / improvement ideas: No coverage of the banner appearing for first visit, persistence across reloads, the 365-day expiry path, or that rejecting actually opts PostHog out. The marketing toggle is hard-coded to never persist
true("Accept All" keeps marketing false) β UI implies it's toggleable; align copy or implementation..cookie-categoryuses a hard-coded#dee2e6border (not token-aware, breaks in dark mode) β migrate to--bs-border-color. Modal close after save/reject callsbootstrap.Modal.getInstance(...).hide()without a null guard. Noprefers-reduced-motiontest for the banner transition.
PostHog Analytics¶
- Purpose: Privacy-first product analytics, loaded only in production and gated by consent + Do-Not-Track, with rich Jekyll-specific custom event tracking.
- Capabilities: Production-only render (
jekyll.environment == "production"+site.posthog.enabled); respectsnavigator.doNotTrack; configurable autocapture/pageview/pageleave/session-recording with input masking + optional IP anonymization; registers page/site properties; custom events for downloads, external links, search, scroll depth (25/50/75/90), code/TOC/sidebar interactions; exposeswindow.zer0Analytics(track/identify/reset); dev fallback no-op stubs. - Source:
- Markup/JS:
_includes/analytics/posthog.html(inline; in_layouts/root.html:115) - Plugin/data:
site.posthog.*config; integrates with cookie-consent'sopt_in/out_capturing - API surface: globals
window.posthog,window.posthogConfig,window.zer0Analytics(track,identify,reset); custom eventsfile_download,external_link_click,search_query,scroll_depth,code_interaction,toc_click,sidebar_navigation; config keyssite.posthog.enabled/api_key/api_host/autocapture/capture_pageview/session_recording/privacy.*/custom_events.* - Tests:
test/visual/security.spec.jsβ secret-pattern tests check thatphc_PostHog keys and other secrets do not appear in the rendered DOM / config tabs.test/test_quality.shtest_secure_configurationsgreps templates forContent-Security-Policy/X-Frame-Options/X-Content-Type-Options(informational, not enforced). No test exercises PostHog loading or consent gating. - Gaps / improvement ideas: Because the include only renders in production, the consent gate and DNT logic are effectively untested (the security spec runs against a dev-style build). Consider a build-fixture test asserting the production build emits the PostHog snippet only when
posthog.enabledand never inlines a rawphc_key in non-production. Multipledocument.addEventListener('click', ...)handlers are registered separately (minor; could be consolidated).console.logstatements remain in production output.
Google Analytics (gtag) & Google Tag Manager¶
- Purpose: Optional Google Analytics (gtag.js) and Google Tag Manager integrations, loaded only in production and skipped on local/dev hostnames.
- Capabilities: GA: injects
gtag/js?id=<site.google_analytics>and runsgtag('config', ...); GTM head: standard async loader (hard-coded containerGTM-NN8P7RZ); GTM body:<noscript>iframe fallback; all three short-circuit onlocalhost/127.0.0.1/0.0.0.0/[::1]/host.docker.internal/*.local/*.testso a prod build served locally does not track. - Source:
- Markup/JS:
_includes/analytics/google-analytics.html(inhead.html:123, prod +site.google_analyticsonly),_includes/analytics/google-tag-manager-head.html(head.html:34, prod only),_includes/analytics/google-tag-manager-body.html - API surface: globals
window.dataLayer,gtag; configsite.google_analytics; hard-coded GTM container idGTM-NN8P7RZ - Tests:
test/visual/security.spec.jssecret-pattern checks indirectly guard against credential leakage. No dedicated analytics-loading test. - Gaps / improvement ideas: GTM container id
GTM-NN8P7RZis hard-coded (theme author's own container) rather than read fromsite.gtm_idβ a downstream fork inherits the author's GTM. Make it config-driven and gate GTM behindif site.gtm_id. GA/GTM are not consent-gated the way PostHog is (they load purely on production hostname check, bypassing the cookie banner) β wire them towindow.cookieManager.hasConsent('analytics')for true GDPR compliance. No automated test confirms the local-hostname skip actually prevents script injection.
Mermaid Diagrams¶
- Purpose: Client-side Mermaid.js diagram rendering (GitHub-Pages-safe, bundled locally) supporting both fenced
``mermaid blocks and.mermaiddivs, theme-aware to Bootstrap'sdata-bs-theme`. - Capabilities: Loads bundled Mermaid v10 (
site.mermaid.src); convertscode.language-mermaid/pre[data-language="mermaid"]into.mermaiddivs; light/dark theme variable sets keyed offdata-bs-theme(treatswizardas dark, falls back toprefers-color-scheme); flowchart/sequence/gantt tuning;securityLevel: loose; MutationObserver re-renders on theme switch; loading placeholder, dark/light SVG overrides, responsive + print styles. Only active whenpage.mermaid: true(gated inhead.html). - Source:
- SCSS: β (inline
<style>in the include) - Markup/JS:
_includes/components/mermaid.html(included in_includes/core/head.html:54whenpage.mermaid) - Plugin/data:
site.mermaid.src; bundlesassets/vendor/font-awesome/css/all.min.css - API surface: classes
.mermaid,code.language-mermaid,pre[data-language="mermaid"]; globalmermaid(vendor); attribute observeddata-bs-theme; configsite.mermaid.src - Tests: No automated tests for diagram conversion or theme re-rendering.
- Gaps / improvement ideas:
securityLevel: 'loose'permits HTML in diagrams (XSS surface if user content is rendered) β document the trade-off or tighten to'strict'for untrusted content. The ~90-line theme config is duplicated between initial init and the MutationObserver re-render β extract to a sharedmermaid.initialize(...)call. No test asserts that``mermaid fences become rendered SVG, nor that adata-bs-themetoggle re-renders.lineColor` is defined twice in the dark themeVariables object.
Keyboard Shortcuts¶
- Purpose: Global keyboard navigation (ES module) for section paging, search, sidebar/TOC toggles, and opening the shortcuts help modal.
- Capabilities: Bindings
[/](prev/next TOC section),/(focus search or dispatchnavigation:searchRequest),b(toggle left sidebar),t(toggle TOC),?(open#zer0-shortcuts-modal); ignores keystrokes while typing in inputs/textarea/select/contenteditable (with atypeof matchesguard against Document-target TypeError);?checked before/fallback so Shift+/ opens help not search; integrates withwindow.zer0Navigationmodules with Bootstrap Offcanvas fallback; dispatchesnavigation:keyboardNav/sidebarToggle/searchRequest;getShortcuts()+destroy(); toggleable viaconfig.keyboard.enabled. - Source:
- JS:
assets/js/modules/navigation/keyboard.js(orchestrated byassets/js/modules/navigation/index.js, loaded viajs-cdn.html); config inassets/js/modules/navigation/config.js - API surface: class
KeyboardShortcuts; events dispatchednavigation:keyboardNav,navigation:sidebarToggle,navigation:searchRequest; consumeswindow.zer0Navigation.getModule('sidebarVisibility'|'tocVisibility'); configconfig.keyboard.enabled,config.keyboard.keys.*,config.selectors.tocLinks(#TableOfContents a); target modal#zer0-shortcuts-modal; gatesite.navigation.keyboard_shortcuts - Tests: No automated tests for keybindings or the typing-guard.
- Gaps / improvement ideas: No coverage that
/opens search,?opens the help modal (and not search), or that the input-focus guard suppresses shortcuts. The shortcut set inconfig.keyboard.keysand thegetShortcuts()map omit?(help) and don't match the labels rendered inshortcuts-modal.htmlβ single-source the list.console.logon init/destroy is noisy in production.
Shortcuts Cheatsheet Modal¶
- Purpose: Static, i18n-able modal that documents the keyboard shortcuts, opened by pressing
?. - Capabilities: Bootstrap centered modal listing
/,[,],b,t,?with localized descriptions from_data/ui-text.yml; always present in the DOM (the?handler is a no-op if absent); labelled heading + close button. - Source:
- Markup:
_includes/components/shortcuts-modal.html(in_layouts/root.html:73) - JS: opened by
assets/js/modules/navigation/keyboard.js - Plugin/data:
_data/ui-text.yml(shortcuts_*keys); gatesite.navigation.keyboard_shortcuts - API surface: ID
#zer0-shortcuts-modal(title#zer0-shortcuts-modal-title); uses Bootstrap.modal/data-bs-dismiss; i18n keysui.shortcuts_title/_search/_prev_section/_next_section/_toggle_sidebar/_toggle_toc/_show_help - Tests: No automated tests.
- Gaps / improvement ideas: The hard-coded
<kbd>list can drift fromconfig.keyboard.keys(e.g. if a fork remaps keys) β generate from config or assert parity in a test. No Playwright test that?opens this modal. The comment says it renders only whenkeyboard_shortcuts != false, but the markup has no visible{% if %}guard in this file β verify the gate lives in the include site.
cheetsheet.js (Bootstrap component demos)¶
- Purpose: Bootstrap-docs-style initializer that activates tooltips/popovers/toasts and wires aside-nav active-state on a cheatsheet/component-demo page.
- Capabilities: Instantiates
bootstrap.Tooltip(delegated via.tooltip-demo),bootstrap.Popover, and auto-showing non-autohide.toast; neutralizeshref="#"/type="submit"clicks;setActiveItem()syncs.bd-asideactive link to the URL hash on load andhashchange. - Source:
- JS:
assets/js/cheetsheet.js(note: filename misspelling preserved; not referenced injs-cdn.htmlβ loaded only by specific demo pages) - API surface: classes
.tooltip-demo,.toast,.bd-aside; selectors[data-bs-toggle="tooltip"],[data-bs-toggle="popover"],[href="#"],[type="submit"]; eventshashchange; depends on globalbootstrap - Tests: No automated tests.
- Gaps / improvement ideas:
setActiveItem()dereferenceslink.parentNode.parentNode.previousElementSiblingand calls.classList/.click()without null guards β throws on markup that doesn't match the expected.bd-asidenesting. Filename "cheetsheet" is misspelled; consider renaming tocheatsheet.js. No usage injs-cdn.htmlmeans it's silently page-opt-in β document where it's loaded.
Halfmoon Theme Switcher¶
- Purpose: Light/dark/auto color-mode toggler (segmented
btn-group) that persists the choice and appliesdata-bs-theme, adapted from Bootstrap's docs color-mode toggler. - Capabilities: Reads/writes
localStorage('theme');autofollowsprefers-color-schemeand live-updates on system change when not explicitly set; setsdata-bs-themeon<html>;showActiveTheme()syncs.active/aria-pressedacross every instance on the page (the control renders in both the settings offcanvas andtheme-controls-bar.html) and still updates the legacy#bd-themedropdown affordances when that markup is present; applies preferred theme before paint to avoid flash. The include carries no ids, so multiple instances are valid HTML; aclassparam (e.g.w-100) sizes it per context. - Source:
- Markup:
_includes/components/halfmoon.html(segmented control; included byinfo-section.html,theme-controls-bar.html) - JS:
assets/js/halfmoon.js(loaded in_includes/core/head.html:42) - API surface: class
.bd-theme-switch; data-attributedata-bs-theme-value(light/dark/auto); attribute setdata-bs-theme; storage keytheme; SVG sprite refs#sun-fill/#moon-stars-fill/#circle-half; legacy-supported ids#bd-theme/#bd-theme-text+.theme-icon-active(consumer-overridden dropdown markup keeps working) - Tests:
test/visual/settings-panel.spec.jsβ clicking dark/light flipsdata-bs-themeand updates.active/aria-pressed. Theme-color/skin specs (features/appearance.spec.js,features/appearance.spec.js) exercise color modes. - Gaps / improvement ideas: The static markup ships all buttons inactive β active state appears when
showActiveTheme()runs onDOMContentLoaded(brief unmarked state, no wrong-marked state).keyboard.js/mermaid treat awizardtheme value as dark, but this switcher only offers light/dark/auto β document thewizardmode's origin.
Misc Widgets (powered-by, component-showcase, js-cdn, svg)¶
- Purpose: Small supporting includes: footer/home "Powered By" credit cards, a live Bootstrap component demo gallery, the local vendor-JS loader, and an inline SVG icon sprite.
- Capabilities: powered-by.html β renders
site.powered_byentries as Bootstrap cards (name, optional version, external link withrel="nofollow noopener noreferrer", icon). component-showcase.html β parameterized (sections=) gallery of live Bootstrap 5.3 examples (alerts, buttons, badges, cards, accordion, tabs, progress, breadcrumbs, table, tooltips, list-group). js-cdn.html β loads bundled Bootstrap, the navigation ES-module orchestrator (cache-busted bysite.time), UI helpers, posts-pagination, search-modal, share-actions, background-customizer, Obsidian wiki-links, and opt-in appearance/user-override scripts (no runtime CDN). svg.html β hidden<svg>sprite of Bootstrap-icon<symbol>s consumed by<use href="#...">. - Source:
- Markup:
_includes/components/powered-by.html,component-showcase.html,js-cdn.html(inroot.html:111),svg.html(inroot.html:57) - JS: scripts referenced by
js-cdn.html(assets/js/*+assets/js/modules/navigation/index.js) - Plugin/data:
site.powered_by,site.appearance_panel,site.user_overrides,site.obsidian.attachments_path,assets/data/wiki-index.json - API surface: powered-by: classes
.card, configsite.powered_by[].name/version/url/icon,site.default_icon. showcase: paraminclude.sections, anchor ids#showcase-*. js-cdn:window.OBSIDIAN_CONFIG, gatessite.appearance_panel/site.user_overrides, cache-bust?v={site.time}. svg:<symbol id="...">consumed via<use href="#id">. - Tests:
test/visual/styling.spec.jsβ "homepage does not reference common third-party CDNs for core assets" and "same-origin CSS assets return 200" validate the no-CDN posture thatjs-cdn.htmlenforces; "HTML references compiled main stylesheet" confirms asset wiring. No tests forpowered_byrendering, the showcase gallery, or SVG sprite integrity. - Gaps / improvement ideas:
core/styling.spec.jsbans CSS CDNs but does not assert that JS (js-cdn.html) is fully same-origin β extend the banned-CDN check to<script src>too. No test that every<use href="#icon">in the theme has a matching<symbol>insvg.html(broken icons fail silently).component-showcase.htmlis large (~22KB) and untested; a smoke test that eachsections=value renders its anchor would catch regressions.powered-by.htmlhas no empty-state whensite.powered_byis unset.
Testing Infrastructure & Coverage¶
The test harness has two halves: a Playwright behavioral/visual suite under test/visual/ (driven by test/playwright.config.js) and a set of Bash theme/integration suites under test/ (orchestrated by test/test_runner.sh). The canonical entrypoint is scripts/bin/test.
How to run¶
Playwright tiers are exposed as projects in test/playwright.config.js (single config, no per-tier files). Defaults: baseURL = http://127.0.0.1:4000 (override with BASE_URL), workers: 1 / fullyParallel: false (predictable Jekyll load), timeout: 45s, snapshots committed to test/visual/snapshots/, screenshot tolerance maxDiffPixels: 150 / threshold: 0.2. The runner script test/test_playwright.sh spawns a short-lived Jekyll server on STYLING_PORT (default 4000, matching the Docker dev server) unless BASE_URL points at an existing one.
| Tier | Local command | CI invocation | Asserts | Platform caveat |
|---|---|---|---|---|
| critical | npm run test:critical |
.github/actions/playwright-tests (project: critical) in ci.yml |
Only @critical-tagged tests β the user-facing essentials (navigation, search, mobile survival, theming baseline, security) |
Platform-independent. This is the PR-blocking gate. |
| smoke | npm run test:smoke |
nightly-extended.yml (project: smoke) |
All specs except the pixel block (grepInvert: /homepage visual snapshot/) β DOM/CSS/layout/a11y behavior on Desktop Chrome |
Platform-independent; runs on macOS. Nightly-only since the 2026-07 tiering: failures file a sticky issue instead of blocking PRs. |
| snapshots | npm run test:snapshots |
same action, project: snapshots, continue-on-error: true, path-filtered on detect-changes.outputs.styling |
Pixel-perfect homepage screenshots for the 9 skins (features/appearance.spec.js homepage visual snapshot tests) |
Baselines are Linux-only (*-snapshots-linux.png). They predate the PR #108 UI overhaul and currently drift large; non-blocking in CI. Running on macOS produces no matching baseline. |
| regression | npm run test:regression |
manual / workflow_dispatch only |
All specs across chromium + firefox + webkit | Cross-browser; not wired into push CI. |
| update baselines | npm run test:update-snapshots (or UPDATE_SNAPSHOTS=1 ./test/test_playwright.sh) |
β | Regenerates the Linux skin baselines | Must run on Linux (or in CI/Docker) to produce valid *-linux.png. |
| shell suites | ./scripts/bin/test (all) or ./test/test_runner.sh --suites <list> |
./scripts/bin/test + test-suite action |
Theme structure, build, install, obsidian, etc. (below) | Platform-independent. |
The 9 committed baselines under test/visual/snapshots/skins.spec.js-snapshots/ are all homepage-only: homepage-{air,aqua,contrast,dark,dirt,mint,neon,plum,sunrise}-snapshots-linux.png.
Playwright specs¶
All live in test/visual/; shared helpers in test/visual/fixtures.js (SKINS [9], VIEWPORTS [mobile/tablet/midDesktop/desktop/wideDesktop], ADMIN_PAGES [8], UI_ROUTES, waitForJekyll, setSkin, clearSkinStorage, gotoBeforeScrollSpy, gotoOrSkip, collectConsoleErrors, boxesOverlap, assertStackedVertically). Counts are approximate β many tests are generated in loops over ADMIN_PAGES/SKINS/VIEWPORTS.
| Spec file | What it covers | ~#tests | Tier |
|---|---|---|---|
core/accessibility.spec.js |
axe-core WCAG 2.1 AA audits (homepage, FAQ, 8 admin pages = 0-violation); component checks (sidebar aria-label, color-input labels, tab ARIA roles, <hr>-in-<li>); skip-link/code-copy focusability; per-viewport advisory scans |
~22 | smoke |
features/admin.spec.js |
Per admin page: 200 status, header+title, breadcrumbs, header icon, content area; desktop sidebar visible + active highlight; mobile toggle/offcanvas open; no console errors | ~55 (8 pages Γ loops) | smoke |
features/admin.spec.js |
Sidebar renders all nav items; internal links β 200; external links have target=_blank/rel=noopener; active-state tracking; clicking a sidebar link navigates |
~12 | smoke |
features/appearance.spec.js |
zer0Bg.toggle() sets data-zer0-bg, localStorage persistence, zer0:bg-toggle event, setOpacity() CSS vars, persistence across navigation |
8 | smoke |
features/admin.spec.js |
/about/config/ Edit & Export tab: tab activates, fields pre-populated, titleβYAML preview, skin dropdown options, download button (guarded with test.skip if absent) |
5 | smoke |
features/admin.spec.js |
/about/config/ viewer: 200, accordion sections, search filter, no-results graceful; section-copy YAML-quoting test is test.fixme (disabled) |
4 (1 fixme) | smoke |
features/admin.spec.js |
/about/settings/environment/: 200, overview cards, Jekyll version present/valid, no undefined/NaN/null, non-empty plugin list; Ruby-version test is test.fixme |
5 (1 fixme) | smoke |
features/layouts.spec.js |
Layout structure: single <h1>, landing features from _data/landing.yml, hero CTA names, pagination aria-current, breadcrumb <ol>/<li>, article single-H1, ? opens shortcuts modal, welcome H1, token wiring, axe smoke |
~12 | smoke |
core/security.spec.js |
/about/config/ secret exposure: hidden pre#cfg-full-yaml sanitized, Raw-YAML tab has no API-key patterns (phc_/sk_/ghp_), page source has no token patterns |
3 | smoke |
features/appearance.spec.js |
Per skin (9): data-theme-skin attr, localStorage persist, restore-after-nav, homepage pixel snapshot; plus currentSkin() + skin-change event |
~38 (attr tests=smoke; 9 snapshots=snapshots tier) | smoke + snapshots |
core/styling.spec.js |
Stylesheet plumbing: same-origin CSS = 200, main.css linked, --bs-primary/--zer0-* resolve, no banned CDNs; navbar/brand chrome render; docs-layout regions |
~9 | smoke |
features/appearance.spec.js |
/about/settings/theme/ Color Editor: 200, color pickers have #RRGGBB, pickerβtext-input sync, YAML export quotes hex |
4 | smoke |
features/navbar.spec.js |
Navbar tiers/labels/brand overlap, mobile quicklinks; dropdown chevron open/close + Esc + outside-click (aria-expanded) |
~7 | smoke |
features/content-enhancements.spec.js |
Code-block header+gutter, code-copy clickβclipboard write+feedback, content-table CSV toolbar, docs ToC/sidebar-FAB chrome | ~6 | smoke |
features/search.spec.js |
Search modal open (/ + toggle button) β type β results, no-match message, close |
~4 | smoke |
core/responsive.spec.js |
Per-viewport key-landmark (header/main/footer) visibility + homepage axe advisory scan across the viewport matrix | ~2 (Γ5 viewports) | smoke |
core/layout-chrome.spec.js |
Footer powered-by links + equal-width nav columns | ~3 | smoke |
features/category-badges.spec.js |
Features page category badges link to in-page anchors | ~1 | smoke |
Shell / theme suites¶
Routing lives in test/test_runner.sh. --suites all runs the six core suites; --suites full adds obsidian + both Playwright tiers. playwright β PLAYWRIGHT_PROJECT=smoke, playwright_snapshots β PLAYWRIGHT_PROJECT=snapshots (legacy aliases stylingβplaywright, visualβplaywright_snapshots).
| Suite | What it checks |
|---|---|
test_core.sh |
Theme file structure/syntax, Jekyll config + build process, core dependencies, basic integration |
test_deployment.sh |
Local + remote install flows, Docker setup/serving, end-to-end deploy workflow (--skip-docker/--skip-remote honored) |
test_quality.sh |
Security scanning, accessibility/WCAG, cross-platform compatibility, performance benchmarks |
test_install_legacy_flags.sh |
Legacy install.sh flags (-m/-f/--help), install modes (full/minimal), error handling, and edge cases ported from the retired test_installation.sh |
test_installer.sh |
Installer regression matrix β every install profile exits 0 with expected agent files + deploy artifacts (--ai tier needs OPENAI_API_KEY) |
test_site_generation.sh |
Config-matrix generator: builds a site for each mode (full/minimal/remote_theme/gem) and validates generated content |
test_obsidian.sh |
3 layers: Ruby obsidian_links.rb unit tests, JS obsidian-wiki-links.js resolver tests, and a jekyll build smoke that asserts assets/data/wiki-index.json is well-formed |
test_playwright.sh |
Boots/reuses Jekyll, runs the requested Playwright project (smoke default, or snapshots/regression via PLAYWRIGHT_PROJECT) |
scripts/bin/test (no suite arg, the way CI calls it) runs the canonical script suites: scripts/test/lib unit tests, scripts/test/theme/validate, scripts/test/integration/* (auto-version, mermaid), and the test/test_install_*.sh e2e suites.
CI gating (.github/workflows/ci.yml, job test)¶
detect-changes emits a styling output (true when test/visual/** or test/playwright.config.js changed). Within the test matrix job:
- Theme suites β
test-suiteaction runscore,deployment,quality,installation,installer,site_generation,obsidian(skip-docker/skip-remote). Blocking. - Script suites β
./scripts/bin/test(lib + theme validate + integration + installer e2e). Blocking. - Playwright smoke β
project: smoke. Blocking β the real behavioral gate. - Playwright snapshots β
project: snapshots,continue-on-error: true, runs only on styling changes. Non-blocking: uploads diff artifacts so drift stays visible but cannot fail the build. Dropcontinue-on-erroronce the Linux baselines are regenerated.
Cross-browser regression-* projects are not in push CI β manual/workflow_dispatch only.
Biggest UI test-coverage gaps (no behavioral Playwright coverage)¶
Prioritized β interactive behaviors the smoke tier never exercises:
- β
Search modal /
search.jsonβ now covered byfeatures/search.spec.js(open via/+ toggle button, type β results, no-match message, close). Remaining: arrow-key result navigation. (was: zero coverage) - AI chat widget (
assets/js/ai-chat.js) β zero coverage. No test opens the widget, sends a message, or exercises dev page-edit mode. (High β still open) - β
Code-copy click β clipboard write β now covered by
features/content-enhancements.spec.js(grants clipboard perms, clicks copy, asserts.copiedfeedback + non-emptyclipboard.readText()+ revert). (was: only focusable; thefeatures/admin.spec.jsclipboard test is stilltest.fixme) - β
Navbar dropdown open/close β now covered by
features/navbar.spec.js(chevron click open/close, Escape-to-close, outside-click-close,aria-expanded). Remaining: arrow/Home/End item navigation. (was: layout/visibility only) - β
Theme customizer "apply" (skin) β now covered by
features/appearance.spec.js(click skin swatch β livedata-theme-skin+--bs-primarychange + YAML export). Note: color pickers are export-only by design (they do not live-apply). (was: API-only) - Obsidian wiki-links / backlinks / callouts in rendered pages β covered only by Ruby/JS unit tests; no Playwright test loads a page with
[[wiki-links]]/embeds/callouts/backlinks and asserts client-side resolution. (Medium) - Keyboard-shortcuts modal completeness β
?-opens is tested; Escape-to-close, focus trapping, and that listed shortcuts fire are not. (Medium) - Background/skin controls as real UI β tests drive the
window.zer0BgAPI directly; no test clicks the actual customizer toggle/slider/skin-swatch a user would use. (Medium) - ToC / sidebar FAB interaction β FABs are asserted visible but never clicked to open/close the offcanvas; ScrollSpy ToC highlighting on scroll is untested. (Low)
- Table CSV export click β the
.table-copy-csvbutton's existence/name is checked, but nothing clicks it and validates the produced CSV. (Low)
Net: coverage is strong on static structure, admin-page rendering, accessibility audits, CSS-var/token wiring, and JS-API-level skin/background state, but thin on user-driven interactions (clicks, typing, keyboard) for the search modal, AI chat, dropdowns, clipboard copy, and theme-apply β exactly the surfaces a UI overhaul is most likely to break.
Coverage Gaps & Improvement Roadmap¶
Coverage summary: π’ 4 good Β· π‘ 47 partial Β· π΄ 60 none (of 111 components, as inventoried). Note: the interactions added after the original sweep β search modal, code-copy, navbar dropdowns, theme-customizer skin apply β were first landed together in a since-dissolved interactions.spec.js; they now live in features/{search,content-enhancements,navbar,appearance}.spec.js respectively and lift 4 high-priority surfaces into behavioral coverage β see the gaps list below.
Untested components (π΄ none)¶
These have no automated behavioral coverage β highest-value targets for new Playwright smoke tests.
- Back-to-Top FAB (Global Chrome & Primary Navigation) β 200px show/hide threshold, smooth-scroll, and FAB stacking order entirely untested.
- Auto-Hide Navbar (Global Chrome & Primary Navigation) β Hide-on-down/show-on-up, body-padding compensation, reduced-motion, and offcanvas-pause all untested; logic duplicated JS/SCSS.
- Nanobar (scroll/load progress bar) (Global Chrome & Primary Navigation) β Render, step animation, and three position modes (top/bottom/navbar mount) all untested.
- Navbar Module (dropdowns/keyboard/tooltips) (Global Chrome & Primary Navigation) β Rich keyboard menu nav, click dropdowns, and compact-desktop tooltips wholly untested; dead _setupDropdownHoverDelay no-op.
- Scroll-Spy Module (Global Chrome & Primary Navigation) β IntersectionObserver active-link highlighting on scroll has no positive coverage.
- Smooth-Scroll Module (Global Chrome & Primary Navigation) β Offset scroll, hash pushState, and mobile-offcanvas-close untested; destroy() is a documented no-op leaking listeners.
- Swipe Gestures Module (Global Chrome & Primary Navigation) β Edge-swipe sidebar/TOC opening untested; no guard distinguishes edge-swipe from content swipe.
- Focus Manager Module (Global Chrome & Primary Navigation) β Focus-return-to-trigger on offcanvas close (a11y requirement) and keyboard-nav body class untested.
- Sidebar State Module (Global Chrome & Primary Navigation) β localStorage persistence, restore-on-load, and expandPathTo untested; lost state would be invisible to CI.
- Nav-tree sidebar (YAML tree mode) (Sidebar, Table of Contents & Docs Layout) β No coverage of collapse/aria-expanded/active state; inconsistent ARIA across depths; slug id collisions possible.
- Sidebar categories (categories mode) (Sidebar, Table of Contents & Docs Layout) β No expand/collapse or active-state test; active styling duplicated in two partials; nested h2 heading-order risk.
- Sidebar folders (auto mode) (Sidebar, Table of Contents & Docs Layout) β Dead wiring: JS needs .nested-list-group but the include emits a flat list, so folder disclosure never fires; untested.
- Scroll-spy (active heading highlight) (Sidebar, Table of Contents & Docs Layout) β No active-link test; competes with Bootstrap data-bs-spy on
<main>; console.log in init. - Docs code-example chrome (.bd-example/.bd-clipboard) (Sidebar, Table of Contents & Docs Layout) β No AnchorJS/clipboard test; dead Algolia bootstrap-docs search binding; .bd-example::after content:null invalid; two parallel copy systems.
- Landing quick-links bar (Landing, Home & Component Polish) β No tests; .landing-quick-links SCSS selector never applies (markup emits .bg-dark) β dead/mismatched rule
- Landing install cards (Landing, Home & Component Polish) β No tests; snippets lack data-copy buttons; #get-started .card.card-header ~ selector is malformed and never matches
- Index layout (Landing, Home & Component Polish) β No direct test; ships no search wiring of its own; duplicate pt-5/py-5 padding
- Section include (components/section.html) (Landing, Home & Component Polish) β Unused by landing.html (which inlines equivalent markup) so effectively unexercised; variant/heading-level logic untested
- Info section / settings offcanvas (components/info-section.html) (Landing, Home & Component Polish) β Entirely untested; Prod/Dev badge and existence-gated admin links have no behavioral coverage
- Share actions (LinkedIn/copy) (Landing, Home & Component Polish) β No tests; excerpt/dedupe/truncate pure functions uncovered; uses its own notify() toast instead of shared window.zer0UI.showToast; openShareWindow called with an unused 2nd arg
- Skeleton loader (Landing, Home & Component Polish) β Token-aware but orphaned β no markup or JS emits .skeleton; undocumented public utility, no reduced-motion guard
- Particles hero background (Landing, Home & Component Polish) β Fully orphaned β scripts not loaded by any layout/include, no #particles-js element exists; would throw on null container if revived; no reduced-motion respect
- Runtime Token Injection (tokens-inline.html) (Theming: Tokens, Color Modes, Skins & Customizers) β No test that theme_color config overrides --zer0-color-* or that the pre-paint localStorage restore runs before main.css.
- Color Modes (light/dark/wizard) (Theming: Tokens, Color Modes, Skins & Customizers) β Halfmoon toggle and wizard mode have no behavioral test; wizard is not selectable from any UI.
- Appearance Panel (appearance.js) (Theming: Tokens, Color Modes, Skins & Customizers) β Untested; UI exposes only primary though it persists/restores secondary+accent too.
- Skin Editor (skin-editor.js) (Theming: Tokens, Color Modes, Skins & Customizers) β Largest theming module, fully untested; custom skins only set --zer0-bg-* (not the component palette) and use inline onclick clipboard handlers.
- Note layout (Content & Collections) β Entire layout untested; .note-navigation SCSS (.nav-link-note) doesn't match the Bootstrap pagination markup actually rendered.
- Notebook layout (Content & Collections) β Uses --bs-+ prefers-color-scheme instead of --zer0 tokens/[data-bs-theme]; .jp-/.input-prompt CSS is inert for Jekyll-rendered notebooks.
- Notes & Notebooks index grids + difficulty badges (Content & Collections) β Filter JS duplicated inline in two pages; filter buttons lack aria-pressed; .badge-beginner/intermediate/advanced defined but unused (markup uses bg-* directly).
- Callout (Content & Collections) β No spec for the five color variants/token wiring; type isn't announced to SR; requires pre-captured markup.
- Post navigation (prev/next cards) (Content & Collections) β Hover/focus/disabled states untested; note.html uses a different (Bootstrap pagination) prev/next pattern β divergent implementations.
- Syntax highlighting (Content & Collections) β Hardcoded hex palette (not --zer0 tokens) so skins don't affect it; no visual-regression snapshot of light/dark token colors.
- Author card (Content & Collections) β article.html re-implements the full card inline (duplication); avatar path convention differs from preview-image.html; no lazy loading.
- Author E-E-A-T block (Content & Collections) β Third author renderer overlapping author-card + inline article block; hardcoded default 'bamr87'; Person microdata unvalidated.
- Post card (Content & Collections) β stretched-link on title competes with image/category anchors (interaction bug); reading-time fallback is a hardcoded '2 min'; badge precedence untested.
- Post-type badge (Content & Collections) β No fallback for unknown post_type (renders nothing); case mapping untested; callers must externally re-check != 'standard'.
- Feature card (Content & Collections) β ui-refresh feature-badge test targets the page-level category nav, not this component; nested references branch + docs/demo buttons untested; bg-light tags low-contrast in dark.
- Preview image (Content & Collections) β No width/height (CLS risk); prefix logic duplicated between include and Ruby plugin; both untested.
- Preview-image generator plugin (Content & Collections) β No Ruby/shell test; has_preview? regex rejects extensionless/query-string URLs; auto_generate is dead code.
- Comments (Giscus) (Content & Collections) β data-theme=preferred_color_scheme ignores manual theme toggle; gating inconsistent (article checks site.giscus, note/notebook check .enabled).
- Share actions (LinkedIn enhancement) (Content & Collections) β Only note.html uses .js-linkedin-share; article/notebook LinkedIn buttons skip the enhancement; copy-then-open flow + toast + dedup untested.
- Collection layout (Content & Collections) β Bespoke card variant (doesn't reuse post-card); literal 'Collection Index -
<key>' heading; no empty state; images lack width/height. - News layout (Content & Collections) β ui-refresh section test targets section.html not news.html; 660+ lines with inline style/script, re-implements cards 4+ times instead of post-card; no variant tests.
- Tag layout (Content & Collections) β No spec visits
/tags/<tag>/; related-tags anchors depend on a tags index contract (fragile); breadcrumb lacks the nav.breadcrumbs+aria-label pattern checked elsewhere. - Full Knowledge Graph (graph page) (Obsidian & Knowledge-Graph Features) β No tests for a complex interactive cytoscape view; canvas graph is inaccessible (single role=img, no keyboard/text fallback).
- Local Graph (sidebar panel + FAB) (Obsidian & Knowledge-Graph Features) β BFS subgraph + permalink-fallback + offcanvas FAB wholly untested; same cytoscape inaccessibility as full graph.
- Backlinks Panel (Linked mentions) (Obsidian & Knowledge-Graph Features) β Liquid substring matcher (no alias support) untested and prone to false positives/negatives; a third independent link-resolution impl.
- Environment Switcher (Admin Tools & Dashboards) β Untested; defines global copyToClipboard colliding by name with config-utility.js; inline onclick conflicts with CSP.
- Env-Var Helper (zer0-env-var) (Admin Tools & Dashboards) β Orphaned (no page includes it); references undefined #repo-link with no null guard β latent JS error; inputs lack labels.
- Setup Wizard (Admin Tools & Dashboards) β Fully untested config generator; no step validation gating; verify include is actually dev-only guarded and yamlValue output is valid YAML.
- Setup Banner & Setup Check (Admin Tools & Dashboards) β Unconfigured-site branch untested; setup-layout 'setup guide' link points to /404.html; dismissal not persisted.
- Dev Shortcuts (Admin Tools & Dashboards) β Untested; double // path-normalization brittle for nested paths; relies on parent-scope is_production.
- AI Chat Assistant (Widgets, Search & Integrations) β Only indirect secret-leak checks; no behavioral/a11y test for toggle, streaming, tool confirmations, or focus trap.
- Site Search (modal + index) (Widgets, Search & Integrations) β No tests; plus a real bug β unified-drawer.html targets #search-modal but the id is siteSearchModal (dead button).
- Cookie Consent (Widgets, Search & Integrations) β No coverage of banner show, persistence, 365-day expiry, or PostHog opt-out; .cookie-category border hard-coded #dee2e6.
- Google Analytics & Tag Manager (Widgets, Search & Integrations) β GTM container GTM-NN8P7RZ hard-coded (not config-driven); GA/GTM not consent-gated like PostHog.
- Mermaid Diagrams (Widgets, Search & Integrations) β securityLevel:'loose' XSS surface; ~90-line theme config duplicated; no render/theme-switch test.
- Keyboard Shortcuts (Widgets, Search & Integrations) β No test for /, ?, or typing-guard; getShortcuts() map omits ? and diverges from shortcuts-modal labels.
- Shortcuts Cheatsheet Modal (Widgets, Search & Integrations) β Hard-coded list can drift from config.keyboard.keys; no test that ? opens it.
- cheetsheet.js (Bootstrap demos) (Widgets, Search & Integrations) β setActiveItem() lacks null guards (throws on unexpected markup); filename misspelled; page-opt-in only.
Partially covered components (π‘ partial)¶
Assert structure but miss key interactions/states.
- Header / Site Shell β Render + skip-link asserted, but lg+ 3-col grid anti-overlap and JS body-padding compensation untested.
- Branding (title/subtitle) β Title overlap/visibility tested; subtitle + title-icon breakpoints untested; possible duplicated default_icon class string typo.
- Primary Navbar (menubar + dropdowns) β Labels/toggle render asserted; dropdown open/close, keyboard nav, icon-only tier, and auto-gen fallback untested.
- Navbar Mobile Quicklinks (tablet chips) β In-window visibility tested; limit:5 truncation, scroll overflow, and md-/lg+ hiding untested.
- Head (document head / asset pipeline) β CSS 200s, main.css link, no-CDN asserted; token cascade order and prod-only analytics gating untested.
- Footer β Powered-by links + tablet columns tested; quick-link auto-detect, policy gating, and subscribe-form a11y untested.
- Breadcrumbs β aria-label + valid
<ol>structure tested; Schema.org microdata and known-section special path unverified. - Offcanvas Sidebars & Unified Drawer β Presence of ToC/toggles asserted; unified-drawer tabs, 3 sidebar nav modes, and FOUC-guard path untested.
- Navbar Extras / FAB Stacking β FAB presence on mobile asserted; non-overlap stacking math never verified despite available boxesOverlap helper.
- Navigation Orchestrator (index.js + config.js) β --zer0-bp-lg token (read by syncBreakpointsFromCss) asserted; window.zer0Navigation init and navigation:ready event untested.
- Keyboard Shortcuts Module β Only '?' help-modal shortcut tested; [/], '/', 'b', 't' and the input-field guard untested.
- Sidebar Visibility Module β Toggle presence asserted; collapse/restore action, persistence, FOUC class, and mobile branch untested.
- TOC Visibility Module β Toggle presence asserted; hide/restore, persistence, FOUC class, and mobile branch untested.
- Docs layout shell (.bd-layout/.bd-sidebar/.bd-main/.bd-toc) β Only region presence asserted; rail-collapse, FOUC pref guard, and rail-width tokens (undefined) untested.
- Section sidebar (topic navigation) β Only layout containment guarded; ships duplicate inline scroll-spy/CSS, no aria-current, hardcoded offsets, no behavioral test.
- Table of Contents (Liquid parser + sidebar-right) β Presence only; parser output/nesting/no-sections fallback untested; legacy .toc ruleset appears unused vs current .bd-toc markup.
- TOC FAB (mobile trigger) β Attachment-only assert; no click/open/aria-expanded test; effective-sidebar logic duplicated with default.html.
- TOC visibility toggle + persistence β Toggle attached but no persistence/aria/event test; console.log left in production init.
- Page intro header (.bd-intro family) β Footer/actions/aria-label asserted; date logic, badges, tag overflow, reading-time, hero contrast untested.
- Content tables (styling + CSV copy) β Toolbar presence + header bg asserted; CSV correctness/colspan/exec fallback untested; quick-index has invalid
<p>-in-<ul>markup. - Landing layout β H1-count + feature-card + CTA-name + axe asserted, but hero .is-loaded fade-in, tertiary GitHub fallback, and no-image placeholder untested
- Home layout β h1 count covered when / uses it; hide_title and rss_subscribe:false branches untested
- Welcome layout β Only h1-presence asserted; accordion, wizard, and site_needs_setup gating untested; hardcoded GitHub README anchor links
- Feature card include (components/feature-card.html) β Only category-badge anchors on /features/ asserted; show_refs and compact modes untested; name-collides with landing's .landing-feature-card
- CTA button include (components/cta-button.html) β Accessible-name asserted indirectly; .zer0-cta emitted but never styled; URL-normalization and external new-tab cue untested
- Bootstrap component polish (UI enhancements) β Table/button-height/code-block asserted; ripple, card hover, badge hover, and window.zer0UI toast/clipboard untested; many hardcoded rgba shadows not token/dark-mode-safe
- Design Tokens (--zer0-* layer)β Only --zer0-color-primary and --zer0-bp-lg asserted; spacing/typography/shadow/motion/layer tokens and --bd-* aliasing untested.
- Palette Generator (palette-generator.js) β YAML quoting + picker/text sync asserted; harmony algorithms, live --bs-* application, and layout-range round-trip (emitted commented-out) untested.
- Theme Customizer & Preview Gallery (admin UI) β Page-load + YAML-quoting tested via the customizer page; card-grid click-to-apply, copy/download buttons, keyboard activation, and skin-list source drift (order=7 vs fallback 'dark') untested.
- Code copy button β Header/gutter presence + keyboard focus asserted, but the copy action, comment-line stripping (drops #-lines, corrupts YAML/shell), and copied-state reset are untested/unannounced.
- Posts pagination β Only aria-current is asserted; ellipsis range builder, prev/next disabling, hash navigation, and showing-X-of-N math untested.
- Article layout β Only single-H1 tested; pros/cons condition mixes and/or without grouping (renders for non-reviews); About-the-Author duplicates author-card; 8 post_type variants untested.
- Wiki-Links ([[Page]]) β Ruby + JS-shim unit coverage of HTML output, but no real-browser DOM-rewrite test and broken links use clickable href=# with no a11y.
- Embeds & Transclusion (![[β¦]]) β Converter output tested; transclude.html Liquid include and Ruby/JS markup divergence (card vs flat excerpt) untested.
- Callouts (> [!type]) β Type mapping/fold/collapse asserted in units, but foldable [!type]- has no expand toggle JS and role=alert a11y is untested.
- Inline Tags (#tag) β Tag rewrite + code-skip tested; tag anchor target (/tags/#slug) existence and badge a11y/contrast unverified.
- Wiki Index (wiki-index.json) β Build smoke validates JSON shape/count only; outgoing-edge extraction heuristic and Ruby/Liquid index parity untested.
- Config Viewer β Search/copy only smoke-tested; section-copy hex-quoting regression is fixme'd and unfixed.
- Config Editor β Spec locators (#cfg-title/#cfg-skin) don't match real ids (#edit-*), so editor tests silently skip β fix selectors.
- Raw YAML / Config Sanitization β sanitize_config_filter.rb has no Ruby unit test; duplicated secret patterns in Liquid + Ruby can drift.
- Navigation Editor β Edit Menus tab is a non-functional stub; two parallel export mechanisms; nav_files list duplicated across 3 includes.
- Collection Manager β Read-only despite 'Manager' name; table/accordion content not directly asserted.
- Analytics Dashboard β Config mirror only (no live PostHog data); raw true/false text vs badge styling inconsistent; field rendering not asserted.
- Statistics Dashboard β Expand toggles/tag-cloud/no-data branch untested; stats.css has duplicated keyframes + dead classes; progress bars 0% without JS.
- PostHog Analytics β Secret-pattern guard only; consent gate + DNT logic untested (production-only render); console.log left in.
- Halfmoon Theme Switcher β Tokens-load asserted but toggle behavior/persistence/aria-pressed untested; markup hard-codes dark active.
- Misc Widgets (powered-by, showcase, js-cdn, svg) β No-CDN posture checked for CSS only (not