# Creating Custom Templates Templates control the HTML structure and presentation of your content. This guide covers advanced template creation, from simple page layouts to complex list views. ## Template Hierarchy FolderWeb uses a three-level template system: 1. **Base template** (`base.php`) — The HTML scaffold wrapping everything 2. **Content template** — Either `page.php` or a list template 3. **Partials** (optional) — Reusable components you create ``` base.php └── page.php or list.php └── Rendered content ``` ## Template Resolution When rendering a page, FolderWeb looks for templates in this order: **For page views:** 1. `custom/templates/page.php` 2. `app/default/templates/page.php` (fallback) **For list views:** 1. `custom/templates/{page_template}.php` (e.g., `list-grid.php`) 2. `custom/templates/list.php` 3. `app/default/templates/{page_template}.php` 4. `app/default/templates/list.php` (fallback) **For base:** 1. `custom/templates/base.php` 2. `app/default/templates/base.php` (fallback) ## Creating a Custom Base Template The base template defines the HTML structure for every page. ### Step 1: Copy the Default ```bash cp app/default/templates/base.php custom/templates/base.php ``` ### Step 2: Customize **custom/templates/base.php:** ```php <?= htmlspecialchars($pageTitle ?? 'My Site') ?> Skip to main content
My Website
1): ?>
$url): ?> >
Privacy Terms Contact

© My Website

``` ### Key Features - **Skip link** for accessibility - **Container divs** for layout control - **Semantic HTML** (header, nav, main, footer) - **ARIA labels** for screen readers - **Open Graph tags** for social media - **Performance metrics** in footer ## Creating Custom Page Templates Page templates wrap single-page content. ### Blog Post Template **custom/templates/page.php:** ```php

by min read

Related Posts

``` ### Portfolio Item Template **custom/templates/page-portfolio.php:** ```php
<?= htmlspecialchars($metadata['title'] ?? '') ?>

Client
Year
Role
View Live Project →
``` **To use:** Set in metadata: ```ini [settings] page_template = "page-portfolio" ``` Wait, that won't work for page templates—only list templates use `page_template`. For page templates, you'd need to select via a plugin or use different template files per directory. Let's stick with one `page.php` that adapts based on metadata. ## Creating Custom List Templates List templates display collections of items. ### Card Grid Layout **custom/templates/list-cards.php:** ```php
<?= htmlspecialchars($item['title']) ?>

Read more →
``` **Corresponding CSS:** ```css .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(300px, 100%), 1fr)); gap: 2rem; margin-top: 2rem; } .card { border-radius: 0.5rem; overflow: hidden; box-shadow: 0 2px 8px oklch(0% 0 0 / 0.1); transition: transform 0.2s, box-shadow 0.2s; &:hover { transform: translateY(-4px); box-shadow: 0 4px 16px oklch(0% 0 0 / 0.15); } } .card-image img { width: 100%; aspect-ratio: 16 / 9; object-fit: cover; } .card-content { padding: 1.5rem; } .card-title { margin: 0 0 0.5rem; font-size: 1.25rem; & a { color: inherit; text-decoration: none; &:hover { text-decoration: underline; } } } .card-date { display: block; font-size: 0.875rem; color: var(--color-text-muted); margin-bottom: 0.75rem; } .card-summary { margin: 0 0 1rem; line-height: 1.6; } .card-link { font-weight: 500; text-decoration: none; &:hover { text-decoration: underline; } } ``` ### Timeline Layout **custom/templates/list-timeline.php:** ```php

``` **CSS:** ```css .timeline { position: relative; max-width: 800px; margin: 2rem 0; padding-left: 2rem; &::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 2px; background: var(--color-border); } } .timeline-year { margin: 2rem 0 1rem; & h2 { font-size: 1.5rem; color: var(--color-accent); } } .timeline-item { position: relative; margin-bottom: 2rem; &::before { content: ''; position: absolute; left: -2.5rem; top: 0.5rem; width: 0.75rem; height: 0.75rem; border-radius: 50%; background: var(--color-accent); border: 2px solid var(--color-bg); } } .timeline-date { display: block; font-size: 0.875rem; color: var(--color-text-muted); margin-bottom: 0.25rem; } .timeline-content { & h3 { margin: 0 0 0.5rem; font-size: 1.125rem; } & p { margin: 0; line-height: 1.6; } } ``` ### Magazine Layout **custom/templates/list-magazine.php:** ```php
<?= htmlspecialchars($featured['title']) ?>

Read article →
<?= htmlspecialchars($item['title']) ?>

``` ## Using Partials (Template Includes) Break complex templates into reusable components. ### Creating a Partial **custom/templates/partials/post-card.php:** ```php
<?= htmlspecialchars($post['title']) ?>

``` ### Using a Partial **custom/templates/list.php:** ```php
``` **Note:** Set `$post` before including, as the partial expects it. ## Conditional Templates Use metadata to vary presentation. ```php
``` **Set in metadata:** ```ini [settings] layout = "wide" ``` ## Template Best Practices ### 1. Always Escape Output ```php

``` ### 2. Check Before Using ```php

By

By

``` ### 3. Use Semantic HTML ```php

Title

Content
Meta
Title
Content
``` ### 4. Add ARIA Labels ```php
``` ### 5. Keep Logic Minimal ```php strtotime($item['date']) > strtotime('-30 days') ); usort($recentPosts, fn($a, $b) => strcmp($b['date'], $a['date'])); ?> ``` ## What's Next? - **[Template Variables Reference](#)** — See all available variables - **[Plugin System](#)** — Add custom variables to templates - **[Styling Guide](#)** — Style your custom templates