Skip to content

Repository files navigation

Docsmith

Latest Version on Packagist Total Downloads on Packagist CI PHP Version Compatibility Laravel Boost Skill

Craft static documentation sites from Markdown with minimal setup.

Docsmith is designed for package and project documentation where you want a fast build flow, a clean default UI, and zero frontend setup.

Features

  • Build static HTML docs from Markdown
  • Default output folder is docs for GitHub Pages workflows
  • Searchable sidebar navigation
  • Global search results powered by generated search-index.json
  • Collapsible grouped navigation with active-page auto-open/scroll
  • Optional right sidebar table of contents
  • Configurable accent color with Laravel red as the default theme
  • Syntax-highlighted fenced code blocks
  • One-click copy button on code snippets
  • Image, video, and PDF publishing from the source tree with rewritten references
  • Repository/edit links and previous/next page navigation
  • Generated search-index.json, sitemap.xml, and .nojekyll artifacts
  • Optional README index compatibility mode for existing repositories

Installation

composer require --dev mrpunyapal/docsmith

Using AI agents (Claude Code, Cursor, Boost, ...)? Docsmith ships installable skills:

npx skills add MrPunyapal/docsmith/resources/boost/skills
# or, in a Laravel project with Boost:
php artisan boost:install   # installs them automatically from composer.json

Quick Start

Create a build script (example: build-docs.php):

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Docsmith\Docsmith;

Docsmith::build(
	source: __DIR__ . '/md',
	title: 'My Package Docs',
	description: 'Documentation generated by Docsmith.',
	accentColor: '#ff2d20',
);

Run it:

php build-docs.php

This writes the generated site into docs by default.

Fluent API

<?php

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Docsmith\Docsmith;

Docsmith::make()
	->source(__DIR__ . '/md')
	->output(__DIR__ . '/docs')
	->title('My Package Docs')
	->description('Documentation generated by Docsmith.')
	->accentColor('#ff2d20')
	->accentColorDark('#ff6b61')
	->repositoryUrl('https://github.com/acme/package')
	->siteUrl('https://acme.github.io/package')
	->editBranch('main')
	->rightSidebar()
	->baseUrl('/')
	->build();

You can change the accent color at build time. Docsmith derives the softer hover and focus colors from the accent, so hex colors give the best results.

If you need to apply ad-hoc overrides, you can append custom CSS during the build:

Docsmith::make()
	->source(__DIR__ . '/md')
	->output(__DIR__ . '/docs')
	->customCss('body { background: #fff }') // raw CSS
	->build();

Or pass a path to a CSS file which will be appended to assets/app.css:

Docsmith::make()
	->source(__DIR__ . '/md')
	->output(__DIR__ . '/docs')
	->customCss(__DIR__ . '/my-overrides.css')
	->build();

Search Behavior

Docsmith ships two search experiences out of the box:

  • Sidebar filter search (filters current navigation links)
  • Global local-index search (queries generated search-index.json and shows clickable results)

The global search index is generated during each build and is static-hosting friendly.

Favicon

Docsmith generates and links a default favicon for every built page. You can override it with a URL, data URI, or a path to a local image file:

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->output(__DIR__ . '/docs')
    ->favicon('https://example.com/favicon.png')
    ->build();

Local favicons are copied into assets/ and linked with the correct relative path for each page.

Media

Images, videos, audio, and PDFs kept in the source directory are published into the built site automatically:

# Installation

![Setup wizard](images/setup.png)

<video controls src="media/demo.mp4"></video>

[Download the spec](files/spec.pdf)

Docsmith copies every media file with its relative path. Supported types: png, jpg, gif, svg, webp, avif, ico, bmp, mp4, webm, mov, m4v, ogv, mp3, wav, ogg, m4a, flac, aac, pdf.

Built pages sit one level deeper than the source mirror (guides/configuration.md becomes guides/configuration/index.html), so relative references are rewritten per page: images/setup.png on that page is emitted as ../images/setup.png.

Remote URLs, root-relative paths (/assets/...), data URIs, and files that were not published are left untouched.

Disable publishing and rewriting with:

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->publishMedia(false)
    ->build();

Open Graph Images

Docsmith can emit og: and twitter: card tags and generate social preview images for you.

Generated images use Node with two packages: Playwright (browser) and capturist (capture runner). Install them once as devDependencies — you do not need to write or maintain a capturist config; Docsmith writes it during the docs build.

npm install -D playwright capturist@^0.1.3
npx playwright install chromium

Requires capturist ≥ 0.1.3 for incremental capture (skip unchanged cards). If Open Graph generation is enabled and these tools (or the Chromium browser) are missing, the docs build fails with the same install instructions.

In CI, install Node deps and Chromium before a docs build that runs capture (or use runCapturist(false) and capture in a later step).

Single default image for all pages

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->title('My Package Docs')
    ->ogGeneratedAll()
    ->build();

This renders one preview card, captures it to docs/og/cover.png, and points every page at it.

Default generated image per page

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->title('My Package Docs')
    ->ogGeneratedPerPage()
    ->build();

Each page gets its own preview at og/<page>.png.

Custom generated template

