Managing Markdown content, YAML configuration, and data directory structure in MintyFlaskSSG

Content Management

Introduction

MintyFlaskSSG uses a Markdown-first approach for content creation combined with YAML files for structured data and configuration. This separation keeps your content clean and readable whilst allowing sophisticated site configuration and internationalisation.

This guide covers how to create and organise content in MintyFlaskSSG, from writing blog posts through to configuring site-wide settings. You'll learn about the data directory structure, Markdown frontmatter, content types, and the central site.yaml configuration file.

Why This Matters

Proper content organisation makes your site maintainable and scalable. Understanding where files live and how they're processed helps you create content efficiently and troubleshoot issues when they arise.

Key Concepts

Markdown Files: Your content lives in .md files with YAML frontmatter for metadata. MintyFlaskSSG uses Flask-FlatPages to process these files.

Data Directory: All content, configuration, and photos live in a single data/ directory outside your application code. This separates content from code and simplifies deployment.

Content Types: MintyFlaskSSG distinguishes between articles (blog posts with dates, categories, tags) and pages (static content like About or Contact).

Post Status: Articles can be published, draft, or review, controlling whether they appear in production builds.


Data Directory Structure

MintyFlaskSSG organises all content in a single data directory, typically located at ../data relative to your application. This structure keeps content separate from code:

data/
├── markdown/
│   ├── articles/           # Blog posts
│   │   ├── my-first-post.md
│   │   ├── another-post.md
│   │   └── draft-post.md
│   └── pages/
│       └── en/             # Static pages by language
│           ├── about.md
│           ├── contact.md
│           └── privacy.md
├── yaml/
│   ├── site.yaml          # Site configuration and i18n
│   ├── docs_nav.yaml      # Documentation navigation (if applicable)
│   └── reading_list.yaml  # Custom data files
└── photos/
    ├── photos_original/   # Original photo library
    └── photos_processed/  # Generated thumbnails and sizes

Directory Purposes

markdown/: All Markdown content files organised by type

markdown/articles/: Blog posts and dated content

markdown/pages/en/: Static pages organised by language code

yaml/: Configuration and structured data files

photos/: Photo library and processed images (see Photo Galleries guide)

Configuration

The data directory location is configured in config.py:

class Config:
    DATA_DIR = None  # Auto-discovers to ../data

    # These paths derive from DATA_DIR automatically:
    FLATPAGES_ROOT = None  # → {DATA_DIR}/markdown
    SITE_YAML_FILEPATH = None  # → {DATA_DIR}/yaml/site.yaml

Override in config_local.py if you use a different structure:

class LocalConfig(Config):
    DATA_DIR = "../content"  # Custom location

Markdown Content

Content Types

MintyFlaskSSG handles two primary content types:

Articles (Blog Posts):

  • Located in data/markdown/articles/
  • Include date, category, tags, and status
  • Appear in blog index, archives, category/tag pages
  • Support draft and review workflows

Pages (Static Content):

  • Located in data/markdown/pages/en/ (or other language codes)
  • Simpler metadata structure
  • Accessed via direct URLs (/about, /contact)
  • Language-specific organisation

Creating an Article

Create a new file in data/markdown/articles/ with a descriptive filename:

---
title: "Getting Started with MintyFlaskSSG"
date: 2025-11-25
category: "tutorials"
tags: ["flask", "static-sites", "python"]
status: "published"
summary: "A comprehensive guide to building your first static site with MintyFlaskSSG"
author: "Your Name"
---

# Getting Started with MintyFlaskSSG

Your article content begins here. Write using standard Markdown syntax.

## Subheadings Work

As do **bold**, _italic_, and `code` formatting.

And much more...

Creating a Page

Create a new file in data/markdown/pages/en/ (adjust language code as needed):

---
title: "About Us"
template: "pages/page.html"
---

# About Us

Static page content here. Pages use simpler frontmatter than articles.

Pages are perfect for:

- About pages
- Contact information
- Privacy policies
- Terms of service

Frontmatter Metadata

Frontmatter is the YAML block at the top of each Markdown file, delimited by ---. It defines metadata about your content.

Article Frontmatter

Required and common fields for blog posts:

