Add typed text pages; serve CSS as compiled-on-demand /css/main.css

Sidecar-less page bundles whose directory ends in a MediaType extension
(.txt/.css/.xml/.json/.js) now render raw (no HTML layout, autoescape off),
are served with that extension's Content-Type, and cache as a flat file
(public/cache/<path>) so Apache serves them directly via a new .htaccess
rule. They're excluded from the content index.

CSS becomes one of these: novaconium/pages/css/main.css/ holds index.twig
({{ sass('main.sass') }}), main.sass, and _colors.sass. App\SassExtension's
sass() Twig function resolves the entry file through the overlay and shells
out to Dart Sass on a cache miss; Lib\SassCompiler wraps the CLI. This
drops the committed App/css/main.css build artifact and the public/css
bind mount. Override styling by copying the whole bundle to App/pages/.

Also ship robots.txt and humans.txt as framework bundles, and drop the
php -S dev router (public/router.php) - Docker/Apache only now.

New: novaconium/src/MediaType.php, SassExtension.php, lib/SassCompiler.php.
Moved: novaconium/sass/ -> novaconium/pages/css/main.css/ (+ App/sass merged).
Docs: AGENTS.md, new /admin/docs/text-pages, and styling/caching/docker/
design-notes/getting-started/project-layout/config docs updated.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Q9cAXC9xcXYnmP7qjsnw7
This commit is contained in:
code
2026-09-10 20:58:37 +00:00
co-authored by Claude Sonnet 5
parent 551efc0c36
commit f2724972af
33 changed files with 442 additions and 720 deletions
@@ -27,8 +27,8 @@
<li><strong>File-based routing</strong> — a directory under <code>App/pages/</code> <em>is</em> a route (Hugo-style page bundles). No route table to maintain.</li>
<li><strong><code>[param]</code> segments</strong> — a directory literally named <code>[param]</code> (e.g. <code>App/pages/products/[id]/</code>) captures any single URL segment into <code>$params['param']</code> for clean URLs, no query strings.</li>
<li><strong>Optional PHP "sidecars"</strong> — drop an <code>index.php</code> next to any <code>index.twig</code> to supply Twig context data, or return a <code>Response</code> (redirect/JSON/XML/HTML) to short-circuit templating entirely.</li>
<li><strong>Static caching, zero config</strong> — sidecar-less pages render once and are written to <code>public/cache/</code>; <code>.htaccess</code> serves the cached file directly on every later hit, skipping PHP and Twig entirely.</li>
<li><strong>Override-by-path</strong> — <code>App/</code> (your project) is checked before <code>novaconium/</code> (the framework defaults) for every page, layout, <code>Lib\</code> class, and even the Sass color palette (<code>App/sass/_colors.sass</code>). Drop a file at the same relative path to override it; nothing needs duplicating to get a working site.</li>
<li><strong>Static caching, zero config</strong> — sidecar-less pages render once and are written to <code>public/cache/</code>; <code>.htaccess</code> serves the cached file directly on every later hit, skipping PHP and Twig entirely. Name a bundle's directory with an extension (<code>css/main.css/</code>, <code>robots.txt/</code>, <code>humans.txt/</code>) and the same mechanism serves it with the right <code>Content-Type</code> — the stylesheet is Sass compiled on the first request, then a plain cached file.</li>
<li><strong>Override-by-path</strong> — <code>App/</code> (your project) is checked before <code>novaconium/</code> (the framework defaults) for every page, layout, and <code>Lib\</code> class. Drop a file at the same relative path to override it; nothing needs duplicating to get a working site.</li>
<li><strong>Layout inheritance</strong> — <code>_layout/layout.twig</code> directories are resolved by walking upward from the matched page, so you can override the layout for a whole subtree.</li>
<li><strong>SEO boilerplate out of the box</strong> — the default layout ships meta description, canonical link, robots, Open Graph, and Twitter Card tags, all overridable per-page via Twig blocks.</li>
<li><strong>Built-in Matomo analytics</strong> — set <code>matomo_url</code> and <code>matomo_site_id</code> in <code>App/config.php</code> to enable tracking site-wide, including automatic 404 tracking. Off by default.</li>
@@ -45,7 +45,7 @@
<li><strong>Content index: sitemap, search, tags</strong> — <code>/sitemap.xml</code>, full-text <code>/search</code> (SQLite FTS5), and blog tag browsing all share one crawler. Off by default; reindexes lazily on demand or via <code>php novaconium/bin/index-content.php</code>.</li>
<li><strong>Blog RSS feed</strong> — <code>/blog/feed</code>, built from the same hand-written post list <code>App/pages/blog/index.php</code> itself renders from, so it works with no database at all.</li>
<li><strong>Syntax-highlighted code blocks</strong> — vendored <a href="https://highlightjs.org/">highlight.js</a> colors PHP/Bash/HTML code blocks site-wide, auto-detected with no per-block markup.</li>
<li><strong>No build step, no Composer</strong> — clone it, point Apache (or <code>php -S</code>) at <code>public/</code>, and it runs. Twig is vendored as source.</li>
<li><strong>No build step, no Composer</strong> — run the Docker image (Apache + PHP + Dart Sass) and it works. Twig is vendored as source.</li>
</ul>
<p>Full details on every one of these live at <a class="icon-link" href="/admin/docs">{{ icons.book() }}/admin/docs</a>, rendered live from this same running instance — routing, sidecars, libraries, database, session, content index, XML sitemap, RSS feeds, layouts, static caching, SEO, Matomo, admin authentication, access control, draft pages, media manager, styling, project layout, and third-party notices.</p>