Compare commits

..

No commits in common. "74672b2d047eb156f81c245ebb0864d0763ad181" and "ca6d87b8851ef0992877b4d37e41141b9fad584e" have entirely different histories.

71 changed files with 1413 additions and 284 deletions

View file

@ -4,7 +4,7 @@ Drop markdown, HTML and PHP files in directories to instantly publish online. A
## 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!

View 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>

View 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>

View 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.

View 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.

View file

@ -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.

View file

@ -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.

View file

@ -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"

View file

@ -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.

View file

@ -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.

View file

@ -1,3 +0,0 @@
# Second Post
This post combines multiple content files into a single page. This section is Markdown.

View file

@ -1,3 +0,0 @@
# Andre innlegg
Dette innlegget kombinerer flere innholdsfiler til en enkelt side. Denne delen er Markdown.

View file

@ -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>

View file

@ -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>

View file

@ -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 </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>

View file

@ -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>

View file

@ -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"

View file

@ -1,8 +0,0 @@
title = "Blog"
summary = "Example blog with list view"
menu = 1
menu_order = 10
[no]
title = "Blogg"
summary = "Eksempelblogg med listevisning"

View file

@ -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

View file

@ -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
![Diagram](diagram.png)
[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.

View file

@ -0,0 +1,2 @@
title = "Cover Images and Assets"
summary = "Learn how cover images and static assets work in FolderWeb"

View file

@ -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.

View file

@ -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"

View file

@ -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):
![Example image](placeholder.jpg)
## 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.

View file

@ -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):
![Eksempelbilde](placeholder.jpg)
## 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.

View file

@ -0,0 +1,2 @@
title = "Markdown Demonstration"
summary = "Shows all the Markdown formatting features available through Parsedown"

View 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

View 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

View file

@ -0,0 +1,3 @@
title = "File-Based Routing"
summary = "Your folder structure is your URL structure—no configuration needed"
date = "2024-11-26"

View 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.

View 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.

View 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.

View file

@ -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>

View file

@ -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>

View 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 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 nytt for å se servertiden oppdatere seg!</em></p>
</section>

View 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>

View 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.

View 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.

View file

@ -0,0 +1,3 @@
title = "Mix Formats"
summary = "Combine Markdown, HTML, and PHP files to create rich, dynamic pages"
date = "2024-11-26"

View 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.

View 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.

View file

@ -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.

View file

@ -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.

View 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.**

View 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.**

View 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"

View file

@ -0,0 +1,3 @@
# Oldest Post
The oldest post in this compact list.

View file

@ -0,0 +1,3 @@
# Older Post
An older post showing date ordering.

View file

@ -0,0 +1,3 @@
# Recent Post
A recent example post.

View file

@ -0,0 +1,3 @@
# Compact Template
This section uses `list-compact.php` for a minimal, clean list view.

View file

@ -0,0 +1,4 @@
title = "Compact Template Example"
[settings]
page_template = "list-compact"

View file

@ -0,0 +1,3 @@
# Grid Template
This section uses `list-grid.php` to display items in a responsive grid with cards.

View file

@ -0,0 +1,3 @@
# Grid Item One
This is an example item in the grid layout.

View file

@ -0,0 +1 @@
summary = "First item demonstrating grid layout"

View file

@ -0,0 +1,3 @@
# Grid Item Three
A third item to show the grid layout with multiple items.

View file

@ -0,0 +1 @@
summary = "Third grid item"

View file

@ -0,0 +1,3 @@
# Grid Item Two
Another example showing how items appear in the grid.

View file

@ -0,0 +1 @@
summary = "Second item in the grid"

View file

@ -0,0 +1,4 @@
title = "Grid Template Example"
[settings]
page_template = "list-grid"

View 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.

View file

@ -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

View file

@ -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

View 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.

View 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.

View file

@ -0,0 +1,2 @@
title = "Multilingual Support"
summary = "Learn how to create content in multiple languages"

