sprain / sprainocchio
A simple blog engine
Requires
- php: >=8.5
- ext-dom: *
- erusev/parsedown: ^1.8
- pimple/pimple: ^3.5
- symfony/finder: ^8.0
- symfony/yaml: ^8.0
- twig/twig: ^3.21
Requires (Dev)
- phpunit/phpunit: ^13.4
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A simple and easy-to-use static-page blog engine written in php
Maintained by Claude
Since October 2026, sprainocchio is maintained by Claude, an AI by Anthropic, as part of an open experiment on sprain.ch/blog, the blog sprainocchio was written for. Claude decides what to work on, writes the code and ships it on its own. Manuel Reinhard, who created sprainocchio, has the last word.
Planned work is tracked in the issues. Every change comes as a pull request that explains why it was made, and is merged only once the test suite passes. Every change to the engine gets a new version: see the changelog and the releases.
Requirements
PHP 8.5 or newer with the dom extension.
Getting started
-
Fetch the project with Composer.
composer create-project sprain/sprainocchio sprainocchio ^0.3 -
Run the build process.
php sprainocchio/builder.php -
Open the generated website.
open sprainocchio/public/index.html
Using sprainocchio in your own project
Instead of writing posts inside the sprainocchio folder, you can keep your blog in its own project and require sprainocchio via Composer. Your project then holds config.yml, posts/, themes/ and optionally pages/ (pages outside the post lists, like an about page) and static/ (files copied into public/ as they are, e.g. images). Build it from the project folder with:
vendor/bin/sprainocchio
Theme assets
Load stylesheets and other files from your theme with asset(), so browsers fetch them again after every change instead of showing a cached old version:
<link rel="stylesheet" href="{{ asset('styles/styles.css') }}"> {# → https://example.com/blog/styles/styles.css?v=3f2a9c1e #}
The version is a hash of the file's content and changes only when the file does. The path is relative to public/, so it works for the theme's public_dirs and for static/. The build fails if the file doesn't exist.
Feed
Every build writes an Atom feed of the 20 newest posts to public/feed.xml. Set its title and the default author in config.yml:
title: My blog author: Jane Doe
Posts with their own author keep it in the feed. Link the feed in your theme's <head> so feed readers find it:
<link rel="alternate" type="application/atom+xml" href="{{ baseUrl }}feed.xml">
Development
vendor/bin/phpunit runs the tests. GitHub Actions runs them on every push and pull request.