---
# Required fields
title: "Your Post Title"
date: 2025-11-25
status: "published" # published, draft, or review

# Common fields
category: "category-name"
tags: ["tag1", "tag2", "tag3"]
summary: "Brief description for listings and SEO"
author: "Author Name"

# Optional fields
template: "custom-template.html" # Override default template
featured: true
cover_image: "path/to/image.jpg"
---

Field Descriptions

title (required): Article title displayed in listings and page header

date (required): Publication date in YYYY-MM-DD format, used for sorting and archives

status (required): Publication status controlling visibility

  • published: Appears on live site and in production builds
  • draft: Only visible in development mode
  • review: Visible in development, marked for review

category: Single category for grouping related content (e.g., "tutorials", "news", "announcements")

tags: List of tags for cross-referencing content (e.g., ["python", "flask", "web-development"])

summary: Brief description shown in article listings and used for meta descriptions

author: Content author name

template: Override default template (advanced usage)

featured: Mark article as featured (boolean, used by themes)

cover_image: Path to cover image (implementation depends on theme)

Page Frontmatter

Pages use simpler frontmatter:

---
title: "Page Title"
template: "pages/page.html" # Optional
description: "Page description for SEO" # Optional
---

Date Format

Dates must use ISO 8601 format: YYYY-MM-DD

date: 2025-11-25  # Correct
date: 25-11-2025  # Wrong
date: 11/25/2025  # Wrong

MintyFlaskSSG parses dates using Python's datetime, which expects ISO format.


Post Status Workflow

The status field controls content visibility across environments:

Status Values

published: Content is live

  • Appears in production builds
  • Shows in article listings, archives, categories, tags
  • Indexed by search
  • Visible in development and production

draft: Work in progress

  • Only visible in development mode
  • Hidden from production builds
  • Not in listings or search
  • Perfect for content you're still writing

review: Ready for review

  • Visible in development mode
  • Hidden from production builds
  • Marked specially in development UI
  • Ready for editorial review before publishing

Workflow Example

# 1. Start writing
status: "draft"

# 2. Ready for review
status: "review"

# 3. Approved and published
status: "published"

Environment Behaviour

Development mode (python run.py):

  • Shows all content regardless of status
  • Drafts and review posts clearly marked
  • Allows preview before publishing

Production builds (flask freeze):

  • Only includes status: "published" content
  • Drafts and review posts excluded
  • Static site contains only public content

The site.yaml File

The site.yaml file in data/yaml/ contains site-wide configuration and internationalisation data. It's the central hub for content that appears across multiple pages.

File Location

data/yaml/site.yaml

Configured automatically via SITE_YAML_FILEPATH when DATA_DIR is set.

Structure Overview

site.yaml contains two main sections:

site:
  # Site configuration

i18n:
  # Internationalisation data by language

Site Configuration

The site section holds site-wide settings:

site:
  name: "Your Site Name"
  tagline: "Your site tagline"
  description: "Site description for SEO"
  url: "https://yoursite.com"

  contact_details:
    main:
      name: "Your Company"
      email: "hello@example.com"
      phone: "+1234567890"
      address:
        street: "123 Main Street"
        city: "Your City"
        postcode: "12345"
        country: "Your Country"

  social_media:
    twitter: "yourusername"
    github: "yourusername"
    linkedin: "yourcompany"

  analytics:
    google_analytics_id: "UA-XXXXXXXXX-X"

  features:
    enable_search: true
    enable_comments: false
    enable_newsletter: true

Access in templates:

{{ site.name }}
{{ site.contact_details.main.email }}
{{ site.social_media.twitter }}

Note

The exact structure of `site` configuration is flexible—organise it to match your theme's needs. The example above shows common patterns.

Internationalisation (i18n)

The i18n section provides language-specific content:

