Compare commits
No commits in common. "74672b2d047eb156f81c245ebb0864d0763ad181" and "ca6d87b8851ef0992877b4d37e41141b9fad584e" have entirely different histories.
74672b2d04
...
ca6d87b885
71 changed files with 1413 additions and 284 deletions
|
|
@ -4,7 +4,7 @@ Drop markdown, HTML and PHP files in directories to instantly publish online. A
|
||||||
|
|
||||||
## How it works
|
## How it works
|
||||||
|
|
||||||
Your file system is your site structure. Create folders, drop in content files – and they're live. Create `content/about/intro.md` and it turns into `yoursite.com/about/`. Need more presentational control then what Markdown offers? Drop in an HTML file. Dynamic content? Add a PHP script. No routing configuration, no build step, just save a file, refresh the browser.
|
Your file system is your site structure. Create folders, drop in content files, and they're live. Create `content/about/intro.md` and it turns into `yoursite.com/about/`. Need more presentational controll then what Markdown offers? Drop in an HTML file. Dynamic content? Add a PHP script. No routing configuration, no build step, just save a file, refresh the browser.
|
||||||
|
|
||||||
Since everything is just files and folders, you can mount your site via SFTP or WebDAV and edit content directly from your local machine — save a file, and the change is live. Create websites like it's 1996!
|
Since everything is just files and folders, you can mount your site via SFTP or WebDAV and edit content directly from your local machine — save a file, and the change is live. Create websites like it's 1996!
|
||||||
|
|
||||||
|
|
|
||||||
51
app/default/content/00-hero.no.php
Normal file
51
app/default/content/00-hero.no.php
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
// Dynamisk hero-seksjon som viser PHP-muligheter
|
||||||
|
$prefix = $langPrefix ?? '';
|
||||||
|
$features = [
|
||||||
|
['icon' => '📁', 'title' => 'Filbasert', 'description' => 'Mapper blir URL-er automatisk', 'url' => $prefix . '/examples/file-based-routing/'],
|
||||||
|
['icon' => '⚡', 'title' => 'Uten byggesteg', 'description' => 'Rediger og oppdater—det er alt', 'url' => $prefix . '/examples/no-build-step/'],
|
||||||
|
['icon' => '🎨', 'title' => 'Bland formater', 'description' => 'Kombiner .md, .html og .php', 'url' => $prefix . '/examples/mix-formats/'],
|
||||||
|
['icon' => '🌍', 'title' => 'Flerspråklig', 'description' => 'Innebygd i18n-støtte', 'url' => $prefix . '/multilingual/'],
|
||||||
|
['icon' => '📝', 'title' => 'Markdown', 'description' => 'Skriv innhold i markdown', 'url' => $prefix . '/examples/2024-12-15-markdown-demo/'],
|
||||||
|
['icon' => '🎭', 'title' => 'Maler', 'description' => 'Egendefinerte layout og stiler', 'url' => $prefix . '/examples/templates-demo/'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$stats = [
|
||||||
|
'files' => count(glob(__DIR__ . '/**/*', GLOB_BRACE)) ?: 0,
|
||||||
|
'generated' => number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) . 'ms',
|
||||||
|
'php_version' => PHP_VERSION,
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<h1 class="hero-title">FolderWeb</h1>
|
||||||
|
<p class="hero-subtitle">
|
||||||
|
Slipp filer i mapper. De rendres umiddelbart.
|
||||||
|
Ingen konfigurasjon, ingen byggesteg, ingen kompleksitet.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="features">
|
||||||
|
<?php foreach ($features as $feature): ?>
|
||||||
|
<a href="<?= htmlspecialchars($feature['url']) ?>" class="feature-card">
|
||||||
|
<span class="feature-icon"><?= $feature['icon'] ?></span>
|
||||||
|
<h3 class="feature-title"><?= htmlspecialchars($feature['title']) ?></h3>
|
||||||
|
<p class="feature-description"><?= htmlspecialchars($feature['description']) ?></p>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-value"><?= $stats['files'] ?></span>
|
||||||
|
<span class="stat-label">Filer i innhold</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-value"><?= $stats['generated'] ?></span>
|
||||||
|
<span class="stat-label">Side generert</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-value">PHP <?= $stats['php_version'] ?></span>
|
||||||
|
<span class="stat-label">Runtime</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
51
app/default/content/00-hero.php
Normal file
51
app/default/content/00-hero.php
Normal file
|
|
@ -0,0 +1,51 @@
|
||||||
|
<?php
|
||||||
|
// Dynamic hero section showcasing PHP capabilities
|
||||||
|
$prefix = $langPrefix ?? '';
|
||||||
|
$features = [
|
||||||
|
['icon' => '📁', 'title' => 'File-Based', 'description' => 'Folders become URLs automatically', 'url' => $prefix . '/examples/file-based-routing/'],
|
||||||
|
['icon' => '⚡', 'title' => 'No Build Step', 'description' => 'Edit and refresh—that\'s it', 'url' => $prefix . '/examples/no-build-step/'],
|
||||||
|
['icon' => '🎨', 'title' => 'Mix Formats', 'description' => 'Combine .md, .html, and .php', 'url' => $prefix . '/examples/mix-formats/'],
|
||||||
|
['icon' => '🌍', 'title' => 'Multilingual', 'description' => 'Built-in i18n support', 'url' => $prefix . '/multilingual/'],
|
||||||
|
['icon' => '📝', 'title' => 'Markdown', 'description' => 'Write content in markdown', 'url' => $prefix . '/examples/2024-12-15-markdown-demo/'],
|
||||||
|
['icon' => '🎭', 'title' => 'Templates', 'description' => 'Custom layouts and styles', 'url' => $prefix . '/examples/templates-demo/'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$stats = [
|
||||||
|
'files' => count(glob(__DIR__ . '/**/*', GLOB_BRACE)) ?: 0,
|
||||||
|
'generated' => number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) . 'ms',
|
||||||
|
'php_version' => PHP_VERSION,
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section class="hero">
|
||||||
|
<h1 class="hero-title">FolderWeb</h1>
|
||||||
|
<p class="hero-subtitle">
|
||||||
|
Drop files in folders. They render immediately.
|
||||||
|
No configuration, no build step, no complexity.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="features">
|
||||||
|
<?php foreach ($features as $feature): ?>
|
||||||
|
<a href="<?= htmlspecialchars($feature['url']) ?>" class="feature-card">
|
||||||
|
<span class="feature-icon"><?= $feature['icon'] ?></span>
|
||||||
|
<h3 class="feature-title"><?= htmlspecialchars($feature['title']) ?></h3>
|
||||||
|
<p class="feature-description"><?= htmlspecialchars($feature['description']) ?></p>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="stats">
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-value"><?= $stats['files'] ?></span>
|
||||||
|
<span class="stat-label">Files in content</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-value"><?= $stats['generated'] ?></span>
|
||||||
|
<span class="stat-label">Page generated</span>
|
||||||
|
</div>
|
||||||
|
<div class="stat">
|
||||||
|
<span class="stat-value">PHP <?= $stats['php_version'] ?></span>
|
||||||
|
<span class="stat-label">Runtime</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
45
app/default/content/01-index.md
Normal file
45
app/default/content/01-index.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!-- The hero.php file renders above this markdown content -->
|
||||||
|
<!-- Files are combined in alphabetical order: hero.php, then index.md -->
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
1. **Empty the content folder** – Delete everything in `/content/` to start fresh
|
||||||
|
2. **Copy the default folder** – Copy `/app/default/` to `/custom/` (one level up from app/) to create your own theme
|
||||||
|
3. **Add your content** – Drop `.md`, `.html`, or `.php` files in `/content/` and they're live
|
||||||
|
4. **Customize templates** – Edit files in `/custom/templates/` to change the look
|
||||||
|
5. **Style it** – Add CSS in `/custom/styles/styles.css` for global styles
|
||||||
|
|
||||||
|
## What Makes FolderWeb Different
|
||||||
|
|
||||||
|
**File-based routing** – Your folder structure is your URL structure. No configuration needed.
|
||||||
|
|
||||||
|
**No JavaScript** – Fast, simple, accessible websites that work everywhere.
|
||||||
|
|
||||||
|
**Modern PHP & CSS** – Clean code using the latest language features and CSS capabilities.
|
||||||
|
|
||||||
|
**No build step** – Edit a file, refresh the page. That's it.
|
||||||
|
|
||||||
|
**Decade-proof** – Minimal dependencies mean this will work for years without breaking.
|
||||||
|
|
||||||
|
## How This Page Works
|
||||||
|
|
||||||
|
This frontpage demonstrates how you can **mix different file types** to create dynamic, custom layouts:
|
||||||
|
|
||||||
|
- **`hero.php`** – Dynamic PHP component with live stats and styling
|
||||||
|
- **`index.md`** – Static markdown content (this section!)
|
||||||
|
|
||||||
|
Files in the same directory are rendered alphabetically. Want a different order? Prefix filenames with numbers like `00-hero.php` and `01-content.md`.
|
||||||
|
|
||||||
|
## Features Demonstrated Here {#features}
|
||||||
|
|
||||||
|
Explore this demo site to see what FolderWeb can do:
|
||||||
|
|
||||||
|
- [Examples](/examples/) – Markdown, metadata, dates, and cover images
|
||||||
|
- [Multilingual](/multilingual/) – Build sites in multiple languages
|
||||||
|
- [Nested Content](/examples/nested/) – Organize content as deep as you need
|
||||||
|
|
||||||
|
## Philosophy
|
||||||
|
|
||||||
|
**Just enough, nothing more.** This framework uses minimal PHP to enable modern conveniences while remaining maintainable for years or decades.
|
||||||
|
|
||||||
|
Read more in `CLAUDE.md` to understand the principles behind FolderWeb.
|
||||||
45
app/default/content/01-index.no.md
Normal file
45
app/default/content/01-index.no.md
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!-- hero.no.php-filen rendres over dette markdown-innholdet -->
|
||||||
|
<!-- Filer kombineres i alfabetisk rekkefølge: hero.no.php, deretter index.no.md -->
|
||||||
|
|
||||||
|
## Hurtigstart
|
||||||
|
|
||||||
|
1. **Tøm innholdsmappen** – Slett alt i `/content/` for å starte fra bunnen av
|
||||||
|
2. **Kopier standardmappen** – Kopier `/app/default/` til `/custom/` (ett nivå opp fra app/) for å lage ditt eget tema
|
||||||
|
3. **Legg til innhold** – Slipp `.md`, `.html`, eller `.php`-filer i `/content/` og de er live
|
||||||
|
4. **Tilpass maler** – Rediger filer i `/custom/templates/` for å endre utseendet
|
||||||
|
5. **Stil det** – Legg til CSS i `/custom/styles/styles.css` for globale stiler
|
||||||
|
|
||||||
|
## Hva gjør FolderWeb annerledes
|
||||||
|
|
||||||
|
**Filbasert routing** – Mappestrukturen din er URL-strukturen din. Ingen konfigurasjon nødvendig.
|
||||||
|
|
||||||
|
**Ingen JavaScript** – Raske, enkle, tilgjengelige nettsteder som fungerer overalt.
|
||||||
|
|
||||||
|
**Moderne PHP & CSS** – Ren kode som bruker de nyeste språkfunksjonene og CSS-mulighetene.
|
||||||
|
|
||||||
|
**Uten byggesteg** – Rediger en fil, oppdater siden. Det er det.
|
||||||
|
|
||||||
|
**Tiår-sikker** – Minimale avhengigheter betyr at dette vil fungere i årevis uten å gå i stykker.
|
||||||
|
|
||||||
|
## Hvordan denne siden fungerer
|
||||||
|
|
||||||
|
Denne forsiden demonstrerer hvordan du kan **blande forskjellige filtyper** for å lage dynamiske, egendefinerte layout:
|
||||||
|
|
||||||
|
- **`hero.no.php`** – Dynamisk PHP-komponent med live statistikk og styling
|
||||||
|
- **`index.no.md`** – Statisk markdown-innhold (denne seksjonen!)
|
||||||
|
|
||||||
|
Filer i samme katalog rendres alfabetisk. Vil du ha en annen rekkefølge? Prefikser filnavn med tall som `00-hero.php` og `01-innhold.md`.
|
||||||
|
|
||||||
|
## Funksjoner demonstrert her {#funksjoner}
|
||||||
|
|
||||||
|
Utforsk denne demosiden for å se hva FolderWeb kan gjøre:
|
||||||
|
|
||||||
|
- [Eksempler](/examples/) – Markdown, metadata, datoer og forsidebilder
|
||||||
|
- [Flerspråklig](/multilingual/) – Bygg nettsteder på flere språk
|
||||||
|
- [Nestet innhold](/examples/nested/) – Organiser innhold så dypt du trenger
|
||||||
|
|
||||||
|
## Filosofi
|
||||||
|
|
||||||
|
**Akkurat nok, ikke mer.** Dette rammeverket bruker minimal PHP for å muliggjøre moderne bekvemmeligheter samtidig som det forblir vedlikeholdbart i årevis eller tiår.
|
||||||
|
|
||||||
|
Les mer i `CLAUDE.md` for å forstå prinsippene bak FolderWeb.
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# About
|
|
||||||
|
|
||||||
This is a simple page. It lives in `content/about/about.md` and renders at `/about/`.
|
|
||||||
|
|
||||||
Any folder with only files (no subdirectories) renders as a page.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# Om
|
|
||||||
|
|
||||||
Dette er en enkel side. Den ligger i `content/about/about.no.md` og vises på `/no/about/`.
|
|
||||||
|
|
||||||
Mapper som bare inneholder filer (ingen undermapper) vises som en side.
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
title = "First Post"
|
|
||||||
summary = "A simple blog post demonstrating date extraction and metadata"
|
|
||||||
|
|
||||||
[no]
|
|
||||||
title = "Første innlegg"
|
|
||||||
summary = "Et enkelt blogginnlegg som viser datouttrekk og metadata"
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# First Post
|
|
||||||
|
|
||||||
This is a blog post. The date `2025-01-15` is extracted from the folder name for sorting and display.
|
|
||||||
|
|
||||||
You can set a custom URL slug in `metadata.ini` to shorten the URL.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
# Første innlegg
|
|
||||||
|
|
||||||
Dette er et blogginnlegg. Datoen `2025-01-15` hentes fra mappenavnet for sortering og visning.
|
|
||||||
|
|
||||||
Du kan sette en egendefinert URL-slug i `metadata.ini` for å forkorte URL-en.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# Second Post
|
|
||||||
|
|
||||||
This post combines multiple content files into a single page. This section is Markdown.
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
# Andre innlegg
|
|
||||||
|
|
||||||
Dette innlegget kombinerer flere innholdsfiler til en enkelt side. Denne delen er Markdown.
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<section>
|
|
||||||
<h2>HTML Section</h2>
|
|
||||||
<p>This section is plain HTML, rendered after the Markdown above. Number prefixes (<code>10-</code>, <code>20-</code>) control the order.</p>
|
|
||||||
<p>You can mix <code>.md</code>, <code>.html</code>, and <code>.php</code> files freely within a single page.</p>
|
|
||||||
</section>
|
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
<section>
|
|
||||||
<h2>HTML-seksjon</h2>
|
|
||||||
<p>Denne delen er ren HTML, vist etter Markdown-en over. Nummerprefiks (<code>10-</code>, <code>20-</code>) styrer rekkefølgen.</p>
|
|
||||||
<p>Du kan blande <code>.md</code>-, <code>.html</code>- og <code>.php</code>-filer fritt på en enkelt side.</p>
|
|
||||||
</section>
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<section>
|
|
||||||
<h2>PHP-seksjon</h2>
|
|
||||||
<p>Denne delen er dynamisk PHP, vist etter HTML-en over.</p>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>Egenskap</th><th>Verdi</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Servertid</td><td><?= date('H:i:s') ?></td></tr>
|
|
||||||
<tr><td>PHP-versjon</td><td><?= PHP_VERSION ?></td></tr>
|
|
||||||
<tr><td>Side generert på</td><td><?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 1) ?>ms</td></tr>
|
|
||||||
<tr><td>Operativsystem</td><td><?= PHP_OS ?></td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p>Oppdater siden for å se klokken endre seg. Dette er serverside-output — ingen JavaScript involvert.</p>
|
|
||||||
</section>
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
<section>
|
|
||||||
<h2>PHP Section</h2>
|
|
||||||
<p>This section is dynamic PHP, rendered after the HTML above.</p>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th>Property</th><th>Value</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td>Server time</td><td><?= date('H:i:s') ?></td></tr>
|
|
||||||
<tr><td>PHP version</td><td><?= PHP_VERSION ?></td></tr>
|
|
||||||
<tr><td>Page generated in</td><td><?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 1) ?>ms</td></tr>
|
|
||||||
<tr><td>Operating system</td><td><?= PHP_OS ?></td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<p>Refresh the page to see the time update. This is live server-side output — no JavaScript involved.</p>
|
|
||||||
</section>
|
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
title = "Second Post"
|
|
||||||
summary = "A post demonstrating mixed content formats"
|
|
||||||
|
|
||||||
[no]
|
|
||||||
title = "Andre innlegg"
|
|
||||||
summary = "Et innlegg som viser blandede innholdsformater"
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
title = "Blog"
|
|
||||||
summary = "Example blog with list view"
|
|
||||||
menu = 1
|
|
||||||
menu_order = 10
|
|
||||||
|
|
||||||
[no]
|
|
||||||
title = "Blogg"
|
|
||||||
summary = "Eksempelblogg med listevisning"
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
<svg width="800" height="400" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="800" height="400" fill="#f0f0f0"/>
|
||||||
|
<circle cx="400" cy="200" r="80" fill="#6366f1" opacity="0.8"/>
|
||||||
|
<text x="400" y="340" font-family="system-ui, sans-serif" font-size="24" fill="#333" text-anchor="middle">Cover Image Example</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 324 B |
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Cover Images and Assets
|
||||||
|
|
||||||
|
FolderWeb automatically detects and uses cover images for social sharing and list views.
|
||||||
|
|
||||||
|
## How Cover Images Work
|
||||||
|
|
||||||
|
Place an image named `cover.jpg`, `cover.png`, or `cover.webp` in your content folder and it's automatically:
|
||||||
|
|
||||||
|
- Used as the Open Graph image for social media
|
||||||
|
- Displayed in list views (when using templates that support it)
|
||||||
|
- Available at the same URL as your content
|
||||||
|
|
||||||
|
## Supported Formats
|
||||||
|
|
||||||
|
- `cover.jpg` or `cover.jpeg`
|
||||||
|
- `cover.png`
|
||||||
|
- `cover.webp`
|
||||||
|
- `cover.gif`
|
||||||
|
|
||||||
|
The system checks for these in order and uses the first one found.
|
||||||
|
|
||||||
|
## Other Assets
|
||||||
|
|
||||||
|
Any file in your content folder is accessible at the same URL path:
|
||||||
|
|
||||||
|
- Images: `photo.jpg`, `diagram.png`
|
||||||
|
- Documents: `download.pdf`, `report.docx`
|
||||||
|
- Data: `data.json`, `spreadsheet.csv`
|
||||||
|
|
||||||
|
Just reference them in your markdown:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|

|
||||||
|
[Download PDF](report.pdf)
|
||||||
|
```
|
||||||
|
|
||||||
|
## This Folder
|
||||||
|
|
||||||
|
This folder includes a simple SVG as the cover image. Check the folder structure to see how it's organized.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
title = "Cover Images and Assets"
|
||||||
|
summary = "Learn how cover images and static assets work in FolderWeb"
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Working with Metadata
|
||||||
|
|
||||||
|
Metadata provides structured information about your pages and controls how they display.
|
||||||
|
|
||||||
|
## What is metadata.ini?
|
||||||
|
|
||||||
|
Every folder can have a `metadata.ini` file that defines:
|
||||||
|
|
||||||
|
- Title (overrides automatic title extraction)
|
||||||
|
- Date (overrides folder-based date detection)
|
||||||
|
- Summary (appears in list views)
|
||||||
|
- Custom template selection
|
||||||
|
- Other custom fields
|
||||||
|
|
||||||
|
## Example metadata.ini
|
||||||
|
|
||||||
|
```ini
|
||||||
|
title = "My Custom Title"
|
||||||
|
date = "2024-11-20"
|
||||||
|
summary = "A brief description that appears in lists"
|
||||||
|
|
||||||
|
[settings]
|
||||||
|
page_template = "list-grid"
|
||||||
|
show_date = false
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Use Cases
|
||||||
|
|
||||||
|
**Override automatic titles** – If you want a title different from the folder name.
|
||||||
|
|
||||||
|
**Set explicit dates** – When the folder name doesn't include a date.
|
||||||
|
|
||||||
|
**Add summaries** – Give context in list views without showing full content.
|
||||||
|
|
||||||
|
**Choose templates** – Different sections can use different list templates.
|
||||||
|
|
||||||
|
**Hide metadata** – Set `show_date = false` to hide dates in lists.
|
||||||
|
|
||||||
|
## Custom Fields
|
||||||
|
|
||||||
|
You can add any fields you want:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
author = "Jane Doe"
|
||||||
|
category = "Tutorial"
|
||||||
|
tags = "metadata, configuration, tutorial"
|
||||||
|
```
|
||||||
|
|
||||||
|
Then access them in custom templates via `$metadata['author']`.
|
||||||
|
|
||||||
|
## This Page's Metadata
|
||||||
|
|
||||||
|
Check out `metadata.ini` in this folder to see how the title, date, and summary are defined.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
title = "Working with Metadata"
|
||||||
|
date = "2024-11-20"
|
||||||
|
summary = "Learn how to use metadata.ini files to control page information and behavior"
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Markdown Demonstration
|
||||||
|
|
||||||
|
This page shows the full range of Markdown formatting available through Parsedown.
|
||||||
|
|
||||||
|
## Text Formatting
|
||||||
|
|
||||||
|
You can use **bold text**, *italic text*, and even ***bold italic***.
|
||||||
|
|
||||||
|
Use `inline code` for technical terms or file paths like `/app/default/templates/`.
|
||||||
|
|
||||||
|
## Lists
|
||||||
|
|
||||||
|
Unordered lists:
|
||||||
|
|
||||||
|
- First item
|
||||||
|
- Second item
|
||||||
|
- Nested item
|
||||||
|
- Another nested item
|
||||||
|
- Third item
|
||||||
|
|
||||||
|
Ordered lists:
|
||||||
|
|
||||||
|
1. Step one
|
||||||
|
2. Step two
|
||||||
|
3. Step three
|
||||||
|
|
||||||
|
## Links and Images
|
||||||
|
|
||||||
|
Here's a [link to the homepage](/).
|
||||||
|
|
||||||
|
Images work too (when you add them to the folder):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Code Blocks
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
function greet(string $name): string {
|
||||||
|
return "Hello, {$name}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo greet('World');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Blockquotes
|
||||||
|
|
||||||
|
> This is a blockquote. Perfect for highlighting important information or quotes from other sources.
|
||||||
|
>
|
||||||
|
> It can span multiple paragraphs.
|
||||||
|
|
||||||
|
## Tables
|
||||||
|
|
||||||
|
| Feature | Status |
|
||||||
|
|---------|--------|
|
||||||
|
| Markdown | ✓ |
|
||||||
|
| HTML | ✓ |
|
||||||
|
| PHP | ✓ |
|
||||||
|
| JavaScript | ✗ |
|
||||||
|
|
||||||
|
## Horizontal Rules
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
That line above is a horizontal rule, useful for separating sections.
|
||||||
|
|
||||||
|
## What You Can Do
|
||||||
|
|
||||||
|
- Write content in simple Markdown
|
||||||
|
- Mix HTML when needed
|
||||||
|
- Include images stored alongside your content
|
||||||
|
- Use all standard Markdown features
|
||||||
|
|
||||||
|
The file for this page is just `index.md` in a dated folder. The date is automatically extracted and displayed.
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
# Markdown-demonstrasjon
|
||||||
|
|
||||||
|
Denne siden viser hele utvalget av Markdown-formatering tilgjengelig gjennom Parsedown.
|
||||||
|
|
||||||
|
## Tekstformatering
|
||||||
|
|
||||||
|
Du kan bruke **fet tekst**, *kursiv tekst*, og til og med ***fet kursiv***.
|
||||||
|
|
||||||
|
Bruk `inline kode` for tekniske termer eller filstier som `/app/default/templates/`.
|
||||||
|
|
||||||
|
## Lister
|
||||||
|
|
||||||
|
Uspesifiserte lister:
|
||||||
|
|
||||||
|
- Første element
|
||||||
|
- Andre element
|
||||||
|
- Nestet element
|
||||||
|
- Enda et nestet element
|
||||||
|
- Tredje element
|
||||||
|
|
||||||
|
Ordnede lister:
|
||||||
|
|
||||||
|
1. Steg én
|
||||||
|
2. Steg to
|
||||||
|
3. Steg tre
|
||||||
|
|
||||||
|
## Lenker og bilder
|
||||||
|
|
||||||
|
Her er en [lenke til forsiden](/).
|
||||||
|
|
||||||
|
Bilder fungerer også (når du legger dem til i mappen):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Kodeblokker
|
||||||
|
|
||||||
|
```php
|
||||||
|
<?php
|
||||||
|
function hils(string $navn): string {
|
||||||
|
return "Hei, {$navn}!";
|
||||||
|
}
|
||||||
|
|
||||||
|
echo hils('Verden');
|
||||||
|
```
|
||||||
|
|
||||||
|
## Blokksitater
|
||||||
|
|
||||||
|
> Dette er et blokksitat. Perfekt for å fremheve viktig informasjon eller sitater fra andre kilder.
|
||||||
|
>
|
||||||
|
> Det kan spenne over flere avsnitt.
|
||||||
|
|
||||||
|
## Tabeller
|
||||||
|
|
||||||
|
| Funksjon | Status |
|
||||||
|
|---------|--------|
|
||||||
|
| Markdown | ✓ |
|
||||||
|
| HTML | ✓ |
|
||||||
|
| PHP | ✓ |
|
||||||
|
| JavaScript | ✗ |
|
||||||
|
|
||||||
|
## Horisontale linjer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Linjen over er en horisontal linje, nyttig for å skille seksjoner.
|
||||||
|
|
||||||
|
## Hva du kan gjøre
|
||||||
|
|
||||||
|
- Skriv innhold i enkel Markdown
|
||||||
|
- Bland inn HTML når det trengs
|
||||||
|
- Inkluder bilder lagret sammen med innholdet ditt
|
||||||
|
- Bruk alle standard Markdown-funksjoner
|
||||||
|
|
||||||
|
Filen for denne siden er bare `index.no.md` i en datert mappe. Datoen blir automatisk hentet ut og vist.
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
title = "Markdown Demonstration"
|
||||||
|
summary = "Shows all the Markdown formatting features available through Parsedown"
|
||||||
52
app/default/content/examples/file-based-routing/index.md
Normal file
52
app/default/content/examples/file-based-routing/index.md
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# File-Based Routing
|
||||||
|
|
||||||
|
FolderWeb's routing is beautifully simple: **your folder structure is your URL structure**. No configuration files, no route definitions, no magic strings.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
When you visit a URL, FolderWeb looks for matching folders and files:
|
||||||
|
|
||||||
|
```
|
||||||
|
/content/
|
||||||
|
├── index.md → /
|
||||||
|
├── about/
|
||||||
|
│ └── index.md → /about/
|
||||||
|
└── blog/
|
||||||
|
├── 2024-11-01-post/
|
||||||
|
│ └── index.md → /blog/post/
|
||||||
|
└── index.md → /blog/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Automatic Features
|
||||||
|
|
||||||
|
**Folder names become URLs** – Create a folder called `projects` and it's instantly available at `/projects/`
|
||||||
|
|
||||||
|
**Date prefixes are stripped** – `2024-11-01-my-post` becomes `/my-post/` in the URL
|
||||||
|
|
||||||
|
**Custom slugs via metadata** – Override the default URL with `slug = "custom-url"` in `metadata.ini`
|
||||||
|
|
||||||
|
**Trailing slashes** – Directories always redirect to include trailing slashes for consistency
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
This very page demonstrates file-based routing! The path is:
|
||||||
|
|
||||||
|
```
|
||||||
|
app/default/content/examples/file-based-routing/index.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Which renders at:
|
||||||
|
|
||||||
|
```
|
||||||
|
/examples/file-based-routing/
|
||||||
|
```
|
||||||
|
|
||||||
|
No routes to define. No configuration to update. Just files and folders.
|
||||||
|
|
||||||
|
## Benefits
|
||||||
|
|
||||||
|
- **Intuitive** – If you can navigate folders, you understand the routing
|
||||||
|
- **Refactor-friendly** – Moving content means moving folders
|
||||||
|
- **No broken links** – URLs match the filesystem
|
||||||
|
- **Fast** – No route matching overhead, direct file lookup
|
||||||
|
- **Predictable** – What you see is what you get
|
||||||
52
app/default/content/examples/file-based-routing/index.no.md
Normal file
52
app/default/content/examples/file-based-routing/index.no.md
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
# Filbasert routing
|
||||||
|
|
||||||
|
FolderWebs routing er vakkert enkelt: **mappestrukturen din er URL-strukturen din**. Ingen konfigurasjonsfiler, ingen rutedefinisjon, ingen magiske strenger.
|
||||||
|
|
||||||
|
## Hvordan det fungerer
|
||||||
|
|
||||||
|
Når du besøker en URL, ser FolderWeb etter matchende mapper og filer:
|
||||||
|
|
||||||
|
```
|
||||||
|
/content/
|
||||||
|
├── index.md → /
|
||||||
|
├── om/
|
||||||
|
│ └── index.md → /om/
|
||||||
|
└── blogg/
|
||||||
|
├── 2024-11-01-innlegg/
|
||||||
|
│ └── index.md → /blogg/innlegg/
|
||||||
|
└── index.md → /blogg/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Automatiske funksjoner
|
||||||
|
|
||||||
|
**Mappenavn blir URL-er** – Lag en mappe kalt `prosjekter` og den er umiddelbart tilgjengelig på `/prosjekter/`
|
||||||
|
|
||||||
|
**Datoprefikser fjernes** – `2024-11-01-mitt-innlegg` blir `/mitt-innlegg/` i URL-en
|
||||||
|
|
||||||
|
**Egendefinerte slugs via metadata** – Overstyr standard URL med `slug = "egendefinert-url"` i `metadata.ini`
|
||||||
|
|
||||||
|
**Avsluttende skråstrek** – Kataloger omdirigerer alltid til å inkludere avsluttende skråstrek for konsistens
|
||||||
|
|
||||||
|
## Eksempel
|
||||||
|
|
||||||
|
Denne siden demonstrerer filbasert routing! Stien er:
|
||||||
|
|
||||||
|
```
|
||||||
|
app/default/content/examples/file-based-routing/index.no.md
|
||||||
|
```
|
||||||
|
|
||||||
|
Som rendres på:
|
||||||
|
|
||||||
|
```
|
||||||
|
/examples/file-based-routing/
|
||||||
|
```
|
||||||
|
|
||||||
|
Ingen ruter å definere. Ingen konfigurasjon å oppdatere. Bare filer og mapper.
|
||||||
|
|
||||||
|
## Fordeler
|
||||||
|
|
||||||
|
- **Intuitivt** – Hvis du kan navigere mapper, forstår du routingen
|
||||||
|
- **Refaktorvennlig** – Å flytte innhold betyr å flytte mapper
|
||||||
|
- **Ingen ødelagte lenker** – URL-er matcher filsystemet
|
||||||
|
- **Rask** – Ingen rutematching overhead, direkte filoppslag
|
||||||
|
- **Forutsigbar** – Det du ser er det du får
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
title = "File-Based Routing"
|
||||||
|
summary = "Your folder structure is your URL structure—no configuration needed"
|
||||||
|
date = "2024-11-26"
|
||||||
5
app/default/content/examples/index.md
Normal file
5
app/default/content/examples/index.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Examples
|
||||||
|
|
||||||
|
This section demonstrates different content types and features you can use in FolderWeb.
|
||||||
|
|
||||||
|
Each example below shows a different capability. Click through to see the source and how it works.
|
||||||
11
app/default/content/examples/mix-formats/00-intro.md
Normal file
11
app/default/content/examples/mix-formats/00-intro.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Mix Formats
|
||||||
|
|
||||||
|
FolderWeb lets you **combine different file types** in the same directory. Markdown, HTML, and PHP files can coexist and render together on a single page.
|
||||||
|
|
||||||
|
## Supported Formats
|
||||||
|
|
||||||
|
- **`.md`** – Markdown for content (this file!)
|
||||||
|
- **`.html`** – Static HTML snippets
|
||||||
|
- **`.php`** – Dynamic PHP components
|
||||||
|
|
||||||
|
All files in the same directory are rendered in **alphabetical order**. Use number prefixes (like `00-`, `01-`, `02-`) to control the sequence.
|
||||||
11
app/default/content/examples/mix-formats/00-intro.no.md
Normal file
11
app/default/content/examples/mix-formats/00-intro.no.md
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Bland formater
|
||||||
|
|
||||||
|
FolderWeb lar deg **kombinere forskjellige filtyper** i samme katalog. Markdown, HTML og PHP-filer kan eksistere side om side og rendres sammen på en enkelt side.
|
||||||
|
|
||||||
|
## Støttede formater
|
||||||
|
|
||||||
|
- **`.md`** – Markdown for innhold (denne filen!)
|
||||||
|
- **`.html`** – Statiske HTML-snippets
|
||||||
|
- **`.php`** – Dynamiske PHP-komponenter
|
||||||
|
|
||||||
|
Alle filer i samme katalog rendres i **alfabetisk rekkefølge**. Bruk talprefikser (som `00-`, `01-`, `02-`) for å kontrollere sekvensen.
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<section style="background: var(--color-accent-light); padding: var(--space-m); border-radius: 0.5rem; margin-block: var(--space-m);">
|
||||||
|
<h2 style="margin-block-start: 0;">HTML Component</h2>
|
||||||
|
<p>This section is rendered from <code>01-html-example.html</code> – a static HTML file with inline styles.</p>
|
||||||
|
<p>HTML files are perfect for:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Custom-styled sections</li>
|
||||||
|
<li>Embedded media (video, audio, iframes)</li>
|
||||||
|
<li>Interactive elements (forms, details/summary)</li>
|
||||||
|
<li>SVG graphics and diagrams</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<section style="background: var(--color-accent-light); padding: var(--space-m); border-radius: 0.5rem; margin-block: var(--space-m);">
|
||||||
|
<h2 style="margin-block-start: 0;">HTML-komponent</h2>
|
||||||
|
<p>Denne seksjonen rendres fra <code>01-html-example.no.html</code> – en statisk HTML-fil med inline stiler.</p>
|
||||||
|
<p>HTML-filer er perfekte for:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Egendefinerte stilte seksjoner</li>
|
||||||
|
<li>Innebygde medier (video, lyd, iframes)</li>
|
||||||
|
<li>Interaktive elementer (skjemaer, detaljer/sammendrag)</li>
|
||||||
|
<li>SVG-grafikk og diagrammer</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
41
app/default/content/examples/mix-formats/02-dynamic.no.php
Normal file
41
app/default/content/examples/mix-formats/02-dynamic.no.php
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
$currentTime = date('H:i:s');
|
||||||
|
$serverInfo = [
|
||||||
|
'PHP-versjon' => PHP_VERSION,
|
||||||
|
'Servertid' => $currentTime,
|
||||||
|
'Filer i denne katalogen' => count(glob(__DIR__ . '/*')),
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section style="background: linear-gradient(135deg, oklch(95% 0.08 150), oklch(98% 0.04 150)); padding: var(--space-m); border-radius: 0.5rem; margin-block: var(--space-m);">
|
||||||
|
<h2 style="margin-block-start: 0;">PHP-komponent</h2>
|
||||||
|
<p>Denne seksjonen rendres fra <code>02-dynamic.no.php</code> – en dynamisk PHP-fil som kjører på serversiden.</p>
|
||||||
|
|
||||||
|
<table style="width: 100%; margin-block: var(--space-s);">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Egenskap</th>
|
||||||
|
<th>Verdi</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($serverInfo as $key => $value): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($key) ?></td>
|
||||||
|
<td><strong><?= htmlspecialchars($value) ?></strong></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>PHP-filer kan:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Generere dynamisk innhold</li>
|
||||||
|
<li>Spørre databaser</li>
|
||||||
|
<li>Lese fra filer eller API-er</li>
|
||||||
|
<li>Beregne verdier i farten</li>
|
||||||
|
<li>Inkludere egendefinert styling og logikk</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><em>Last inn denne siden på nytt for å se servertiden oppdatere seg!</em></p>
|
||||||
|
</section>
|
||||||
41
app/default/content/examples/mix-formats/02-dynamic.php
Normal file
41
app/default/content/examples/mix-formats/02-dynamic.php
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
<?php
|
||||||
|
$currentTime = date('H:i:s');
|
||||||
|
$serverInfo = [
|
||||||
|
'PHP Version' => PHP_VERSION,
|
||||||
|
'Server Time' => $currentTime,
|
||||||
|
'Files in this directory' => count(glob(__DIR__ . '/*')),
|
||||||
|
];
|
||||||
|
?>
|
||||||
|
|
||||||
|
<section style="background: linear-gradient(135deg, oklch(95% 0.08 150), oklch(98% 0.04 150)); padding: var(--space-m); border-radius: 0.5rem; margin-block: var(--space-m);">
|
||||||
|
<h2 style="margin-block-start: 0;">PHP Component</h2>
|
||||||
|
<p>This section is rendered from <code>02-dynamic.php</code> – a dynamic PHP file that executes server-side.</p>
|
||||||
|
|
||||||
|
<table style="width: 100%; margin-block: var(--space-s);">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Property</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<?php foreach ($serverInfo as $key => $value): ?>
|
||||||
|
<tr>
|
||||||
|
<td><?= htmlspecialchars($key) ?></td>
|
||||||
|
<td><strong><?= htmlspecialchars($value) ?></strong></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<p>PHP files can:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Generate dynamic content</li>
|
||||||
|
<li>Query databases</li>
|
||||||
|
<li>Read from files or APIs</li>
|
||||||
|
<li>Calculate values on-the-fly</li>
|
||||||
|
<li>Include custom styling and logic</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<p><em>Refresh this page to see the server time update!</em></p>
|
||||||
|
</section>
|
||||||
38
app/default/content/examples/mix-formats/03-conclusion.md
Normal file
38
app/default/content/examples/mix-formats/03-conclusion.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
## Why Mix Formats?
|
||||||
|
|
||||||
|
Different content types have different needs:
|
||||||
|
|
||||||
|
**Markdown** – Fast authoring, readable source, perfect for articles and documentation
|
||||||
|
|
||||||
|
**HTML** – Full control over structure, ideal for custom layouts and embedded media
|
||||||
|
|
||||||
|
**PHP** – Dynamic content, server-side logic, database queries, real-time data
|
||||||
|
|
||||||
|
By mixing them freely, you get the best of all worlds. Write most content in Markdown for speed, add HTML for custom sections, and use PHP when you need dynamic behavior.
|
||||||
|
|
||||||
|
## This Page's Structure
|
||||||
|
|
||||||
|
This very page demonstrates format mixing:
|
||||||
|
|
||||||
|
```
|
||||||
|
/examples/mix-formats/
|
||||||
|
├── 00-intro.md (Markdown introduction)
|
||||||
|
├── 01-html-example.html (Static HTML component)
|
||||||
|
├── 02-dynamic.php (Dynamic PHP component)
|
||||||
|
├── 03-conclusion.md (Markdown conclusion)
|
||||||
|
└── metadata.ini (Page metadata)
|
||||||
|
```
|
||||||
|
|
||||||
|
All four content files render seamlessly as one cohesive page. The numbered prefixes ensure they appear in the intended order.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
**Blog with special sections** – Markdown posts with custom HTML callouts or PHP-generated stats
|
||||||
|
|
||||||
|
**Documentation with demos** – Markdown explanations with live PHP examples
|
||||||
|
|
||||||
|
**Landing pages** – Mix markdown content with custom HTML heroes and PHP dynamic elements
|
||||||
|
|
||||||
|
**Portfolio sites** – Markdown project descriptions with HTML galleries and PHP filtering
|
||||||
|
|
||||||
|
The flexibility is yours.
|
||||||
38
app/default/content/examples/mix-formats/03-conclusion.no.md
Normal file
38
app/default/content/examples/mix-formats/03-conclusion.no.md
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
## Hvorfor blande formater?
|
||||||
|
|
||||||
|
Forskjellige innholdstyper har forskjellige behov:
|
||||||
|
|
||||||
|
**Markdown** – Rask redigering, lesbar kilde, perfekt for artikler og dokumentasjon
|
||||||
|
|
||||||
|
**HTML** – Full kontroll over struktur, ideelt for egendefinerte layout og innebygde medier
|
||||||
|
|
||||||
|
**PHP** – Dynamisk innhold, server-side logikk, databasespørringer, sanntidsdata
|
||||||
|
|
||||||
|
Ved å blande dem fritt, får du det beste fra alle verdener. Skriv mesteparten av innholdet i Markdown for hastighet, legg til HTML for egendefinerte seksjoner, og bruk PHP når du trenger dynamisk oppførsel.
|
||||||
|
|
||||||
|
## Denne sidens struktur
|
||||||
|
|
||||||
|
Denne siden demonstrerer formatblanding:
|
||||||
|
|
||||||
|
```
|
||||||
|
/examples/mix-formats/
|
||||||
|
├── 00-intro.no.md (Markdown-introduksjon)
|
||||||
|
├── 01-html-example.no.html (Statisk HTML-komponent)
|
||||||
|
├── 02-dynamic.no.php (Dynamisk PHP-komponent)
|
||||||
|
├── 03-conclusion.no.md (Markdown-konklusjon)
|
||||||
|
└── metadata.ini (Sidemetadata)
|
||||||
|
```
|
||||||
|
|
||||||
|
Alle fire innholdsfilene rendres sømløst som én sammenhengende side. De nummererte prefiksene sikrer at de vises i ønsket rekkefølge.
|
||||||
|
|
||||||
|
## Bruksområder
|
||||||
|
|
||||||
|
**Blogg med spesialseksjoner** – Markdown-innlegg med egendefinerte HTML-callouts eller PHP-generert statistikk
|
||||||
|
|
||||||
|
**Dokumentasjon med demoer** – Markdown-forklaringer med live PHP-eksempler
|
||||||
|
|
||||||
|
**Landingssider** – Bland markdown-innhold med egendefinerte HTML-heroer og PHP-dynamiske elementer
|
||||||
|
|
||||||
|
**Porteføljesider** – Markdown-prosjektbeskrivelser med HTML-gallerier og PHP-filtrering
|
||||||
|
|
||||||
|
Fleksibiliteten er din.
|
||||||
3
app/default/content/examples/mix-formats/metadata.ini
Normal file
3
app/default/content/examples/mix-formats/metadata.ini
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
title = "Mix Formats"
|
||||||
|
summary = "Combine Markdown, HTML, and PHP files to create rich, dynamic pages"
|
||||||
|
date = "2024-11-26"
|
||||||
5
app/default/content/examples/nested/index.md
Normal file
5
app/default/content/examples/nested/index.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Nested Content
|
||||||
|
|
||||||
|
FolderWeb supports content nested as deep as you need. Your folder structure is your URL structure.
|
||||||
|
|
||||||
|
This section demonstrates multi-level nesting. Explore the subdirectories to see how it works.
|
||||||
5
app/default/content/examples/nested/level-two/index.md
Normal file
5
app/default/content/examples/nested/level-two/index.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Level Two
|
||||||
|
|
||||||
|
This is two levels deep: `/examples/nested/level-two/`
|
||||||
|
|
||||||
|
You can organize content in hierarchies that make sense for your site structure.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# Level Three
|
||||||
|
|
||||||
|
This is three levels deep: `/examples/nested/level-two/level-three/`
|
||||||
|
|
||||||
|
The URL structure automatically follows your folder organization. No routing configuration needed.
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# Level Four
|
||||||
|
|
||||||
|
Four levels deep: `/examples/nested/level-two/level-three/level-four/`
|
||||||
|
|
||||||
|
## How Deep Can You Go?
|
||||||
|
|
||||||
|
As deep as your filesystem allows. Each folder can contain:
|
||||||
|
|
||||||
|
- Content files (`.md`, `.html`, `.php`)
|
||||||
|
- Subdirectories (which become sub-URLs)
|
||||||
|
- Assets (images, PDFs, etc.)
|
||||||
|
- A `metadata.ini` file
|
||||||
|
- A `styles.css` file for page-specific styles
|
||||||
|
|
||||||
|
## Navigation
|
||||||
|
|
||||||
|
There's no automatic breadcrumb generation in the default theme, but you can add it in a custom template by parsing the URL path.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
**Documentation sites** – Organize by category/subcategory/topic
|
||||||
|
|
||||||
|
**Photo galleries** – Year/month/album/photo
|
||||||
|
|
||||||
|
**Course content** – Course/module/lesson/exercise
|
||||||
|
|
||||||
|
**Product catalogs** – Category/subcategory/product
|
||||||
|
|
||||||
|
The simplicity of file-based routing means your content organization is transparent and portable.
|
||||||
47
app/default/content/examples/no-build-step/index.md
Normal file
47
app/default/content/examples/no-build-step/index.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# No Build Step
|
||||||
|
|
||||||
|
Modern web development often involves complex build pipelines: transpilers, bundlers, minifiers, watchers, and more. FolderWeb takes a different approach: **edit a file, refresh the page, see the result**.
|
||||||
|
|
||||||
|
## What This Means
|
||||||
|
|
||||||
|
**No npm install** – No package.json, no node_modules folder, no dependency hell
|
||||||
|
|
||||||
|
**No compilation** – Write PHP, HTML, CSS, and Markdown directly
|
||||||
|
|
||||||
|
**No watching** – No background processes monitoring file changes
|
||||||
|
|
||||||
|
**No bundling** – Files are served as-is, leveraging HTTP/2 multiplexing
|
||||||
|
|
||||||
|
**No transpiling** – Modern PHP 8.4+ and CSS work in all browsers
|
||||||
|
|
||||||
|
## The Development Flow
|
||||||
|
|
||||||
|
Traditional workflow:
|
||||||
|
```
|
||||||
|
Edit file → Save → Wait for build → Reload → See changes
|
||||||
|
```
|
||||||
|
|
||||||
|
FolderWeb workflow:
|
||||||
|
```
|
||||||
|
Edit file → Save → Reload → See changes
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it. No waiting. No build errors. No cache invalidation headaches.
|
||||||
|
|
||||||
|
## But What About...
|
||||||
|
|
||||||
|
**Performance?** – PHP is fast. No JavaScript means pages load instantly. CSS is cacheable with MD5 versioning.
|
||||||
|
|
||||||
|
**Minification?** – HTTP/2 compression handles this. Serve from behind a CDN if needed.
|
||||||
|
|
||||||
|
**Modern CSS?** – CSS nesting, custom properties, oklch() colors, grid—all native, all supported.
|
||||||
|
|
||||||
|
**Development speed?** – Instant feedback loop beats any hot reload system.
|
||||||
|
|
||||||
|
## Why This Matters
|
||||||
|
|
||||||
|
Build steps add complexity. Every dependency is a potential breaking change. Every tool is another thing to learn, configure, and maintain.
|
||||||
|
|
||||||
|
FolderWeb will work the same way in 5 years, 10 years, even 20 years. Your content is just files. Your templates are just PHP. Your styles are just CSS.
|
||||||
|
|
||||||
|
**Simple lasts.**
|
||||||
47
app/default/content/examples/no-build-step/index.no.md
Normal file
47
app/default/content/examples/no-build-step/index.no.md
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Uten byggesteg
|
||||||
|
|
||||||
|
Moderne webutvikling involverer ofte komplekse byggepipelines: transpilere, bundlere, minifisere, overvåkere og mer. FolderWeb tar en annen tilnærming: **rediger en fil, oppdater siden, se resultatet**.
|
||||||
|
|
||||||
|
## Hva dette betyr
|
||||||
|
|
||||||
|
**Ingen npm install** – Ingen package.json, ingen node_modules-mappe, ingen avhengighetshelvete
|
||||||
|
|
||||||
|
**Ingen kompilering** – Skriv PHP, HTML, CSS og Markdown direkte
|
||||||
|
|
||||||
|
**Ingen overvåkning** – Ingen bakgrunnsprosesser som overvåker filendringer
|
||||||
|
|
||||||
|
**Ingen bundling** – Filer serveres som de er, ved å utnytte HTTP/2-multipleksing
|
||||||
|
|
||||||
|
**Ingen transpilering** – Moderne PHP 8.4+ og CSS fungerer i alle nettlesere
|
||||||
|
|
||||||
|
## Utviklingsflyten
|
||||||
|
|
||||||
|
Tradisjonell arbeidsflyt:
|
||||||
|
```
|
||||||
|
Rediger fil → Lagre → Vent på bygg → Last inn på nytt → Se endringer
|
||||||
|
```
|
||||||
|
|
||||||
|
FolderWeb-arbeidsflyt:
|
||||||
|
```
|
||||||
|
Rediger fil → Lagre → Last inn på nytt → Se endringer
|
||||||
|
```
|
||||||
|
|
||||||
|
Det er det. Ingen venting. Ingen byggefeil. Ingen cache-invalideringshodebryet.
|
||||||
|
|
||||||
|
## Men hva med...
|
||||||
|
|
||||||
|
**Ytelse?** – PHP er rask. Ingen JavaScript betyr at sider lastes øyeblikkelig. CSS er cachebar med MD5-versjonering.
|
||||||
|
|
||||||
|
**Minifisering?** – HTTP/2-kompresjon håndterer dette. Server fra bak en CDN om nødvendig.
|
||||||
|
|
||||||
|
**Moderne CSS?** – CSS-nesting, custom properties, oklch()-farger, grid—alt native, alt støttet.
|
||||||
|
|
||||||
|
**Utviklingshastighet?** – Øyeblikkelig tilbakemeldingssløyfe slår ethvert hot reload-system.
|
||||||
|
|
||||||
|
## Hvorfor dette betyr noe
|
||||||
|
|
||||||
|
Byggesteg legger til kompleksitet. Hver avhengighet er en potensiell breaking change. Hvert verktøy er noe nytt å lære, konfigurere og vedlikeholde.
|
||||||
|
|
||||||
|
FolderWeb vil fungere på samme måte om 5 år, 10 år, til og med 20 år. Innholdet ditt er bare filer. Malene dine er bare PHP. Stilene dine er bare CSS.
|
||||||
|
|
||||||
|
**Enkelt varer.**
|
||||||
3
app/default/content/examples/no-build-step/metadata.ini
Normal file
3
app/default/content/examples/no-build-step/metadata.ini
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
title = "No Build Step"
|
||||||
|
summary = "Edit, save, refresh—that's it. No compilation, no bundling, no waiting"
|
||||||
|
date = "2024-11-26"
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Oldest Post
|
||||||
|
|
||||||
|
The oldest post in this compact list.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Older Post
|
||||||
|
|
||||||
|
An older post showing date ordering.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Recent Post
|
||||||
|
|
||||||
|
A recent example post.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Compact Template
|
||||||
|
|
||||||
|
This section uses `list-compact.php` for a minimal, clean list view.
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
title = "Compact Template Example"
|
||||||
|
|
||||||
|
[settings]
|
||||||
|
page_template = "list-compact"
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Grid Template
|
||||||
|
|
||||||
|
This section uses `list-grid.php` to display items in a responsive grid with cards.
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Grid Item One
|
||||||
|
|
||||||
|
This is an example item in the grid layout.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
summary = "First item demonstrating grid layout"
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Grid Item Three
|
||||||
|
|
||||||
|
A third item to show the grid layout with multiple items.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
summary = "Third grid item"
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
# Grid Item Two
|
||||||
|
|
||||||
|
Another example showing how items appear in the grid.
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
summary = "Second item in the grid"
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
title = "Grid Template Example"
|
||||||
|
|
||||||
|
[settings]
|
||||||
|
page_template = "list-grid"
|
||||||
24
app/default/content/examples/templates-demo/index.md
Normal file
24
app/default/content/examples/templates-demo/index.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# List Templates
|
||||||
|
|
||||||
|
FolderWeb includes several list templates you can use for different presentation styles.
|
||||||
|
|
||||||
|
This section demonstrates the different templates by using subdirectories. Each subdirectory below uses a different template via `metadata.ini`.
|
||||||
|
|
||||||
|
## Available Templates
|
||||||
|
|
||||||
|
**list.php** – Default linear list with optional cover images
|
||||||
|
|
||||||
|
**list-grid.php** – Grid layout with cards, great for galleries or portfolios
|
||||||
|
|
||||||
|
**list-compact.php** – Minimal list showing just titles and dates
|
||||||
|
|
||||||
|
## Choosing a Template
|
||||||
|
|
||||||
|
In any folder's `metadata.ini`, add:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[settings]
|
||||||
|
page_template = "list-grid"
|
||||||
|
```
|
||||||
|
|
||||||
|
The three subdirectories below each use a different template. Compare them to see which fits your content best.
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
# Welcome to FolderWeb
|
|
||||||
|
|
||||||
This is the default content. Replace it by adding your own files to the `content/` directory.
|
|
||||||
|
|
||||||
Your folder structure becomes your site structure — create a folder, drop in a `.md`, `.html`, or `.php` file, and it's a page.
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
1. Create a `content/` directory in your project root
|
|
||||||
2. Copy `app/default/` to `custom/` to start customizing
|
|
||||||
3. Add content files and refresh the browser
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
# Velkommen til FolderWeb
|
|
||||||
|
|
||||||
Dette er standardinnholdet. Erstatt det ved å legge til egne filer i `content/`-mappen.
|
|
||||||
|
|
||||||
Mappestrukturen din blir nettstedets struktur — opprett en mappe, legg inn en `.md`-, `.html`- eller `.php`-fil, og den blir en side.
|
|
||||||
|
|
||||||
## Kom i gang
|
|
||||||
|
|
||||||
1. Opprett en `content/`-mappe i prosjektets rotmappe
|
|
||||||
2. Kopier `app/default/` til `custom/` for å begynne å tilpasse
|
|
||||||
3. Legg til innholdsfiler og oppdater nettleseren
|
|
||||||
53
app/default/content/multilingual/index.en.md
Normal file
53
app/default/content/multilingual/index.en.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Multilingual Support
|
||||||
|
|
||||||
|
**Currently viewing: English (EN)**
|
||||||
|
|
||||||
|
This page demonstrates FolderWeb's built-in language support. Use the language switcher in the header to toggle between **EN** and **NO** - the page content will change to match your selected language.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
Creating multilingual content is straightforward:
|
||||||
|
|
||||||
|
1. **Add language codes to filenames** – Create `index.en.md` and `index.no.md`
|
||||||
|
2. **Language switcher appears automatically** – When multiple versions exist
|
||||||
|
3. **URLs get language prefixes** – Non-default languages use `/en/page/`, `/no/page/`, etc.
|
||||||
|
4. **Share metadata** – One `metadata.ini` file per directory works for all languages
|
||||||
|
|
||||||
|
## File Structure Example
|
||||||
|
|
||||||
|
```
|
||||||
|
/content/about/
|
||||||
|
├── index.en.md # English version
|
||||||
|
├── index.no.md # Norwegian version
|
||||||
|
└── metadata.ini # Shared metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Enable languages in `/app/default/config.ini`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[languages]
|
||||||
|
default = "en"
|
||||||
|
available = "en,no"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Translation Strings
|
||||||
|
|
||||||
|
UI text comes from language files in `/app/default/languages/`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
; en.ini
|
||||||
|
home = "Home"
|
||||||
|
read_more = "Read more"
|
||||||
|
|
||||||
|
; no.ini
|
||||||
|
home = "Hjem"
|
||||||
|
read_more = "Les mer"
|
||||||
|
```
|
||||||
|
|
||||||
|
Translation strings are optional—templates fall back to sensible defaults if missing.
|
||||||
|
|
||||||
|
## Try It
|
||||||
|
|
||||||
|
Use the language switcher in the header to toggle between English and Norwegian versions of this page. The content stays the same, just translated.
|
||||||
53
app/default/content/multilingual/index.no.md
Normal file
53
app/default/content/multilingual/index.no.md
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Flerspråklig støtte
|
||||||
|
|
||||||
|
**Viser nå: Norsk (NO)**
|
||||||
|
|
||||||
|
Denne siden demonstrerer FolderWebs innebygde språkstøtte. Bruk språkvelgeren i headeren for å bytte mellom **EN** og **NO** - sideinnholdet vil endre seg til å matche det valgte språket.
|
||||||
|
|
||||||
|
## Slik fungerer det
|
||||||
|
|
||||||
|
Å opprette flerspråklig innhold er enkelt:
|
||||||
|
|
||||||
|
1. **Legg til språkkoder i filnavn** – Opprett `index.en.md` og `index.no.md`
|
||||||
|
2. **Språkvelgeren vises automatisk** – Når flere versjoner finnes
|
||||||
|
3. **URL-er får språkprefikser** – Ikke-standardspråk bruker `/en/side/`, `/no/side/`, osv.
|
||||||
|
4. **Del metadata** – Én `metadata.ini`-fil per katalog fungerer for alle språk
|
||||||
|
|
||||||
|
## Eksempel på filstruktur
|
||||||
|
|
||||||
|
```
|
||||||
|
/content/om/
|
||||||
|
├── index.en.md # Engelsk versjon
|
||||||
|
├── index.no.md # Norsk versjon
|
||||||
|
└── metadata.ini # Delt metadata
|
||||||
|
```
|
||||||
|
|
||||||
|
## Konfigurasjon
|
||||||
|
|
||||||
|
Aktiver språk i `/app/default/config.ini`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[languages]
|
||||||
|
default = "en"
|
||||||
|
available = "en,no"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Oversettelsesstrenger
|
||||||
|
|
||||||
|
UI-tekst kommer fra språkfiler i `/app/default/languages/`:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
; en.ini
|
||||||
|
home = "Home"
|
||||||
|
read_more = "Read more"
|
||||||
|
|
||||||
|
; no.ini
|
||||||
|
home = "Hjem"
|
||||||
|
read_more = "Les mer"
|
||||||
|
```
|
||||||
|
|
||||||
|
Oversettelsesstrenger er valgfrie—maler faller tilbake til fornuftige standardverdier hvis de mangler.
|
||||||
|
|
||||||
|
## Prøv det
|
||||||
|
|
||||||
|
Bruk språkvelgeren i headeren for å bytte mellom engelske og norske versjoner av denne siden. Innholdet forblir det samme, bare oversatt.
|
||||||
2
app/default/content/multilingual/metadata.ini
Normal file
2
app/default/content/multilingual/metadata.ini
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
title = "Multilingual Support"
|
||||||
|
summary = "Learn how to create content in multiple languages"
|
||||||
112
app/default/content/styles.css
Normal file
112
app/default/content/styles.css
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
/* Hero section for frontpage */
|
||||||
|
.hero {
|
||||||
|
background: linear-gradient(135deg, oklch(95% 0.05 250) 0%, oklch(98% 0.02 250) 100%);
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
padding: var(--space-m);
|
||||||
|
margin-block: var(--space-s);
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
& .hero-title {
|
||||||
|
font-size: clamp(2rem, 5vw, 2.5rem);
|
||||||
|
margin-block: 0 var(--space-xs);
|
||||||
|
background: linear-gradient(135deg, oklch(40% 0.15 250), oklch(30% 0.2 280));
|
||||||
|
background-clip: text;
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
font-weight: 900;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .hero-subtitle {
|
||||||
|
font-size: clamp(1rem, 2vw, 1.125rem);
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin-block-end: var(--space-m);
|
||||||
|
max-width: 32rem;
|
||||||
|
margin-inline: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .cta-button {
|
||||||
|
display: inline-block;
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: white;
|
||||||
|
padding: var(--space-xs) var(--space-m);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-block-start: var(--space-xs);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: oklch(45% 0.15 250);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.features {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(min(100%, 9rem), 1fr));
|
||||||
|
gap: var(--space-s);
|
||||||
|
margin-block: var(--space-m);
|
||||||
|
|
||||||
|
& .feature-card {
|
||||||
|
background: var(--color-background);
|
||||||
|
padding: var(--space-s);
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
text-align: center;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
text-decoration: none;
|
||||||
|
color: inherit;
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px oklch(0% 0 0 / 0.1);
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .feature-icon {
|
||||||
|
font-size: 2rem;
|
||||||
|
display: block;
|
||||||
|
margin-block-end: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .feature-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-block: var(--space-xs);
|
||||||
|
color: var(--color-text);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .feature-description {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--color-muted);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--space-m);
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-block-start: var(--space-m);
|
||||||
|
padding-block-start: var(--space-s);
|
||||||
|
border-block-start: 1px solid var(--color-border);
|
||||||
|
|
||||||
|
& .stat {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
& .stat-value {
|
||||||
|
display: block;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
& .stat-label {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--color-muted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,8 @@
|
||||||
/* RESET */
|
|
||||||
* { box-sizing: border-box; margin-bottom: 0 }
|
|
||||||
|
|
||||||
/* VARIABLES */
|
|
||||||
:root {
|
:root {
|
||||||
--color-text: oklch(20% 0 0);
|
--color-text: oklch(20% 0 0);
|
||||||
--color-background: oklch(98% 0 0);
|
--color-background: oklch(98% 0 0);
|
||||||
--color-accent: oklch(50% 0.15 250);
|
--color-accent: oklch(50% 0.15 250);
|
||||||
|
--color-accent-light: oklch(95% 0.05 250);
|
||||||
--color-border: oklch(85% 0 0);
|
--color-border: oklch(85% 0 0);
|
||||||
--color-muted: oklch(50% 0 0);
|
--color-muted: oklch(50% 0 0);
|
||||||
|
|
||||||
|
|
@ -14,13 +11,17 @@
|
||||||
--space-m: 2rem;
|
--space-m: 2rem;
|
||||||
--space-l: 4rem;
|
--space-l: 4rem;
|
||||||
|
|
||||||
--size-content: 42rem;
|
--size-content: 65ch;
|
||||||
|
--size-constrained: 42rem;
|
||||||
|
--size-wide: 90rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GLOBAL */
|
|
||||||
html {
|
html {
|
||||||
font-family: system-ui, sans-serif;
|
font-family: system-ui, sans-serif;
|
||||||
font-size: clamp(16px, 2.3vw, 20px);
|
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
color: var(--color-text);
|
color: var(--color-text);
|
||||||
background: var(--color-background);
|
background: var(--color-background);
|
||||||
|
|
@ -29,99 +30,139 @@ html {
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
grid-template-columns:
|
||||||
|
[full-start] minmax(var(--space-s), 1fr)
|
||||||
|
[content-start] minmax(0, var(--size-constrained))
|
||||||
|
[content-end] minmax(var(--space-s), 1fr)
|
||||||
|
[full-end];
|
||||||
grid-template-rows: auto 1fr auto;
|
grid-template-rows: auto 1fr auto;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CONTENT CENTERING */
|
|
||||||
.contain, :where(main > article, main > section, main > div) {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: minmax(var(--space-s), 1fr) minmax(0, var(--size-content)) minmax(var(--space-s), 1fr);
|
|
||||||
& > * { grid-column: 2 }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TYPOGRAPHY */
|
|
||||||
h1, h2, h3, h4, h5, h6 {
|
h1, h2, h3, h4, h5, h6 {
|
||||||
font-weight: 400;
|
line-height: 1.2;
|
||||||
line-height: 1.3;
|
margin-block: 1.5em 0.5em;
|
||||||
margin-top: 1.3em;
|
|
||||||
text-wrap: pretty;
|
|
||||||
}
|
}
|
||||||
h1 { font-size: 2.3rem }
|
|
||||||
h4 { font-weight: 700 }
|
|
||||||
|
|
||||||
p, ul, ol, dl { margin-block: 1em }
|
h1 { font-size: clamp(2rem, 5vw, 3rem); }
|
||||||
|
h2 { font-size: clamp(1.5rem, 4vw, 2rem); }
|
||||||
|
h3 { font-size: clamp(1.25rem, 3vw, 1.5rem); }
|
||||||
|
|
||||||
|
p, ul, ol, dl {
|
||||||
|
max-width: var(--size-content);
|
||||||
|
margin-block: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--color-accent);
|
color: var(--color-accent);
|
||||||
text-decoration: none;
|
text-underline-offset: 0.2em;
|
||||||
&:hover { text-decoration: underline }
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
img { max-width: 100%; height: auto; display: block }
|
header, main, footer {
|
||||||
|
grid-column: content;
|
||||||
|
}
|
||||||
|
|
||||||
/* HEADER */
|
|
||||||
header {
|
header {
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-block-end: 1px solid var(--color-border);
|
||||||
|
padding-block: var(--space-s);
|
||||||
& > div {
|
display: flex;
|
||||||
display: flex;
|
flex-wrap: wrap;
|
||||||
flex-wrap: wrap;
|
gap: var(--space-s);
|
||||||
gap: var(--space-s);
|
align-items: center;
|
||||||
align-items: center;
|
|
||||||
padding-block: var(--space-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
& nav {
|
& nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--space-s);
|
gap: var(--space-s);
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&:first-child { flex: 1 }
|
&:first-child {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
& a {
|
& a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
&:hover { text-decoration: underline }
|
|
||||||
&[aria-current] { font-weight: bold }
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[aria-current] {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .language-switcher {
|
||||||
|
margin-inline-start: auto;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* MAIN */
|
main {
|
||||||
main { padding-block: var(--space-m) }
|
padding-block: var(--space-m);
|
||||||
|
}
|
||||||
|
|
||||||
/* FOOTER */
|
|
||||||
footer {
|
footer {
|
||||||
border-top: 1px solid var(--color-border);
|
border-block-start: 1px solid var(--color-border);
|
||||||
padding-block: var(--space-s);
|
padding-block: var(--space-s);
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
color: var(--color-muted);
|
color: var(--color-muted);
|
||||||
& p { margin: 0 }
|
|
||||||
|
& nav {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-s);
|
||||||
|
margin-block-end: var(--space-xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CODE */
|
|
||||||
code {
|
code {
|
||||||
background: oklch(95% 0.02 250);
|
background: var(--color-accent-light);
|
||||||
padding: 0.125em 0.25em;
|
padding: 0.125em 0.25em;
|
||||||
border-radius: 0.25em;
|
border-radius: 0.25em;
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
background: oklch(95% 0.02 250);
|
background: var(--color-accent-light);
|
||||||
padding: var(--space-s);
|
padding: var(--space-s);
|
||||||
border-radius: 0.5em;
|
border-radius: 0.5em;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
& code { background: none; padding: 0 }
|
|
||||||
|
& code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TABLES */
|
table {
|
||||||
table { border-collapse: collapse; width: 100%; margin-block: var(--space-s) }
|
border-collapse: collapse;
|
||||||
th, td { padding: var(--space-xs); text-align: start; border-bottom: 1px solid var(--color-border) }
|
width: 100%;
|
||||||
th { font-weight: bold }
|
margin-block: var(--space-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
padding: var(--space-xs);
|
||||||
|
text-align: start;
|
||||||
|
border-block-end: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
/* BLOCKQUOTES */
|
|
||||||
blockquote {
|
blockquote {
|
||||||
margin-inline: 0;
|
margin-inline: 0;
|
||||||
padding-inline-start: var(--space-s);
|
padding-inline-start: var(--space-s);
|
||||||
|
|
@ -129,41 +170,9 @@ blockquote {
|
||||||
color: var(--color-muted);
|
color: var(--color-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ARTICLE */
|
article {
|
||||||
article time { color: var(--color-muted); font-size: 0.875rem }
|
& time {
|
||||||
|
color: var(--color-muted);
|
||||||
/* LIST VIEWS */
|
font-size: 0.875rem;
|
||||||
.list-item {
|
|
||||||
margin-bottom: var(--space-m);
|
|
||||||
& img { margin-bottom: var(--space-xs) }
|
|
||||||
& h2 { margin-top: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fill, minmax(min(100%, 18rem), 1fr));
|
|
||||||
gap: var(--space-m);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card {
|
|
||||||
border: 1px solid var(--color-border);
|
|
||||||
border-radius: 0.5rem;
|
|
||||||
padding: var(--space-s);
|
|
||||||
& img { margin-bottom: var(--space-xs); border-radius: 0.25rem }
|
|
||||||
& h3 { margin-top: 0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
.compact-list {
|
|
||||||
list-style: none;
|
|
||||||
padding: 0;
|
|
||||||
& li { border-bottom: 1px solid var(--color-border); padding-block: var(--space-xs) }
|
|
||||||
& a {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: var(--space-s);
|
|
||||||
&:hover strong { text-decoration: underline }
|
|
||||||
}
|
}
|
||||||
& time { white-space: nowrap; font-size: 0.875rem }
|
|
||||||
& p { margin-top: var(--space-xs); color: var(--color-muted); font-size: 0.9rem }
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,3 @@
|
||||||
<?php
|
|
||||||
$customCssPath = dirname(__DIR__, 2) . '/custom/styles/base.css';
|
|
||||||
$defaultCssPath = __DIR__ . '/../styles/styles.css';
|
|
||||||
if (file_exists($customCssPath)) {
|
|
||||||
$cssUrl = '/app/styles/base.css';
|
|
||||||
$cssHash = hash_file('md5', $customCssPath);
|
|
||||||
} else {
|
|
||||||
$cssUrl = '/app/default-styles/styles.css';
|
|
||||||
$cssHash = hash_file('md5', $defaultCssPath);
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="<?= htmlspecialchars($currentLang ?? 'en') ?>">
|
<html lang="<?= htmlspecialchars($currentLang ?? 'en') ?>">
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -21,7 +10,7 @@ if (file_exists($customCssPath)) {
|
||||||
<?php if (!empty($socialImageUrl)): ?>
|
<?php if (!empty($socialImageUrl)): ?>
|
||||||
<meta property="og:image" content="<?= htmlspecialchars($socialImageUrl) ?>">
|
<meta property="og:image" content="<?= htmlspecialchars($socialImageUrl) ?>">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<link rel="stylesheet" href="<?= $cssUrl ?>?v=<?= $cssHash ?>">
|
<link rel="stylesheet" href="/app/default/styles/styles.css">
|
||||||
<?php if (!empty($pageCssUrl)): ?>
|
<?php if (!empty($pageCssUrl)): ?>
|
||||||
<link rel="stylesheet" href="<?= htmlspecialchars($pageCssUrl) ?>?v=<?= htmlspecialchars($pageCssHash ?? '') ?>">
|
<link rel="stylesheet" href="<?= htmlspecialchars($pageCssUrl) ?>?v=<?= htmlspecialchars($pageCssHash ?? '') ?>">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
@ -30,34 +19,41 @@ if (file_exists($customCssPath)) {
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="contain">
|
<header>
|
||||||
<div>
|
<nav>
|
||||||
<nav>
|
<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/"><?= htmlspecialchars($homeLabel ?? ($translations['home'] ?? 'Home')) ?></a>
|
||||||
<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/"><?= htmlspecialchars($translations['home'] ?? $homeLabel ?? 'Home') ?></a>
|
<?php if (!empty($navigation)): ?>
|
||||||
<?php if (!empty($navigation)): ?>
|
<?php foreach ($navigation as $item): ?>
|
||||||
<?php foreach ($navigation as $item): ?>
|
|
||||||
<a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a>
|
<a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a>
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</nav>
|
|
||||||
<?php if (!empty($languageUrls) && count($languageUrls) > 1): ?>
|
|
||||||
<nav class="language-switcher" aria-label="Language">
|
|
||||||
<?php foreach ($languageUrls as $lang => $url): ?>
|
|
||||||
<a href="<?= htmlspecialchars($url) ?>" <?= ($lang === $currentLang) ? 'aria-current="true"' : '' ?>><?= htmlspecialchars(strtoupper($lang)) ?></a>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</nav>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</nav>
|
||||||
|
<?php if (!empty($languageUrls) && count($languageUrls) > 1): ?>
|
||||||
|
<nav class="language-switcher" aria-label="Language">
|
||||||
|
<?php foreach ($languageUrls as $lang => $url): ?>
|
||||||
|
<a href="<?= htmlspecialchars($url) ?>" <?= ($lang === $currentLang) ? 'aria-current="true"' : '' ?>><?= htmlspecialchars(strtoupper($lang)) ?></a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</nav>
|
||||||
|
<?php endif; ?>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<?= $wrappedContent ?? $content ?>
|
<?= $content ?>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="contain">
|
<footer>
|
||||||
<p>Generated in <?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) ?>ms</p>
|
<nav>
|
||||||
|
<a href="https://mastodon.social/@example" rel="me">Mastodon</a>
|
||||||
|
<a href="https://bsky.app/profile/example.bsky.social">Bluesky</a>
|
||||||
|
</nav>
|
||||||
|
<p>
|
||||||
|
<?php if (!empty($translations['footer_handcoded'])): ?>
|
||||||
|
<?= htmlspecialchars($translations['footer_handcoded']) ?> <?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) ?><?= htmlspecialchars($translations['footer_page_time'] ?? 'ms') ?>
|
||||||
|
<?php else: ?>
|
||||||
|
Generated in <?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) ?>ms
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<?php if (!empty($pageJsUrl)): ?>
|
<?php if (!empty($pageJsUrl)): ?>
|
||||||
<script defer src="<?= htmlspecialchars($pageJsUrl) ?>?v=<?= htmlspecialchars($pageJsHash ?? '') ?>"></script>
|
<script defer src="<?= htmlspecialchars($pageJsUrl) ?>?v=<?= htmlspecialchars($pageJsHash ?? '') ?>"></script>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,61 @@
|
||||||
<?php if (!empty($pageContent)): ?>
|
<?php if (!empty($pageContent)): ?>
|
||||||
<article class="list-intro">
|
<div class="list-intro">
|
||||||
<?= $pageContent ?>
|
<?= $pageContent ?>
|
||||||
</article>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<section>
|
<ul class="compact-list">
|
||||||
<ul class="compact-list">
|
<?php foreach ($items as $item): ?>
|
||||||
<?php foreach ($items as $item): ?>
|
<li>
|
||||||
<li>
|
<a href="<?= htmlspecialchars($item['url']) ?>">
|
||||||
<a href="<?= htmlspecialchars($item['url']) ?>">
|
<strong><?= htmlspecialchars($item['title']) ?></strong>
|
||||||
<strong><?= htmlspecialchars($item['title']) ?></strong>
|
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
|
||||||
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
|
<time><?= htmlspecialchars($item['date']) ?></time>
|
||||||
<time><?= htmlspecialchars($item['date']) ?></time>
|
|
||||||
<?php endif; ?>
|
|
||||||
</a>
|
|
||||||
<?php if (!empty($item['summary'])): ?>
|
|
||||||
<p><?= htmlspecialchars($item['summary']) ?></p>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</li>
|
</a>
|
||||||
<?php endforeach; ?>
|
<?php if (!empty($item['summary'])): ?>
|
||||||
</ul>
|
<p><?= htmlspecialchars($item['summary']) ?></p>
|
||||||
</section>
|
<?php endif; ?>
|
||||||
|
</li>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.compact-list {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
max-width: var(--size-content);
|
||||||
|
|
||||||
|
& li {
|
||||||
|
border-block-end: 1px solid var(--color-border);
|
||||||
|
padding-block: var(--space-s);
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
padding-block-start: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& a {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: var(--space-s);
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover strong {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& time {
|
||||||
|
white-space: nowrap;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& p {
|
||||||
|
margin-block-start: var(--space-xs);
|
||||||
|
color: var(--color-muted);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,48 @@
|
||||||
<?php if (!empty($pageContent)): ?>
|
<?php if (!empty($pageContent)): ?>
|
||||||
<article class="list-intro">
|
<div class="list-intro">
|
||||||
<?= $pageContent ?>
|
<?= $pageContent ?>
|
||||||
</article>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<section>
|
<div class="grid">
|
||||||
<div class="grid">
|
<?php foreach ($items as $item): ?>
|
||||||
<?php foreach ($items as $item): ?>
|
<article class="card">
|
||||||
<article class="card">
|
<?php if (!empty($item['cover'])): ?>
|
||||||
<?php if (!empty($item['cover'])): ?>
|
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="">
|
||||||
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="">
|
<?php endif; ?>
|
||||||
<?php endif; ?>
|
|
||||||
|
<h3><a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a></h3>
|
||||||
|
|
||||||
|
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
|
||||||
|
<time><?= htmlspecialchars($item['date']) ?></time>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (!empty($item['summary'])): ?>
|
||||||
|
<p><?= htmlspecialchars($item['summary']) ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</article>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3><a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a></h3>
|
<style>
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
|
||||||
|
gap: var(--space-m);
|
||||||
|
}
|
||||||
|
|
||||||
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
|
.card {
|
||||||
<time><?= htmlspecialchars($item['date']) ?></time>
|
border: 1px solid var(--color-border);
|
||||||
<?php endif; ?>
|
border-radius: 0.5rem;
|
||||||
|
padding: var(--space-s);
|
||||||
<?php if (!empty($item['summary'])): ?>
|
|
||||||
<p><?= htmlspecialchars($item['summary']) ?></p>
|
& img {
|
||||||
<?php endif; ?>
|
margin-block-end: var(--space-s);
|
||||||
</article>
|
border-radius: 0.25rem;
|
||||||
<?php endforeach; ?>
|
}
|
||||||
</div>
|
|
||||||
</section>
|
& h3 {
|
||||||
|
margin-block-start: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,25 @@
|
||||||
<?php if (!empty($pageContent)): ?>
|
<?php if (!empty($pageContent)): ?>
|
||||||
<article class="list-intro">
|
<div class="list-intro">
|
||||||
<?= $pageContent ?>
|
<?= $pageContent ?>
|
||||||
</article>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<section>
|
<div class="list">
|
||||||
<?php foreach ($items as $item): ?>
|
<?php foreach ($items as $item): ?>
|
||||||
<article class="list-item">
|
<article class="list-item">
|
||||||
<?php if (!empty($item['cover'])): ?>
|
<?php if (!empty($item['cover'])): ?>
|
||||||
<a href="<?= htmlspecialchars($item['url']) ?>">
|
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="">
|
||||||
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="">
|
|
||||||
</a>
|
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<h2><a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a></h2>
|
<h2><a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a></h2>
|
||||||
|
|
||||||
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
|
<?php if (($metadata['show_date'] ?? true) && !empty($item['date'])): ?>
|
||||||
<time><?= htmlspecialchars($item['date']) ?></time>
|
<time><?= htmlspecialchars($item['date']) ?></time>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
<?php if (!empty($item['summary'])): ?>
|
<?php if (!empty($item['summary'])): ?>
|
||||||
<p><?= htmlspecialchars($item['summary']) ?></p>
|
<p><?= htmlspecialchars($item['summary']) ?></p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</article>
|
</article>
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</section>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue