VOOZH about

URL: https://deepwiki.com/calevans/staticforge/5.1-content-files-and-frontmatter

⇱ Content Files & Frontmatter | calevans/staticforge | DeepWiki


Loading...
Last indexed: 11 February 2026 (5f6a2a)
Menu

Content Files & Frontmatter

Purpose and Scope

This page documents the structure of content files in StaticForge and the frontmatter metadata system. Content files are text files (Markdown or HTML) that contain both metadata and content. The metadata, defined in a YAML frontmatter block at the top of each file, controls how StaticForge processes, renders, and publishes the file.

For information about how Markdown is converted to HTML, see Markdown Processing. For HTML-specific processing details, see HTML Processing. For details about how URLs are generated from file paths and metadata, see URL Generation.


File Format Overview

StaticForge processes two types of content files:

File TypeExtensionFrontmatter DelimiterContent Format
Markdown.md--- (triple dash)Markdown syntax
HTML.html<!-- --- --> (HTML comment)Raw HTML

Both formats use YAML for frontmatter metadata, but the delimiters differ to accommodate syntax requirements.

Sources: src/Core/FileDiscovery.php145-154


Frontmatter Syntax

Markdown Files

Markdown files use triple-dash delimiters to separate YAML frontmatter from content:


Parsing Implementation: The system uses a regex pattern /^---\s*\n(.*?)\n---\s*\n/s to extract the YAML block, then parses it with Symfony's YAML parser.

Sources: src/Core/FileDiscovery.php156-173 content/guide/quick-start.md137-156

HTML Files

HTML files embed YAML frontmatter within an HTML comment block:


Parsing Implementation: The system uses a regex pattern /^<!--\s*\n---\s*\n(.*?)\n---\s*\n-->\s*\n/s to extract the YAML block from HTML comments.

Sources: src/Core/FileDiscovery.php175-192 content/guide/quick-start.md207-227

Diagram: Frontmatter Parsing Pipeline


Sources: src/Core/FileDiscovery.php30-49 src/Core/FileDiscovery.php136-220


Common Metadata Fields

Core Fields

FieldTypeRequiredPurpose
titlestringYes*Page title used in HTML <title> and headings
descriptionstringRecommendedPage description for meta tags and SEO
urlstringNoExplicit URL override (rarely needed)

*While not technically enforced, title is expected by most templates and features.

Sources: content/development/templates.md78-83 content/guide/quick-start.md139-142

Navigation & Organization

FieldTypePurposeExample
menustringPosition in numbered menus"1.5" or "2.1.3"
categorystringContent category; affects URL structure"blog", "tutorials"
tagsarray or stringContent tags for classification["php", "web"] or "php, web"

Menu Notation: The format is "menu_number.position" or "menu_number.position.subposition". For example:

  • menu: "1.1" - First item in menu 1
  • menu: "2.3" - Third item in menu 2
  • menu: "1.2.1" - First dropdown item under menu 1, position 2

Multiple Menus: To include a page in multiple menus, use comma separation: menu: "1.5, 2.3"

Sources: content/guide/quick-start.md266-296 content/development/features.md419-423

Publishing Control

FieldTypeDefaultPurpose
draftbooleanfalseIf true, file excluded unless SHOW_DRAFTS=true
datestring/datenonePublication date; future dates excluded from build

Draft Behavior: Files with draft: true are skipped during FileDiscovery unless the environment variable SHOW_DRAFTS is set to true.

Future Date Filtering: Files with a date field set to a future date are automatically excluded from the build. The system uses strtotime() to parse dates and compares against time().

Sources: src/Core/FileDiscovery.php92-118 content/guide/configuration.md313-326

Template & Rendering

FieldTypeDefaultPurpose
templatestring"base"Template file to use for rendering
typestringnoneSpecial content type marker

Type Field Values:

  • type: "category" - Marks a category definition file
  • type: "calendar_event" - Marks a calendar event

Sources: content/development/templates.md1-6 content/development/architecture.md45-58

SEO & Discovery

FieldTypeDefaultPurpose
robotsstring"yes"If "no", adds to robots.txt disallow list
search_indexbooleantrueIf false, excludes from search index
og_imagestringnoneOpen Graph image URL/path
herostringnoneHero image path for page header

Sources: src/Features/RobotsTxt/Services/RobotsTxtService.php109-126 src/Features/Search/Services/SearchIndexService.php191-225 content/guide/quick-start.md1-8


Metadata-Driven Behavior

Diagram: Metadata Decision Flow


Sources: src/Core/FileDiscovery.php79-129

File Discovery Filtering