Pass a file path or raw HTML snippet. The tokens {site_title}, {title}, and {description} are replaced per page:

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->ogTemplate(__DIR__ . '/og-card.html', scope: 'per-page')
    ->build();

The template is rendered inside a 1200×630 shell, so you only need the card markup.

Link to an existing image

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->ogLink('https://example.com/og/cover.png')
    ->build();

The link can be an absolute URL or a root-relative path.

Advanced / low-level

If you prefer, the structured ogImage(...) method exposes every option:

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->ogImage(
        type: 'generated',
        scope: 'per-page',
        template: __DIR__ . '/og-card.html',
        scale: 2,
        viewport: ['width' => 1200, 'height' => 630],
    )
    ->build();

Per-page overrides via frontmatter

Individual pages can override the image, title, or description used in their OG tags:

---
og_image: /assets/page-og.png
og_title: Custom Social Title
og_description: A custom description for social shares.
---

# Page Title

Capture step (advanced)

By default, enabling a generated OG mode runs capture during build().

Capture is incremental via capturist’s built-in cache. Docsmith writes cache into capturist.config.json (manifest at og/.capturist-cache.json). Unchanged preview HTML skips Playwright; rebuilds print Open Graph images up to date.

Force a full regenerate by deleting og/*.png and/or og/.capturist-cache.json, or run capturist with --force.

Skip the capture step (e.g. CI that installs Playwright later):

Docsmith::make()
    ->ogGeneratedAll()
    ->captureOg(false)
    ->build();

Force a full recapture (ignore cache):

Docsmith::make()
    ->ogGeneratedAll()
    ->forceOg()
    ->build();

HTML card previews and capturist.config.json are still written under the output directory so a later capture step can use them.

CI with Open Graph

- uses: actions/setup-node@v4
  with:
    node-version: '20'
    cache: 'npm'
- run: npm install
- run: npx playwright install chromium --with-deps
- run: php build-docs.php

README Index Compatibility Mode

For repositories that maintain docs links in README sections, you can import that structure directly.

Docsmith::make()
	->readmeIndex(__DIR__ . '/README.md')
	->readmeSkipSections(['Contributing', 'Author', 'Notes'])
	->title('Repository Docs')
	->description('Generated from README index.')
	->build();

Supported list styles include patterns used by:

  • laravel-undocumented
  • laravel-attributes-list

Versioned Docs

Multiple versions of one documentation set. Pages get v1/v2/v3 pill buttons; no dropdown.

Docsmith::make()
    ->source(__DIR__ . '/md')
    ->output(__DIR__ . '/dist')
    ->versions([
        ['slug' => 'v1', 'label' => 'v1.0', 'default' => true],
        ['slug' => 'v2', 'label' => 'v2.0'],
    ])
    ->build();

Each version reads md/{slug}/; the default version mounts at the site root, siblings under /{slug}/. See Versioned Docs.

Docs Hub

Several independent documentation sets in one site, one sidebar dropdown to switch between them.

Docsmith::make()
    ->output(__DIR__ . '/dist')
    ->hub([
        'package-a' => ['label' => 'Package A', 'source' => __DIR__ . '/md/a'],
        'package-b' => ['label' => 'Package B', 'source' => __DIR__ . '/md/b'],
    ])
    ->build();

Each entry gets one dropdown option mounted at /{slug}/; a hub entry may embed its own versions list for pills on its pages. See Docs Hub.

Remote Sources

Pull Markdown from other Git repositories into your build — no git binary, no provider APIs, no clones. DocSmith speaks standard Git smart-HTTPS directly and works with any host.

// docsmith.sources.php
return [
    [
        'repository' => 'https://github.com/laravel/framework.git',
        'ref' => '12.x',
        'path' => 'docs',
        'target' => 'laravel',   // materialized to md/laravel
    ],
];
php bin/docsmith sync          # fetch remote sources
php bin/docsmith build --sync  # or fetch + build in one step

Plain builds never touch the network; without a docsmith.sources.php, nothing changes. Synced sources are plain local directories afterward — they work with a normal build, a versioned build, or a hub equally. Private repositories are supported: add 'token' => '${ACME_PAT}' to an entry (or set DOCSMITH_TOKEN / GITHUB_TOKEN), and a .env next to the manifest is loaded automatically. See Remote Sources for caching, safety limits, and programmatic usage, and Workflows for end-to-end recipes (hub from several repos, branch-based versions, CI).

Output Model

  • md/index.md -> index.html
  • md/installation.md -> installation/index.html
  • md/guides/configuration.md -> guides/configuration/index.html

If there is no index.md in the source, Docsmith generates a landing page automatically.

GitHub Pages

Because the default output path is docs, a typical package workflow is:

  1. Keep Markdown source in md
  2. Build static site into docs
  3. Publish from docs via GitHub Pages

Development

composer test
composer docs:build

Contributing

Contributions are welcome. Feel free to open issues and pull requests.

License

The MIT License (MIT). See LICENSE for details.

About

Craft documentation sites from Markdown with minimal setup.

Topics

Resources

Stars

33 stars

Watchers

1 watching

Forks

Releases

Sponsor this project

Contributors

Languages