i18n:
  en:
    site_name: "My Site"
    site_tagline: "Building the future"

    navigation:
      - nav-item:
          title: "Home"
          url: "/"
      - nav-item:
          title: "Blog"
          url: "/articles/all"
      - nav-item:
          title: "About"
          url: "/about"

    sections:
      section_footer:
        copyright:  2025 Your Company"
        links:
          - link:
              title: "Privacy Policy"
              url: "/privacy"
          - link:
              title: "Terms"
              url: "/terms"

  nl:
    site_name: "Mijn Site"
    site_tagline: "De toekomst bouwen"

    navigation:
      - nav-item:
          title: "Home"
          url: "/"
      - nav-item:
          title: "Blog"
          url: "/articles/all"
      - nav-item:
          title: "Over Ons"
          url: "/over-ons"

Access in templates:

<title>{{ i18n_yaml.site_name }}</title>

<nav>
  {% for nav in navigation %}
    {% set item = nav['nav-item'] %}
    <a href="{{ item.url }}">{{ item.title }}</a>
  {% endfor %}
</nav>

The system automatically injects i18n_yaml containing the current language's data into all templates.

Note

For comprehensive internationalisation documentation, including language detection and content organisation, see the **Internationalisation** guide.

Markdown Syntax Support

MintyFlaskSSG uses Flask-FlatPages with Python-Markdown and several extensions. You can write rich content using standard Markdown plus enhanced features.

Basic Markdown

Standard Markdown syntax works as expected:

# Heading 1

## Heading 2

### Heading 3

**Bold text**
_Italic text_
`Inline code`

- Unordered list item
- Another item
  - Nested item

1. Ordered list item
2. Another item