The FileDiscovery service applies multiple filters before adding files to the build:

  1. Extension Filter: Only processes files with registered extensions (.md, .html)
  2. Draft Filter: Checks draft metadata and SHOW_DRAFTS environment variable
  3. Date Filter: Compares date metadata against current timestamp

Implementation:

if (metadata['draft'] === true && !SHOW_DRAFTS) {
 skip file
}

if (metadata['date'] exists && strtotime(metadata['date']) > time()) {
 skip file
}

Sources: src/Core/FileDiscovery.php86-118 tests/Unit/Core/FileDiscoveryFutureDateTest.php46-65

URL Generation

The category field directly affects URL structure:

Without Category:

content/blog/my-post.md → /blog/my-post.html

With Category:

content/blog/my-post.md
frontmatter: category: "tutorials"
→ /tutorials/my-post.html

The category value is slugified (lowercased, special characters removed) and inserted into the URL path. This behavior is handled by FileDiscovery::generateUrl().

Sources: src/Core/FileDiscovery.php222-269

Template Selection

Template selection follows a three-tier priority:

  1. Frontmatter template field: template: "docs" explicitly sets template
  2. Category template: If category: "blog", looks for blog-post.html.twig
  3. Default fallback: Uses base.html.twig

This resolution happens in the TemplateRenderer during the RENDER event.

Sources: content/development/templates.md189-194 content/development/architecture.md146-151

Menu Integration

The menu field is processed by the MenuBuilder feature during the POST_GLOB event. Files with menu values are collected, sorted by position, and converted to HTML navigation.

Sources: content/guide/quick-start.md265-296 content/guide/site-config.md96-108

Search Indexing Control

The search_index field controls whether a page is included in the search index:


The SearchIndexService checks this field during the POST_RENDER event. Additionally, paths can be excluded via configuration in siteconfig.yaml:


Sources: src/Features/Search/Services/SearchIndexService.php191-225 tests/Unit/Features/Search/SearchIndexServiceTest.php73-95

SEO Control

The robots field controls robots.txt inclusion:


Files with robots: no are added to the robots.txt Disallow list by the RobotsTxtService during the POST_GLOB event.

Sources: src/Features/RobotsTxt/Services/RobotsTxtService.php109-126


Discovered Files Data Structure

After parsing, each file is stored in the discovered_files container variable with this structure:


This array is the single source of truth for all features and processing steps. Features access it via:


Sources: src/Core/FileDiscovery.php30-49 content/development/architecture.md39-61


Diagram: Frontmatter Fields and System Components


Sources: src/Core/FileDiscovery.php1-296 src/Features/MenuBuilder/Services/StaticMenuProcessor.php26-99 src/Features/Search/Services/SearchIndexService.php28-78


Example Content Files

Complete Markdown Example


...


**Sources:** <FileRef file-url="https://github.com/calevans/staticforge/blob/5f6a2abf/content/guide/quick-start.md?plain=1#L1-L9" min=1 max=9 file-path="content/guide/quick-start.md">Hii</FileRef> <FileRef file-url="https://github.com/calevans/staticforge/blob/5f6a2abf/content/development/templates.md?plain=1#L1-L8" min=1 max=8 file-path="content/development/templates.md">Hii</FileRef>

### Complete HTML Example

```html
<!--
---
title: "Contact Us"
description: "Get in touch with our team"
menu: "1.5, 2.3"
template: "standard_page"
search_index: false
robots: yes
---
-->

<h1>Contact Us</h1>

<p>We'd love to hear from you!</p>

<form action="/contact" method="post">
 <label for="name">Name:</label>
 <input type="text" id="name" name="name" required>
 
 <label for="email">Email:</label>
 <input type="email" id="email" name="email" required>
 
 <label for="message">Message:</label>
 <textarea id="message" name="message" required></textarea>
 
 <button type="submit">Send</button>
</form>

Sources: content/guide/quick-start.md207-227


Best Practices

Metadata Minimalism

Only include metadata fields you actually need. At minimum, set title:


Additional fields should be added based on specific requirements (navigation, categorization, SEO).

Date Format

Use ISO 8601 date format for consistency:


Or full timestamp:


The system uses strtotime() for parsing, which supports various formats, but ISO 8601 is recommended for clarity.

Tag Format

Tags can be specified as either an array or a comma-separated string:


The SearchIndexService normalizes both formats to arrays for processing.

Sources: src/Features/Search/Services/SearchIndexService.php47-51

Menu Position Spacing

Use decimal spacing in menu positions to allow insertion of new items without renumbering:


Sources: content/guide/quick-start.md278-283