Update getting started documentation

Remove redundant quick start section
Simplify requirements and installation
Clarify local development setup
Streamline first page creation
Add shared hosting deployment instructions
Update tutorial content structure
Improve content format explanations
Clarify asset handling
Simplify metadata documentation
Update styling documentation
Improve template explanations
Remove unnecessary examples
Make documentation more concise
This commit is contained in:
Ruben 2026-02-07 19:14:13 +01:00
parent cef370ca0c
commit 8855a9b5be
4 changed files with 226 additions and 759 deletions

View file

@ -1,66 +1,26 @@
# Working with Templates
# Templates
Templates control how your content is presented. FolderWeb uses a simple PHP-based template system—no complex templating languages, just HTML with a sprinkle of PHP.
Templates control how content is presented. FolderWeb uses plain PHP templates — HTML with embedded PHP for dynamic output.
## Template Types
FolderWeb has three template levels:
### 1. Base Template (`base.php`)
### Base Template (`base.php`)
The HTML scaffold wrapping every page:
The HTML scaffold wrapping every page. Contains `<head>`, navigation, footer, and a `$content` placeholder for the inner template.
```
<!DOCTYPE html>
<html lang="en">
<head>
<title>Page Title</title>
<link rel="stylesheet" href="...">
</head>
<body>
<header><!-- Navigation --></header>
<main><!-- Page content here --></main>
<footer><!-- Footer --></footer>
</body>
</html>
```
### Page Template (`page.php`)
**You typically customize this once** to set up your site structure.
Wraps single-page content. Receives the rendered content and metadata.
### 2. Page Template (`page.php`)
### List Templates (`list.php`, `list-grid.php`, `list-compact.php`)
Wraps single-page content:
```php
<article>
<?= $content ?>
</article>
```
**Customize this** to control how individual pages look.
### 3. List Template (`list.php`, `list-grid.php`, `list-compact.php`)
Displays multiple items from subdirectories:
```php
<?= $pageContent ?>
<div class="item-list">
<?php foreach ($items as $item): ?>
<article>
<h2><a href="<?= $item['url'] ?>"><?= $item['title'] ?></a></h2>
<p><?= $item['summary'] ?></p>
</article>
<?php endforeach; ?>
</div>
```
**Customize this** to control how lists of content (blogs, portfolios, etc.) appear.
Displays collections of items from subdirectories. Receives an `$items` array and optional intro content.
## Template Location
Templates live in your `custom/` directory:
Templates live in `custom/templates/`:
```
custom/
@ -72,13 +32,11 @@ custom/
└── list-compact.php # Compact list layout
```
**FolderWeb falls back** to `app/default/templates/` if a custom template doesn't exist.
FolderWeb falls back to `app/default/templates/` for any template not present in `custom/`.
## Customizing the Base Template
## Base Template
Let's modify `base.php` to add your site name and custom navigation:
**custom/templates/base.php:**
The base template wraps every page. Here is a minimal example:
```php
<!DOCTYPE html>
@ -87,7 +45,7 @@ Let's modify `base.php` to add your site name and custom navigation:
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= htmlspecialchars($pageTitle) ?></title>
<?php if (isset($metaDescription)): ?>
<meta name="description" content="<?= htmlspecialchars($metaDescription) ?>">
<?php endif; ?>
@ -97,7 +55,7 @@ Let's modify `base.php` to add your site name and custom navigation:
<?php endif; ?>
<link rel="stylesheet" href="/custom/styles/base.css?v=<?= $cssHash ?? '' ?>">
<?php if (isset($pageCssUrl)): ?>
<link rel="stylesheet" href="<?= $pageCssUrl ?>?v=<?= $pageCssHash ?? '' ?>">
<?php endif; ?>
@ -105,7 +63,7 @@ Let's modify `base.php` to add your site name and custom navigation:
<body>
<header>
<nav>
<a href="/" class="logo">My Site</a>
<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/">My Site</a>
<ul>
<?php foreach ($navigation as $item): ?>
<li>
@ -124,9 +82,6 @@ Let's modify `base.php` to add your site name and custom navigation:
<footer>
<p>&copy; <?= date('Y') ?> My Site</p>
<p>
<small>Generated in <?= number_format($pageLoadTime, 4) ?>s</small>
</p>
</footer>
<?php if (!empty($pageJsUrl)): ?>
@ -136,36 +91,26 @@ Let's modify `base.php` to add your site name and custom navigation:
</html>
```
**Key points:**
- Always escape user content: `htmlspecialchars($var)`
- Use short echo tags: `<?= $var ?>`
- Check if variables exist: `isset($var)`
- The `$content` variable contains the rendered page/list content
Key points:
- `$content` contains the rendered output from the page or list template
- Always escape user-facing values with `htmlspecialchars()`
- Check optional variables with `isset()` before using them
## Customizing Page Templates
## Page Template
The page template wraps your single-page content. Let's add a reading time estimate:
**custom/templates/page.php:**
The page template wraps individual page content:
```php
<article>
<?php if (isset($metadata['title'])): ?>
<header>
<h1><?= htmlspecialchars($metadata['title']) ?></h1>
<?php if (isset($metadata['date']) && ($metadata['show_date'] ?? true)): ?>
<time datetime="<?= $metadata['date'] ?>">
<?= $metadata['formatted_date'] ?>
</time>
<?php endif; ?>
<?php
// Estimate reading time (avg 200 words/min)
$wordCount = str_word_count(strip_tags($content));
$readingTime = max(1, round($wordCount / 200));
?>
<p class="reading-time"><?= $readingTime ?> min read</p>
</header>
<?php endif; ?>
@ -175,11 +120,9 @@ The page template wraps your single-page content. Let's add a reading time estim
</article>
```
## Customizing List Templates
## List Template
List templates display collections of content. Let's create a custom blog list:
**custom/templates/list.php:**
List templates display items from subdirectories:
```php
<?php if ($pageContent): ?>
@ -190,51 +133,38 @@ List templates display collections of content. Let's create a custom blog list:
<div class="blog-list">
<?php foreach ($items as $item): ?>
<article class="blog-item">
<article>
<?php if (isset($item['cover_image'])): ?>
<a href="<?= $item['url'] ?>">
<img
src="<?= $item['cover_image'] ?>"
alt="<?= htmlspecialchars($item['title']) ?>"
loading="lazy"
>
<img src="<?= $item['cover_image'] ?>"
alt="<?= htmlspecialchars($item['title']) ?>"
loading="lazy">
</a>
<?php endif; ?>
<header>
<h2>
<a href="<?= $item['url'] ?>">
<?= htmlspecialchars($item['title']) ?>
</a>
</h2>
<?php if (isset($item['date'])): ?>
<time datetime="<?= $item['date'] ?>">
<?= $item['formatted_date'] ?? $item['date'] ?>
</time>
<?php endif; ?>
</header>
<h2>
<a href="<?= $item['url'] ?>">
<?= htmlspecialchars($item['title']) ?>
</a>
</h2>
<?php if (isset($item['date'])): ?>
<time datetime="<?= $item['date'] ?>">
<?= $item['formatted_date'] ?? $item['date'] ?>
</time>
<?php endif; ?>
<?php if (isset($item['summary'])): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
<a href="<?= $item['url'] ?>">Read more →</a>
</article>
<?php endforeach; ?>
</div>
```
## Choosing List Templates
## Choosing a List Template
You can create multiple list templates and select them per directory:
**Available by default:**
- `list.php` — Simple vertical list
- `list-grid.php` — Card grid layout
- `list-compact.php` — Minimal compact list
**Select in metadata.ini:**
Select which list template to use per directory in `metadata.ini`:
```ini
title = "Projects"
@ -243,11 +173,14 @@ title = "Projects"
page_template = "list-grid"
```
Now the `projects/` directory uses the grid layout.
Built-in options:
- `list` — vertical list (default)
- `list-grid` — card grid
- `list-compact` — minimal compact list
## Creating Custom List Templates
Let's create a timeline template for a blog:
Create a new file in `custom/templates/`. For example, a timeline layout:
**custom/templates/list-timeline.php:**
@ -255,12 +188,10 @@ Let's create a timeline template for a blog:
<?= $pageContent ?>
<div class="timeline">
<?php
<?php
$currentYear = null;
foreach ($items as $item):
foreach ($items as $item):
$year = isset($item['date']) ? date('Y', strtotime($item['date'])) : null;
// Print year marker if it changed
if ($year && $year !== $currentYear):
$currentYear = $year;
?>
@ -268,199 +199,91 @@ Let's create a timeline template for a blog:
<h3><?= $year ?></h3>
</div>
<?php endif; ?>
<article class="timeline-item">
<time><?= $item['formatted_date'] ?? '' ?></time>
<div class="timeline-content">
<h4><a href="<?= $item['url'] ?>"><?= htmlspecialchars($item['title']) ?></a></h4>
<?php if (isset($item['summary'])): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
</div>
<h4><a href="<?= $item['url'] ?>"><?= htmlspecialchars($item['title']) ?></a></h4>
<?php if (isset($item['summary'])): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
</article>
<?php endforeach; ?>
</div>
```
**Use in metadata.ini:**
Use it with:
```ini
[settings]
page_template = "list-timeline"
```
## Available Template Variables
## Template Variables
Templates have access to these variables (see [Reference: Template Variables](#) for complete list):
### Base template
**Base template:**
```php
$content // Rendered page/list HTML
$pageTitle // Page title for <title> tag
$metaDescription // SEO description
$navigation // Array of menu items
$homeLabel // "Home" link text (translated)
$currentLang // Current language code
$languageUrls // Links to other language versions
$translations // Translated UI strings
$cssHash // Cache-busting hash for CSS
$pageCssUrl // Page-specific CSS URL (if exists)
$pageJsUrl // Page-specific JS URL (if exists)
$pageLoadTime // Page generation time
```
| Variable | Type | Description |
|---|---|---|
| `$content` | string | Rendered page/list HTML |
| `$pageTitle` | string | Page title for `<title>` |
| `$metaDescription` | string | SEO description |
| `$navigation` | array | Menu items (`title`, `url`, `order`) |
| `$homeLabel` | string | Translated "Home" text |
| `$currentLang` | string | Current language code |
| `$langPrefix` | string | URL language prefix (`""` or `"/no"`) |
| `$languageUrls` | array | Links to other language versions |
| `$translations` | array | Translated UI strings |
| `$cssHash` | string | Cache-busting hash for base CSS |
| `$pageCssUrl` | string | Page-specific CSS URL (if exists) |
| `$pageJsUrl` | string | Page-specific JS URL (if exists) |
| `$pageLoadTime` | float | Page generation time |
**Page template:**
```php
$content // Rendered HTML
$metadata // Metadata array (title, date, etc.)
```
### Page template
**List template:**
```php
$items // Array of items to display
$pageContent // Optional intro content from page
$metadata // Directory metadata
| Variable | Type | Description |
|---|---|---|
| `$content` | string | Rendered HTML from content files |
| `$metadata` | array | Page metadata (title, date, etc.) |
// Each $item has:
$item['url'] // Full URL to item
$item['title'] // Item title
$item['summary'] // Short description
$item['date'] // ISO date (YYYY-MM-DD)
$item['formatted_date'] // Localized date string
$item['cover_image'] // Cover image URL (if exists)
```
### List template
| Variable | Type | Description |
|---|---|---|
| `$items` | array | Items to display |
| `$pageContent` | string | Rendered intro content |
| `$metadata` | array | Directory metadata |
Each item in `$items`:
| Key | Type | Description |
|---|---|---|
| `url` | string | Full URL to item |
| `title` | string | Item title |
| `summary` | string | Short description |
| `date` | string | ISO date (YYYY-MM-DD) |
| `formatted_date` | string | Localized date string |
| `cover_image` | string | Cover image URL |
See the [Template Variables Reference](../03-reference/03-template-variables.md) for the complete list.
## Best Practices
### 1. Always Escape Output
**Escape output.** Always use `htmlspecialchars()` for user-facing values. The `$content` variable is already rendered HTML and does not need escaping.
**Check variables.** Use `isset()` before accessing optional values:
```php
<!-- Bad -->
<h1><?= $title ?></h1>
<!-- Good -->
<h1><?= htmlspecialchars($title) ?></h1>
```
**Exception:** Already-sanitized HTML like `$content` (rendered from Markdown).
### 2. Check Variables Exist
```php
<!-- Bad -->
<p><?= $metadata['summary'] ?></p>
<!-- Good -->
<?php if (isset($metadata['summary'])): ?>
<p><?= htmlspecialchars($metadata['summary']) ?></p>
<?php endif; ?>
```
### 3. Use Short Echo Tags
**Keep logic minimal.** Templates should display data, not process it. Complex logic belongs in plugins.
```php
<!-- Verbose -->
<?php echo htmlspecialchars($title); ?>
**Use semantic HTML.** Prefer `<article>`, `<header>`, `<nav>`, `<time>` over generic `<div>` elements.
<!-- Concise -->
<?= htmlspecialchars($title) ?>
```
## Next Steps
### 4. Keep Logic Minimal
Templates should display data, not process it. Complex logic belongs in plugins.
```php
<!-- Bad: complex logic in template -->
<?php
$posts = array_filter($items, function($item) {
return strtotime($item['date']) > strtotime('-30 days');
});
usort($posts, function($a, $b) {
return strcmp($b['date'], $a['date']);
});
?>
<!-- Good: prepare data in a plugin, display in template -->
<?php foreach ($recentPosts as $post): ?>
...
<?php endforeach; ?>
```
### 5. Use Semantic HTML
```php
<!-- Bad -->
<div class="title">Title</div>
<div class="content">Content</div>
<!-- Good -->
<article>
<h1>Title</h1>
<div class="content">Content</div>
</article>
```
## Practical Examples
### Simple Portfolio Page Template
```php
<article class="portfolio-item">
<header>
<?php if (isset($metadata['cover_image'])): ?>
<img src="<?= $metadata['cover_image'] ?>" alt="">
<?php endif; ?>
<h1><?= htmlspecialchars($metadata['title'] ?? 'Untitled') ?></h1>
</header>
<div class="content">
<?= $content ?>
</div>
<?php if (isset($metadata['project_url'])): ?>
<footer>
<a href="<?= htmlspecialchars($metadata['project_url']) ?>"
class="button">View Project →</a>
</footer>
<?php endif; ?>
</article>
```
### Card Grid List Template
```php
<?= $pageContent ?>
<div class="card-grid">
<?php foreach ($items as $item): ?>
<article class="card">
<?php if (isset($item['cover_image'])): ?>
<img src="<?= $item['cover_image'] ?>" alt="" loading="lazy">
<?php endif; ?>
<div class="card-content">
<h3>
<a href="<?= $item['url'] ?>">
<?= htmlspecialchars($item['title']) ?>
</a>
</h3>
<?php if (isset($item['summary'])): ?>
<p><?= htmlspecialchars($item['summary']) ?></p>
<?php endif; ?>
</div>
</article>
<?php endforeach; ?>
</div>
```
## What's Next?
You now know how to customize templates. Next, learn about:
- **[Template Variables Reference](#)** — Complete list of available variables
- **[Creating Plugins](#)** — Extend functionality and add custom data to templates
- **[Internationalization](#)** — Build multilingual sites
Or explore the examples in `app/default/content/examples/templates-demo/` to see templates in action.
- [Template Variables Reference](../03-reference/03-template-variables.md) — Complete variable list
- [Internationalization](../03-reference/04-internationalization.md) — Multi-language support
- [Configuration Reference](../03-reference/01-configuration.md) — All configuration options