[Link text](https://example.com)
![Image alt text](path/to/image.jpg)

> Blockquote text
> Continues here

Code Blocks

Fenced code blocks with syntax highlighting:

```python
def hello_world():
    return "Hello, World!"
```

```javascript
function helloWorld() {
  return "Hello, World!";
}
```

```bash
python run.py
```

Syntax highlighting uses Pygments with the configuration from config.py:

FLATPAGES_MARKDOWN_EXTENSION_CONFIGS = {
    'codehilite': {
        'css_class': 'highlight',
        'linenums': False
    }
}

Tables

Markdown tables with alignment:

| Feature      | Supported | Notes               |
| ------------ | --------- | ------------------- |
| Tables       | ✓         | Full support        |
| Alignment    | ✓         | Left, center, right |
| Complex data | ✓         | Multiple columns    |

| Left aligned | Center aligned | Right aligned |
| :----------- | :------------: | ------------: |
| Text         |      Text      |          Text |
| More text    |   More text    |     More text |

Admonitions

Custom admonition blocks for callouts:

:::note
This is a note admonition with important information.
:::

:::warning
This is a warning about something critical.
:::

:::tip
This is a helpful tip for users.
:::

:::important
This is important information that needs attention.
:::

:::devnote
Development notes only visible in development mode.
:::

Admonitions render as styled blocks matching your theme's design system.

Definition Lists

Term 1
: Definition of term 1

Term 2
: Definition of term 2
: Another definition of term 2

Footnotes

Here's a sentence with a footnote[^1].

Another sentence with a footnote[^2].

[^1]: This is the first footnote.

[^2]: This is the second footnote.

Abbreviations

The HTML specification is maintained by the W3C.

_[HTML]: Hyper Text Markup Language
_[W3C]: World Wide Web Consortium

Table of Contents

Add a table of contents using the [TOC] marker:

# Article Title

[TOC]

## Section 1

Content here...

## Section 2

More content...

Configuration in config.py:

FLATPAGES_MARKDOWN_EXTENSION_CONFIGS = {
    'toc': {
        'anchorlink': True  # Makes headings clickable
    }
}

Note

For complete Markdown extension documentation and configuration options, see **Markdown Extensions** in the Reference section.

Working with Content

File Naming

Use descriptive, URL-friendly filenames:

Good:

  • getting-started-with-flask.md
  • 2025-year-in-review.md
  • understanding-python-decorators.md

Avoid:

  • post1.md (not descriptive)
  • My Great Post!.md (spaces and special characters)
  • draft.md (too generic)

The filename becomes the URL slug: getting-started-with-flask.md/articles/getting-started-with-flask

Organising Articles

All articles live in data/markdown/articles/ as flat files. MintyFlaskSSG automatically organises them by:

  • Date: Archive pages by year and month
  • Category: Category listing pages
  • Tags: Tag listing pages
  • Status: Published vs draft/review filtering

You don't need to create subdirectories—the system handles organisation through frontmatter metadata.

Organising Pages

Pages are organised by language in subdirectories:

data/markdown/pages/
├── en/
│   ├── about.md
│   ├── contact.md
│   └── privacy.md
└── nl/
    ├── over-ons.md
    ├── contact.md
    └── privacy.md

URL structure: /about (English), /nl/over-ons (Dutch)

Reading Time

MintyFlaskSSG automatically calculates reading time based on word count:

# Configuration
READING_SPEED_WPM = 225  # Words per minute

Override per article:

---
title: "Technical Deep Dive"
wpm: 175 # Slower reading for technical content
---

Access in templates:

{{ page.html|calculate_reading_time }} min read

Content Preview

During development, start the Flask server to preview content:

python run.py

Navigate to:

  • /articles/all - All published articles
  • /articles/drafts - Draft articles (dev only)
  • /articles/review - Review articles (dev only)
  • /articles/your-post-slug - Individual posts
  • /about - Static pages

Changes to Markdown files reload automatically with FLATPAGES_AUTO_RELOAD = True.


Content Best Practices

Frontmatter

Do:

✓ Always include required fields (title, date, status)
✓ Use ISO date format (YYYY-MM-DD)
✓ Write descriptive summaries for SEO
✓ Tag content consistently
✓ Use meaningful category names

Don't:

✗ Leave status unset (defaults may vary)
✗ Use spaces in category or tag names
✗ Forget to set date on published posts
✗ Overuse tags (3-5 tags is typically sufficient)
✗ Use special characters in metadata values

Content Organisation

Do:

✓ Use descriptive, URL-friendly filenames
✓ Keep article slugs short but meaningful
✓ Organise pages by language
✓ Use consistent category naming
✓ Tag related content for cross-referencing

Don't:

✗ Create subdirectories in articles/ (use categories instead)
✗ Use generic filenames like post1.md
✗ Mix languages in the same pages/ subdirectory
✗ Rename published articles (breaks URLs)
✗ Use dates in filenames (redundant with frontmatter)

Writing

Do:

✓✓ Write clear, descriptive summaries
✓ Use headings to structure long content
✓ Include code examples with syntax highlighting
✓ Add admonitions for important notes
✓ Test links before publishing

Don't:

✗ Use HTML when Markdown suffices
✗ Forget to preview content before publishing
✗ Hardcode URLs (use relative paths)
✗ Embed large images without optimisation
✗ Write without considering reading time

site.yaml Management

Do:

✓ Keep structure organised and well-commented
✓ Use consistent indentation (2 spaces)
✓ Test YAML syntax with a validator
✓ Store all shared content here
✓ Use meaningful key names

Don't:

✗ Store content-specific data (use frontmatter instead)
✗ Mix tabs and spaces
✗ Use complex nested structures unnecessarily
✗ Hardcode sensitive data (use environment variables)
✗ Forget to update all languages in i18n


Common Use Cases

Use Case 1: Publishing a Blog Post

Create and publish a new article:

---
title: "My First Post"
date: 2025-11-25
category: "general"
tags: ["first-post", "introduction"]
status: "published"
summary: "Welcome to my new blog built with MintyFlaskSSG"
author: "Your Name"
---

# My First Post

Content here...

Save to data/markdown/articles/my-first-post.md. The post appears immediately at /articles/my-first-post.

Use Case 2: Draft Workflow

Start with draft status:

status: "draft"

Write and preview in development mode. When ready for review:

status: "review"

After approval, publish:

status: "published"

Use Case 3: Adding a Static Page

Create data/markdown/pages/en/services.md:

---
title: "Our Services"
---

# Our Services

We offer comprehensive web development services...

Access at /services (English) or localise to other languages by creating equivalent files in other language directories.

Use Case 4: Categorised Content Series

Create a tutorial series with consistent category:

# Tutorial 1
category: "flask-tutorial"
tags: ["flask", "python", "tutorial", "beginner"]

# Tutorial 2
category: "flask-tutorial"
tags: ["flask", "python", "tutorial", "intermediate"]

# Tutorial 3
category: "flask-tutorial"
tags: ["flask", "python", "tutorial", "advanced"]

Readers can view all tutorials at /articles/category/flask-tutorial.

Use Case 5: Featured Content

Mark important posts as featured:

featured: true

Themes can display featured posts prominently on the homepage or in special sections.


Troubleshooting

Problem: Article Not Appearing

Symptoms:

  • Article exists but doesn't show in listings
  • 404 error when accessing article URL
  • Article missing from category/tag pages

Diagnosis: Check the article's status:

status: "published" # Must be "published"

Verify the file is in the correct location:

data/markdown/articles/your-article.md

Solution: Ensure status: "published" and the file is in articles/ directory. Restart development server if changes aren't reflected.

Problem: Date Parsing Error

Symptoms:

  • Error message about date format
  • Article displays but with incorrect date
  • Archives don't show article

Diagnosis: Check date format in frontmatter:

date: 2025-11-25  # Correct: ISO format
date: 25/11/2025  # Wrong: incorrect format

Solution: Use ISO 8601 date format: YYYY-MM-DD

Problem: site.yaml Not Loading

Symptoms:

  • Template variables empty or missing
  • Navigation not rendering
  • 500 error on page load

Diagnosis: Check YAML syntax:

# Install yamllint if needed
pip install yamllint

# Validate syntax
yamllint data/yaml/site.yaml

Solution: Fix YAML syntax errors. Common issues:

  • Inconsistent indentation (use 2 spaces)
  • Missing colons after keys
  • Unquoted strings containing special characters

Problem: Markdown Not Rendering

Symptoms:

  • Raw Markdown visible in browser
  • HTML tags escaped
  • Images not displaying

Diagnosis: Check if content is wrapped in template output:

{# Correct #}
{{ page.html|safe }}

{# Wrong - escapes HTML #}
{{ page.html }}

Solution: Ensure templates use |safe filter when outputting Markdown HTML. Check your theme's templates.

Problem: Reading Time Not Calculating

Symptoms:

  • Reading time shows as 0 or blank
  • Template error about calculate_reading_time

Diagnosis: Verify filter is registered:

# In app/__init__.py
from app.utils import calculate_reading_time
app.jinja_env.filters['calculate_reading_time'] = calculate_reading_time

Solution: Ensure the filter is properly registered. Check application initialisation code.


Quick Reference

Article Frontmatter Template

---
title: "Article Title"
date: 2025-11-25
status: "published"
category: "category-name"
tags: ["tag1", "tag2", "tag3"]
summary: "Brief description"
author: "Author Name"
---

Page Frontmatter Template

---
title: "Page Title"
template: "pages/page.html"
---

Status Values

Status Development Production Use For
published ✓ Visible ✓ Visible Published content
draft ✓ Visible ✗ Hidden Work in progress
review ✓ Visible ✗ Hidden Ready for review

Configuration Keys

# Content directories
POSTS_DIR = "articles"
PAGES_DIR = "pages"
FLATPAGES_ROOT = "{DATA_DIR}/markdown"

# Content processing
FLATPAGES_AUTO_RELOAD = True
FLATPAGES_EXTENSION = '.md'
READING_SPEED_WPM = 225

# Site configuration
SITE_YAML_FILEPATH = "{DATA_DIR}/yaml/site.yaml"

Conclusion

MintyFlaskSSG's content management system balances simplicity with power. Markdown files keep your content readable and version-controllable, whilst YAML frontmatter provides rich metadata. The data directory structure organises everything logically outside your application code.

Key takeaways:

  1. Markdown-first: Write content in clean Markdown with YAML frontmatter for metadata rather than mixing content with code.

  2. Status workflow: Use draft, review, and published statuses to manage content lifecycle without complex publishing systems.

  3. Separation of concerns: Content lives in data/, code lives in app/—keep them separate for maintainability.

  4. site.yaml centralisation: Shared content, navigation, and configuration belong in site.yaml rather than scattered across templates.

  5. URL-friendly naming: Use descriptive, hyphenated filenames that become clean URLs automatically.

Start with the basics—create a few articles and pages—then explore advanced features like categories, tags, and internationalisation as your site grows.