<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
     xmlns:atom="http://www.w3.org/2005/Atom"
     xmlns:content="http://purl.org/rss/1.0/modules/content/"
     xmlns:media="http://search.yahoo.com/mrss/">
    <channel>
        <title># notACMS</title>
        <link>https://notacms.holas.pl/</link>
        <description><![CDATA[AI-friendly static site generator. Zero database. Pure Markdown.]]></description>
        <language>en</language>
        <atom:link href="https://notacms.holas.pl/feed/" rel="self" type="application/rss+xml"/>
                <lastBuildDate>Fri, 24 Apr 2026 00:00:00 +0000</lastBuildDate>
                        <item>
            <title><![CDATA[notACMS 1.1.0 — Bare core, demo theme, pick your own]]></title>
            <link>https://notacms.holas.pl/blog/release-1-1-0/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/blog/release-1-1-0/</guid>
                        <pubDate>Fri, 24 Apr 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[Two themes, one pick on first build The headline change in 1.1.0 is that notACMS now ships two themes out of the box, and you pick one on your very first build: ./notACMS deploy --demo # default — the amber-phosphor design you see here ./notACMS deploy --bare # a minimal wireframe: system fonts, light mode, ~200 lines of CSS ddev build supports the same flags. Whichever you choose, every feature w…]]></description>
            <content:encoded><![CDATA[<h2>Two themes, one pick on first build<a id="two-themes-one-pick-on-first-build" href="#two-themes-one-pick-on-first-build" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>The headline change in 1.1.0 is that notACMS now ships two themes out of the box, and you pick one on your very first build:</p>
<pre><code class="language-bash">./notACMS deploy --demo    # default — the amber-phosphor design you see here
./notACMS deploy --bare    # a minimal wireframe: system fonts, light mode, ~200 lines of CSS
</code></pre>
<p><code>ddev build</code> supports the same flags. Whichever you choose, every feature works: multi-language content, blog, RSS, sitemap, search, contact form, images with responsive variants, reading time, reading progress. The difference is only in how it looks — and, critically, in how much you inherit before you start customising.</p>
<h3>Bare core<a id="bare-core" href="#bare-core" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>The core at <code>templates/</code>, <code>assets/</code>, <code>translations/</code> is now a wireframe. It's intentionally minimal so that overriding a single block doesn't drag in a visual language you have to fight. If you're building a bespoke design, start from bare and you'll own the full look from day one.</p>
<h3>Demo theme<a id="demo-theme" href="#demo-theme" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>The demo lives at <code>docs/demo/</code> and is copied into <code>local/</code> when you choose <code>--demo</code>. It's the full amber-phosphor design you're reading now — dark mode, search overlay, docs sidebar, theme toggle, the lot. Start here if you want a polished design today and plan to tweak, not rebuild.</p>
<h2>The <code>local/</code> override system<a id="the-local-override-system" href="#the-local-override-system" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Both themes consume the same mechanism: every file in <code>local/</code> takes precedence over the matching core path.</p>
<table>
<thead>
<tr>
<th>Layer</th>
<th>Override location</th>
<th>Resolver</th>
</tr>
</thead>
<tbody>
<tr>
<td>Twig templates</td>
<td><code>local/templates/*.html.twig</code></td>
<td>Symfony kernel</td>
</tr>
<tr>
<td>SCSS entrypoint</td>
<td><code>local/assets/styles/app_local.scss</code></td>
<td>Sass bundle</td>
</tr>
<tr>
<td>JS entrypoint</td>
<td><code>local/assets/app.js</code></td>
<td>AssetMapper importmap</td>
</tr>
<tr>
<td>Translations</td>
<td><code>local/translations/messages.*.yaml</code></td>
<td>Symfony translator</td>
</tr>
<tr>
<td>Content</td>
<td><code>local/content/**</code></td>
<td><code>notacms_content</code> parameter</td>
</tr>
<tr>
<td>Nginx snippets</td>
<td><code>local/docker/nginx/*.conf</code></td>
<td>Container entrypoint</td>
</tr>
</tbody>
</table>
<p>Core is never edited. <code>git pull</code> stays clean. Customisations live in your site's repo, not in a fork of this one.</p>
<h2>Upgrading from 1.0.0<a id="upgrading-from-100" href="#upgrading-from-100" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>If you were running 1.0.0 and want your site to look exactly as it did, the compatibility package is a one-liner:</p>
<pre><code class="language-bash">cp -r docs/customization/old-template/. local/
ddev build
</code></pre>
<p>That drops the complete pre-1.1.0 theme — templates, SCSS, fonts, images, translations — into <code>local/</code>. Your site renders exactly as before. Then you can remove files from <code>local/</code> selectively as you adopt new design elements.</p>
<p>The breaking changes you'll actually hit if you customised the old core:</p>
<ul>
<li><strong>SCSS entrypoint renamed</strong>: <code>local/assets/styles/local.scss</code> → <code>local/assets/styles/app_local.scss</code>. Rename the file and update the import in <code>local/assets/app.js</code>.</li>
<li><strong>Core colour SCSS variables replaced with CSS custom properties</strong>: <code>$color-body</code> → <code>var(--text)</code>, <code>$color-link</code> → <code>var(--accent)</code>, and so on. Full mapping in <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/UPGRADE-1.1.md">UPGRADE-1.1.md</a>.</li>
<li><strong>Translation keys removed</strong> from core (still present in the demo theme): <code>header.tagline</code>, <code>nav.projects</code>, <code>nav.search</code>, <code>blog.published_on</code>, <code>blog.comments_disabled</code>. If your templates call <code>'...'|trans</code> on those, update them.</li>
<li><strong><code>docs/examples/</code> directory removed</strong>: the useful bits moved to <code>docs/customization/old-template/</code>; the scratch override examples were superseded by the bare/demo model.</li>
</ul>
<p>See <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/UPGRADE-1.1.md">UPGRADE-1.1.md</a> for the full list with before/after snippets.</p>
<h2>Also new in 1.1.0<a id="also-new-in-110" href="#also-new-in-110" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<ul>
<li><strong>Reading time</strong> and <strong>reading progress</strong> on posts, docs, and releases.</li>
<li><strong>Language switcher</strong> as a Twig extension (<code>lang_switch_urls</code>) with proper fallback chains for archives, paginated lists, and home pages.</li>
<li><strong>Post excerpts strip heading anchors</strong> — no more stray <code>#</code> characters in listing cards.</li>
<li><strong>Test suite scaffolding</strong> at <code>tests/Unit/</code>, <code>tests/Integration/</code>, <code>tests/Fixtures/</code> with initial coverage and a <code>test</code> command runnable from the host.</li>
<li><strong>AI-agent skills</strong> under <code>.claude/skills/</code> for working with the repo: adding locales, doc alignment checks, site sweeps, translations, and upgrade-guide generation.</li>
</ul>
<h2>Full changelog<a id="full-changelog" href="#full-changelog" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Every change with its category: <a rel="nofollow noopener noreferrer" target="_blank" href="https://github.com/holas1337/notACMS/blob/main/CHANGELOG.md#110---2026-04-24">CHANGELOG.md</a>.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[announcement]]></category>
                    </item>
                <item>
            <title><![CDATA[notACMS v1.0.0]]></title>
            <link>https://notacms.holas.pl/blog/release-1-0-0/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/blog/release-1-0-0/</guid>
                        <pubDate>Thu, 09 Apr 2026 00:00:00 +0000</pubDate>
                        <description><![CDATA[What&#039;s in v1.0.0 After several months of internal use on personal projects, I&#039;m tagging the first stable release. The core feature set is solid enough to build real sites with. Content pipeline The content pipeline is the heart of notACMS. It reads your local/content/ directory, parses frontmatter, and generates a complete static site in one command. Markdown content with YAML frontmatter CommonMa…]]></description>
            <content:encoded><![CDATA[<h2>What's in v1.0.0<a id="whats-in-v100" href="#whats-in-v100" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>After several months of internal use on personal projects, I'm tagging the first stable release. The core feature set is solid enough to build real sites with.</p>
<h3>Content pipeline<a id="content-pipeline" href="#content-pipeline" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>The content pipeline is the heart of notACMS. It reads your <code>local/content/</code> directory, parses frontmatter, and generates a complete static site in one command.</p>
<ul>
<li>Markdown content with YAML frontmatter</li>
<li>CommonMark rendering with heading permalinks</li>
<li>Excerpt generation from prose content</li>
<li>Reading time calculation</li>
</ul>
<h3>Multilingual routing<a id="multilingual-routing" href="#multilingual-routing" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Locales are defined in <code>_site.yaml</code>. Each locale gets its own URL space, with optional path overrides in <code>_routes.yaml</code>. hreflang tags are generated automatically.</p>
<pre><code class="language-yaml">site:
  locales:
    en:
      label: &quot;English&quot;
      date_format: &quot;M d, Y&quot;
    pl:
      label: &quot;Polski&quot;
      date_format: &quot;d.m.Y&quot;
</code></pre>
<h3>Pagefind search<a id="pagefind-search" href="#pagefind-search" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Full-text search is built into the static output via <a rel="nofollow noopener noreferrer" target="_blank" href="https://pagefind.app/">Pagefind</a>. The build command generates the search index automatically. No external API, no server-side search — just a static index that works offline.</p>
<h3>Image processing<a id="image-processing" href="#image-processing" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>Images co-located with content are processed during build. notACMS generates WebP variants at multiple widths, automatically updates <code>src</code> attributes with responsive <code>srcset</code>, and handles the path mapping between content and output directories.</p>
<h3>DDEV local development<a id="ddev-local-development" href="#ddev-local-development" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h3>
<p>The development environment is fully containerized with DDEV. <code>ddev start</code> gives you PHP 8.5, Nginx, and all build tools. <code>ddev build</code> produces the static output. <code>ddev code-check</code> runs PHPStan and PHP CS Fixer.</p>
<h2>Breaking changes<a id="breaking-changes" href="#breaking-changes" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>This is the first stable release. If you've been using a pre-1.0 version, review the <code>_site.yaml</code> schema — the <code>social</code> key changed from a list to a map.</p>
<h2>Upgrade<a id="upgrade" href="#upgrade" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<pre><code class="language-bash">git pull
ddev composer install
ddev build
</code></pre>
<h2>What's next<a id="whats-next" href="#whats-next" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>v1.1.0 will focus on the design system and documentation. This site — built with notACMS — will become the official documentation and design reference.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[release]]></category>
                        <category><![CDATA[announcement]]></category>
                    </item>
                <item>
            <title><![CDATA[Why I built notACMS]]></title>
            <link>https://notacms.holas.pl/blog/the-idea/</link>
            <guid isPermaLink="true">https://notacms.holas.pl/blog/the-idea/</guid>
                        <pubDate>Fri, 01 Nov 2024 00:00:00 +0000</pubDate>
                        <description><![CDATA[Longer backstory on my personal blog: I left WordPress. The problem I kept running into Every PHP project I worked on eventually needed a website. Documentation, a landing page, a blog — the usual. And every time I reached for a tool, I ended up in the same place: a tool that wanted me to think like it, not like a PHP developer. Hugo is fast but its template language is alien. Jekyll requires Ruby…]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Longer backstory on my personal blog: <a rel="nofollow noopener noreferrer" target="_blank" href="https://holas.pl/blog/why-i-left-wordpress/">I left WordPress</a>.</p>
</blockquote>
<h2>The problem I kept running into<a id="the-problem-i-kept-running-into" href="#the-problem-i-kept-running-into" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>Every PHP project I worked on eventually needed a website. Documentation, a landing page, a blog — the usual. And every time I reached for a tool, I ended up in the same place: a tool that wanted me to think like it, not like a PHP developer.</p>
<p>Hugo is fast but its template language is alien. Jekyll requires Ruby. WordPress is the wrong shape for static content. Next.js is powerful but it turns a Markdown publishing problem into a JavaScript bundler problem.</p>
<p>I kept thinking: I already know Symfony. I already know Twig. I already have Composer. Why do I need to learn a new ecosystem just to publish text?</p>
<h2>The zero-database decision<a id="the-zero-database-decision" href="#the-zero-database-decision" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>The first thing I decided: no database. Not &quot;database optional&quot; — no database at all. Content lives in files. The build process reads files. The output is files.</p>
<p>This forces a certain discipline. Content structure has to be explicit and predictable. There's no database query to reach for when you want to find related posts or generate a sitemap. Everything has to be derivable from the file tree.</p>
<p>That constraint turned out to be the right one. It makes the system easy to reason about, easy to back up, and trivially easy for AI tools to work with.</p>
<h2>AI-friendly by design<a id="ai-friendly-by-design" href="#ai-friendly-by-design" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>I started thinking about this seriously when I began using LLMs in my daily work. Asking an AI to help generate content, translate pages, or validate YAML schemas is straightforward when the format is plain text.</p>
<p>With a database-backed CMS, you'd need to explain the schema, write SQL, manage migrations. With notACMS, you hand the AI a directory of Markdown files and it can read, generate, and edit content directly — because the format is just text.</p>
<p>This isn't a feature I added after the fact. It's the reason the system is designed the way it is.</p>
<h2>The Symfony foundation<a id="the-symfony-foundation" href="#the-symfony-foundation" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>I built notACMS on top of Symfony 7 for the same reason I use Symfony for everything else: it's explicit, typed, and well-documented. The DI container, console commands, Twig — all of this is standard Symfony. There's nothing novel about the infrastructure.</p>
<p>The content model is the interesting part. The routing system reads <code>_routes.yaml</code> and generates Symfony routes from your content tree. The build command renders each route and writes static HTML. The service layer is thin and replaceable.</p>
<h2>What's next<a id="whats-next" href="#whats-next" class="heading-anchor" aria-hidden="true" title="Permalink">#</a></h2>
<p>This first release is a working foundation. The things I want to build:</p>
<ul>
<li>Pagefind search integration (coming in v1.0.0)</li>
<li>Better image pipeline with WebP generation</li>
<li>More complete multilingual support</li>
<li>A proper Design Reference page (that's what this site is becoming)</li>
</ul>
<p>The repository is public. If you want to follow along or contribute, the link is in the footer.</p>
]]></content:encoded>
                                    <category><![CDATA[releases]]></category>
                                    <category><![CDATA[announcement]]></category>
                        <category><![CDATA[open-source]]></category>
                    </item>
            </channel>
</rss>
