A PHP static site generator. Uses Twig for templating, supports YAML/JSON/PHP/XML/INI data sources, dynamic page parameters, static file sync, and a watch mode with partial rebuilds.
- PHP 8.2+ (CLI)
inotifywait(Linux/WSL) oufswatch(macOS) pour le mode watch
composer global require amund/kiss-php
kiss init my-site
cd my-site
kiss build| Command | Description |
|---|---|
kiss init [dir] |
Creates a new site in the folder (or the current folder) |
kiss build |
Build the entire website |
kiss watch |
Build and then monitor the files (using inotifywait or fswatch) |
kiss reset [all|dist|cache] |
Remove web/ and/or tmp/ |
kiss route [list|name] |
List the routes or rebuild a specific route |
kiss copy [path] |
Sync a file from copy/ to web/ |
kiss test |
Validate the configuration (warmup without build) |
kiss.yml β Site configuration
βββ copy/ β Static files (copied as-is to web/)
βββ data/ β Global data (YAML/JSON/PHP/XML/INI/MD)
βββ route/ β Route definitions (YAML/JSON/PHP/XML/INI)
βββ template/ β Twig templates
βββ web/ β Generated site (dist)
File kiss.yml (also supports .yaml, .php, .json, .xml, .ini).
debug: true
path:
copy: copy
data: data
route: route
template: template
dist: web
cache: systemEnvironment variables (take precedence):
KISS_DEBUG=trueKISS_VERBOSE=true
Files in data/ are loaded automatically and available in Twig via {{ global.* }}.
Example β data/site.yml:
name: My Site
tagline: Great static siteIn a Twig template:
<h1>{{ global.site.name }}</h1>
<p>{{ global.site.tagline }}</p>Markdown files (.md) in data/ support frontmatter delimited by ---:
---
title: My Article
date: 2024-01-01
---
Content **markdown** here.The frontmatter fields are available directly, and the body is available as content. Combine with the |markdown filter in your templates:
<h1>{{ title }}</h1>
<div>{{ content|markdown|raw }}</div>route/index.yml:
path: /index.html
template: page.twig
data:
title: Homeroute/blog.yml:
path: /{slug}.html
template: post.twig
data:
blog_title: "My Blog"
items:
- slug: hello-world
title: Hello World
- slug: second-article
title: Second articleEach item generates a page. data contains the metadata, which is merged into each child page.
path: /{slug}.html
template: post.twig
items:
$ref: data/articles.ymlpath: /blog
template: blog_list.twig
data:
title: "Blog"
items:
$ref: data/posts.ymlAccess in the archive template :
<h1>{{ title }}</h1>
{% for post in items %}
<a href="{{ path('blog', {slug: post.slug}) }}">{{ post.title }}</a>
{% endfor %}{{ path('about') }} β /about
{{ path('blog', {slug: 'hello'}) }} β /hello.html
{{ path('blog') }} β /blog (page 1)
{{ path('blog', {page: 2}) }} β /blog/page/2{% set blog = route('blog') %}
<h1>{{ blog.title }}</h1>
{% for post in blog.items %}
...
{% endfor %}route/blog.yml:
path: /blog
template: blog_list.twig
paginate: 10
data:
title: "Archives"
items:
$ref: data/posts.ymlGenerates blog/index.html (page 1) and blog/page/{n}/index.html (subsequent pages).
{% for post in items %}
...
{% endfor %}
{% if prevPage %}
<a href="{{ path('blog', {page: prevPage}) }}">β Previous</a>
{% endif %}
{% if nextPage %}
<a href="{{ path('blog', {page: nextPage}) }}">Next β</a>
{% endif %}{param}orpaginateβitemsis requireddatais reserved for metadata- The
$refshould be set toitems, notdata
- Route manifest:
tmp/kiss/route-manifest.phptracks which files each route generated; orphaned files are cleaned on rebuild. - Template deps:
tmp/kiss/template-deps.phptraces dependencies between templates (extends/include/embed) for partial rebuilds. - DataTree: caches remote and local data sources as serialized PHP files.
- Watch:
inotifywaitdetects changes and recompiles only the impacted routes.
MIT