Blog Post Collections — Individual Articles

Create and edit individual blog posts in Dax Pro: frontmatter fields, images, tags, excerpts, ordering, and Markdown body. Each file in content/posts/*.md becomes an article at /posts/{slug}/.

What a Blog Post Collection Is

Every article on your blog is a single Markdown file inside the content/posts/ folder. The filename becomes the URL:

content/posts/why-static-sites-win.md     → /posts/why-static-sites-win/

content/posts/blazor-vs-dax-pro.md → /posts/blazor-vs-dax-pro/

content/posts/decap-cms-guide.md → /posts/decap-cms-guide/

Every post shares the same template (post.dax), the same layout, and the same frontmatter structure. The differences are the content: title, description, body, image, and tags.

When you add a new file to content/posts/, the post appears in /posts/ automatically and gets its own URL. When you delete one, both disappear. There is no separate index to update.

This chapter covers everything a post owner needs to know:

1. The frontmatter structure — every field in every post

2. Writing the Markdown body — the content readers see

3. Images — featured images, inline images, and OpenGraph

4. Tags — how they create tag pages and cross-link content

5. Excerpt — the summary that appears on the listing

6. Ordering — how posts sort in the listing and on the homepage

7. Creating a post — via Decap CMS and via file

8. Editing and deleting — both methods

9. Reading time — how to estimate it

10. Common mistakes — patterns that break post pages


The Frontmatter Structure

Every post starts with a frontmatter block. Here is a complete example:

---

layout: "post.dax"

title: "Why Modern Enterprises Are Abandoning Dynamic WordPress for .NET SSG"

description: "An executive breakdown of how replacing dynamic SQL databases with compiled .NET static sites reduces operational hosting expenses by 85% while boosting organic Core Web Vitals scores."

image: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1200&q=80&auto=format&fit=crop"

date: 2026-03-28

category: "Architecture"

read_time: "5 min read"

read_more: "Read Analysis"

author: "Robert Vance"

tags: ["dotnet", "static", "wordpress", "performance", "migration"]

excerpt: "An executive breakdown of how replacing dynamic SQL databases with compiled .NET static sites reduces operational hosting expenses by 85% while boosting organic Core Web Vitals scores."


Every field explained:

Field Purpose Required?
layout The template used to render the post. Must be post.dax. Yes
title The post's H1, browser title, listing card title, prev/next link title. Yes
description Meta description for SEO and OpenGraph description. Yes
image Featured image. Used as OpenGraph image, listing card thumbnail, and post cover. Optional
date Publication date in YYYY-MM-DD format. Determines ordering. Yes
category Single category name. Shown in the meta line on listing cards and post header. Optional
read_time Human-readable read estimate. You determine the value; the engine does not calculate. Optional
read_more "Read more" label on the listing card. Defaults to "Read Article" if omitted. Optional
author Author name. Shown in the meta line and post header. Optional
tags List of lowercase tags. Each becomes a tag page at /tags/{tag}/. Optional
excerpt Short summary shown on the listing card. Distinct from description. Recommended

Do not change layout. It must always be post.dax.


Field-by-Field Guide

title

The most important field. It appears in six places:

1. The

on the post page

2. The </code> in the browser tab</p> <p>3. The listing card on <code>/posts/</code></p> <p>4. The Insights card on the homepage (if the post is among the three most recent)</p> <p>5. The prev/next navigation on adjacent posts</p> <p>6. The tag pages where the post appears</p> <p><strong>Guidelines for post titles:</strong></p> <ul><li><strong>Descriptive over clever.</strong> "Why Static Sites Beat WordPress for Enterprise Content" outperforms "The Static Revolution" in search and clicks.</li> <li><strong>Include your target keyword</strong> near the start. If you are writing about Decap CMS, start with "Decap CMS" or put it within the first three words.</li> <li><strong>60–70 characters is ideal.</strong> Longer titles get truncated in search results and social cards.</li> <li><strong>Avoid clickbait.</strong> Enterprise readers distrust headlines like "You Won't Believe What Happened Next."</li> <h3><code>description</code></h3> <p>The meta description. Used for:</p> <li><code><meta name="description"></code> (what search engines show in results)</li> <li>OpenGraph description (what social media shows in link previews)</li> <li>Fallback for the listing card if <code>excerpt</code> is not set</li> <p><strong>Guidelines:</strong></p> <li><strong>150–160 characters.</strong> Longer gets truncated.</li> <li><strong>Include the primary keyword.</strong></li> <li><strong>Write it as a complete sentence</strong> that makes someone want to click.</li> <li><strong>Do not just repeat the title.</strong> The description should add information.</li> <p><strong>Example:</strong></p> <p>Title: <code>Why Modern Enterprises Are Abandoning WordPress for .NET SSG</code></p> <p>Description: <code>An executive breakdown of how compiled .NET static sites reduce hosting costs by 85% while boosting Core Web Vitals.</code></p> <p>The title makes a claim; the description adds the specifics.</p> <h3><code>image</code></h3> <p>The featured image. Used in three places:</p> <p>1. <strong>OpenGraph image</strong> — what appears when the post is shared on Twitter, LinkedIn, Facebook, Slack</p> <p>2. <strong>Listing card thumbnail</strong> — the image at the top of the post card on <code>/posts/</code> and the homepage</p> <p>3. <strong>Post cover</strong> — a large image at the top of the post page itself</p> <p><strong>Guidelines:</strong></p> <li><strong>Recommended dimensions:</strong> 1200×630 pixels. This is the OpenGraph standard. Twitter, Facebook, LinkedIn all crop to about this ratio (1.91:1).</li> <li><strong>Format:</strong> <code>.webp</code> (smallest, modern), <code>.jpg</code> (universal), <code>.png</code> (largest, use only when transparency is needed).</li> <li><strong>Content:</strong> the image should relate to the post. A technical post about CI/CD might show a terminal; a post about performance might show a speedometer; a post about migration might show a bridge.</li> <li><strong>No text overlay.</strong> Text in images gets cut off in different crops. Let the title carry the message.</li> <p><strong>Free image sources:</strong></p> <li><a href="https://unsplash.com">Unsplash</a> — high quality, free for commercial use</li> <li><a href="https://pexels.com">Pexels</a> — similar</li> <li><a href="https://pixabay.com">Pixabay</a> — similar</li> <p><strong>To use an Unsplash image:</strong></p> <p>1. Find an image</p> <p>2. Copy the image URL (Unsplash provides direct URLs)</p> <p>3. Append <code>?w=1200&q=80&auto=format&fit=crop</code> to the URL</p> <p>4. Paste into <code>image:</code></p> <p>For example:</p> <pre><code>image: "https://images.unsplash.com/photo-1451187580459-43490279c0fa?w=1200&q=80&auto=format&fit=crop" <p></code></pre></p> <p>The <code>w=1200</code> parameter sets width to 1200 pixels. <code>q=80</code> sets quality to 80%. <code>auto=format</code> uses the best format for the browser. <code>fit=crop</code> crops to the requested aspect ratio.</p> <p><strong>For your own images:</strong></p> <p>1. Place the file in <code>public/images/</code></p> <p>2. Reference it with a path starting with <code>/</code>:</p> <pre><code>image: "/images/my-post-image.jpg" <p></code></pre></p> <h3><code>date</code></h3> <p>Publication date in <code>YYYY-MM-DD</code> format. Determines:</p> <li>Ordering in <code>/posts/</code> listing (most recent first)</li> <li>Whether the post appears in the homepage Insights section (top three most recent)</li> <li>Ordering in tag pages</li> <li><code>datePublished</code> in the JSON-LD schema</li> <p><strong>Format is strict:</strong></p> <pre><code>date: 2026-05-12 # correct <p>date: May 12, 2026 # wrong</p> <p>date: 05/12/2026 # wrong</p> <p></code></pre></p> <p><strong>Dates in the future are still published.</strong> Dax Pro has no scheduling. A post dated 2027 will appear immediately at the top of the listing because its date is the "most recent." If you want to unpublish a post, delete its file.</p> <p><strong>Changing a post's date changes its position.</strong> A post dated 2026-01-01 appears after a post dated 2026-05-01. Use this to control ordering.</p> <h3><code>category</code></h3> <p>A single category name. Shown in the meta line on listing cards and post headers. Examples:</p> <pre><code>category: "Architecture" <p>category: "Decap CMS"</p> <p>category: "Performance"</p> <p>category: "Migration"</p> <p>category: "Tutorial"</p> <p></code></pre></p> <p><strong>Categories are cosmetic.</strong> They do not create a category page (unlike tags). They are displayed text. Choose categories that match the post's topic.</p> <p><strong>Use consistent categories across posts.</strong> If you have three posts about Decap CMS, label them all <code>Decap CMS</code>, not "Decap CMS" and "Decap" and "CMS." Consistency helps readers scan the archive.</p> <p><strong>Categories are not indexed.</strong> Unlike tags, categories do not have their own URLs. If you want indexable categorization, use tags.</p> <h3><code>read_time</code></h3> <p>A human-readable read estimate. Examples:</p> <pre><code>read_time: "5 min read" <p>read_time: "10 min read"</p> <p>read_time: "1 min read"</p> <p></code></pre></p> <p><strong>You determine this value.</strong> Dax Pro does not calculate it. The number is shown in the meta line of listing cards and post headers.</p> <p><strong>Estimation:</strong></p> <li><strong>Average reading speed:</strong> 200–250 words per minute for technical content, 250–300 for prose.</li> <li><strong>A 1,000-word post:</strong> about 4–5 minutes.</li> <li><strong>A 2,500-word post:</strong> about 10–12 minutes.</li> <li><strong>Add 30 seconds</strong> if the post has code blocks that readers will study.</li> <li><strong>Add 1 minute</strong> if the post has images that readers will scan.</li> <p><strong>Be honest.</strong> Under-estimating sets false expectations; a "3 min read" that takes 10 minutes frustrates readers. Over-estimating discourages clicks.</p> <h3><code>read_more</code></h3> <p>The "read more" label on the listing card. Defaults to "Read Article" if omitted. Examples:</p> <pre><code>read_more: "Read Analysis" <p>read_more: "Read Guide"</p> <p>read_more: "Read Blueprint"</p> <p>read_more: "Read Tutorial"</p> <p>read_more: "Read Article"</p> <p></code></pre></p> <p><strong>Customize per post type:</strong></p> <li>Analysis posts → "Read Analysis"</li> <li>Tutorial posts → "Read Guide" or "Read Tutorial"</li> <li>Reference posts → "Read Blueprint"</li> <li>Opinion posts → "Read Article"</li> <p><strong>Or use one label everywhere.</strong> "Read Article" is a safe default for all posts. Customizing per post is optional; it adds a small amount of personality.</p> <h3><code>author</code></h3> <p>Author name. Shown in the meta line of the listing card and post header. Examples:</p> <pre><code>author: "Robert Vance" <p>author: "Elena Chen"</p> <p>author: "Marcus Sterling"</p> <p>author: "Axcora Team"</p> <p></code></pre></p> <p><strong>Single author per post.</strong> Dax Pro supports one author field. If you have multiple authors, pick the primary author or use a team name.</p> <p><strong>Author bios:</strong> the <code>author</code> field is just a name. There is no separate author page or bio. If you want author bios, that is a developer feature — see the Developer Guide.</p> <h3><code>tags</code></h3> <p>A list of lowercase tags. Each tag creates a tag page and cross-links the post.</p> <pre><code>tags: ["dotnet", "static", "wordpress", "performance", "migration"] <p></code></pre></p> <p><strong>Tags must be lowercase.</strong> A tag <code>"DotNet"</code> produces a URL <code>/tags/DotNet/</code> that does not match the generated <code>/tags/dotnet/</code> page. Uppercase tags break.</p> <p><strong>Common tags:</strong></p> <pre><code>dotnet, razor, blazor, decapcms, static, enterprise, migration, <p>azure, cicd, performance, accessibility, wcag, theming, brand,</p> <p>audit, compliance, headless, csharp, wordpress, drupal, sitecore</p> <p></code></pre></p> <p><strong>Number of tags per post:</strong> two to six is typical. One is fine. Twenty dilutes the taxonomy.</p> <p><strong>Tags are cumulative.</strong> A tag page shows every post and service with that tag. Adding a new tag to one post creates a new tag page listing that one post. Adding the same tag to a second post adds it to the tag page.</p> <p><strong>Tags are case-sensitive.</strong> <code>"dotnet"</code> and <code>"DotNet"</code> are different tags. Always lowercase.</p> <p><strong>No spaces.</strong> <code>"content migration"</code> should be <code>"content-migration"</code> or <code>"migration"</code>. Spaces break URLs.</p> <h3><code>excerpt</code></h3> <p>The short summary shown on the listing card. Distinct from <code>description</code>:</p> <li><strong><code>description</code></strong> — for search engines and social media. 150–160 characters. Written for click-through from search results.</li> <li><strong><code>excerpt</code></strong> — for the listing card on <code>/posts/</code>. 100–200 characters. Written for scanning.</li> <p><strong>Example:</strong></p> <p>Description: <code>An executive breakdown of how compiled .NET static sites reduce hosting costs by 85% while boosting Core Web Vitals.</code></p> <p>Excerpt: <code>An executive breakdown of how replacing dynamic SQL databases with compiled .NET static sites reduces operational hosting expenses by 85%.</code></p> <p>The excerpt is slightly longer and more specific. It appears on the listing card where there is more space than in a search result.</p> <p><strong>If <code>excerpt</code> is omitted</strong>, the listing card falls back to <code>description</code>. This works fine for most posts. Writing a separate excerpt adds precision but is not required.</p> <hr/> <h2>Writing the Markdown Body</h2> <p>After the closing <code>---</code>, the body begins. It is rendered as the main content of the post page.</p> <p><strong>A well-structured post body has:</strong></p> <p>1. <strong>A compelling opening</strong> — one or two paragraphs that state the problem or hook. Do not waste the opening on throat-clearing.</p> <p>2. <strong>Section headings</strong> — one <code>##</code> per major point, one <code>###</code> per sub-point.</p> <p>3. <strong>Body paragraphs</strong> — short paragraphs (2–4 sentences each) are easier to scan than long ones.</p> <p>4. <strong>Lists, code blocks, images</strong> — break up text and give the reader visual anchors.</p> <p>5. <strong>A concluding section</strong> — a short summary or call to action.</p> <p><strong>Example structure:</strong></p> <pre><code>## The Core Problem <p>[Two to four sentences setting up the problem.]</p> <h2>Why Current Solutions Fail</h2> <p>[Analysis of alternatives.]</p> <h3>Specific failure mode 1</h3> <p>[Detail.]</p> <h3>Specific failure mode 2</h3> <p>[Detail.]</p> <h2>The Alternative</h2> <p>[Your approach.]</p> <h2>Conclusion</h2> <p>[Summary and next step.]</p> <p></code></pre></p> <p><strong>Markdown features supported:</strong></p> <li><code>##</code> and <code>###</code> for section headings</li> <li>Standard paragraphs</li> <li>Bulleted lists with <code>-</code></li> <li>Numbered lists with <code>1.</code>, <code>2.</code>, <code>3.</code></li> <li><code><strong>bold</strong></code> and <code><em>italic</em></code></li> <li><code><a href="url">link text</a></code> for inline links</li> <li><code><img class="img-fluid" src="url" alt="alt text" /></code> for inline images</li> <li>`<code> </code>inline code<code> </code><code> for code references</li> <li>Fenced code blocks with triple backticks</li> <li>Blockquotes with </code>><code></li> <li>Tables (Markdown syntax)</li> <p><strong>Length:</strong> there is no enforced minimum or maximum. Technical posts typically run 800–2,000 words. Tutorials may run 2,000–4,000. News posts may run 300–500. Write as long as the topic requires and no longer.</p> <p><strong>Structure matters more than length.</strong> A well-structured 800-word post reads better than an unstructured 2,000-word post.</p> <hr/> <h2>Adding Images to the Body</h2> <p>In addition to the featured image in the frontmatter, you can add images within the Markdown body. These render as inline images.</p> <p><strong>Syntax:</strong></p> <pre><code><img class="img-fluid" src="/images/screenshot.webp" alt="Alt text describing the image" /> <p></code></pre></p> <p><strong>Alt text matters.</strong> It is read by screen readers (accessibility) and used by search engines. Write meaningful alt text:</p> <pre><code><img class="img-fluid" src="/images/build-output.webp" alt="Dax Pro terminal showing the build command output" /> <p></code></pre></p> <p>Not:</p> <pre><code><img class="img-fluid" src="/images/build-output.webp" alt="image" /> <p></code></pre></p> <p><strong>Image paths:</strong></p> <li><strong>In </code>public/images/<code></strong> — use </code>/images/filename.jpg<code></li> <li><strong>External URLs</strong> — use the full URL: </code>https://example.com/image.jpg<code></li> <p><strong>Supported formats:</strong> </code>.jpg<code>, </code>.png<code>, </code>.webp<code>, </code>.gif<code>, </code>.svg<code>.</p> <p><strong>File size matters.</strong> Images over 500 KB slow the page. Compress before uploading:</p> <li><strong>TinyPNG</strong> (<a href="https://tinypng.com/">tinypng.com</a>) — compresses PNG and JPG</li> <li><strong>Squoosh</strong> (<a href="https://squoosh.app/">squoosh.app</a>) — compresses to WebP</li> <li><strong>ImageOptim</strong> — macOS app for bulk compression</li> <p><strong>Lazy loading:</strong> Dax Pro's templates apply </code>loading="lazy"<code> to images. Images below the fold do not block rendering. Images above the fold (the featured image) use </code>fetchpriority="high"<code> for LCP optimization.</p> <hr/> <h2>Creating a New Post</h2> <h3>Via Decap CMS</h3> <p>1. Open </code>https://yoursite.com/admin/<code></p> <p>2. Click <strong>📝 Posts</strong> in the left sidebar</p> <p>3. Click <strong>New Post</strong> (top-right of the posts list)</p> <p>4. Fill in the fields:</p> <div class="table-responsive"><table> <thead><tr> <th>Field</th> <th>Value</th> <p></tr></thead></p> <tbody> <tr> <td><strong>Title</strong></td> <td>Your post title</td> <p></tr></p> <tr> <td><strong>Description</strong></td> <td>150–160 character summary for SEO</td> <p></tr></p> <tr> <td><strong>Image</strong></td> <td>Optional featured image URL or uploaded file</td> <p></tr></p> <tr> <td><strong>Date</strong></td> <td>Publication date</td> <p></tr></p> <tr> <td><strong>Category</strong></td> <td>Single category name</td> <p></tr></p> <tr> <td><strong>Read Time</strong></td> <td>e.g., </code>5 min read<code></td> <p></tr></p> <tr> <td><strong>Read More Label</strong></td> <td>e.g., </code>Read Analysis<code></td> <p></tr></p> <tr> <td><strong>Author</strong></td> <td>Author name</td> <p></tr></p> <tr> <td><strong>Tags</strong></td> <td>Comma-separated lowercase tags</td> <p></tr></p> <tr> <td><strong>Excerpt</strong></td> <td>100–200 character summary for the listing card</td> <p></tr></p> <tr> <td><strong>Body</strong></td> <td>Markdown body content</td> <p></tr></p> <p></tbody></table></div></p> <p>5. Click <strong>Publish</strong></p> <p>6. The file </code>content/posts/{slug}.md<code> is created</p> <p>7. The host rebuilds and the post appears at </code>/posts/{slug}/<code></p> <p><strong>Slug rules:</strong> the title determines the filename. </code>Why Static Sites Win<code> → </code>why-static-sites-win.md<code> → </code>/posts/why-static-sites-win/<code>. Choose titles that produce the URLs you want.</p> <p><strong>Uploading images:</strong> the Image field can either accept a URL (paste any external image URL) or upload a file. Uploading places the file in </code>public/images/<code> and inserts the path automatically.</p> <h3>Directly in the File System</h3> <p>1. Open </code>content/posts/<code> in VS Code</p> <p>2. Create a new file (e.g., </code>my-new-post.md<code>)</p> <p>3. Paste the frontmatter template:</p> <pre><code>--- <p>layout: "post.dax"</p> <p>title: "My New Post Title"</p> <p>description: "A 150-character summary for search engines."</p> <p>image: "https://images.unsplash.com/photo-1234567890?w=1200&q=80&auto=format&fit=crop"</p> <p>date: 2026-05-12</p> <p>category: "Tutorial"</p> <p>read_time: "5 min read"</p> <p>read_more: "Read Article"</p> <p>author: "Your Name"</p> <p>tags: ["dotnet", "tutorial"]</p> <p>excerpt: "A 150-character summary for the listing card."</p> <hr/> <h2>Opening Section</h2> <p>Body content goes here.</p> <h2>Second Section</h2> <p>More content.</p> <p></code></pre></p> <p>4. Save</p> <p>5. Run </code>dotnet run -- build<code></p> <p>6. The post appears at </code>/posts/my-new-post/<code></p> <p><strong>Pitfalls of direct creation:</strong></p> <li><strong>Filename determines URL.</strong> </code>My New Post.md<code> produces </code>/posts/My New Post/<code> — broken (spaces in URL). Use lowercase with hyphens: </code>my-new-post.md<code>.</li> <li><strong>Date format is strict.</strong> </code>2026-05-12<code>, not </code>May 12, 2026<code>.</li> <li><strong>Frontmatter delimiters are strict.</strong> Line 1 is </code>---<code>, closing delimiter is </code>---<code> followed by a blank line.</li> <hr/> <h2>Editing an Existing Post</h2> <h3>Via Decap CMS</h3> <p>1. Open </code>https://yoursite.com/admin/<code></p> <p>2. Click <strong>📝 Posts</strong></p> <p>3. Click the post to edit</p> <p>4. Update fields or body</p> <p>5. Click <strong>Publish</strong></p> <p>The change commits to Git, the host rebuilds, the post updates within a minute.</p> <h3>Directly in the File System</h3> <p>1. Open </code>content/posts/{slug}.md<code> in VS Code</p> <p>2. Edit the frontmatter or body</p> <p>3. Save</p> <p>4. Run </code>dotnet run -- build<code> (or </code>dotnet run -- start<code> for live preview)</p> <p>5. Commit and push for production</p> <pre><code>git add content/posts/{slug}.md <p>git commit -m "Update post: {title}"</p> <p>git push</p> <p></code></pre></p> <hr/> <h2>Deleting a Post</h2> <h3>Via Decap CMS</h3> <p>1. Open </code>https://yoursite.com/admin/<code></p> <p>2. Click <strong>📝 Posts</strong></p> <p>3. Click the post</p> <p>4. Click the <strong>⋯</strong> menu in the top-right</p> <p>5. Choose <strong>Delete entry</strong></p> <p>6. Confirm</p> <h3>Directly in the File System</h3> <pre><code>rm content/posts/{slug}.md <p>dotnet run -- build</p> <p>git add content/posts/</p> <p>git commit -m "Remove post: {title}"</p> <p>git push</p> <p></code></pre></p> <p>The post disappears from </code>/posts/<code>. Any tags it had that no other post or service uses disappear from </code>/tags/<code>. The homepage Insights section fills the vacancy with the next most recent post.</p> <hr/> <h2>Ordering and the Homepage</h2> <p><strong>Post ordering</strong> in </code>/posts/<code> and on the homepage Insights section is by </code>date<code> in descending order.</p> <p><strong>The homepage Insights section shows three posts.</strong> These are the three posts with the most recent dates.</p> <p><strong>If you have three posts and one is dated 2026-05-01, one 2026-04-01, one 2026-03-01:</strong></p> <li>Homepage Insights shows all three</li> <li></code>/posts/<code> shows all three</li> <p><strong>If you have ten posts and dates range from 2026-05-01 to 2026-01-01:</strong></p> <li>Homepage Insights shows the three most recent (May, April, March)</li> <li></code>/posts/<code> shows all ten, paginated</li> <p><strong>To feature a specific post on the homepage:</strong></p> <p>1. Change its </code>date<code> to a more recent value than the current three</p> <p>2. Rebuild</p> <p>The post appears in the top three.</p> <p><strong>To remove a post from the homepage Insights:</strong></p> <p>1. Change its </code>date<code> to an older value</p> <p>2. Rebuild</p> <p>The post falls out of the top three.</p> <p><strong>You cannot pin a post without affecting its date.</strong> The homepage Insights is purely date-driven. If you want to highlight a post that is not the most recent, the workaround is to update its date to be more recent.</p> <hr/> <h2>Draft Workflow</h2> <p>Dax Pro does not have built-in draft support. There is no </code>draft: true<code> field that hides a post from the build. If a post is in </code>content/posts/<code>, it is published.</p> <p><strong>For draft work, use one of these patterns:</strong></p> <p><strong>Pattern 1: Local drafts.</strong> Write the post in </code>content/posts/draft-post.md<code> while working. Do not commit the file to Git until it is ready. Push when ready.</p> <p><strong>Pattern 2: Archive folder.</strong> Create </code>_archive/drafts/<code> outside </code>content/<code>. Write posts there. Move to </code>content/posts/<code> when ready to publish.</p> <p><strong>Pattern 3: Feature branch.</strong> Create a Git branch for the draft. Work on the branch. Merge to </code>main<code> when ready. On Netlify, </code>main<code> deploys; other branches get previews (preview URLs) but not production deployment.</p> <p><strong>Pattern 4: Future date.</strong> Set the date to a future value and skip building locally until the date arrives. But: the host still builds on push, so the post is live from the moment it is pushed. This pattern does not actually prevent publication.</p> <p><strong>Recommended: Pattern 1 or 3.</strong> Local drafts are simplest. Feature branches add preview environments.</p> <hr/> <h2>Reading Time Estimation</h2> <p>You determine the </code>read_time<code> value. Here is a formula:</p> <p><strong>1. Count the words in the post body.</strong></p> <li>Word processors, VS Code word count extensions, and online tools all provide this.</li> <li>Do not count the frontmatter (it is not shown to readers).</li> <p><strong>2. Divide by reading speed.</strong></p> <li>200–250 words per minute for technical content</li> <li>250–300 words per minute for prose</li> <li>150 words per minute for very technical, code-heavy content</li> <p><strong>3. Round up.</strong></p> <li>3.2 minutes → "4 min read"</li> <li>4.8 minutes → "5 min read"</li> <p><strong>Example:</strong></p> <p>A post has 1,500 words of technical content. Divide by 220:</p> <pre><code>1500 / 220 = 6.8 minutes <p></code></pre></p> <p>Round up to 7. Set </code>read_time: "7 min read"<code>.</p> <p><strong>Add time for multimedia:</strong></p> <li>Each code block: +15 seconds</li> <li>Each image: +10 seconds</li> <li>Each diagram: +30 seconds</li> <p>If the 1,500-word post has 5 code blocks and 3 images:</p> <pre><code>1500 / 220 = 6.8 min <p>5 code blocks × 15 sec = 75 sec = 1.25 min</p> <p>3 images × 10 sec = 30 sec = 0.5 min</p> <p>Total: 6.8 + 1.25 + 0.5 = 8.55 min</p> <p>Round up: 9 min read</p> <p></code></pre></p> <p>Set </code>read_time: "9 min read"<code>.</p> <p><strong>Be honest.</strong> Under-estimating sets false expectations. Over-estimating discourages clicks. A little buffer (round up) is better than a little optimism.</p> <hr/> <h2>Common Blog Post Mistakes</h2> <p><strong>Mistake 1: Uppercase tags.</strong></p> <pre><code>tags: ["DotNet", "Razor"] # wrong <p>tags: ["dotnet", "razor"] # correct</p> <p></code></pre></p> <p>Uppercase tags break the tag page links.</p> <p><strong>Mistake 2: Spaces in tags.</strong></p> <pre><code>tags: ["content migration"] # wrong <p>tags: ["content-migration"] # correct</p> <p></code></pre></p> <p>Spaces break URL slugs.</p> <p><strong>Mistake 3: Missing </code>date<code>.</strong></p> <p>Without a date, the post is treated as having the oldest possible date. It appears last in the listing and never on the homepage Insights.</p> <p><strong>Mistake 4: Invalid date format.</strong></p> <pre><code>date: 05/12/2026 # wrong <p>date: May 12, 2026 # wrong</p> <p>date: 2026-05-12 # correct</p> <p></code></pre></p> <p>The date must be </code>YYYY-MM-DD<code>.</p> <p><strong>Mistake 5: Spaces in the filename.</strong></p> <pre><code># File: My New Post.md → /posts/My New Post/ (broken URL) <h1>File: my-new-post.md → /posts/my-new-post/ (correct)</h1> <p></code></pre></p> <p>Always lowercase, always hyphens, no spaces.</p> <p><strong>Mistake 6: Duplicate slugs.</strong></p> <p>Two posts named "Decap CMS Guide" and "Decap CMS Guide!" both become </code>decap-cms-guide.md<code> (or similar) — one overwrites the other. Ensure unique titles or unique filenames.</p> <p><strong>Mistake 7: Bad image URLs.</strong></p> <p>If </code>image: "https://example.com/broken.jpg"<code> points to a 404, the listing card shows a broken image. Test the URL in a browser before publishing.</p> <p><strong>Mistake 8: Very long title.</strong></p> <p></code>Why Modern Enterprises Are Abandoning Dynamic WordPress for .NET SSG and What It Means for the Future<code> becomes a very long slug. Shorten the title or accept the long URL.</p> <p><strong>Mistake 9: Excerpt too long.</strong></p> <p>Excerpts over 200 characters get truncated on listing cards, sometimes mid-word. Keep excerpts under 200 characters.</p> <p><strong>Mistake 10: Forgetting the excerpt.</strong></p> <p>Without an excerpt, the listing card shows the description. This is usually fine. But if the description is written for SEO (starts with "Learn how..." or "Discover the..."), it reads awkwardly on a listing card. Write an excerpt or write the description as an excerpt-style summary.</p> <p><strong>Mistake 11: Changing a post's date without rebuilding.</strong></p> <p>Changing the date in the frontmatter affects ordering, but only after a rebuild. If the date changed and the order did not, you forgot to build.</p> <p><strong>Mistake 12: Placing the post in the wrong folder.</strong></p> <p></code>content/posts-archive/old-post.md<code> is not </code>content/posts/<code>. Files outside </code>content/posts/<code> are not part of the posts collection. They render at their own URL but do not appear in </code>/posts/<code>.</p> <hr/> <h2>Verifying a Post Page</h2> <p>After creating or editing a post, verify:</p> <p>1. <strong>URL works</strong> — navigate to </code>/posts/{slug}/<code>. The page loads.</p> <p>2. <strong>Title</strong> — browser tab and H1 match.</p> <p>3. <strong>Breadcrumb</strong> — shows "Home / Insights / Title".</p> <p>4. <strong>Description</strong> — view source, confirm </code><meta name="description"><code> matches.</p> <p>5. <strong>Featured image</strong> — if set, appears as the post cover and as the OpenGraph image.</p> <p>6. <strong>Meta line</strong> — shows category, date, author, read time.</p> <p>7. <strong>Body</strong> — all Markdown renders correctly.</p> <p>8. <strong>Tags</strong> — if set, appears at the bottom of the post as clickable chips.</p> <p>9. <strong>Prev/next</strong> — if adjacent posts exist, links appear at the bottom.</p> <p>10. <strong>Listing</strong> — the post appears in </code>/posts/<code> with correct title, excerpt, image, and metadata.</p> <p>11. <strong>Homepage</strong> — if the post is among the three most recent, it appears on the homepage Insights.</p> <p>12. <strong>Social card</strong> — paste the URL into <a href="https://www.opengraph.xyz/">opengraph.xyz</a> or similar tool to see how it appears when shared.</p> <p>13. <strong>Mobile</strong> — resize browser. Text wraps, images scale, body reads cleanly.</p> <p>If the post shows raw YAML in the browser, the frontmatter is malformed. Check the four rules:</p> <li>Line 1 is </code>---<code></li> <li>Closing </code>---` followed by a blank line</li> <li>Quoted values properly closed</li> <li>Unix line endings (LF)</li> <hr/> <h2>What You Have Now</h2> <p>After this chapter, you can:</p> <li>✅ Understand the frontmatter structure of a blog post</li> <li>✅ Write body content in Markdown with headings, lists, images, code</li> <li>✅ Set a featured image for OpenGraph and listing cards</li> <li>✅ Add lowercase tags that create tag pages</li> <li>✅ Write excerpts distinct from descriptions</li> <li>✅ Estimate reading time accurately</li> <li>✅ Create posts via Decap CMS or the file system</li> <li>✅ Edit and delete posts cleanly</li> <li>✅ Control homepage Insights by managing dates</li> <li>✅ Handle drafts via local-only or Git branches</li> <li>✅ Verify post rendering, listing, tags, and social cards</li> <li>✅ Fix the twelve most common blog post mistakes</li> <p></ul>Blog posts are done. The next chapter covers <strong>GitHub Setup</strong> — creating a GitHub account, creating a repository, pushing your project, and creating a personal access token for Netlify and Decap CMS.</p> <p>→ Continue to <a href="/docs/github-setup">GitHub Setup</a></p> </div> </div> </article> </main> <script> (function() { const docsToggle = document.getElementById('dax-docs-toggle'); const docsSidebar = document.querySelector('.dax-docs-sidebar'); const docsOverlay = document.getElementById('dax-docs-overlay'); if (!docsToggle || !docsSidebar) return; const closeDocs = () => { docsToggle.setAttribute('aria-expanded', 'false'); docsSidebar.classList.remove('open'); if (docsOverlay) docsOverlay.classList.remove('open'); document.body.classList.remove('dax-docs-open'); }; docsToggle.addEventListener('click', () => { const isOpen = docsSidebar.classList.contains('open'); if (isOpen) { closeDocs(); } else { docsToggle.setAttribute('aria-expanded', 'true'); docsSidebar.classList.add('open'); if (docsOverlay) docsOverlay.classList.add('open'); document.body.classList.add('dax-docs-open'); } }); if (docsOverlay) docsOverlay.addEventListener('click', closeDocs); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closeDocs(); }); document.querySelectorAll('.dax-docs-nav-link').forEach(link => { link.addEventListener('click', closeDocs); }); })(); </script> <footer class="dax-footer"> <div class="dax-container"> <div class="dax-footer-top"> <div class="dax-footer-brand-col"> <a href="/" class="dax-brand"> <span class="dax-brand-symbol">.NET</span> <span class="dax-brand-name" style="color: #FFFFFF;">DAX<span class="dax-brand-accent">PRO</span></span> </a> <p class="dax-footer-desc">The ultra-luxurious corporate theme designed for high-velocity Microsoft .NET Static Site Generators and Decap CMS. Powered by pure semantic HTML5, CSS3, and modern engineering principles.</p> <div class="dax-footer-compatibility"> <span>Microsoft .NET 8 / 9</span> <span>·</span> <span>Decap CMS 3.x</span> <span>·</span> <span>Pure HTML5</span> </div> </div> <div> <h4 class="dax-footer-col-title">Architecture</h4> <ul class="dax-footer-links"> <li><a href="/services/dotnet-ssg-architecture/" class="dax-footer-link">Static Compilation</a></li> <li><a href="/services/decap-cms-workflow/" class="dax-footer-link">Decap CMS Bridge</a></li> <li><a href="/services/azure-devops-github-actions/" class="dax-footer-link">Azure CI/CD Pipeline</a></li> <li><a href="/services/performance-wcag-audits/" class="dax-footer-link">Security Audits</a></li> <li><a href="/services/legacy-cms-migration/" class="dax-footer-link">Benchmark Telemetry</a></li> </ul> </div> <div> <h4 class="dax-footer-col-title">Resources</h4> <ul class="dax-footer-links"> <li><a href="/docs/" class="dax-footer-link">Documentation</a></li> <li><a href="/posts/" class="dax-footer-link">Technical Insights</a></li> <li><a href="/tags/" class="dax-footer-link">Architecture Directory</a></li> <li><a href="https://dax.axcora.com" class="dax-footer-link">Dax Core Project</a></li> </ul> </div> <div> <h4 class="dax-footer-col-title">Company</h4> <ul class="dax-footer-links"> <li><a href="/about/" class="dax-footer-link">About Dax Pro</a></li> <li><a href="/services/" class="dax-footer-link">Enterprise Services</a></li> <li><a href="/license/" class="dax-footer-link">Commercial License</a></li> <li><a href="/contact/" class="dax-footer-link">Contact Engineering</a></li> </ul> </div> </div> <div class="dax-footer-bottom"> <div>© 2026 Dax Pro. Developed for the Microsoft .NET SSG ecosystem and Decap CMS community. All rights reserved.</div> <div class="dax-footer-bottom-links"> <a href="/privacy/" class="dax-footer-link">Privacy Policy</a> <a href="/terms/" class="dax-footer-link">Terms of Service</a> <a href="/license/" class="dax-footer-link">License</a> </div> </div> </div> </footer> <script src="/js/main.js" defer></script> </body> </html>