View 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);
}
}
}

View file

@ -1,11 +1,8 @@
/* RESET */
* { box-sizing: border-box; margin-bottom: 0 }
/* VARIABLES */
:root {
--color-text: oklch(20% 0 0);
--color-background: oklch(98% 0 0);
--color-accent: oklch(50% 0.15 250);
--color-accent-light: oklch(95% 0.05 250);
--color-border: oklch(85% 0 0);
--color-muted: oklch(50% 0 0);
@ -14,13 +11,17 @@
--space-m: 2rem;
--space-l: 4rem;
--size-content: 42rem;
--size-content: 65ch;
--size-constrained: 42rem;
--size-wide: 90rem;
}
* {
box-sizing: border-box;
}
/* GLOBAL */
html {
font-family: system-ui, sans-serif;
font-size: clamp(16px, 2.3vw, 20px);
line-height: 1.6;
color: var(--color-text);
background: var(--color-background);
@ -29,99 +30,139 @@ html {
body {
margin: 0;
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;
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 {
font-weight: 400;
line-height: 1.3;
margin-top: 1.3em;
text-wrap: pretty;
line-height: 1.2;
margin-block: 1.5em 0.5em;
}
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 {
color: var(--color-accent);
text-underline-offset: 0.2em;
&:hover {
text-decoration: none;
&:hover { text-decoration: underline }
}
}
img { max-width: 100%; height: auto; display: block }
header, main, footer {
grid-column: content;
}
/* HEADER */
header {
border-bottom: 1px solid var(--color-border);
& > div {
border-block-end: 1px solid var(--color-border);
padding-block: var(--space-s);
display: flex;
flex-wrap: wrap;
gap: var(--space-s);
align-items: center;
padding-block: var(--space-s);
}
& nav {
display: flex;
gap: var(--space-s);
flex-wrap: wrap;
align-items: center;
&:first-child { flex: 1 }
&:first-child {
flex: 1;
}
& a {
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 { padding-block: var(--space-m) }
main {
padding-block: var(--space-m);
}
/* FOOTER */
footer {
border-top: 1px solid var(--color-border);
border-block-start: 1px solid var(--color-border);
padding-block: var(--space-s);
font-size: 0.875rem;
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 {
background: oklch(95% 0.02 250);
background: var(--color-accent-light);
padding: 0.125em 0.25em;
border-radius: 0.25em;
font-size: 0.9em;
}
pre {
background: oklch(95% 0.02 250);
background: var(--color-accent-light);
padding: var(--space-s);
border-radius: 0.5em;
overflow-x: auto;
& code { background: none; padding: 0 }
& code {
background: none;
padding: 0;
}
}
/* TABLES */
table { border-collapse: collapse; width: 100%; margin-block: var(--space-s) }
th, td { padding: var(--space-xs); text-align: start; border-bottom: 1px solid var(--color-border) }
th { font-weight: bold }
table {
border-collapse: collapse;
width: 100%;
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 {
margin-inline: 0;
padding-inline-start: var(--space-s);
@ -129,41 +170,9 @@ blockquote {
color: var(--color-muted);
}
/* ARTICLE */
article time { color: var(--color-muted); font-size: 0.875rem }
/* LIST VIEWS */
.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 }
article {
& time {
color: var(--color-muted);
font-size: 0.875rem;
}
& time { white-space: nowrap; font-size: 0.875rem }
& p { margin-top: var(--space-xs); color: var(--color-muted); font-size: 0.9rem }
}

View file

@ -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>
<html lang="<?= htmlspecialchars($currentLang ?? 'en') ?>">
<head>
@ -21,7 +10,7 @@ if (file_exists($customCssPath)) {
<?php if (!empty($socialImageUrl)): ?>
<meta property="og:image" content="<?= htmlspecialchars($socialImageUrl) ?>">
<?php endif; ?>
<link rel="stylesheet" href="<?= $cssUrl ?>?v=<?= $cssHash ?>">
<link rel="stylesheet" href="/app/default/styles/styles.css">
<?php if (!empty($pageCssUrl)): ?>
<link rel="stylesheet" href="<?= htmlspecialchars($pageCssUrl) ?>?v=<?= htmlspecialchars($pageCssHash ?? '') ?>">
<?php endif; ?>
@ -30,10 +19,9 @@ if (file_exists($customCssPath)) {
<?php endif; ?>
</head>
<body>
<header class="contain">
<div>
<header>
<nav>
<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/"><?= htmlspecialchars($translations['home'] ?? $homeLabel ?? 'Home') ?></a>
<a href="<?= htmlspecialchars($langPrefix ?? '') ?>/"><?= htmlspecialchars($homeLabel ?? ($translations['home'] ?? 'Home')) ?></a>
<?php if (!empty($navigation)): ?>
<?php foreach ($navigation as $item): ?>
<a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a>
@ -47,17 +35,25 @@ if (file_exists($customCssPath)) {
<?php endforeach; ?>
</nav>
<?php endif; ?>
</div>
</header>
<main>
<?= $wrappedContent ?? $content ?>
<?= $content ?>
</main>
<footer class="contain">
<p>Generated in <?= number_format((microtime(true) - $_SERVER['REQUEST_TIME_FLOAT']) * 1000, 2) ?>ms</p>
<footer>
<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>
<?php if (!empty($pageJsUrl)): ?>
<script defer src="<?= htmlspecialchars($pageJsUrl) ?>?v=<?= htmlspecialchars($pageJsHash ?? '') ?>"></script>
<?php endif; ?>

View file

@ -1,11 +1,10 @@
<?php if (!empty($pageContent)): ?>
<article class="list-intro">
<div class="list-intro">
<?= $pageContent ?>
</article>
</div>
<?php endif; ?>
<section>
<ul class="compact-list">
<ul class="compact-list">
<?php foreach ($items as $item): ?>
<li>
<a href="<?= htmlspecialchars($item['url']) ?>">
@ -19,5 +18,44 @@
<?php endif; ?>
</li>
<?php endforeach; ?>
</ul>
</section>
</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>

View file

@ -1,11 +1,10 @@
<?php if (!empty($pageContent)): ?>
<article class="list-intro">
<div class="list-intro">
<?= $pageContent ?>
</article>
</div>
<?php endif; ?>
<section>
<div class="grid">
<div class="grid">
<?php foreach ($items as $item): ?>
<article class="card">
<?php if (!empty($item['cover'])): ?>
@ -23,5 +22,27 @@
<?php endif; ?>
</article>
<?php endforeach; ?>
</div>
</section>
</div>
<style>
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
gap: var(--space-m);
}
.card {
border: 1px solid var(--color-border);
border-radius: 0.5rem;
padding: var(--space-s);
& img {
margin-block-end: var(--space-s);
border-radius: 0.25rem;
}
& h3 {
margin-block-start: 0;
}
}
</style>

View file

@ -1,16 +1,14 @@
<?php if (!empty($pageContent)): ?>
<article class="list-intro">
<div class="list-intro">
<?= $pageContent ?>
</article>
</div>
<?php endif; ?>
<section>
<div class="list">
<?php foreach ($items as $item): ?>
<article class="list-item">
<?php if (!empty($item['cover'])): ?>
<a href="<?= htmlspecialchars($item['url']) ?>">
<img src="<?= htmlspecialchars($item['cover']) ?>" alt="">
</a>
<?php endif; ?>
<h2><a href="<?= htmlspecialchars($item['url']) ?>"><?= htmlspecialchars($item['title']) ?></a></h2>
@ -24,4 +22,4 @@
<?php endif; ?>
</article>
<?php endforeach; ?>
</section>
</div>