calevans/staticforge-chapternav

StaticForge Feature: ChapterNav

Maintainers

Package info

github.com/calevans/staticforge-chapternav

pkg:composer/calevans/staticforge-chapternav

Transparency log

Statistics

Installs: 24

Dependents: 1

Suggesters: 0

Stars: 0

Open Issues: 0

3.1.0 2026-08-27 20:12 UTC

This package is auto-updated.

Last update: 2026-08-27 20:15:56 UTC


README

What it does: Automatically generates sequential prev/next navigation links for documentation pages based on menu ordering.

Requires PHP 8.5+ and StaticForge 3.x.

Installation

composer require calevans/staticforge-chapternav
php vendor/bin/staticforge feature:setup calevans/staticforge-chapternav

Configuration

Set via your siteconfig.yaml file.

# Chapter Navigation Configuration
chapter_nav:
  menus: "2"
  prev_symbol: ""
  next_symbol: ""
Key Default Meaning
menus (none) Which MenuBuilder menu numbers get sequential navigation. A single number or a comma-separated list.
prev_symbol Prefix for the "previous chapter" link.
next_symbol Suffix for the "next chapter" link.

menus accepts a bare number (menus: 2), a quoted one (menus: "2"), a comma-separated list (menus: "2, 3"), or a YAML list (menus: [2, 3]). Entries that are not numeric are ignored with a warning in the build log — menu numbers are always integers, because that is what MenuBuilder produces.

Environment variable fallback

If siteconfig.yaml has no chapter_nav: block at all, these are read instead. Prefer siteconfig.yaml; these exist for parity with older installs.

CHAPTER_NAV_MENUS="2"
CHAPTER_NAV_PREV_SYMBOL=""
CHAPTER_NAV_NEXT_SYMBOL=""

The two sources are not merged. A present chapter_nav: block wins outright.

Disabling Chapter Navigation

To completely disable chapter navigation processing, either:

  • Set menus: "" (empty string) in your siteconfig.yaml
  • Remove the chapter_nav section from siteconfig.yaml and ensure the CHAPTER_NAV_MENUS environment variable is not set.

How It Works

Chapter Navigation uses the menu ordering from MenuBuilder to create sequential navigation between pages. Pages that appear in the configured menus automatically get prev/next links based on their menu position.

It runs on POST_GLOB at priority 150, after MenuBuilder (priority 100) has published its menu structure. MenuBuilder must be enabled; if it is disabled, ChapterNav logs a warning and does nothing.

Example Setup

Frontmatter is YAML.

---
title: "Quick Start Guide"
menu: 2.1
template: docs
---
---
title: "Configuration Guide"
menu: 2.2
template: docs
---

Results:

  • Quick Start Guide (2.1): Shows only "Next →" link to Configuration Guide
  • Configuration Guide (2.2): Shows "← Prev" to Quick Start

Third-level menu entries (menu = 2.2.1) are dropdown children. They still get sequential navigation, but a dropdown's own label — the X.Y.0 entry under a parent that is not itself a page — is skipped, since it links nowhere.

Using in Templates

The chapter navigation HTML is automatically generated. To display it in your template:

{% if features.ChapterNav.pages[source_file] is defined %}
  {% for menu_num, nav_data in features.ChapterNav.pages[source_file] %}
    {{ nav_data.html|raw }}
  {% endfor %}
{% endif %}

Navigation Data Structure

Each page gets:

  • prev - Previous page data (title, url, file) or null
  • current - Current page data
  • next - Next page data or null
  • html - Pre-generated HTML for the navigation

features.ChapterNav is written only when there is something to write. If no menus are configured, or MenuBuilder published no menu files, the key is absent entirely — hence the is defined guard above. A configured menu that matches no pages yields pages: [].

URL handling

Every URL passes through a scheme allowlist: relative, fragment, and protocol-relative URLs are allowed, as are http, https, and mailto. Anything else — javascript:, data:, and obfuscated variants — is replaced with # and logged as a warning.

This applies to prev.url, current.url, and next.url as well as to the pre-generated html, so building your own <a href="{{ nav.next.url }}"> is as safe as using nav_data.html. The values in the data are not HTML-escaped — Twig autoescapes them for you, and escaping twice would turn every & in a query string into &amp;amp;. Inside html, where Twig cannot help because it is emitted with |raw, titles, symbols, and hrefs are escaped.

Development

composer install
composer test     # PHPUnit
composer phpcs    # PSR-12
composer phpstan  # static analysis, level 8
composer check    # all three