Configuration

Site config

Site-wide options live under docs in otfw.config.js — brand, version, GitHub link, and the top-level nav (which supports per-link icons).

JavaScript
import { defineDocsConfig } from "@opentf/web-docs/config";

export default defineDocsConfig({
  docs: {
    title: "OTF Web",
    version: "v0.16.0",
    logo: "/logo.png",
    github: "https://github.com/Open-Tech-Foundation/Web-App-Framework",
    // Source repo — required for per-page "Edit this page" links (with lastUpdated on).
    repoUrl: "https://github.com/Open-Tech-Foundation/Web-App-Framework",
    nav: [
      { label: "Home", href: "/" },
      { label: "Docs", href: "/docs" },
    ],
    // Foot of every docs/API page: git-derived "Last updated" + GitHub edit link.
    lastUpdated: true,
  },
});

The sidebar tree

The sidebar is generated from the folder tree under your docs directory. A folder with a page.* becomes a link; a folder without one becomes a group subheading over its children. Nesting is arbitrary.

Each folder's _meta.js sets the order and labels of its children. Listed keys come first in declared order; unlisted folders are appended alphabetically.

JavaScript
// app/docs/_meta.js
export default {
  index: "Introduction",
  "getting-started": "Getting Started",
  "core-concepts": "Core Concepts",
  packages: "Packages",
  reference: "Reference",
};
Labels and order in one place

A string value is the label ("getting-started": "Getting Started"). Omit a folder from _meta.js and it still appears — just alphabetically, using a humanized name.

Mobile navigation

On screens narrower than 768px the sidebar doesn't fit beside the content, so it becomes an off-canvas drawer. A hamburger button appears at the start of the navbar and slides the sidebar in over a dimmed backdrop:

  • It shows only on small screens, and only on pages that have a sidebar (docs and other DocsLayout sections) — never on a plain marketing page.

  • The drawer locks page scroll while open and closes on navigation, Esc, a tap on the backdrop, or a resize back up to desktop width.

  • prefers-reduced-motion disables the slide animation.

To keep the bar uncluttered on phones, the navbar also collapses to a single row — brand on the left, a compact action cluster (search · GitHub · theme) on the right — and its top-level links move into the drawer, above the section tree, so the whole navigation is reachable from one place.

This is automatic — there's nothing to configure. On desktop the sidebar is the sticky column and the top-level links sit in the navbar, as usual.

Works across layouts

The burger lives in the navbar and the drawer in the docs grid, which may be separate layouts. They stay in sync through a small global toggle and an otfw:sidebar event, so the button's open/closed state is always correct.

Page frontmatter

Per-page frontmatter controls how a page shows up:

KeyEffect
titleSidebar label, breadcrumb, and document title.
descriptionMeta description for SEO.
sidebar_labelOverrides the sidebar text when it should differ from title.
orderTie-breaks ordering among siblings.
lastUpdatedOverride the "Last updated" date, or false to hide it on this page.

Last updated & Edit this page

Turn on docs.lastUpdated: true to show a "Last updated on …" line at the foot of each page in every DocsLayout section (/docs, /api, and any other section folder). The date comes from the file's last git commit — no manual bookkeeping, and it reflects when the content actually changed.

Pair it with docs.repoUrl (your GitHub repository root, not a specific file) to also render an "Edit this page" link beside the timestamp. The link targets <repoUrl>/edit/main/<path-to-source> for the current route's page.mdx / page.jsx.

JavaScript
export default defineDocsConfig({
  docs: {
    // …
    repoUrl: "https://github.com/org/repo",
    lastUpdated: true,
  },
});

repoUrl alone is not enough — the last-updated build plugin must be active (lastUpdated: true on docs or blog) to emit the per-route source paths the edit link needs.

The same SSG build also emits an Open Graph article:modified_time meta tag per page, so crawlers see the modification date.

Overrides and git history

A lastUpdated: value in a page's frontmatter pins the date (any YYYY-MM-DD works); lastUpdated: false hides the line for that page. If the file isn't in git history (e.g. a shallow CI clone — use fetch-depth: 0), the line is simply omitted; there is no file-mtime fallback, since mtime doesn't track content changes.

The blog supports the same option: blog.lastUpdated: true adds the line to a post only when it was edited after its publish date, so a fresh post stays clean.