--- version: "1.0" date: "2025-01-15" author: "DWS Foldsite Team" title: "Template System" description: "Master Foldsite's powerful cascading template system" summary: "Learn how Foldsite's hierarchical template discovery works - from simple defaults to sophisticated, context-aware layouts." quick_tips: - "base.html is required and wraps every page on your site" - "Templates cascade from specific to general - Foldsite uses the first match" - "Use Jinja2 syntax for dynamic content and logic" --- # Template System Foldsite's template system is both powerful and intuitive. Templates are HTML files with **Jinja2** syntax that define how your content is displayed. ## The Template Hierarchy Foldsite uses a **cascading template discovery system**. When rendering a page, it searches for templates from **most specific** to **most general**, using the first match found. ###Understanding Template Priority Think of it like CSS specificity - more specific selectors override general ones: ``` Specific file > Type + Extension > Type + Category > Generic type ``` ### Example: Rendering `content/blog/my-post.md` Foldsite searches in this order: 1. `templates/blog/my-post.html` ← Exact file match 2. `templates/blog/__file.md.html` ← Markdown files in blog/ 3. `templates/blog/__file.document.html` ← Document files in blog/ 4. `templates/__file.md.html` ← All markdown files 5. `templates/__file.document.html` ← All document files 6. `templates/__file.html` ← Any file 7. **First match wins!** ## Required Template: base.html **Every Foldsite project MUST have a `base.html`** in the templates root. This wraps every page on your site. ### Minimal base.html ```html My Site {% for style in styles %} {% endfor %} {{ content|safe }} ``` ### Available Variables in base.html - `{{ content|safe }}` - Rendered page content (required) - `{{ styles }}` - List of CSS files to load - `{{ currentPath }}` - Current page path - `{{ metadata }}` - Frontmatter from markdown files - All template helper functions ### Complete base.html Example ```html {% if metadata and metadata.title %}{{ metadata.title }} - {% endif %}My Site {% for style in styles %} {% endfor %}
{{ content|safe }}
``` ## Template Naming Patterns ### File Templates Templates for individual files use this pattern: **Pattern:** `__file.{extension}.html` or `__file.{category}.html` **Examples:** - `__file.md.html` - All markdown files - `__file.document.html` - All document types (md, txt, html) - `__file.image.html` - Individual image pages (rare) - `__file.jpg.html` - Specific to JPG files ### Folder Templates Templates for directory views: **Pattern:** `__folder.{category}.html` **Examples:** - `__folder.md.html` - Folders containing mostly markdown - `__folder.image.html` - Photo gallery folders - `__folder.html` - Generic folder view ### Specific Page Templates Override for specific pages: **Pattern:** `{filename}.html` **Examples:** - `index.html` - Only for index.md - `about.html` - Only for about.md - `contact.html` - Only for contact.md ## File Categories Foldsite automatically categorizes files by extension: | Category | Extensions | Template | Use Case | |----------|-----------|----------|----------| | **document** | `.md`, `.txt` | `__file.document.html` | Text content | | **image** | `.jpg`, `.png`, `.gif` | `__file.image.html` | Photos | | **multimedia** | `.mp4`, `.mp3` | `__file.multimedia.html` | Video/audio | | **other** | Everything else | `__file.other.html` | Downloads | Files can have **multiple categories**. For example, `.md` files are both `md` and `document`. ## Template Variables Every template receives these variables: ### Always Available - `content` - Rendered HTML content - `styles` - List of CSS file paths - `currentPath` - Path relative to content root - `metadata` - Frontmatter dict (for markdown files) ### Markdown Files Only For `.md` files, `metadata` contains frontmatter: ```markdown --- title: "My Blog Post" date: "2025-01-15" author: "Your Name" tags: ["python", "web"] --- # Content here... ``` Access in templates: ```jinja

{{ metadata.title }}

By {{ metadata.author }}

