<?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/">
  <channel>
    <title>Ochra — Notes on painting</title>
    <link>https://ochra.app/blog</link>
    <description>What goes wrong on the paper, why it happens, and what to do next. One question at a time.</description>
    <language>en</language>
    <atom:link href="https://ochra.app/blog/rss.xml" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sat, 12 Sep 2026 14:54:07 GMT</lastBuildDate>
    <item>
      <title>Hello world — how this blog works</title>
      <link>https://ochra.app/blog/hello-world</link>
      <guid isPermaLink="true">https://ochra.app/blog/hello-world</guid>
      <pubDate>Thu, 01 Jan 2026 00:00:00 GMT</pubDate>
      <description>A worked example of the markdown blog pipeline: validated frontmatter, generated social cards, an RSS feed and a sitemap entry, all produced at build time.</description>
      <category>engineering</category>
      <category>getting-started</category>
      <content:encoded><![CDATA[<p>This post exists so the blog engine has something to render. It is a plain
markdown file in <code>apps/marketing/content/blog/</code>, and everything you see on this
page was produced from it at build time — there is no CMS, no database and no
client-side JavaScript on the page.</p>
<p>Keep it as a reference, or delete it once you have posts of your own. Deleting
every post is safe: the index falls back to an empty state rather than failing
the build.</p>
<h2>What the build does with this file</h2>
<p><code>next build</code> reads the file, validates its frontmatter against a Zod schema and
renders the body. A missing <code>title</code>, a malformed <code>date</code> or an <code>ogImage</code> that is
not on disk fails the build loudly, so a broken post never reaches production.</p>
<p>From this one file the build emits:</p>
<ul>
<li><code>/blog/hello-world</code> — the post page, with <code>Article</code> and <code>BreadcrumbList</code>
JSON-LD and its own Open Graph and Twitter card.</li>
<li>an entry on <code>/blog</code>, ordered newest first, with an estimated reading time.</li>
<li>an item in <code>/blog/rss.xml</code>, with root-relative links rewritten to absolute
ones so they resolve in a feed reader.</li>
<li>a <code>&lt;url&gt;</code> entry in <code>sitemap.xml</code>, carrying the post&#39;s real <code>lastmod</code>.</li>
</ul>
<h2>Images</h2>
<p>Put images in <code>public/blog/&lt;slug&gt;/</code> and reference them with a site-absolute
path. The build reads each PNG&#39;s real dimensions out of its header and writes
them onto the <code>&lt;img&gt;</code> tag, so the browser reserves the right space before the
bytes arrive and the text below does not jump:</p>
<p><img src="https://ochra.app/blog/hello-world/cover.png" alt="A placeholder card showing four rounded tiles in a two-by-two grid"></p>
<p>Alt text is not optional — an informative image without it fails the build.</p>
<h2>Drafts</h2>
<p>Set <code>draft: true</code> to stage a post. Drafts are excluded from the index, the feed
and the sitemap, and their page is not exported at all, so the URL 404s until
you flip the flag back.</p>
<p>That is the whole contract. <code>content/blog/README.md</code> documents every frontmatter
field in a table.</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
