Feature
Data files
Point a PHPDOC annotation at a YAML or JSON file and get structured data back.
When an annotation value ends in .yaml, .yml, .json or .md and resolves to a file next to the page, it is parsed and injected as data instead of a string. @stats _stats.json here became a decoded object:
And @articles _articles.yaml — a YAML sequence — arrived as an array of objects to loop over:
-
Compiling PHP to static HTML
How the render pipeline turns a _page.php into a flat .html file.
-
PHP 8.5 in the browser sandbox
Running a full standard library in WebAssembly, no server process.
-
The image autogenerator
One engine, three call sites — Sass, PHP and the <img asset> tag.
Finally, @readme https://…/README.md was fetched over the network at build time and converted from Markdown:
Kirigami README (fetched at build)
Kirigami
A static site generator that turns PHP into fast, dependency-free HTML — no server required.
Overview
Kirigami lets you build static websites using a language you already know — PHP — and compiles them straight into clean, production-ready HTML. There's no server to provision, no runtime to patch, and no infrastructure to maintain: the PHP engine runs entirely in WebAssembly (via @kirigami/php-wasm), directly inside the Node.js process. You get the full expressiveness of PHP templating with the simplicity, speed, and portability of a static site.
Here's what Kirigami brings to your workflow:
- Real PHP templating — includes, loops, Markdown, YAML — compiled directly to clean HTML, no server needed at runtime.
- Integrated asset pipeline with esbuild for JS and Sass for styles, wired in from the start.
- A
watchmode with hot-reload, so changes show up instantly during development. - A single-command production export — a fully static site ready to deploy anywhere (GitHub Pages, Netlify, any static host), complete with a license banner and an auto-generated sitemap.
- Instant project scaffolding from official templates via
kiri create. - Scriptable automation, running PHP scripts on demand or as build-pipeline hooks with
kiri run.
Every project is driven by a single configuration file, kirigami.yaml, at the project root.
Table of contents
- Kirigami
Monorepo structure
This repository is an npm workspaces monorepo, organized as follows:
| Package | Description |
|---|---|
packages/kirigami |
The heart of the project: the kiri CLI (build, export, watch, run, create, phpinfo). |
packages/php-prepros |
The PHP → HTML compiler that powers the CLI (template rendering, sitemap generation, and more). |
packages/php-wasm |
A custom PHP WebAssembly build for Node.js (JSPI only, no browser support). |
packages/struct-walker |
Recursively walks YAML/JSON structures, resolving relative file references and converting assets to data URIs. |
packages/sdk |
Shared runtime for plugins: the hook registry and the on-disk Cache. |
packages/canva |
Shared Sass/JS design system reused across Kirigami projects. |
packages/plugin-highlight |
Official plugin: build-time syntax highlighting (highlight.js), 0 runtime JS. |
packages/plugin-extlink |
Official plugin: external link preview cards, SCRAPER-backed, cached to disk. |
packages/plugin-embed |
Official plugin: YouTube/Vimeo oEmbed video cards, resolved client-side. |
Requirements
- Node.js
>= 24.0.0 - npm
>= 10.2.3
Installation
Get the CLI via the @kirigami/kirigami package:
npm install -D @kirigami/kirigami
That's it — the kiri command is ready to go (via npx kiri or an npm script).
Quick start
- Drop a
kirigami.yamlat the root of your project:
kirigami:
project: "My Site"
baseurl: "https://mysite.com"
root: "src"
prepros:
before: "_layouts/header.php"
after: "_layouts/footer.php"
format: true
- Write your
_*.phppages inside thesrc/folder (or whereverrootpoints). - Fire up dev mode and watch it come alive:
npx kiri watch
- Ship it! Export the fully static site for production:
npx kiri export
CLI commands
| Command | Description |
|---|---|
kiri build |
Compiles the project for development (runs every configured task once, no minification/export step). |
kiri export |
Compiles and exports the project for production (forces every task + copies static files). |
kiri watch |
Starts dev mode: watches project files and rebuilds automatically on change. |
kiri serve |
Same as kiri watch, plus a local server and browser hot-reload (Server-Sent Events, no server framework). |
kiri run <script> |
Runs a PHP command script from the scripts/ folder inside the Kirigami runtime. |
kiri create <template> |
Creates a new project from an official template — ships its own CLAUDE.md, Claude Ready out of the box. |
kiri install <plugin...> |
Installs a plugin and prints the plugins: entry to paste into kirigami.yaml. |
kiri cache purge [mask] |
Purges the local .node.db / .cache.db / .cookie.txt caches (or just the keys matching mask). |
kiri phpinfo |
Prints phpinfo() from the embedded PHP-WASM runtime. |
Every command comes with its own detailed help: kiri <command> --help.
Configuration (kirigami.yaml)
The whole project is configured through a single kirigami.yaml file at the project root, validated against a JSON schema. Point your editor at it and enjoy full autocompletion:
# yaml-language-server: $schema=https://cdn.jsdelivr.net/gh/php-kirigami/kirigami@main/packages/kirigami/kirigami.schema.json
Here's a real-world example, showing most of the available sections:
kirigami:
project: My Website
baseurl: https://example.com
banner: assets/banner.txt
root: src
# Any extra key becomes a PHP variable available in every template
author: Jane Doe
person: John Smith
jobtitle: Founder
email: hello@example.com
facebook: https://www.facebook.com/example
area: Somewhere, Country
gtag: G-XXXXXXXXXX
description: >
A short description of the site, also exposed as $description.
knowsabout:
- Topic one
- Topic two
keywords:
- keyword one
- keyword two
prepros:
format: true
network: true
# mountext: ['.webp']
includes: [_lib/functions.php]
before: _layouts/header.php
after: _layouts/footer.php
image:
# format: avif # webp | avif (default: webp)
source: assets/images # relative to cwd() (default: assets/images)
dest: images # relative to kirigami.root (default: images)
plugins:
- name: "@kirigami/plugin-highlight"
active: true
options: {}
esbuild:
# minify: false
sass:
# style: expanded
scripts:
- name: convert-images-to-webp
mount: ["assets/images/**/*.jpg"]
trigger: before-build # before-build, before-export or after-export
tasks:
- name: js-core
type: esbuild
entry: scripts/kirigami.core.js
- name: scss-core
type: sass
entry: styles/kirigami.core.scss
kirigami: — core project settings
| Key | Required | Description |
|---|---|---|
project |
✅ | Human-readable project/site name. Printed in the CLI banner and available as $project in every template. |
baseurl |
✅ | Root URL of the deployed site, no trailing slash. Used to build absolute <loc> entries in sitemap.xml and available as $baseurl. |
root |
✅ | Path (relative to the project root) to the directory holding your _*.php source pages. All task entry paths and prepros rendering are relative to it. |
banner |
– | Path to a text file used as the license/copyright banner stamped on every exported .js/.css/.html file. May contain the ###DATE### token, replaced at load time. Falls back to an auto-generated banner if omitted. |
| (anything else) | – | Free-form project data (string, number, boolean, list, or nested map). Every extra key is exposed as a PHP variable of the same name (author → $author, etc.) in page templates, before/after includes, and prepros.includes files. |
prepros: — the PHP → HTML compiler
Just declaring this block (even empty) automatically prepends a forced prepros task that renders every page and regenerates sitemap.xml.
| Key | Description |
|---|---|
before |
PHP file (relative to root) included before every page's body — typically your <head>/layout opening. |
after |
PHP file included after every page's body — typically your layout closing. |
format |
Pretty-print the compiled HTML output (4-space indentation). Defaults to false. |
network |
Enable outbound HTTP(S) requests inside the sandboxed WASM runtime (needed for remote @tag fetches and cURL/scraper classes). Defaults to false. |
mountext |
Extra file extensions auto-mounted into the virtual filesystem, in addition to the built-in defaults (.php, .json, .yaml, .yml, .md, .db, .txt). |
includes |
PHP files include_once'd right after config load, before any page renders — the natural place to register tags/hooks/Markdown plugins. |
image: — image autogenerator
Options for the built-in image autogenerator. One feature, four entry points that share this config, one engine and the same output files: the img-asset() and colors() Sass functions, the PHP IMG::asset() / IMG::palette() helpers, and the <img asset="…"> tag in page templates — all resize/encode through the IMG class (GD/Imagick) in the WASM runtime, so there is no native image dependency. Optional — the defaults below apply even when the block is absent.
| Key | Description |
|---|---|
format |
Output format for generated images: webp or avif. Defaults to webp. |
source |
Folder holding the source images, relative to cwd(). Defaults to assets/images. |
dest |
Destination folder for generated images, relative to kirigami.root. Defaults to images. |
plugins: — Kirigami plugins
A list of plugins loaded through @kirigami/sdk. Each entry's package name must match @kirigami/plugin-*, <scope>/kirigami-plugin-*, or kirigami-plugin-*.
| Key | Description |
|---|---|
name |
Plugin package name (following one of the naming conventions above). |
active |
Whether the plugin is loaded. |
options |
Free-form object passed to the plugin; its shape depends on the plugin. |
esbuild: / sass: — global build options
Both are free-form objects passed straight through to the underlying build call, after Kirigami's own defaults (so they can override them): esbuild: maps to esbuild's own BuildOptions, sass: to Dart Sass's own Options. sass: also accepts before / after — arrays of extra .scss files compiled respectively before and after the entry (paths relative to cwd()). Leaving a block empty (as in the example above) is equivalent to omitting it entirely.
export: — production export options
| Key | Description |
|---|---|
path |
Output directory for kiri export, relative to the project root. Defaults to dist. |
ignore |
Extra gitignore-style patterns of files/directories to exclude from the export copy. |
scripts: — named PHP scripts
Each entry maps to a PHP file at scripts/<name>.php, runnable manually with kiri run <name> [args...] or fired automatically at a pipeline checkpoint.
| Key | Description |
|---|---|
name |
Script identifier — must match an existing scripts/<name>.php file. |
mount |
Glob patterns (relative to the project root) of extra local files to mount into the sandbox before the script runs. |
trigger |
Fires the script automatically: before-build (start of both build and export), before-export (very start of export), or after-export (once export has finished writing every task's output). |
tasks: — the build pipeline
An ordered list of build tasks, run in array order — on top of the implicit prepros task (added automatically whenever the prepros block is present) and the implicit dist task (added automatically during kiri export).
type |
Purpose | Required fields |
|---|---|---|
esbuild |
Bundles/minifies a JS or TS entry point. Supports build & watch. | name, entry |
sass |
Compiles a .scss/.sass entry point, minified with csso on export. Supports build & watch. |
name, entry |
prepros |
Renders pages + sitemap.xml. Watch only on a plain kiri build/watch — the implicit task added by the prepros: block is always forced. |
name |
dist |
Copies kirigami.root into the export output dir. Implicit/forced only, added automatically by kiri export. |
name, path |
Continuous deployment
Kirigami ships an official reusable GitHub Action, php-kirigami/kiribuild (v2) — published on the GitHub Marketplace, installable straight from a workflow file's Actions sidebar. The action itself only installs Node + kiri and runs kiri export — checkout, committing back whatever the build regenerated, and the actual Pages upload/deploy are wired by the caller's own workflow steps, so the full flow stays explicit:
# .github/workflows/page.yml
name: Build & Deploy
on:
push: { branches: [main] }
permissions: { contents: write, pages: write, id-token: write }
concurrency: { group: pages, cancel-in-progress: true }
jobs:
build-and-deploy:
runs-on: ubuntu-latest
environment: { name: github-pages, url: ${{ steps.deployment.outputs.page_url }} }
steps:
- uses: actions/checkout@v7
- uses: php-kirigami/kiribuild@v2
with:
node-version: "24"
- name: Commit regenerated files
run: |
if [ -n "$(git status --porcelain)" ]; then
git config user.name "kirigami[bot]"
git config user.email "kirigami-bot@users.noreply.github.com"
git add -A && git commit -m "chore: update generated files [skip ci]" && git push
fi
- uses: actions/upload-pages-artifact@v5
with: { path: dist }
- id: deployment
uses: actions/deploy-pages@v5
Every official template (kiri create) already ships this workflow at .github/workflows/page.yml — copy it from there. Check the action's own documentation for its inputs.
License
This project is distributed under the MIT license, except for the @kirigami/php-wasm package, which is distributed under GPL-2.0-or-later (see its README).
Author
MIT © Maxime Larrivée-Roy, 2026