{% for tag in metadata.tags %} {{ tag }} {% endfor %} ``` ## Template Helpers Foldsite provides powerful helper functions accessible in all templates: ### Content Discovery ```jinja {# Get folder contents #} {% for file in get_folder_contents(currentPath) %} {{ file.name }} {% endfor %} {# Get sibling files #} {% for sibling in get_sibling_content_files(currentPath) %} {{ sibling[0] }} {% endfor %} {# Get sibling folders #} {% for folder in get_sibling_content_folders(currentPath) %} {{ folder[0] }} {% endfor %} ``` ### Blog Functions ```jinja {# Recent blog posts #} {% for post in get_recent_posts(limit=5, folder='blog') %}

{{ post.title }}

{% endfor %} {# Posts by tag #} {% for post in get_posts_by_tag('python', limit=10) %} {{ post.title }} {% endfor %} {# All tags #} {% for tag in get_all_tags() %} {{ tag.name }} ({{ tag.count }}) {% endfor %} ``` ### Navigation ```jinja {# Breadcrumbs #} {% for crumb in generate_breadcrumbs(currentPath) %} {% if not crumb.is_current %} {{ crumb.title }} {% else %} {{ crumb.title }} {% endif %} {% if not loop.last %} / {% endif %} {% endfor %} {# Navigation menu #} {% for item in get_navigation_items(max_items=10) %} {{ item.title }} {% endfor %} {# Related posts #} {% for post in get_related_posts(currentPath, limit=3) %} {{ post.title }} {% endfor %} ``` See [Template Helpers Reference](template-helpers.md) for complete documentation. ## Cascading Through Directories Templates cascade down through your directory structure. Place templates in subdirectories to override for specific sections. ### Example Structure ``` templates/ ├── base.html # Site-wide wrapper ├── __file.md.html # Default for all markdown ├── __folder.image.html # Default for galleries ├── blog/ │ ├── __file.md.html # Override for blog posts │ └── __folder.md.html # Override for blog index └── photos/ └── __folder.image.html # Override for photo galleries ``` ### How It Works **Rendering `content/blog/my-post.md`:** 1. Looks in `templates/blog/` first 2. Finds `blog/__file.md.html` ← **Uses this** 3. Never checks root `__file.md.html` **Rendering `content/projects/project.md`:** 1. Looks in `templates/projects/` first 2. Doesn't find specific template 3. Falls back to `templates/__file.md.html` ← **Uses this** ## Practical Examples ### Simple Blog Post Template `templates/__file.md.html`: ```html
{% if metadata %}

{{ metadata.title }}

{% if metadata.author %}

By {{ metadata.author }}

{% endif %}
{% endif %}
{{ content|safe }}
{% if metadata and metadata.tags %} {% endif %}
``` ### Blog Index Template `templates/__folder.md.html`: ```html

Recent Posts

{% for post in get_recent_posts(limit=10, folder=currentPath) %}

{{ post.title }}

{% if post.metadata.description %}

{{ post.metadata.description }}

{% endif %}
{% endfor %}
``` ### Photo Gallery Template `templates/__folder.image.html`: ```html ``` ## Error Pages Custom error template: `templates/__error.html` ```html

Error {{ error_code }}

{{ error_message }}

{{ error_description }}

Return Home
``` Variables available: - `error_code` - HTTP status code (404, 500, etc.) - `error_message` - Short message ("Not Found") - `error_description` - Detailed description ## Jinja2 Syntax Quick Reference ### Variables ```jinja {{ variable }} {{ metadata.title }} {{ post.url }} ``` ### Filters ```jinja {{ content|safe }} {# Don't escape HTML #} {{ title|upper }} {# Uppercase #} {{ date|default('Unknown') }} {# Default value #} {{ items|length }} {# Count items #} ``` ### Conditionals ```jinja {% if metadata %}

{{ metadata.title }}

{% endif %} {% if metadata and metadata.title %} ... {% elif metadata %} ... {% else %} ... {% endif %} ``` ### Loops ```jinja {% for item in items %}

{{ item }}

{% endfor %} {% for key, value in metadata.items() %}

{{ key }}: {{ value }}

{% endfor %} {# Loop variables #} {% for item in items %} {{ loop.index }} {# 1-indexed #} {{ loop.index0 }} {# 0-indexed #} {{ loop.first }} {# True on first iteration #} {{ loop.last }} {# True on last iteration #} {% endfor %} ``` ### Filters and Functions ```jinja {% for file in get_folder_contents()|sort(attribute='date') %} ... {% endfor %} {% for post in get_recent_posts(limit=5)|reverse %} ... {% endfor %} ``` ## Best Practices ### 1. Start Simple, Add Complexity as Needed Begin with basic templates: ``` templates/ ├── base.html ├── __file.md.html └── __folder.md.html ``` Add specific overrides only when you need different styling or layout. ### 2. Keep base.html Minimal Your base template should handle: - HTML document structure - CSS loading - Site-wide navigation - Footer Leave content-specific layouts to page templates. ### 3. Use Template Helpers Don't manually read files or iterate directories. Use helpers: ```jinja ✓ Good: {% for post in get_recent_posts(limit=5) %} ✗ Bad: {# Trying to manually list files - won't work #} ``` ### 4. Leverage the Cascade Put general templates at the root, specific ones in subdirectories: ``` templates/ ├── __file.md.html # Default for all markdown └── blog/ └── __file.md.html # Special layout for blog ``` ### 5. Test with Debug Mode Enable debug mode in `config.toml` to see template discovery: ```toml [server] debug = true ``` This shows which templates Foldsite considered and why it chose the one it did. ## Common Patterns ### Pattern: Site Navigation In `base.html`: ```html ``` ### Pattern: Sidebar with Recent Posts ```html ``` ### Pattern: Tag Cloud ```html
{% for tag in get_all_tags() %} {{ tag.name }} {% endfor %}
``` ## Next Steps - **[Template Discovery](templates/template-discovery.md)** - Deep dive into how templates are found - **[Template Helpers Reference](templates/template-helpers.md)** - Complete API documentation - **[Template Recipes](../recipes/)** - Ready-to-use template examples - **[Styles Guide](../styles/)** - Styling your templates Master the template system, and you can build any type of site with Foldsite!