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 buildsdraft: Only visible in development modereview: 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
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
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)

> 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
Working with Content
File Naming
Use descriptive, URL-friendly filenames:
Good:
getting-started-with-flask.md2025-year-in-review.mdunderstanding-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:
-
Markdown-first: Write content in clean Markdown with YAML frontmatter for metadata rather than mixing content with code.
-
Status workflow: Use draft, review, and published statuses to manage content lifecycle without complex publishing systems.
-
Separation of concerns: Content lives in
data/, code lives inapp/—keep them separate for maintainability. -
site.yaml centralisation: Shared content, navigation, and configuration belong in site.yaml rather than scattered across templates.
-
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.