diff --git a/app/config.ini b/app/config.ini new file mode 100644 index 0000000..fd603b4 --- /dev/null +++ b/app/config.ini @@ -0,0 +1,3 @@ +[languages] +default = "no" +available = "no,en" diff --git a/app/config.php b/app/config.php index fac17c9..337d4ce 100644 --- a/app/config.php +++ b/app/config.php @@ -1,17 +1,11 @@ default - $defaultConfig = __DIR__ . '/default/config.ini'; - $customConfig = __DIR__ . '/../custom/config.ini'; - - // Start with default config - $config = file_exists($defaultConfig) ? parse_ini_file($defaultConfig, true) : []; - - // Merge with custom config if it exists - if (file_exists($customConfig)) { - $config = array_replace_recursive($config, parse_ini_file($customConfig, true) ?: []); - } + // Load configuration + $configFile = file_exists(__DIR__ . '/../custom/config.ini') + ? __DIR__ . '/../custom/config.ini' + : __DIR__ . '/config.ini'; + $config = parse_ini_file($configFile, true); // Load global plugins getPluginManager()->loadGlobalPlugins($config); diff --git a/app/default/config.ini b/app/default/config.ini index 1306d4f..67c9dd9 100644 --- a/app/default/config.ini +++ b/app/default/config.ini @@ -1,6 +1,3 @@ [languages] default = "en" -available = "no,en" - -[plugins] -enabled = "languages" +available = "en" diff --git a/app/default/content/00-hero.no.php b/app/default/content/00-hero.no.php deleted file mode 100644 index 22e2f5d..0000000 --- a/app/default/content/00-hero.no.php +++ /dev/null @@ -1,50 +0,0 @@ - 'πŸ“', 'title' => 'Filbasert', 'description' => 'Mapper blir URL-er automatisk', 'url' => '/examples/file-based-routing/'], - ['icon' => '⚑', 'title' => 'Uten byggesteg', 'description' => 'Rediger og oppdaterβ€”det er alt', 'url' => '/examples/no-build-step/'], - ['icon' => '🎨', 'title' => 'Bland formater', 'description' => 'Kombiner .md, .html og .php', 'url' => '/examples/mix-formats/'], - ['icon' => '🌍', 'title' => 'FlersprΓ₯klig', 'description' => 'Innebygd i18n-stΓΈtte', 'url' => '/multilingual/'], - ['icon' => 'πŸ“', 'title' => 'Markdown', 'description' => 'Skriv innhold i markdown', 'url' => '/examples/2024-12-15-markdown-demo/'], - ['icon' => '🎭', 'title' => 'Maler', 'description' => 'Egendefinerte layout og stiler', 'url' => '/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, -]; -?> - -
-

FolderWeb

-

- Slipp filer i mapper. De rendres umiddelbart. - Ingen konfigurasjon, ingen byggesteg, ingen kompleksitet. -

- -
- - - -

-

-
- -
- -
-
- - Filer i innhold -
-
- - Side generert -
-
- PHP - Runtime -
-
-
diff --git a/app/default/content/00-hero.php b/app/default/content/00-hero.php deleted file mode 100644 index 13d8567..0000000 --- a/app/default/content/00-hero.php +++ /dev/null @@ -1,50 +0,0 @@ - 'πŸ“', 'title' => 'File-Based', 'description' => 'Folders become URLs automatically', 'url' => '/examples/file-based-routing/'], - ['icon' => '⚑', 'title' => 'No Build Step', 'description' => 'Edit and refreshβ€”that\'s it', 'url' => '/examples/no-build-step/'], - ['icon' => '🎨', 'title' => 'Mix Formats', 'description' => 'Combine .md, .html, and .php', 'url' => '/examples/mix-formats/'], - ['icon' => '🌍', 'title' => 'Multilingual', 'description' => 'Built-in i18n support', 'url' => '/multilingual/'], - ['icon' => 'πŸ“', 'title' => 'Markdown', 'description' => 'Write content in markdown', 'url' => '/examples/2024-12-15-markdown-demo/'], - ['icon' => '🎭', 'title' => 'Templates', 'description' => 'Custom layouts and styles', 'url' => '/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, -]; -?> - -
-

FolderWeb

-

- Drop files in folders. They render immediately. - No configuration, no build step, no complexity. -

- -
- - - -

-

-
- -
- -
-
- - Files in content -
-
- - Page generated -
-
- PHP - Runtime -
-
-
diff --git a/app/default/content/00-welcome.php b/app/default/content/00-welcome.php new file mode 100644 index 0000000..c59e257 --- /dev/null +++ b/app/default/content/00-welcome.php @@ -0,0 +1,6 @@ +
+

Welcome to FolderWeb

+

+ A minimalist PHP framework that turns folders into websites. No JavaScript, no build tools, just simple files. +

+
diff --git a/app/default/content/00a-getting-started.md b/app/default/content/00a-getting-started.md new file mode 100644 index 0000000..8d11e2a --- /dev/null +++ b/app/default/content/00a-getting-started.md @@ -0,0 +1,7 @@ +## Getting Started + +This is demo content to help you understand how FolderWeb works. To replace it with your own content: + +1. Create a `/content` folder in your project root +2. Add your content files (`.md`, `.html`, or `.php`) +3. This demo will automatically disappear diff --git a/app/default/content/01-core-concepts.md b/app/default/content/01-core-concepts.md new file mode 100644 index 0000000..de0044f --- /dev/null +++ b/app/default/content/01-core-concepts.md @@ -0,0 +1,15 @@ +## Core Concepts + +### File-Based Routing + +Drop a file in a folder and it's instantly accessible at a URL matching that path. Your folder structure becomes your URL structure. + +### Multiple Content Types + +- **Markdown** - Write in `.md` files, automatically converted to HTML +- **HTML** - Pure HTML files for complete control +- **PHP** - Dynamic content when you need it + +### Multi-File Pages + +Any folder without subfolders renders all content files (`.md`, `.html`, `.php`) in alphanumerical order. Mix formats freely! diff --git a/app/default/content/01-index.md b/app/default/content/01-index.md deleted file mode 100644 index 145e99a..0000000 --- a/app/default/content/01-index.md +++ /dev/null @@ -1,45 +0,0 @@ - - - -## 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. diff --git a/app/default/content/01-index.no.md b/app/default/content/01-index.no.md deleted file mode 100644 index dc0a430..0000000 --- a/app/default/content/01-index.no.md +++ /dev/null @@ -1,45 +0,0 @@ - - - -## 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. diff --git a/app/default/content/02-features.html b/app/default/content/02-features.html new file mode 100644 index 0000000..b766430 --- /dev/null +++ b/app/default/content/02-features.html @@ -0,0 +1,13 @@ +
+

Smart Features

+ + + +

Explore the Demo

+

Check out the Articles and About pages to see different content types in action.

+
diff --git a/app/default/content/03-this-page-demo.md b/app/default/content/03-this-page-demo.md new file mode 100644 index 0000000..e5d7cb5 --- /dev/null +++ b/app/default/content/03-this-page-demo.md @@ -0,0 +1,11 @@ +## About This Frontpage + +**This frontpage demonstrates the multi-file approach!** It's composed of: + +1. `00-welcome.php` - Hero header (PHP/HTML) +2. `00a-getting-started.md` - Getting started guide (Markdown) +3. `01-core-concepts.md` - Core concepts (Markdown) +4. `02-features.html` - Features and links (HTML) +5. `03-this-page-demo.md` - This explanation (Markdown) + +All files in the root `/content` folder are rendered together, just like pages in subfolders. Name your files anything you wantβ€”they'll render in alphanumerical order. diff --git a/app/default/content/about/00-introduction.md b/app/default/content/about/00-introduction.md new file mode 100644 index 0000000..470d79f --- /dev/null +++ b/app/default/content/about/00-introduction.md @@ -0,0 +1,15 @@ +# About FolderWeb + +FolderWeb is a minimalist PHP framework designed for simplicity, longevity, and maintainability. It's built on a simple philosophy: **just enough, nothing more**. + +## Philosophy + +Modern web development has become unnecessarily complex. Build tools, package managers, JavaScript frameworks that change every few monthsβ€”it's exhausting and unsustainable. + +FolderWeb is different. It's built to: + +- **Work for decades** without requiring constant updates +- **Be understandable** by reading a few hundred lines of code +- **Stay maintainable** without specialized knowledge +- **Load fast** with no JavaScript overhead +- **Just work** without configuration or setup diff --git a/app/default/content/about/01-design-principles.html b/app/default/content/about/01-design-principles.html new file mode 100644 index 0000000..fcdd4d7 --- /dev/null +++ b/app/default/content/about/01-design-principles.html @@ -0,0 +1,15 @@ +
+

Design Principles

+ +

Minimalism

+

Use only what is strictly necessary. No frameworks, no build tools, no package managers for frontend code. Every line of code must justify its existence.

+ +

File-Based Everything

+

Your folder structure is your URL structure. Drop a file in a folder and it's instantly accessible. No routes to configure, no databases to set up.

+ +

Override, Never Modify

+

Custom templates and styles go in /custom/ and automatically override defaults. The core files in /app/default/ remain untouched and updateable.

+ +

Modern Standards

+

Use modern PHP 8.4+ features (property hooks, readonly classes, modern array functions) and modern CSS capabilities. Avoid JavaScript entirelyβ€”it's not needed for content-focused sites.

+
diff --git a/app/default/content/about/02-technology-stack.php b/app/default/content/about/02-technology-stack.php new file mode 100644 index 0000000..a2b5292 --- /dev/null +++ b/app/default/content/about/02-technology-stack.php @@ -0,0 +1,21 @@ +
+

Technology Stack

+ +

Backend

+ + +

Frontend

+ + + +
diff --git a/app/default/content/about/03-what-it-is-not.md b/app/default/content/about/03-what-it-is-not.md new file mode 100644 index 0000000..89ca489 --- /dev/null +++ b/app/default/content/about/03-what-it-is-not.md @@ -0,0 +1,19 @@ +## What It's Not + +FolderWeb is **not**: + +- A CMS with an admin panel +- A single-page application framework +- A solution for complex web applications +- Trying to scale to millions of users +- Following current trends and fads + +## What It Is + +FolderWeb **is**: + +- A simple way to publish content +- A foundation that will work for decades +- A teaching tool for web fundamentals +- A protest against unnecessary complexity +- Perfect for documentation, blogs, portfolios, small business sites diff --git a/app/default/content/about/04-get-started.md b/app/default/content/about/04-get-started.md new file mode 100644 index 0000000..80c52c8 --- /dev/null +++ b/app/default/content/about/04-get-started.md @@ -0,0 +1,21 @@ +## Get Started + +Ready to build something simple and lasting? + +1. Create a `/content` folder +2. Add your first `.md` file +3. That's itβ€”you're publishing + +No build step. No npm install. No configuration files. Just content. + +--- + +**This page demonstrates FolderWeb's multi-file approach.** Notice how this page is built from multiple files that render in alphanumerical order: + +- `00-introduction.md` - Markdown content +- `01-design-principles.html` - Static HTML +- `02-technology-stack.php` - Dynamic PHP (shows current date) +- `03-what-it-is-not.md` - More Markdown +- `04-get-started.md` - This section + +Mix file types freely. They all render together seamlessly! diff --git a/app/default/content/about/metadata.ini b/app/default/content/about/metadata.ini new file mode 100644 index 0000000..7e73f4a --- /dev/null +++ b/app/default/content/about/metadata.ini @@ -0,0 +1,3 @@ +title = "About FolderWeb" +menu = true +menu_order = 2 diff --git a/app/default/content/about/styles.css b/app/default/content/about/styles.css new file mode 100644 index 0000000..5dbefaa --- /dev/null +++ b/app/default/content/about/styles.css @@ -0,0 +1,19 @@ +/* Page-specific styles for About page */ + +article { + border-left: 4px solid oklch(0.65 0.15 250); + padding-left: 1.5rem; +} + +article h2 { + color: oklch(0.50 0.12 250); + font-size: 1.8rem; +} + +aside { + background-color: oklch(0.95 0.02 250); + padding: 1.5rem; + border-radius: 0.5rem; + margin-top: 2rem; + border: 2px solid oklch(0.85 0.05 250); +} diff --git a/app/default/content/articles/2025-10-15-markdown-guide/article.md b/app/default/content/articles/2025-10-15-markdown-guide/article.md new file mode 100644 index 0000000..a3bc6d2 --- /dev/null +++ b/app/default/content/articles/2025-10-15-markdown-guide/article.md @@ -0,0 +1,206 @@ +# Markdown Guide + +Markdown is a lightweight markup language that's easy to write and read. FolderWeb uses [Parsedown](https://parsedown.org/) to convert your Markdown files into beautiful HTML. + +## Headings + +Use `#` symbols for headings: + +```markdown +# Heading 1 +## Heading 2 +### Heading 3 +#### Heading 4 +##### Heading 5 +###### Heading 6 +``` + +## Emphasis + +Make text **bold** or *italic*: + +```markdown +*italic text* or _italic text_ +**bold text** or __bold text__ +***bold and italic*** or ___bold and italic___ +``` + +## Lists + +### Unordered Lists + +```markdown +- Item one +- Item two +- Item three + - Nested item + - Another nested item +``` + +### Ordered Lists + +```markdown +1. First item +2. Second item +3. Third item + 1. Nested item + 2. Another nested item +``` + +## Links + +```markdown +[Link text](https://example.com) +[Link with title](https://example.com "Title text") +``` + +Example: [Visit FolderWeb](#) + +## Images + +```markdown +![Alt text](image.jpg) +![Alt text with title](image.jpg "Image title") +``` + +## Code + +### Inline Code + +Use backticks for `inline code`: + +```markdown +Use the `$variable` in your code +``` + +### Code Blocks + +Use triple backticks for code blocks: + +````markdown +```php + +``` +```` + +Renders as: + +```php + +``` + +## Blockquotes + +```markdown +> This is a blockquote. +> It can span multiple lines. +> +> And multiple paragraphs. +``` + +Result: + +> This is a blockquote. +> It can span multiple lines. + +## Horizontal Rules + +Create a horizontal rule with three or more hyphens, asterisks, or underscores: + +```markdown +--- +*** +___ +``` + +--- + +## Tables + +```markdown +| Header 1 | Header 2 | Header 3 | +|----------|----------|----------| +| Cell 1 | Cell 2 | Cell 3 | +| Cell 4 | Cell 5 | Cell 6 | +``` + +Result: + +| Header 1 | Header 2 | Header 3 | +|----------|----------|----------| +| Cell 1 | Cell 2 | Cell 3 | +| Cell 4 | Cell 5 | Cell 6 | + +## Best Practices + +### Use Semantic Structure + +Start with `# H1` for your page title, then use `## H2`, `### H3`, etc. for sections. + +### Write Readable Markdown + +```markdown +# Good Example + +This paragraph is easy to read with proper spacing. + +## Section Heading + +- List items are clear +- Each on its own line + +--- + +# Bad Example +No spacing makes it hard to read. +##SectionWithoutSpace +-ListItemsSmashed-Together +``` + +### Links in FolderWeb + +Internal links work best with absolute paths: + +```markdown +[About page](/about/) +[Articles](/articles/) +[Specific article](/articles/2025-10-15-markdown-guide/) +``` + +## Advanced Features + +### HTML in Markdown + +You can use HTML directly in Markdown when needed: + +```markdown +
+ Custom HTML content +
+``` + +### Escaping Characters + +Use backslash to escape Markdown characters: + +```markdown +\*This text is not italic\* +\[This is not a link\] +``` + +## Tips for FolderWeb + +1. **Use descriptive filenames** - `article.md` is better than `content.md` +2. **Add metadata** - Use `metadata.ini` for titles, dates, and summaries +3. **Include images** - Place images in the same directory as your content +4. **Add cover images** - Use `cover.jpg` for list view thumbnails + +## Further Reading + +- [Markdown Guide](https://www.markdownguide.org/) - Comprehensive Markdown reference +- [Parsedown Documentation](https://parsedown.org/) - The parser FolderWeb uses +- [CommonMark Spec](https://commonmark.org/) - Markdown specification diff --git a/app/default/content/articles/2025-10-15-markdown-guide/metadata.ini b/app/default/content/articles/2025-10-15-markdown-guide/metadata.ini new file mode 100644 index 0000000..053268d --- /dev/null +++ b/app/default/content/articles/2025-10-15-markdown-guide/metadata.ini @@ -0,0 +1,3 @@ +title = "Markdown Guide" +date = "2025-10-15" +summary = "Master Markdown syntax for beautiful, semantic content in FolderWeb." diff --git a/app/default/content/articles/2025-10-28-templates-and-customization/article.md b/app/default/content/articles/2025-10-28-templates-and-customization/article.md new file mode 100644 index 0000000..16c1d0c --- /dev/null +++ b/app/default/content/articles/2025-10-28-templates-and-customization/article.md @@ -0,0 +1,165 @@ +# Templates and Customization + +FolderWeb is built on a simple principle: **never modify defaults, always override**. This guide shows you how to customize your site while keeping it maintainable. + +## The Override System + +FolderWeb checks for custom files before falling back to defaults: + +1. Check `/custom/templates/` β†’ Use custom template +2. Fall back to `/app/default/templates/` β†’ Use default template + +This means you can override any part of the system without touching the core files. + +## Available Templates + +### Base Template +The main HTML structure with header, navigation, and footer. + +**Override**: `/custom/templates/base.php` + +### Page Template +Wraps single pages and articles. + +**Override**: `/custom/templates/page.php` + +### List Templates + +FolderWeb includes multiple list view variants: + +- `list.php` - Simple list +- `list-grid.php` - Grid layout +- `list-card-grid.php` - Card grid with images +- `list-faq.php` - Expandable FAQ format + +**Select via metadata**: + +```ini +page_template = "list-card-grid" +``` + +## Customizing Styles + +### Add Your Own CSS + +Create `/custom/styles/base.css` and it automatically overrides default styles. + +Example custom stylesheet: + +```css +:root { + --color-primary: oklch(0.5 0.2 270); + --color-background: oklch(0.98 0 0); + --font-body: 'Georgia', serif; +} + +article h1 { + color: var(--color-primary); + font-size: clamp(2rem, 5vw, 3rem); +} +``` + +### Modern CSS Features + +FolderWeb's default styles use modern CSS: + +- **CSS Nesting** - Scope styles naturally +- **OKLCH Colors** - Perceptually uniform colors +- **CSS Grid** - Flexible layouts +- **Clamp()** - Responsive sizing +- **Logical Properties** - Better internationalization + +## Custom Fonts + +1. Place font files in `/custom/fonts/` +2. Reference them in your custom CSS: + +```css +@font-face { + font-family: 'MyFont'; + src: url('/fonts/myfont.woff2') format('woff2'); +} + +body { + font-family: 'MyFont', sans-serif; +} +``` + +## Metadata Options + +Control content behavior with `metadata.ini` files: + +```ini +; Basic fields +title = "Page Title" +date = "2025-10-28" +summary = "Short description" + +; Navigation +menu = true +menu_order = 1 + +; Templates +page_template = "list-card-grid" + +; Redirects +redirect = "https://example.com" + +; Attachments +pdf = "document.pdf" +``` + +## Template Variables + +Templates have access to specific variables: + +### Base Template +- `$content` - Page content +- `$currentLang` - Current language code +- `$navigation` - Navigation items array +- `$pageTitle` - Page title + +### Page Template +- `$content` - Article content +- `$pageMetadata` - Metadata array +- `$translations` - Translation strings + +### List Templates +- `$items` - Array of subitems +- `$metadata` - Directory metadata +- `$pageContent` - Intro text +- `$translations` - Translation strings + +## Creating a Custom Template + +Example custom page template: + +```php + +
+ + + + + + + +
+``` + +## Best Practices + +1. **Never modify `/app/default/`** - Always create overrides in `/custom/` +2. **Use metadata** - Keep configuration in `metadata.ini` files +3. **Leverage CSS variables** - Easy theming without rewriting styles +4. **Keep it simple** - The less custom code, the easier to maintain + +## Next Steps + +- Explore default templates in `/app/default/templates/` +- Study the default CSS in `/app/default/styles/base.css` +- Check out the [Markdown Guide](/articles/2025-10-15-markdown-guide/) for content formatting diff --git a/app/default/content/articles/2025-10-28-templates-and-customization/metadata.ini b/app/default/content/articles/2025-10-28-templates-and-customization/metadata.ini new file mode 100644 index 0000000..b69d0a7 --- /dev/null +++ b/app/default/content/articles/2025-10-28-templates-and-customization/metadata.ini @@ -0,0 +1,3 @@ +title = "Templates and Customization" +date = "2025-10-28" +summary = "Customize your FolderWeb site with templates, styles, and metadata options." diff --git a/app/default/content/articles/2025-11-01-getting-started/article.md b/app/default/content/articles/2025-11-01-getting-started/article.md new file mode 100644 index 0000000..8bc1e00 --- /dev/null +++ b/app/default/content/articles/2025-11-01-getting-started/article.md @@ -0,0 +1,79 @@ +# Getting Started with FolderWeb + +FolderWeb is designed to be the simplest way to publish content on the web. This guide will walk you through the core concepts and get you publishing in minutes. + +## Installation + +FolderWeb requires PHP 8.4+ and Apache with `mod_rewrite` enabled. + +### Using Docker (Recommended for Development) + +```bash +cd development +docker compose up +``` + +Visit `http://localhost:8080` to see your site. + +### Manual Installation + +1. Point Apache's document root to the `/content` directory +2. Ensure the `/app` directory is accessible at the same level +3. Enable `mod_rewrite` in Apache +4. That's it! + +## Creating Your First Page + +The easiest way to understand FolderWeb is to create some content. + +### Create a Simple Page + +1. Create a new directory: `/content/hello/` +2. Add a file: `/content/hello/page.md` +3. Write some Markdown: + +```markdown +# Hello World + +This is my first page in FolderWeb! +``` + +Your page is now live at `/hello/` + +### Create an Article with Metadata + +For richer content, add metadata: + +1. Create: `/content/articles/2025-11-01-my-article/` +2. Add metadata: `/content/articles/2025-11-01-my-article/metadata.ini` + +```ini +title = "My First Article" +date = "2025-11-01" +summary = "A brief description of my article" +``` + +3. Add content: `/content/articles/2025-11-01-my-article/article.md` + +The date in the folder name is automatically extracted and displayed. + +## File Types + +FolderWeb supports three content types: + +- **Markdown (`.md`)** - Write in Markdown, rendered as HTML +- **HTML (`.html`)** - Pure HTML for complete control +- **PHP (`.php`)** - Dynamic content when needed + +## File Naming Conventions + +- **Page content**: `page.md`, `page.html`, `page.php` +- **Articles/posts**: `article.md`, `post.md`, `single.md` (and `.html`/`.php` variants) +- **Frontpage**: `/content/frontpage.php` +- **Index override**: `index.php` in any directory takes precedence + +## Next Steps + +- Read the [Templates Guide](/articles/2025-10-28-templates-and-customization/) to customize your site +- Learn [Markdown syntax](/articles/2025-10-15-markdown-guide/) for better content +- Explore the default templates in `/app/default/templates/` diff --git a/app/default/content/articles/2025-11-01-getting-started/metadata.ini b/app/default/content/articles/2025-11-01-getting-started/metadata.ini new file mode 100644 index 0000000..977942b --- /dev/null +++ b/app/default/content/articles/2025-11-01-getting-started/metadata.ini @@ -0,0 +1,3 @@ +title = "Getting Started with FolderWeb" +date = "2025-11-01" +summary = "Learn the basics of FolderWeb and create your first content in minutes." diff --git a/app/default/content/articles/2025-11-02-multi-file-content/00-introduction.md b/app/default/content/articles/2025-11-02-multi-file-content/00-introduction.md new file mode 100644 index 0000000..bf15bdf --- /dev/null +++ b/app/default/content/articles/2025-11-02-multi-file-content/00-introduction.md @@ -0,0 +1,14 @@ +# Multi-File Content Pages + +One of FolderWeb's most powerful features is the ability to compose a single page from multiple content files. This gives you flexibility in how you organize and author your content. + +## How It Works + +When a folder contains **no subdirectories**, FolderWeb treats it as a **page-type folder**. All `.md`, `.html`, and `.php` files in that folder are rendered in **alphanumerical order**. + +This means you can: + +- Break long content into manageable sections +- Mix file formats freely (Markdown, HTML, PHP) +- Reorder sections by renaming files +- Include dynamic PHP content alongside static content diff --git a/app/default/content/articles/2025-11-02-multi-file-content/01-examples.md b/app/default/content/articles/2025-11-02-multi-file-content/01-examples.md new file mode 100644 index 0000000..5633aa1 --- /dev/null +++ b/app/default/content/articles/2025-11-02-multi-file-content/01-examples.md @@ -0,0 +1,24 @@ +## File Naming Examples + +Here are some example naming patterns: + +``` +/my-page/ + 00-introduction.md + 01-getting-started.md + 02-advanced-topics.html + 03-conclusion.php +``` + +Files render in this order: +1. `00-introduction.md` +2. `01-getting-started.md` +3. `02-advanced-topics.html` +4. `03-conclusion.php` + +## Folder Types + +FolderWeb automatically determines folder type: + +- **Page-type folder**: No subdirectories β†’ Renders all content files as a single page +- **Article-type folder**: Has subdirectories β†’ Shows list view with links to subdirectories diff --git a/app/default/content/articles/2025-11-02-multi-file-content/02-use-cases.html b/app/default/content/articles/2025-11-02-multi-file-content/02-use-cases.html new file mode 100644 index 0000000..547c1ab --- /dev/null +++ b/app/default/content/articles/2025-11-02-multi-file-content/02-use-cases.html @@ -0,0 +1,15 @@ +
+

Use Cases

+ +

Long Documentation

+

Break lengthy documentation into logical sections. Each section gets its own file, making editing and maintenance easier.

+ +

Mixed Content Types

+

Use Markdown for simple text, HTML for complex layouts, and PHP for dynamic contentβ€”all on the same page.

+ +

Collaborative Editing

+

Multiple authors can work on different sections simultaneously without merge conflicts.

+ +

Progressive Enhancement

+

Start with simple Markdown files. Later, enhance specific sections with HTML or PHP without restructuring.

+
diff --git a/app/default/content/articles/2025-11-02-multi-file-content/03-dynamic-demo.php b/app/default/content/articles/2025-11-02-multi-file-content/03-dynamic-demo.php new file mode 100644 index 0000000..5c49108 --- /dev/null +++ b/app/default/content/articles/2025-11-02-multi-file-content/03-dynamic-demo.php @@ -0,0 +1,16 @@ +
+

Dynamic Content Example

+ +

This section is a PHP file that generates dynamic content. Here are some examples:

+ +
+

Server Information

+ +
+ +

PHP files can access all the same variables and functions available throughout FolderWeb, making it easy to create dynamic, data-driven content.

+
diff --git a/app/default/content/articles/2025-11-02-multi-file-content/04-best-practices.md b/app/default/content/articles/2025-11-02-multi-file-content/04-best-practices.md new file mode 100644 index 0000000..ded8a89 --- /dev/null +++ b/app/default/content/articles/2025-11-02-multi-file-content/04-best-practices.md @@ -0,0 +1,23 @@ +## Best Practices + +### Use Descriptive Prefixes + +Number your files with two-digit prefixes (`00-`, `01-`, `02-`) to maintain clear ordering: + +- Allows up to 100 sections before needing three digits +- Keeps files sorted in file managers +- Makes reordering easy (just rename) + +### Choose the Right Format + +- **Markdown (`.md`)** - For most content. Simple, clean, readable. +- **HTML (`.html`)** - For complex layouts or embedded media. +- **PHP (`.php`)** - For dynamic content, calculations, or data display. + +### Keep It Simple + +Don't overcomplicate. If your page works well as a single file, keep it that way. Use multiple files when they genuinely make maintenance easier. + +--- + +**This article itself demonstrates the multi-file approach.** View the source folder to see how it's structured! diff --git a/app/default/content/articles/2025-11-02-multi-file-content/metadata.ini b/app/default/content/articles/2025-11-02-multi-file-content/metadata.ini new file mode 100644 index 0000000..ac44f1d --- /dev/null +++ b/app/default/content/articles/2025-11-02-multi-file-content/metadata.ini @@ -0,0 +1,3 @@ +title = "Multi-File Content Pages" +date = "2025-11-02" +summary = "Learn how to create pages from multiple content files in any format" diff --git a/app/default/content/articles/metadata.ini b/app/default/content/articles/metadata.ini new file mode 100644 index 0000000..6c3ca7f --- /dev/null +++ b/app/default/content/articles/metadata.ini @@ -0,0 +1,4 @@ +title = "Articles" +menu = true +menu_order = 1 +page_template = "list" diff --git a/app/default/content/articles/page.md b/app/default/content/articles/page.md new file mode 100644 index 0000000..9594a48 --- /dev/null +++ b/app/default/content/articles/page.md @@ -0,0 +1,3 @@ +# Articles + +A collection of guides and tutorials to help you get the most out of FolderWeb. diff --git a/app/default/content/articles/styles.css b/app/default/content/articles/styles.css new file mode 100644 index 0000000..99cbd5b --- /dev/null +++ b/app/default/content/articles/styles.css @@ -0,0 +1,22 @@ +/* Page-specific styles for Articles list */ + +.list-item { + border: 2px solid oklch(0.85 0.05 250); + transition: border-color 0.3s ease; +} + +.list-item:hover { + border-color: oklch(0.65 0.15 250); +} + +.list-item h2 a { + color: oklch(0.50 0.12 250); +} + +.list-item .date { + background-color: oklch(0.95 0.02 250); + padding: 0.25rem 0.75rem; + border-radius: 1rem; + font-size: 0.85rem; + display: inline-block; +} diff --git a/app/default/content/examples/2024-10-05-cover-image-demo/cover.svg b/app/default/content/examples/2024-10-05-cover-image-demo/cover.svg deleted file mode 100644 index 31e832f..0000000 --- a/app/default/content/examples/2024-10-05-cover-image-demo/cover.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - Cover Image Example - diff --git a/app/default/content/examples/2024-10-05-cover-image-demo/index.md b/app/default/content/examples/2024-10-05-cover-image-demo/index.md deleted file mode 100644 index 78e749c..0000000 --- a/app/default/content/examples/2024-10-05-cover-image-demo/index.md +++ /dev/null @@ -1,39 +0,0 @@ -# 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. diff --git a/app/default/content/examples/2024-10-05-cover-image-demo/metadata.ini b/app/default/content/examples/2024-10-05-cover-image-demo/metadata.ini deleted file mode 100644 index a305d97..0000000 --- a/app/default/content/examples/2024-10-05-cover-image-demo/metadata.ini +++ /dev/null @@ -1,2 +0,0 @@ -title = "Cover Images and Assets" -summary = "Learn how cover images and static assets work in FolderWeb" diff --git a/app/default/content/examples/2024-11-20-metadata-example/index.md b/app/default/content/examples/2024-11-20-metadata-example/index.md deleted file mode 100644 index c3bcae9..0000000 --- a/app/default/content/examples/2024-11-20-metadata-example/index.md +++ /dev/null @@ -1,53 +0,0 @@ -# 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. diff --git a/app/default/content/examples/2024-11-20-metadata-example/metadata.ini b/app/default/content/examples/2024-11-20-metadata-example/metadata.ini deleted file mode 100644 index 060c614..0000000 --- a/app/default/content/examples/2024-11-20-metadata-example/metadata.ini +++ /dev/null @@ -1,3 +0,0 @@ -title = "Working with Metadata" -date = "2024-11-20" -summary = "Learn how to use metadata.ini files to control page information and behavior" diff --git a/app/default/content/examples/2024-12-15-markdown-demo/index.md b/app/default/content/examples/2024-12-15-markdown-demo/index.md deleted file mode 100644 index 949972c..0000000 --- a/app/default/content/examples/2024-12-15-markdown-demo/index.md +++ /dev/null @@ -1,74 +0,0 @@ -# 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 - 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. diff --git a/app/default/content/examples/2024-12-15-markdown-demo/index.no.md b/app/default/content/examples/2024-12-15-markdown-demo/index.no.md deleted file mode 100644 index e7edd3a..0000000 --- a/app/default/content/examples/2024-12-15-markdown-demo/index.no.md +++ /dev/null @@ -1,74 +0,0 @@ -# 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 - 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. diff --git a/app/default/content/examples/2024-12-15-markdown-demo/metadata.ini b/app/default/content/examples/2024-12-15-markdown-demo/metadata.ini deleted file mode 100644 index 94e5c22..0000000 --- a/app/default/content/examples/2024-12-15-markdown-demo/metadata.ini +++ /dev/null @@ -1,2 +0,0 @@ -title = "Markdown Demonstration" -summary = "Shows all the Markdown formatting features available through Parsedown" diff --git a/app/default/content/examples/file-based-routing/index.md b/app/default/content/examples/file-based-routing/index.md deleted file mode 100644 index a1c51bf..0000000 --- a/app/default/content/examples/file-based-routing/index.md +++ /dev/null @@ -1,52 +0,0 @@ -# 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 diff --git a/app/default/content/examples/file-based-routing/index.no.md b/app/default/content/examples/file-based-routing/index.no.md deleted file mode 100644 index 73387e1..0000000 --- a/app/default/content/examples/file-based-routing/index.no.md +++ /dev/null @@ -1,52 +0,0 @@ -# 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 diff --git a/app/default/content/examples/file-based-routing/metadata.ini b/app/default/content/examples/file-based-routing/metadata.ini deleted file mode 100644 index 54fe86d..0000000 --- a/app/default/content/examples/file-based-routing/metadata.ini +++ /dev/null @@ -1,3 +0,0 @@ -title = "File-Based Routing" -summary = "Your folder structure is your URL structureβ€”no configuration needed" -date = "2024-11-26" diff --git a/app/default/content/examples/index.md b/app/default/content/examples/index.md deleted file mode 100644 index 4690055..0000000 --- a/app/default/content/examples/index.md +++ /dev/null @@ -1,5 +0,0 @@ -# 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. diff --git a/app/default/content/examples/mix-formats/00-intro.md b/app/default/content/examples/mix-formats/00-intro.md deleted file mode 100644 index 26c34f3..0000000 --- a/app/default/content/examples/mix-formats/00-intro.md +++ /dev/null @@ -1,11 +0,0 @@ -# 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. diff --git a/app/default/content/examples/mix-formats/00-intro.no.md b/app/default/content/examples/mix-formats/00-intro.no.md deleted file mode 100644 index 358eb64..0000000 --- a/app/default/content/examples/mix-formats/00-intro.no.md +++ /dev/null @@ -1,11 +0,0 @@ -# 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. diff --git a/app/default/content/examples/mix-formats/01-html-example.html b/app/default/content/examples/mix-formats/01-html-example.html deleted file mode 100644 index 7324c01..0000000 --- a/app/default/content/examples/mix-formats/01-html-example.html +++ /dev/null @@ -1,11 +0,0 @@ -
-

HTML Component

-

This section is rendered from 01-html-example.html – a static HTML file with inline styles.

-

HTML files are perfect for:

- -
diff --git a/app/default/content/examples/mix-formats/01-html-example.no.html b/app/default/content/examples/mix-formats/01-html-example.no.html deleted file mode 100644 index 32142f6..0000000 --- a/app/default/content/examples/mix-formats/01-html-example.no.html +++ /dev/null @@ -1,11 +0,0 @@ -
-

HTML-komponent

-

Denne seksjonen rendres fra 01-html-example.no.html – en statisk HTML-fil med inline stiler.

-

HTML-filer er perfekte for:

- -
diff --git a/app/default/content/examples/mix-formats/02-dynamic.no.php b/app/default/content/examples/mix-formats/02-dynamic.no.php deleted file mode 100644 index 28277f7..0000000 --- a/app/default/content/examples/mix-formats/02-dynamic.no.php +++ /dev/null @@ -1,41 +0,0 @@ - PHP_VERSION, - 'Servertid' => $currentTime, - 'Filer i denne katalogen' => count(glob(__DIR__ . '/*')), -]; -?> - -
-

PHP-komponent

-

Denne seksjonen rendres fra 02-dynamic.no.php – en dynamisk PHP-fil som kjΓΈrer pΓ₯ serversiden.

- - - - - - - - - - $value): ?> - - - - - - -
EgenskapVerdi
- -

PHP-filer kan:

- - -

Last inn denne siden pΓ₯ nytt for Γ₯ se servertiden oppdatere seg!

-
diff --git a/app/default/content/examples/mix-formats/02-dynamic.php b/app/default/content/examples/mix-formats/02-dynamic.php deleted file mode 100644 index 7c0e989..0000000 --- a/app/default/content/examples/mix-formats/02-dynamic.php +++ /dev/null @@ -1,41 +0,0 @@ - PHP_VERSION, - 'Server Time' => $currentTime, - 'Files in this directory' => count(glob(__DIR__ . '/*')), -]; -?> - -
-

PHP Component

-

This section is rendered from 02-dynamic.php – a dynamic PHP file that executes server-side.

- - - - - - - - - - $value): ?> - - - - - - -
PropertyValue
- -

PHP files can:

- - -

Refresh this page to see the server time update!

-
diff --git a/app/default/content/examples/mix-formats/03-conclusion.md b/app/default/content/examples/mix-formats/03-conclusion.md deleted file mode 100644 index 00f04f6..0000000 --- a/app/default/content/examples/mix-formats/03-conclusion.md +++ /dev/null @@ -1,38 +0,0 @@ -## 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. diff --git a/app/default/content/examples/mix-formats/03-conclusion.no.md b/app/default/content/examples/mix-formats/03-conclusion.no.md deleted file mode 100644 index 833aaba..0000000 --- a/app/default/content/examples/mix-formats/03-conclusion.no.md +++ /dev/null @@ -1,38 +0,0 @@ -## 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. diff --git a/app/default/content/examples/mix-formats/metadata.ini b/app/default/content/examples/mix-formats/metadata.ini deleted file mode 100644 index 29e9702..0000000 --- a/app/default/content/examples/mix-formats/metadata.ini +++ /dev/null @@ -1,3 +0,0 @@ -title = "Mix Formats" -summary = "Combine Markdown, HTML, and PHP files to create rich, dynamic pages" -date = "2024-11-26" diff --git a/app/default/content/examples/nested/index.md b/app/default/content/examples/nested/index.md deleted file mode 100644 index 33718dd..0000000 --- a/app/default/content/examples/nested/index.md +++ /dev/null @@ -1,5 +0,0 @@ -# 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. diff --git a/app/default/content/examples/nested/level-two/index.md b/app/default/content/examples/nested/level-two/index.md deleted file mode 100644 index 8766f2b..0000000 --- a/app/default/content/examples/nested/level-two/index.md +++ /dev/null @@ -1,5 +0,0 @@ -# Level Two - -This is two levels deep: `/examples/nested/level-two/` - -You can organize content in hierarchies that make sense for your site structure. diff --git a/app/default/content/examples/nested/level-two/level-three/index.md b/app/default/content/examples/nested/level-two/level-three/index.md deleted file mode 100644 index a9a3114..0000000 --- a/app/default/content/examples/nested/level-two/level-three/index.md +++ /dev/null @@ -1,5 +0,0 @@ -# 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. diff --git a/app/default/content/examples/nested/level-two/level-three/level-four/index.md b/app/default/content/examples/nested/level-two/level-three/level-four/index.md deleted file mode 100644 index a2904b0..0000000 --- a/app/default/content/examples/nested/level-two/level-three/level-four/index.md +++ /dev/null @@ -1,29 +0,0 @@ -# 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. diff --git a/app/default/content/examples/no-build-step/index.md b/app/default/content/examples/no-build-step/index.md deleted file mode 100644 index 69caba1..0000000 --- a/app/default/content/examples/no-build-step/index.md +++ /dev/null @@ -1,47 +0,0 @@ -# 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.** diff --git a/app/default/content/examples/no-build-step/index.no.md b/app/default/content/examples/no-build-step/index.no.md deleted file mode 100644 index 675a87a..0000000 --- a/app/default/content/examples/no-build-step/index.no.md +++ /dev/null @@ -1,47 +0,0 @@ -# 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.** diff --git a/app/default/content/examples/no-build-step/metadata.ini b/app/default/content/examples/no-build-step/metadata.ini deleted file mode 100644 index aeef2e9..0000000 --- a/app/default/content/examples/no-build-step/metadata.ini +++ /dev/null @@ -1,3 +0,0 @@ -title = "No Build Step" -summary = "Edit, save, refreshβ€”that's it. No compilation, no bundling, no waiting" -date = "2024-11-26" diff --git a/app/default/content/examples/templates-demo/compact-example/2024-10-10-oldest-post/index.md b/app/default/content/examples/templates-demo/compact-example/2024-10-10-oldest-post/index.md deleted file mode 100644 index 0269bef..0000000 --- a/app/default/content/examples/templates-demo/compact-example/2024-10-10-oldest-post/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Oldest Post - -The oldest post in this compact list. diff --git a/app/default/content/examples/templates-demo/compact-example/2024-11-15-older-post/index.md b/app/default/content/examples/templates-demo/compact-example/2024-11-15-older-post/index.md deleted file mode 100644 index 634aeb5..0000000 --- a/app/default/content/examples/templates-demo/compact-example/2024-11-15-older-post/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Older Post - -An older post showing date ordering. diff --git a/app/default/content/examples/templates-demo/compact-example/2024-12-01-recent-post/index.md b/app/default/content/examples/templates-demo/compact-example/2024-12-01-recent-post/index.md deleted file mode 100644 index 70d8cc0..0000000 --- a/app/default/content/examples/templates-demo/compact-example/2024-12-01-recent-post/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Recent Post - -A recent example post. diff --git a/app/default/content/examples/templates-demo/compact-example/index.md b/app/default/content/examples/templates-demo/compact-example/index.md deleted file mode 100644 index 552ee94..0000000 --- a/app/default/content/examples/templates-demo/compact-example/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Compact Template - -This section uses `list-compact.php` for a minimal, clean list view. diff --git a/app/default/content/examples/templates-demo/compact-example/metadata.ini b/app/default/content/examples/templates-demo/compact-example/metadata.ini deleted file mode 100644 index a98e845..0000000 --- a/app/default/content/examples/templates-demo/compact-example/metadata.ini +++ /dev/null @@ -1,4 +0,0 @@ -title = "Compact Template Example" - -[settings] -page_template = "list-compact" diff --git a/app/default/content/examples/templates-demo/grid-example/index.md b/app/default/content/examples/templates-demo/grid-example/index.md deleted file mode 100644 index 33a8ba9..0000000 --- a/app/default/content/examples/templates-demo/grid-example/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Grid Template - -This section uses `list-grid.php` to display items in a responsive grid with cards. diff --git a/app/default/content/examples/templates-demo/grid-example/item-one/index.md b/app/default/content/examples/templates-demo/grid-example/item-one/index.md deleted file mode 100644 index 91c74d9..0000000 --- a/app/default/content/examples/templates-demo/grid-example/item-one/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Grid Item One - -This is an example item in the grid layout. diff --git a/app/default/content/examples/templates-demo/grid-example/item-one/metadata.ini b/app/default/content/examples/templates-demo/grid-example/item-one/metadata.ini deleted file mode 100644 index 39293ad..0000000 --- a/app/default/content/examples/templates-demo/grid-example/item-one/metadata.ini +++ /dev/null @@ -1 +0,0 @@ -summary = "First item demonstrating grid layout" diff --git a/app/default/content/examples/templates-demo/grid-example/item-three/index.md b/app/default/content/examples/templates-demo/grid-example/item-three/index.md deleted file mode 100644 index 5e945e1..0000000 --- a/app/default/content/examples/templates-demo/grid-example/item-three/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Grid Item Three - -A third item to show the grid layout with multiple items. diff --git a/app/default/content/examples/templates-demo/grid-example/item-three/metadata.ini b/app/default/content/examples/templates-demo/grid-example/item-three/metadata.ini deleted file mode 100644 index db23a37..0000000 --- a/app/default/content/examples/templates-demo/grid-example/item-three/metadata.ini +++ /dev/null @@ -1 +0,0 @@ -summary = "Third grid item" diff --git a/app/default/content/examples/templates-demo/grid-example/item-two/index.md b/app/default/content/examples/templates-demo/grid-example/item-two/index.md deleted file mode 100644 index bf06247..0000000 --- a/app/default/content/examples/templates-demo/grid-example/item-two/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Grid Item Two - -Another example showing how items appear in the grid. diff --git a/app/default/content/examples/templates-demo/grid-example/item-two/metadata.ini b/app/default/content/examples/templates-demo/grid-example/item-two/metadata.ini deleted file mode 100644 index d32da37..0000000 --- a/app/default/content/examples/templates-demo/grid-example/item-two/metadata.ini +++ /dev/null @@ -1 +0,0 @@ -summary = "Second item in the grid" diff --git a/app/default/content/examples/templates-demo/grid-example/metadata.ini b/app/default/content/examples/templates-demo/grid-example/metadata.ini deleted file mode 100644 index 958c6a0..0000000 --- a/app/default/content/examples/templates-demo/grid-example/metadata.ini +++ /dev/null @@ -1,4 +0,0 @@ -title = "Grid Template Example" - -[settings] -page_template = "list-grid" diff --git a/app/default/content/examples/templates-demo/index.md b/app/default/content/examples/templates-demo/index.md deleted file mode 100644 index e6a777d..0000000 --- a/app/default/content/examples/templates-demo/index.md +++ /dev/null @@ -1,24 +0,0 @@ -# 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. diff --git a/app/default/content/multilingual/index.en.md b/app/default/content/multilingual/index.en.md deleted file mode 100644 index 0fa5c7b..0000000 --- a/app/default/content/multilingual/index.en.md +++ /dev/null @@ -1,53 +0,0 @@ -# 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. diff --git a/app/default/content/multilingual/index.no.md b/app/default/content/multilingual/index.no.md deleted file mode 100644 index a610b23..0000000 --- a/app/default/content/multilingual/index.no.md +++ /dev/null @@ -1,53 +0,0 @@ -# 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. diff --git a/app/default/content/multilingual/metadata.ini b/app/default/content/multilingual/metadata.ini deleted file mode 100644 index 66a04f1..0000000 --- a/app/default/content/multilingual/metadata.ini +++ /dev/null @@ -1,2 +0,0 @@ -title = "Multilingual Support" -summary = "Learn how to create content in multiple languages" diff --git a/app/default/content/styles.css b/app/default/content/styles.css index 0abceb8..c4fd295 100644 --- a/app/default/content/styles.css +++ b/app/default/content/styles.css @@ -1,112 +1,34 @@ -/* Hero section for frontpage */ +/* Page-specific styles for homepage */ + .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; - } - } + display: flex; + flex-direction: column; + justify-content: flex-end; + padding: 1rem .4rem; + background-color: oklch(0.85 0.05 250); + min-height: 40vh; + text-align: center; } -.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; - } - } +.hero h1 { + font-size: clamp(2.5rem, 6vw, 4rem); + 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); - } - } +.cta-section { + background: linear-gradient(135deg, oklch(0.65 0.15 250), oklch(0.50 0.12 250)); + padding: 2rem 1rem; + margin-top: 3rem; + text-align: center; + color: white; +} + +.cta-section p { + color: white; + font-size: clamp(1.1rem, 3vw, 1.3rem); +} + +.cta-content { + max-width: 42rem; + margin: 0 auto; } diff --git a/app/default/languages/en.ini b/app/default/languages/en.ini index c21972d..db8f040 100644 --- a/app/default/languages/en.ini +++ b/app/default/languages/en.ini @@ -1,13 +1,13 @@ ; English translations -; These strings are used in templates and can be customized -; If a string is missing, the key name will be used as fallback - home = "Home" categories = "Categories" tags = "Tags" read_more = "Read more" read_article = "Read article" +read_full_answer = "Read full answer" +download_pdf = "Download PDF" summary = "Summary" -footer_handcoded = "Generated in" +footer_text = "Footer content goes here" +footer_handcoded = "This page was generated in" footer_page_time = "ms" months = "January,February,March,April,May,June,July,August,September,October,November,December" diff --git a/app/default/languages/no.ini b/app/default/languages/no.ini index b64f9df..cd7c114 100644 --- a/app/default/languages/no.ini +++ b/app/default/languages/no.ini @@ -1,13 +1,13 @@ ; Norwegian translations -; These strings are used in templates and can be customized -; If a string is missing, English fallback will be used - home = "Hjem" categories = "Kategorier" -tags = "Emneknagger" +tags = "Stikkord" read_more = "Les mer" read_article = "Les artikkel" -summary = "Sammendrag" -footer_handcoded = "Generert pΓ₯" +read_full_answer = "Les hele svaret" +download_pdf = "Last ned PDF" +summary = "Oppsummering" +footer_text = "Bunntekst her" +footer_handcoded = "Denne siden ble generert pΓ₯" footer_page_time = "ms" months = "januar,februar,mars,april,mai,juni,juli,august,september,oktober,november,desember" diff --git a/app/default/styles/base.css b/app/default/styles/base.css new file mode 100644 index 0000000..7f67bc8 --- /dev/null +++ b/app/default/styles/base.css @@ -0,0 +1,191 @@ +/* MINIMAL CSS RESET*/ +* { margin-bottom: 0; } + +/* VARIABLES */ +:root { + --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + --font-heading: Georgia, "Times New Roman", serif; + --color-primary: #4a90e2; + --color-primary: oklch(0.65 0.15 250); + --color-secondary: #2c5aa0; + --color-secondary: oklch(0.50 0.12 250); + --color-light: #f0f4f8; + --color-light: oklch(0.97 0.01 250); + --color-grey: #404040; + --color-grey: oklch(0.37 0 0); +} + +/* GLOBAL */ +html { font-family: var(--font-body); font-size: clamp(16px, 2.3vw, 20px); scroll-behavior: smooth; } +body { margin: 0; color: var(--color-grey) } +p, ul, ol, aside { line-height: 1.5em; hyphens: auto } +img { max-width: 100%; height: auto; } +h1 { color: var(--color-primary); font-size: 2.3rem } +h1, h2, h3, h4, h5, h6 { + font-family: var(--font-heading); + font-weight: 400; + line-height: 1.3em; + margin-top: 1.3em; + text-wrap: pretty; +} +a { + color: var(--color-primary); + text-decoration: none; + &:hover { color: var(--color-secondary) } +} + +.grid-container { + display: grid; + grid-template-rows: auto 1fr auto; + grid-template-columns: 1fr; + grid-template-areas: "header" "main" "footer"; + height: 100%; + width: 100%; + justify-content: center; + min-height: 100vh; + align-items: stretch; +} + +.contain, :where(main>article, main>aside, main>section) { + display: grid; + grid-template-columns: minmax(.4rem, 1fr) minmax(0, 42rem) minmax(.3rem, 1fr); + > * { + grid-column: 2; + } +} + +.escape { + grid-column: 1 / -1 !important; +} + +/* HEADER */ +header { + border-bottom: 3px #00000022 solid; + grid-area: header; + > div { + padding-bottom: .2rem; + display: flex; + .logo { + margin-right: .3rem; + svg { + width: 7rem; + height: 100%; + color: var(--color-primary); + } + } + + nav { + display:flex; + align-items: center; + justify-content:flex-end; + flex: 1; + ul { + display: flex; + list-style: none; + flex-wrap: wrap; + margin-top: .4rem; + padding: 0; + justify-content: flex-end; + li { + margin-left:0.4rem; + margin-top:0.4rem; + } + } + } + } +} + +/* MAIN */ +main { + grid-area: main; + background-color: var(--color-light); + padding-bottom: 2rem; + + aside { margin-top: 1.3em } + article { + .intro { + font-size: 1.2rem; + line-height: 1.35em; + } + } + .button { + margin-top: 1.3rem; + justify-self: start; + } +} + +/* BUTTONS */ +.button { + display: inline-block; + text-decoration: none; + border-radius: 2rem; + padding: 0.35rem 1rem; + background-color: transparent; + color: var(--color-grey); + outline: 0.08rem var(--color-grey) solid; + + &:hover { + background-color: var(--color-grey); + color: white; + outline: none; + } + + &:active, &.active { + background-color: var(--color-primary); + color: white; + outline: none; + } + + &:focus { + background-color: var(--color-primary); + color: white; + outline: none; + } + + &.inverted { + background-color: transparent; + color: white; + outline: 0.08rem white solid; + + &:hover { + background-color: white; + color: var(--color-primary); + outline: none; + } + + &:active, &.active { + background-color: var(--color-light); + color: var(--color-primary); + outline: none; + } + + &:focus { + color: white; + background-color: var(--color-grey); + outline: none; + } +} + + &.bigger { + font-size: 1.2em; + padding: calc(0.35rem * 1.2) calc(1rem * 1.2); + border-radius: calc(1rem * 1.2); + } +} + +/* FOOTER */ + +footer { + color: var(--color-light); + a { + color: var(--color-light); + &:hover { color: white; text-decoration: underline } + } + background-color: var(--color-secondary); + grid-area: footer; + > div { + margin: 1rem 0; + text-align: center; + .generated { font-size: .6rem } + } +} diff --git a/app/default/styles/styles.css b/app/default/styles/styles.css deleted file mode 100644 index 86203ca..0000000 --- a/app/default/styles/styles.css +++ /dev/null @@ -1,188 +0,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); - - --space-xs: 0.5rem; - --space-s: 1rem; - --space-m: 2rem; - --space-l: 4rem; - - --size-content: 65ch; - --size-constrained: 42rem; - --size-wide: 90rem; -} - -/* Base */ -* { - box-sizing: border-box; -} - -html { - font-family: system-ui, sans-serif; - line-height: 1.6; - color: var(--color-text); - background: var(--color-background); -} - -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; -} - -/* Typography */ -h1, h2, h3, h4, h5, h6 { - line-height: 1.2; - margin-block: 1.5em 0.5em; -} - -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; -} - -/* Links */ -a { - color: var(--color-accent); - text-underline-offset: 0.2em; - - &:hover { - text-decoration: none; - } -} - -/* Layout */ -header, main, footer { - grid-column: content; -} - -header { - 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; - - & nav { - display: flex; - gap: var(--space-s); - flex-wrap: wrap; - - &:first-child { - flex: 1; - } - - & a { - text-decoration: none; - - &:hover { - text-decoration: underline; - } - - &[aria-current] { - font-weight: bold; - } - } - } - - & .language-switcher { - margin-inline-start: auto; - } -} - -main { - padding-block: var(--space-m); -} - -footer { - border-block-start: 1px solid var(--color-border); - padding-block: var(--space-s); - font-size: 0.875rem; - color: var(--color-muted); - - & nav { - display: flex; - gap: var(--space-s); - margin-block-end: var(--space-xs); - } - - & p { - margin: 0; - } -} - -/* Images */ -img { - max-width: 100%; - height: auto; - display: block; -} - -/* Code */ -code { - background: var(--color-accent-light); - padding: 0.125em 0.25em; - border-radius: 0.25em; - font-size: 0.9em; -} - -pre { - background: var(--color-accent-light); - padding: var(--space-s); - border-radius: 0.5em; - overflow-x: auto; - - & 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-block-end: 1px solid var(--color-border); -} - -th { - font-weight: bold; -} - -/* Blockquote */ -blockquote { - margin-inline: 0; - padding-inline-start: var(--space-s); - border-inline-start: 4px solid var(--color-accent); - color: var(--color-muted); -} - -/* Article metadata */ -article { - & time { - color: var(--color-muted); - font-size: 0.875rem; - } -} diff --git a/app/default/templates/base.php b/app/default/templates/base.php index ccf18d6..9611239 100644 --- a/app/default/templates/base.php +++ b/app/default/templates/base.php @@ -1,55 +1,69 @@ + + - - - <?= htmlspecialchars($pageTitle ?? 'FolderWeb') ?> - - - - - - - - - - + + + + + + + + + + + + <?= htmlspecialchars($pageTitle ?? 'Site Title') ?> - -
- - 1): ?> - - -
-
- -
+ +
+
+
+ + +
+
- +
+ +
+ + +
diff --git a/app/default/templates/list-card-grid.php b/app/default/templates/list-card-grid.php new file mode 100644 index 0000000..ec5f1dd --- /dev/null +++ b/app/default/templates/list-card-grid.php @@ -0,0 +1,82 @@ + +
+ +
+ + +
+
+ +
+ + + <?= htmlspecialchars($item['title']) ?> + + +

+ + + +

+ +

+ + +

+ +
+ + + + + + + + +
+
+ +
+
+ + diff --git a/app/default/templates/list-compact.php b/app/default/templates/list-compact.php deleted file mode 100644 index f556df5..0000000 --- a/app/default/templates/list-compact.php +++ /dev/null @@ -1,61 +0,0 @@ - -
- -
- - - - - diff --git a/app/default/templates/list-faq.php b/app/default/templates/list-faq.php new file mode 100644 index 0000000..e2791dd --- /dev/null +++ b/app/default/templates/list-faq.php @@ -0,0 +1,141 @@ + +
+ +
+ + +
+
+ +
+ +

+ +
+
+ +

:

+ + +
+
+ +
+
+ + diff --git a/app/default/templates/list-grid.php b/app/default/templates/list-grid.php index 43af018..0aad44c 100644 --- a/app/default/templates/list-grid.php +++ b/app/default/templates/list-grid.php @@ -1,48 +1,94 @@ -
- -
+
+ +
-
- -
- - - - -

- - - - - - -

- -
- -
+
+
+ + + +
+
diff --git a/app/default/templates/list.php b/app/default/templates/list.php index ed28f1d..8d7ab48 100644 --- a/app/default/templates/list.php +++ b/app/default/templates/list.php @@ -1,25 +1,55 @@ -
- -
+
+ +
-
- -
- - - - -

- - - - - - -

- -
- -
+
+ +
+ + + <?= htmlspecialchars($item['title']) ?> + + +

+ + + +

+ +

+ + +

+ + +
+ +
+ + diff --git a/app/default/templates/page.php b/app/default/templates/page.php index a511e6f..6ed654c 100644 --- a/app/default/templates/page.php +++ b/app/default/templates/page.php @@ -1,3 +1,25 @@ -
- -
+ + + + + diff --git a/app/docs/language-plugin.md b/app/docs/language-plugin.md index a8f2fbc..a07492c 100644 --- a/app/docs/language-plugin.md +++ b/app/docs/language-plugin.md @@ -490,3 +490,5 @@ content/ ## See Also - [Plugin System Reference](plugin-system.md) +- [How to Create Multi-Language Sites](../../docs/how-to/multi-language.md) +- [Configuration Reference](../../docs/reference/configuration.md) diff --git a/app/plugins/global/languages.php b/app/plugins/global/languages.php index 8f14978..588a956 100644 --- a/app/plugins/global/languages.php +++ b/app/plugins/global/languages.php @@ -46,10 +46,7 @@ Hooks::add(Hook::PROCESS_CONTENT, function(mixed $data, string $dirOrType, strin // Filter content files by language variant if (is_array($data) && !empty($data) && isset($data[0]['path'])) { - error_log("filterFilesByLanguage called with " . count($data) . " files, current lang: $currentLang"); - $filtered = filterFilesByLanguage($data, $dirOrType, $ctx); - error_log("Filtered to " . count($filtered) . " files"); - return $filtered; + return filterFilesByLanguage($data, $dirOrType, $ctx); } return $data; diff --git a/app/router.php b/app/router.php index 199b9ac..ea17ebc 100644 --- a/app/router.php +++ b/app/router.php @@ -24,34 +24,6 @@ if (file_exists($assetPath) && is_file($assetPath)) { exit; } -// Check for assets in content directory (CSS, images, etc.) -$contentAssetPath = $ctx->contentDir . '/' . $ctx->requestPath; -if (file_exists($contentAssetPath) && is_file($contentAssetPath)) { - $ext = pathinfo($contentAssetPath, PATHINFO_EXTENSION); - // Define MIME types for asset files - $mimeTypes = [ - 'css' => 'text/css', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'png' => 'image/png', - 'gif' => 'image/gif', - 'webp' => 'image/webp', - 'svg' => 'image/svg+xml', - 'pdf' => 'application/pdf', - 'woff' => 'font/woff', - 'woff2' => 'font/woff2', - 'ttf' => 'font/ttf', - 'otf' => 'font/otf', - ]; - - $extLower = strtolower($ext); - if (isset($mimeTypes[$extLower])) { - header('Content-Type: ' . $mimeTypes[$extLower]); - readfile($contentAssetPath); - exit; - } -} - // Handle frontpage if (empty($ctx->requestPath)) { $contentFiles = findAllContentFiles($ctx->contentDir); diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..292e66b --- /dev/null +++ b/docs/README.md @@ -0,0 +1,211 @@ +# FolderWeb Documentation + +Welcome to the FolderWeb documentation! This comprehensive guide covers everything you need to know about building and maintaining websites with FolderWeb. + +## πŸ“š Documentation Organization + +This documentation follows the [Diataxis framework](https://diataxis.fr/), organizing content into four distinct types to help you find exactly what you need: + +### πŸŽ“ Tutorial (Learning-Oriented) + +**Purpose**: Learn by doing +**For**: Newcomers to FolderWeb + +- **[Getting Started](tutorial/00-getting-started.md)** - Build your first FolderWeb site from scratch in 10 minutes + +Start here if you're new to FolderWeb. This hands-on tutorial walks you through creating a complete website with pages, blog posts, and custom styling. + +### πŸ“‹ How-To Guides (Task-Oriented) + +**Purpose**: Solve specific problems +**For**: Users with a specific goal + +- **[Custom Templates](how-to/custom-templates.md)** - Override default templates with your own designs +- **[Custom Styles](how-to/custom-styles.md)** - Customize appearance using CSS +- **[Multi-Language Sites](how-to/multi-language.md)** - Set up and manage multiple languages +- **[Working with Metadata](how-to/working-with-metadata.md)** - Control content with metadata.ini files + +Use these guides when you know what you want to accomplish and need step-by-step instructions. + +### πŸ“– Reference (Information-Oriented) + +**Purpose**: Look up technical details +**For**: Users who need precise information + +- **[File Structure](reference/file-structure.md)** - Complete directory layout and file conventions +- **[Metadata](reference/metadata.md)** - All available metadata fields and their usage +- **[Templates](reference/templates.md)** - Template types and available variables +- **[Configuration](reference/configuration.md)** - Configuration options and format +- **[CSS Variables](reference/css-variables.md)** - All CSS custom properties for styling + +Consult these documents when you need to look up specific technical details or API information. + +### πŸ’‘ Explanation (Understanding-Oriented) + +**Purpose**: Understand concepts and design decisions +**For**: Users who want to understand the "why" + +- **[Philosophy](explanation/philosophy.md)** - Design principles and thinking behind FolderWeb +- **[Architecture](explanation/architecture.md)** - How FolderWeb works under the hood + +Read these to gain deeper understanding of FolderWeb's design and architecture. + +## πŸš€ Quick Start + +```bash +# 1. Create project directory +mkdir my-website && cd my-website + +# 2. Copy framework files +cp -r /path/to/folderweb/app ./app + +# 3. Create your first page +mkdir content +echo "# Welcome" > content/index.md + +# 4. Start development server +php -S localhost:8000 -t . app/router.php + +# 5. Open http://localhost:8000 +``` + +**Next**: Follow the complete [Getting Started Tutorial](tutorial/00-getting-started.md) + +## 🎯 Common Tasks + +Quick links to frequently needed guides: + +| Task | Guide | +|------|-------| +| Create a custom template | [Custom Templates](how-to/custom-templates.md) | +| Change colors and fonts | [Custom Styles](how-to/custom-styles.md) | +| Add multiple languages | [Multi-Language Sites](how-to/multi-language.md) | +| Configure page metadata | [Working with Metadata](how-to/working-with-metadata.md) | +| Look up all metadata fields | [Metadata Reference](reference/metadata.md) | +| Find template variables | [Templates Reference](reference/templates.md) | +| Understand file organization | [File Structure Reference](reference/file-structure.md) | + +## πŸ’‘ Key Concepts + +- **File-based routing**: Your folder structure defines your URL structure +- **Template fallback**: Custom templates automatically override defaults +- **Content types**: Single-file pages, multi-file pages, or list views +- **Language support**: Built-in multi-language with URL prefixes +- **Metadata control**: Configure behavior with simple INI files +- **No build process**: Save and refresh - see changes immediately + +## πŸ“‹ Requirements + +- **PHP**: 8.4 or higher +- **Web server**: Apache, Nginx, or PHP's built-in server +- **Extensions**: Standard PHP (no special extensions required) + +## πŸ—‚οΈ Documentation Files + +### Complete File List + +``` +docs/ +β”œβ”€β”€ README.md # This file +β”œβ”€β”€ index.md # Documentation homepage +β”‚ +β”œβ”€β”€ tutorial/ +β”‚ └── 00-getting-started.md # Step-by-step tutorial +β”‚ +β”œβ”€β”€ how-to/ +β”‚ β”œβ”€β”€ custom-templates.md # Override templates +β”‚ β”œβ”€β”€ custom-styles.md # Customize CSS +β”‚ β”œβ”€β”€ multi-language.md # Multi-language setup +β”‚ └── working-with-metadata.md # Metadata usage +β”‚ +β”œβ”€β”€ reference/ +β”‚ β”œβ”€β”€ file-structure.md # Directory layout +β”‚ β”œβ”€β”€ metadata.md # Metadata fields +β”‚ β”œβ”€β”€ templates.md # Template reference +β”‚ β”œβ”€β”€ configuration.md # Config options +β”‚ └── css-variables.md # CSS customization +β”‚ +└── explanation/ + β”œβ”€β”€ philosophy.md # Design principles + └── architecture.md # Technical architecture +``` + +## πŸ“– Reading Paths + +Choose your path based on your needs: + +### Path 1: Complete Beginner + +1. [Getting Started Tutorial](tutorial/00-getting-started.md) +2. [Custom Styles](how-to/custom-styles.md) +3. [Working with Metadata](how-to/working-with-metadata.md) +4. [Philosophy](explanation/philosophy.md) + +### Path 2: Experienced Developer + +1. [Philosophy](explanation/philosophy.md) +2. [Architecture](explanation/architecture.md) +3. [File Structure Reference](reference/file-structure.md) +4. Browse How-To Guides as needed + +### Path 3: Specific Task + +1. Find your task in [How-To Guides](how-to/) +2. Consult [Reference](reference/) for details +3. Return to task completion + +## πŸ€” Getting Help + +### Documentation Not Enough? + +1. **Check the code**: FolderWeb is deliberately simple - reading the source is encouraged +2. **Review examples**: Look at the demo content in `/app/default/content/` +3. **Test locally**: Experiment with a test site to understand behavior + +### Common Issues + +| Problem | Solution | +|---------|----------| +| Styles not loading | Hard refresh browser (Ctrl+Shift+R) | +| 404 errors | Verify folder exists with content files | +| Language not working | Check `available` in config.ini | +| Metadata not showing | Validate INI syntax with PHP parser | +| Custom template ignored | Ensure file is in `/custom/templates/` | + +## 🌟 Philosophy Highlights + +FolderWeb embraces: + +- **Simplicity**: Just enough, nothing more +- **Longevity**: Works today, works in 2035 +- **Transparency**: Readable code, clear behavior +- **Files**: Your content, fully portable +- **No build**: Save and refresh workflow + +Read the complete [Philosophy](explanation/philosophy.md) to understand FolderWeb's design principles. + +## πŸ“ Contributing to Documentation + +Documentation improvements are welcome: + +- Fix typos or unclear explanations +- Add missing examples +- Improve existing guides +- Suggest new how-to guides + +Keep documentation: +- Clear and concise +- Accurate and tested +- Organized according to Diataxis principles + +## πŸ”— External Resources + +- [Diataxis Framework](https://diataxis.fr/) - Documentation organization system +- [PHP 8.4 Documentation](https://www.php.net/manual/en/) - PHP reference +- [Markdown Guide](https://www.markdownguide.org/) - Markdown syntax +- [MDN Web Docs](https://developer.mozilla.org/) - HTML and CSS reference + +--- + +**Start here**: [Getting Started Tutorial](tutorial/00-getting-started.md) +**Main index**: [Documentation Index](index.md) diff --git a/docs/explanation/architecture.md b/docs/explanation/architecture.md new file mode 100644 index 0000000..08809d8 --- /dev/null +++ b/docs/explanation/architecture.md @@ -0,0 +1,739 @@ +# Architecture + +Understanding how FolderWeb works under the hood. + +## High-Level Overview + +FolderWeb follows a simple request-response flow: + +``` +HTTP Request + ↓ +router.php (entry point) + ↓ +Parse request path + ↓ +Find content files + ↓ +Determine content type (page/list/file) + ↓ +Render content + ↓ +Wrap in templates + ↓ +HTTP Response +``` + +## Core Components + +### 1. Router (`app/router.php`) + +**Purpose**: Entry point for all requests, determines what to serve + +**Responsibilities**: +- Receive HTTP requests +- Check for root-level assets (`/custom/assets/`) +- Parse request path +- Dispatch to appropriate renderer +- Handle redirects (trailing slashes) +- Serve 404 for missing content + +**Key Flow**: +```php +// 1. Check for root-level assets +if (file_exists("/custom/assets/$path")) { + serve_static_file(); +} + +// 2. Empty path = home page (render all root content files) +if (empty($path)) { + render_all_files_in_root(); +} + +// 3. Parse request path +$result = parseRequestPath($ctx); + +// 4. Handle based on type +match($result['type']) { + 'page' => renderMultipleFiles(...), + 'file' => renderFile(...), + 'directory' => renderListView(...), + 'not_found' => show_404() +}; +``` + +**Location**: `/app/router.php` (lines 1-100+) + +### 2. Content Discovery (`app/content.php`) + +**Purpose**: Find and parse content files and directories + +**Key Functions**: + +#### `parseRequestPath($ctx)` +Analyzes request path and determines content type. + +**Returns**: +```php +[ + 'type' => 'page' | 'file' | 'directory' | 'not_found', + 'path' => '/full/system/path', + 'files' => [...], // For page type + // ... other data +] +``` + +**Logic**: +1. Resolve translated slugs to real paths +2. Check if path exists +3. If directory: + - Has subdirectories? β†’ `type: 'directory'` (list view) + - Has content files only? β†’ `type: 'page'` (multi-file) +4. If matches file? β†’ `type: 'file'` +5. Otherwise β†’ `type: 'not_found'` + +#### `findAllContentFiles($dir, $lang, $defaultLang, $availableLangs)` +Scans directory for content files. + +**Process**: +1. Read directory contents +2. Filter for valid extensions (`.md`, `.html`, `.php`) +3. Parse filenames for language suffix +4. Filter by current language: + - Show `.{lang}.ext` files for that language + - Show default files (no suffix) only if no language variant +5. Sort alphanumerically +6. Return array of file paths + +**Example**: +```php +// Directory contains: +// - index.md +// - index.no.md +// - about.md + +// English request (lang=en, default=en): +findAllContentFiles() β†’ ['index.md', 'about.md'] + +// Norwegian request (lang=no): +findAllContentFiles() β†’ ['index.no.md', 'about.md'] +``` + +#### `loadMetadata($dirPath, $lang, $defaultLang)` +Loads and merges metadata for a directory. + +**Process**: +1. Check for `metadata.ini` in directory +2. Parse INI file with sections +3. Start with base values +4. Override with language-specific section if exists +5. Return merged array + +**Example**: +```ini +title = "About" +summary = "Learn about us" + +[no] +title = "Om" +summary = "LΓ¦r om oss" +``` + +For Norwegian request: +```php +loadMetadata(..., 'no', 'en') β†’ [ + 'title' => 'Om', // Overridden + 'summary' => 'LΓ¦r om oss' // Overridden +] +``` + +#### `resolveTranslatedPath($ctx, $requestPath)` +Maps translated slugs back to real directory names. + +**Example**: +```ini +; In content/about/metadata.ini: +[no] +slug = "om-oss" +``` + +Request to `/no/om-oss/` resolves to `content/about/`. + +**Process**: +1. Split path into segments +2. For each segment: + - Load metadata of parent directory + - Check if any subdirectory has matching translated slug + - Replace segment with real directory name +3. Return resolved path + +### 3. Rendering Engine (`app/rendering.php`) + +**Purpose**: Convert content to HTML and wrap in templates + +**Key Functions**: + +#### `renderContentFile($filePath)` +Converts a single content file to HTML. + +**Process**: +```php +switch (extension) { + case 'md': + return Parsedown->text(file_contents); + case 'html': + return file_contents; + case 'php': + ob_start(); + include $filePath; // $ctx available + return ob_get_clean(); +} +``` + +#### `renderFile($ctx, $filePath)` +Renders single file wrapped in templates. + +**Process**: +1. Convert file to HTML +2. Load metadata +3. Wrap in page template +4. Wrap in base template +5. Return HTML + +#### `renderMultipleFiles($ctx, $filePaths, $pageDir)` +Renders multiple files as single page. + +**Process**: +1. Convert each file to HTML +2. Concatenate HTML (in order) +3. Load metadata +4. Wrap in page template +5. Wrap in base template +6. Return HTML + +**Used for**: Multi-file pages (documentation, long articles) + +#### `renderTemplate($ctx, $content, $statusCode = 200)` +Wraps content in base template. + +**Process**: +1. Extract variables for template +2. Set HTTP status code +3. Include base template +4. Return HTML + +**Variables provided**: +- `$content` - Rendered HTML +- `$ctx` - Context object +- `$currentLang`, `$navigation`, `$homeLabel`, etc. + +### 4. Context Object (`app/context.php`) + +**Purpose**: Immutable request context with computed properties + +**Implementation**: +```php +readonly class Context { + public function __construct( + public private(set) string $contentDir, + public private(set) string $currentLang, + // ... other properties + ) {} + + // Computed property (PHP 8.4 hook) + public string $langPrefix { + get => $this->currentLang !== $this->defaultLang + ? "/{$this->currentLang}" + : ''; + } + + // Lazy-loaded computed property + public array $navigation { + get => buildNavigation($this); + } +} +``` + +**Benefits**: +- **Immutability**: Cannot be changed after creation +- **Type safety**: All properties typed +- **Computed values**: Calculated on-demand +- **No globals**: Passed explicitly + +**Creation**: +```php +$ctx = createContext(); +``` + +This function: +1. Loads configuration +2. Extracts language from URL +3. Determines content directory +4. Resolves template paths +5. Returns readonly Context object + +### 5. Configuration (`app/config.php`) + +**Purpose**: Load and merge configuration + +**Process**: +1. Parse `/app/config.ini` (defaults) +2. Parse `/custom/config.ini` if exists +3. Merge arrays (custom overrides defaults) +4. Extract language settings +5. Validate configuration + +**Configuration Used**: +```ini +[languages] +default = "en" +available = "en,no,fr" +``` + +### 6. Helper Functions (`app/helpers.php`) + +**Purpose**: Utility functions used throughout + +**Key Helpers**: + +| Function | Purpose | +|----------|---------| +| `resolveTemplate($name, $type)` | Find custom or default template | +| `getSubdirectories($dir)` | List subdirectories only | +| `extractTitle($filePath, $lang, $defaultLang)` | Extract H1 from content | +| `formatNorwegianDate($date)` | Format date as "2. november 2025" | +| `extractDateFromFolder($name)` | Parse date from folder name | +| `findCoverImage($dir)` | Locate cover image | +| `findPdfFile($dir)` | Find first PDF | + +### 7. Static File Server (`app/static.php`) + +**Purpose**: Serve CSS, fonts, and other static assets + +**Process**: +1. Validate path (prevent directory traversal) +2. Resolve real path +3. Check file exists and is readable +4. Determine MIME type +5. Set headers +6. Output file contents + +**Routes**: +- `/app/styles/base.css` β†’ Custom or default CSS +- `/app/default-styles/base.css` β†’ Default CSS +- `/custom/fonts/*` β†’ Custom fonts + +## Data Flow + +### Request Flow Diagram + +``` +1. HTTP Request: /blog/2025-11-02-post/ + ↓ +2. router.php receives request + ↓ +3. createContext() + β”œβ”€ Load config + β”œβ”€ Extract language from URL + β”œβ”€ Determine content directory + └─ Return Context object + ↓ +4. parseRequestPath($ctx) + β”œβ”€ resolveTranslatedPath() - map slug to real path + β”œβ”€ Check path exists + β”œβ”€ findAllContentFiles() - scan for content + └─ Return ['type' => 'file', 'path' => '...'] + ↓ +5. renderFile($ctx, $filePath) + β”œβ”€ renderContentFile() - convert to HTML + β”œβ”€ loadMetadata() - get metadata + β”œβ”€ Apply page template + └─ Apply base template + ↓ +6. HTTP Response: HTML +``` + +### List View Flow + +``` +1. Request: /blog/ + ↓ +2. parseRequestPath() β†’ type: 'directory' + ↓ +3. Load directory metadata + β”œβ”€ Get page_template setting + └─ Get other directory metadata + ↓ +4. getSubdirectories() - find all subdirs + ↓ +5. For each subdirectory: + β”œβ”€ loadMetadata() - get title, date, summary + β”œβ”€ findCoverImage() - locate cover + β”œβ”€ findPdfFile() - locate PDF + └─ Build item array + ↓ +6. Render list template with $items + ↓ +7. Wrap in base template + ↓ +8. Return HTML +``` + +## File Organization + +### Separation of Concerns + +``` +app/ +β”œβ”€β”€ router.php # Entry point, request handling +β”œβ”€β”€ content.php # Content discovery, parsing +β”œβ”€β”€ rendering.php # HTML generation, templates +β”œβ”€β”€ context.php # Request context +β”œβ”€β”€ config.php # Configuration loading +β”œβ”€β”€ helpers.php # Utility functions +β”œβ”€β”€ constants.php # Constants (extensions) +└── static.php # Static file serving +``` + +Each file has a single responsibility. + +### Template Resolution + +Templates use fallback chain: + +``` +1. /custom/templates/{name}.php + ↓ (if not found) +2. /app/default/templates/{name}.php +``` + +**Implementation**: +```php +function resolveTemplate($name, $type = 'templates') { + $custom = __DIR__ . "/../custom/$type/$name"; + $default = __DIR__ . "/default/$type/$name"; + return file_exists($custom) ? $custom : $default; +} +``` + +This pattern applies to: +- Templates +- Styles +- Languages +- Any overridable resource + +## Language Handling + +### URL Structure + +``` +/ β†’ Default language +/no/ β†’ Norwegian +/fr/page/ β†’ French page +``` + +### Language Extraction + +From URL path: +```php +// Request: /no/blog/post/ +$segments = explode('/', trim($path, '/')); +$firstSegment = $segments[0] ?? ''; + +if (in_array($firstSegment, $availableLangs)) { + $currentLang = $firstSegment; + $pathWithoutLang = implode('/', array_slice($segments, 1)); +} else { + $currentLang = $defaultLang; + $pathWithoutLang = $path; +} +``` + +### Content Filtering + +Files with language suffixes (`.{lang}.ext`) are filtered: + +```php +// Parse filename +$parts = explode('.', $filename); +$lastPart = $parts[count($parts) - 2] ?? null; + +// Check if second-to-last part is a language +if (in_array($lastPart, $availableLangs)) { + $fileLang = $lastPart; +} else { + $fileLang = $defaultLang; +} + +// Include file if: +// - It matches current language, OR +// - It's default language AND no specific variant exists +``` + +## Navigation Building + +### Process + +```php +function buildNavigation($ctx) { + $items = []; + + // 1. Scan content root for directories + $dirs = getSubdirectories($ctx->contentDir); + + // 2. For each directory + foreach ($dirs as $dir) { + // Load metadata + $metadata = loadMetadata($dir, $ctx->currentLang, $ctx->defaultLang); + + // Skip if menu = false + if (!($metadata['menu'] ?? false)) continue; + + // Build item + $items[] = [ + 'title' => $metadata['title'] ?? basename($dir), + 'url' => $ctx->langPrefix . '/' . basename($dir) . '/', + 'order' => $metadata['menu_order'] ?? 999 + ]; + } + + // 3. Sort by menu_order + usort($items, fn($a, $b) => $a['order'] <=> $b['order']); + + return $items; +} +``` + +### Caching + +Navigation is a computed property, calculated once per request: + +```php +public array $navigation { + get => buildNavigation($this); +} +``` + +PHP memoizes the result automatically. + +## Performance Characteristics + +### Time Complexity + +| Operation | Complexity | Notes | +|-----------|------------|-------| +| Route resolution | O(1) | Direct file checks | +| Content file scan | O(n) | n = files in directory | +| Metadata loading | O(1) | Single file read | +| Template rendering | O(m) | m = content size | +| Navigation build | O(d) | d = top-level directories | + +### Space Complexity + +- **Memory**: O(c) where c = content size +- **No caching**: Each request independent +- **Stateless**: No session storage + +### Optimization Points + +1. **OPcache**: PHP bytecode caching (biggest impact) +2. **Web server cache**: Serve cached HTML +3. **Reverse proxy**: Varnish, Cloudflare +4. **Minimize file reads**: Context created once per request + +## Security Architecture + +### Path Validation + +Multiple layers prevent directory traversal: + +```php +// 1. Remove .. segments +$path = str_replace('..', '', $path); + +// 2. Resolve to real path +$realPath = realpath($path); + +// 3. Ensure within content directory +if (!str_starts_with($realPath, $contentDir)) { + return 404; +} + +// 4. Check readable +if (!is_readable($realPath)) { + return 404; +} +``` + +### Output Escaping + +All user-generated content escaped: + +```php + +``` + +This prevents XSS attacks. + +### MIME Type Validation + +Static files served with correct MIME types: + +```php +$mimeTypes = [ + 'css' => 'text/css', + 'woff2' => 'font/woff2', + 'jpg' => 'image/jpeg', + // ... +]; + +header('Content-Type: ' . $mimeTypes[$extension]); +``` + +## Error Handling + +### HTTP Status Codes + +- **200 OK**: Successful content render +- **301 Moved Permanently**: Missing trailing slash +- **404 Not Found**: Content doesn't exist + +### 404 Handling + +When content not found: + +```php +renderTemplate($ctx, '

404 Not Found

', 404); +``` + +Base template rendered with 404 status. + +## Extension Points + +### Custom Templates + +Override any template: + +```php +// Framework checks: +$custom = '/custom/templates/list-my-custom.php'; +if (file_exists($custom)) { + include $custom; +} else { + include '/app/default/templates/list.php'; +} +``` + +### Custom Functions + +Add your own in `/custom/functions.php`: + +```php + + +# Dynamic Content + +Current time: + +The language is: currentLang ?> +``` + +## Testing Architecture + +### Manual Testing + +1. Create test content +2. Start dev server: `php -S localhost:8000 -t . app/router.php` +3. Visit URLs, verify output +4. Check different languages +5. Test edge cases (missing files, invalid paths) + +### Automated Testing (Future) + +Possible test structure: + +```php +// tests/RouterTest.php +test('renders home page', function() { + $response = request('/'); + expect($response->status)->toBe(200); + expect($response->body)->toContain('

'); +}); + +test('handles 404', function() { + $response = request('/nonexistent/'); + expect($response->status)->toBe(404); +}); +``` + +## Deployment Architecture + +### Simple Deployment + +```bash +# 1. Clone repository +git clone https://github.com/you/your-site + +# 2. Point web server to directory +# Document root: /path/to/site +# Rewrite all requests to: /app/router.php + +# 3. Done +``` + +### With Build Step (Optional) + +```bash +# 1. Clone and build +git clone ... +cd site + +# 2. Process custom styles (optional) +# E.g., PostCSS, autoprefixer + +# 3. Deploy +rsync -av . server:/var/www/site/ +``` + +### Zero-Downtime Deployment + +```bash +# 1. Deploy to new directory +rsync -av . server:/var/www/site-new/ + +# 2. Symlink switch +ln -sfn /var/www/site-new /var/www/site-current + +# 3. Web server serves from /var/www/site-current +``` + +## Related + +- [Philosophy](philosophy.md) +- [Getting Started Tutorial](../tutorial/00-getting-started.md) +- [File Structure Reference](../reference/file-structure.md) +- [Template Reference](../reference/templates.md) diff --git a/docs/explanation/philosophy.md b/docs/explanation/philosophy.md new file mode 100644 index 0000000..cace2af --- /dev/null +++ b/docs/explanation/philosophy.md @@ -0,0 +1,496 @@ +# Philosophy + +Understanding the principles and thinking behind FolderWeb. + +## Core Idea + +**Your file system is your content management system.** + +FolderWeb embraces the simplest possible approach to web publishing: create a folder structure that mirrors your site hierarchy, drop files into folders, and they immediately become pages. No database, no admin panel, no build process. + +## Design Principles + +### 1. Just Enough, Nothing More + +FolderWeb applies minimal PHP to enable modern conveniences while remaining maintainable for years or decades. Every feature must justify its existence by solving a real problem without creating new complexity. + +**What this means:** +- No frameworks that might be abandoned +- No build tools that need maintenance +- No package managers introducing dependencies +- No abstractions unless they provide lasting value + +**Example**: Instead of using a routing library, FolderWeb uses PHP's native file functions to map folders to URLs. This will work identically in 2025 and 2035. + +### 2. Longevity Over Novelty + +Code should outlive trends. FolderWeb prioritizes stability and backward compatibility over cutting-edge features. + +**What this means:** +- Standard PHP (no exotic extensions) +- Plain HTML and CSS (no JavaScript required) +- Simple file formats (Markdown, INI) +- Conventions over configuration + +**Why it matters**: A site built today should still work in 10 years without updates. The web's foundational technologies (HTML, CSS, PHP) change slowly and maintain backward compatibility. + +### 3. Transparent and Readable + +You should be able to open any file and immediately understand what it does. No magic, no hidden behavior. + +**What this means:** +- Sparse, meaningful comments +- Descriptive function names +- Simple control flow +- Minimal abstraction layers + +**Example**: Want to know how templates work? Open `app/rendering.php` and read 100 lines of straightforward PHP. No framework documentation needed. + +### 4. Files Are Content + +Your content lives in plain text files you can edit with any text editor. You own your content completely. + +**What this means:** +- Content is portable (copy files, migrate easily) +- Version control friendly (Git tracks changes) +- No lock-in (files work without FolderWeb) +- Backup-friendly (copy a folder) + +**Example**: Your entire site is a folder structure. Zip it, move it to another server, extract it, and it works. No database export/import, no migration scripts. + +## What FolderWeb Is + +### A File-Based Router + +FolderWeb maps your folder structure to URLs: +``` +content/blog/2025-11-02-post/ β†’ yoursite.com/blog/2025-11-02-post/ +``` + +That's it. No route definitions, no controllers, no configuration. + +### A Content Renderer + +FolderWeb converts Markdown to HTML, wraps it in templates, and serves it. Three steps: +1. Find content files +2. Convert to HTML +3. Wrap in template + +### A Minimal Template System + +FolderWeb provides just enough templating to avoid repetition: +- Base template for site structure +- Page template for content wrapper +- List templates for directory views + +All using plain PHP includes. No template language to learn. + +### A Convention Framework + +FolderWeb establishes conventions that eliminate configuration: +- `metadata.ini` for structured data +- `cover.jpg` for images +- `YYYY-MM-DD-slug` for dates +- `filename.lang.ext` for translations + +Learn the conventions once, apply them everywhere. + +## What FolderWeb Is Not + +### Not a CMS + +No admin panel. Edit files directly with your text editor, commit to Git, deploy. + +**Why**: Admin panels add complexity, require maintenance, create security risks, and limit what you can do. Text files are simpler and more powerful. + +### Not a Static Site Generator + +FolderWeb renders pages on request, not at build time. + +**Why**: No build step means immediate feedback. Save a file, refresh your browser, see changes. No waiting for builds, no deployment pipelines required (though you can add them). + +### Not a JavaScript Framework + +Zero JavaScript in the framework. HTML and CSS only. + +**Why**: JavaScript adds complexity, breaks without it, requires builds/transpilation, and changes rapidly. HTML and CSS are stable and sufficient for content sites. + +### Not Opinionated About Design + +FolderWeb provides minimal default styles. Your design is your own. + +**Why**: Design trends change. FolderWeb gives you a clean foundation and gets out of the way. Override everything in `/custom/`. + +## Design Decisions Explained + +### Why PHP 8.4+? + +**Modern features without complexity.** + +PHP 8.4 provides: +- Readonly classes (immutability) +- Property hooks (computed properties) +- Arrow functions (concise code) +- Modern array functions +- Asymmetric visibility (controlled access) + +These features make code clearer without adding dependencies or build steps. PHP 8.4 will be supported for years. + +**Tradeoff**: Requires newer PHP, but gains clarity and performance. + +### Why No Database? + +**Files are simpler.** + +Databases add: +- Setup complexity +- Backup complexity +- Migration complexity +- Performance tuning +- Additional failure points + +For content sites, files provide: +- Version control integration +- Simple backups (copy folder) +- Portability +- Transparent storage +- No setup required + +**When you might need a database**: User-generated content, real-time updates, complex queries, thousands of pages. For those cases, use a different tool. + +### Why INI Files for Metadata? + +**Simple, readable, PHP-native.** + +INI format: +- No parsing library needed (built into PHP) +- Human-readable and editable +- Supports sections for languages +- Familiar format + +**Alternatives considered**: +- **YAML**: Requires library, complex syntax +- **JSON**: Not as human-friendly, no comments +- **TOML**: Requires library, less familiar +- **Frontmatter**: Mixes content and metadata + +### Why Markdown? + +**Readable as plain text, converts to HTML.** + +Markdown is: +- Easy to learn (15 minutes) +- Readable without rendering +- Widely supported +- Future-proof (plain text) +- Version control friendly + +**Alternatives supported**: HTML (for complex layouts), PHP (for dynamic content). + +### Why No Build Tools? + +**Immediate feedback, zero setup.** + +Build tools add: +- Installation steps +- Configuration files +- Waiting for builds +- Build failures to debug +- Another thing that can break + +Without builds: +- Save file β†’ refresh browser β†’ see result +- No setup (just PHP) +- Nothing to configure +- Nothing to break + +**Tradeoff**: Can't use Sass, TypeScript, etc. But you can use modern CSS, which is very capable. + +### Why Trailing Slashes? + +**Consistency and clarity.** + +``` +/blog/ # Directory (list view) +/blog # Redirects to /blog/ +``` + +Trailing slashes clarify that URLs represent directories, not files. Consistent URLs prevent duplicate content and simplify routing. + +### Why Language Prefixes? + +**Clear, hackable URLs.** + +``` +yoursite.com/en/about/ # English +yoursite.com/no/about/ # Norwegian +``` + +Language in URL: +- User sees current language +- Can manually change URL +- Bookmarkable per language +- SEO-friendly (clear language signal) + +**Default language has no prefix** (shorter, cleaner URLs for primary audience). + +## Architectural Patterns + +### Immutable Context + +The `Context` object is readonly (PHP 8.4): +```php +readonly class Context { + public function __construct( + public private(set) string $contentDir, + public private(set) string $currentLang, + // ... + ) {} +} +``` + +**Why**: Prevents accidental mutation, makes code predictable, enables safe sharing of state. + +### Computed Properties + +Properties calculate values on-demand: +```php +public string $langPrefix { + get => $this->currentLang !== $this->defaultLang + ? "/{$this->currentLang}" + : ''; +} +``` + +**Why**: Keeps related logic together, avoids storing derived data, updates automatically. + +### Function-Based API + +Core functionality exposed as functions, not classes: +```php +renderFile($ctx, $filePath); +findAllContentFiles($dir, $lang, $defaultLang, $availableLangs); +``` + +**Why**: Simple to understand, easy to test, no object lifecycle to manage. + +### Template Fallback + +Check custom, fall back to default: +```php +$custom = "/custom/templates/$name.php"; +$default = "/app/default/templates/$name.php"; +return file_exists($custom) ? $custom : $default; +``` + +**Why**: Never modify defaults, always override. Clean separation between framework and customization. + +## Performance Philosophy + +### Performance Through Simplicity + +FolderWeb is fast because it does less: +- No database queries +- No heavy frameworks +- No JavaScript parsing +- Minimal file reads +- Direct PHP includes + +**Measured performance**: Page load time displayed in footer. Pride in speed through simplicity. + +### Caching Strategy + +FolderWeb doesn't implement caching. Instead: +- Use OPcache (PHP bytecode cache) +- Use web server caching (Apache/Nginx) +- Use reverse proxy (Varnish, Cloudflare) +- CSS versioned automatically (MD5 hash) + +**Why**: Let specialized tools handle caching. FolderWeb focuses on core functionality. + +### Optimization Priorities + +1. **Avoid work**: Don't render what's not needed +2. **Use native functions**: PHP's file functions are optimized +3. **Minimal abstraction**: Fewer layers = less overhead + +## Security Philosophy + +### Defense in Depth + +Multiple security layers: +- Path validation (prevent directory traversal) +- Realpath checks (resolve symlinks, verify paths) +- Content root enforcement (files must be in document root) +- Output escaping (prevent XSS) +- MIME type validation (proper content types) + +### Simplicity Is Security + +Less code = smaller attack surface: +- No database (no SQL injection) +- No user input rendering (no XSS in content) +- No file uploads (no upload vulnerabilities) +- No authentication (no auth bypasses) + +**For user-generated content**: Use a different tool. FolderWeb is for static content you control. + +## Maintenance Philosophy + +### Code Should Age Gracefully + +FolderWeb aims to require zero maintenance: +- Standard PHP (no exotic dependencies) +- Minimal third-party code (one library: Parsedown) +- Stable APIs (PHP doesn't break backward compatibility) +- No framework upgrades needed + +**Goal**: Deploy once, forget about it. Check in years later, it still works. + +### Convention Over Configuration + +Fewer configuration options = less to maintain: +- File conventions replace config +- Sensible defaults for everything +- Only configure what's necessary (languages) + +### Documentation Is Core + +Documentation is part of the project: +- Comprehensive reference +- Clear examples +- Explanation of decisions +- How-to guides for common tasks + +**Why**: Future you (or someone else) will thank present you. + +## When to Use FolderWeb + +### Ideal For + +- **Blogs**: Write Markdown, publish immediately +- **Documentation**: Multi-file pages, clear structure +- **Portfolios**: Grid layouts, cover images +- **Marketing sites**: Static content, fast loading +- **Personal sites**: Simple, maintainable +- **Long-term projects**: Will work for decades + +### Not Ideal For + +- **User-generated content**: No database, no auth +- **E-commerce**: Needs dynamic inventory, checkout +- **Social networks**: Real-time updates, complex data +- **SPAs**: JavaScript-heavy, API-driven +- **Large-scale sites**: Thousands of pages (consider static generation) + +### Perfect Fit Scenario + +You want a blog or content site that: +- You control all content +- Loads fast +- Requires minimal maintenance +- Will work for years without updates +- Integrates with Git workflow +- Gives you complete control + +## Comparison to Alternatives + +### vs WordPress + +**WordPress**: Full-featured CMS, database-driven, plugin ecosystem, admin panel, requires regular updates + +**FolderWeb**: File-based, no database, no plugins, no admin, zero maintenance + +**Choose WordPress if**: You need plugins, non-technical editors, or a proven ecosystem + +**Choose FolderWeb if**: You want simplicity, longevity, and complete control + +### vs Jekyll/Hugo (Static Generators) + +**Static Generators**: Build at deploy time, generate HTML files, fast serving, requires builds + +**FolderWeb**: Renders on request, no build step, immediate feedback, simpler workflow + +**Choose Static Generator if**: You want maximum performance, have build infrastructure + +**Choose FolderWeb if**: You want immediate feedback, simpler deployment, dynamic capabilities + +### vs Laravel/Symfony (PHP Frameworks) + +**Frameworks**: Full-stack, MVC architecture, ORM, routing, complex features + +**FolderWeb**: Minimal, file-based routing, no ORM, single purpose + +**Choose Framework if**: You're building a complex web application + +**Choose FolderWeb if**: You're publishing content and want simplicity + +## Future Direction + +### Stability Over Features + +FolderWeb aims to reach "done" status: +- Core functionality complete +- No major features needed +- Focus on documentation and examples +- Bug fixes and security updates only + +### Possible Additions + +Only if they maintain simplicity: +- More template examples +- Additional default styles (opt-in) +- Performance optimizations +- Better error messages + +### Will Never Add + +Features that contradict philosophy: +- JavaScript requirement +- Database integration +- Build process +- Admin panel +- User authentication +- Complex plugin system + +## Contributing to FolderWeb + +### Align With Philosophy + +Proposed changes should: +- Maintain simplicity +- Avoid new dependencies +- Work with PHP 8.4+ +- Be maintainable long-term +- Solve real problems + +### Ideal Contributions + +- Bug fixes +- Performance improvements +- Better documentation +- Example templates +- Test cases +- Clarification of existing code + +### Before Adding Features + +Ask: +1. Can this be solved in userland (custom templates/code)? +2. Does this add complexity for all users? +3. Will this need maintenance in 5 years? +4. Is this truly necessary? + +## Conclusion + +FolderWeb is deliberately simple. It does one thingβ€”publishes content from filesβ€”and does it well. It resists feature creep, embraces constraints, and prioritizes longevity. + +This isn't the right tool for every project. But for content sites that value simplicity, maintainability, and longevity, it might be perfect. + +The code you write today should work in 2035. FolderWeb is built on that principle. + +## Related + +- [Getting Started Tutorial](../tutorial/00-getting-started.md) +- [Architecture Overview](architecture.md) +- [File Structure Reference](../reference/file-structure.md) diff --git a/docs/how-to/custom-styles.md b/docs/how-to/custom-styles.md new file mode 100644 index 0000000..761de27 --- /dev/null +++ b/docs/how-to/custom-styles.md @@ -0,0 +1,398 @@ +# How to Customize Styles + +This guide shows you how to override the default styles with your own CSS. + +## Overview + +FolderWeb uses a fallback system for styles: +1. Check `/custom/styles/base.css` +2. Fall back to `/app/default/styles/base.css` + +The framework automatically versions CSS files with MD5 hashes for cache busting. + +## Quick Start + +### Step 1: Create Custom Stylesheet + +```bash +mkdir -p custom/styles +touch custom/styles/base.css +``` + +### Step 2: Override CSS Variables + +The easiest way to customize is to override CSS custom properties: + +```css +:root { + --color-primary: oklch(0.65 0.20 30); /* Orange */ + --color-secondary: oklch(0.50 0.18 30); /* Dark orange */ + --color-light: oklch(0.98 0.01 30); /* Warm white */ + --color-grey: oklch(0.40 0 0); /* Grey */ + + --font-body: "Helvetica Neue", Arial, sans-serif; + --font-heading: "Georgia", serif; + + --spacing-unit: 1.5rem; + --border-radius: 8px; +} +``` + +### Step 3: Test Your Changes + +Refresh your browser. If changes don't appear, do a hard refresh (Ctrl+Shift+R or Cmd+Shift+R). + +## Available CSS Variables + +### Colors + +```css +--color-primary: oklch(0.65 0.15 250); /* Primary blue */ +--color-secondary: oklch(0.50 0.12 250); /* Dark blue */ +--color-light: oklch(0.97 0.01 250); /* Off-white */ +--color-grey: oklch(0.37 0 0); /* Dark grey */ +``` + +**Note**: FolderWeb uses OKLCH colors for perceptually uniform color spaces. You can also use hex, rgb, or hsl if preferred. + +### Typography + +```css +--font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; +--font-heading: Georgia, "Times New Roman", serif; + +--font-size-base: 1.125rem; /* 18px */ +--font-size-small: 0.875rem; /* 14px */ +--line-height-base: 1.6; +--line-height-heading: 1.2; +``` + +### Spacing + +```css +--spacing-unit: 1.5rem; /* Base spacing (24px) */ +--spacing-small: 0.75rem; /* 12px */ +--spacing-large: 3rem; /* 48px */ +``` + +### Layout + +```css +--max-width: 70rem; /* Content max-width */ +--border-radius: 4px; /* Corner rounding */ +``` + +## Adding Custom Fonts + +### Step 1: Add Font Files + +```bash +mkdir -p custom/fonts +# Copy your .woff2 files here +cp ~/Downloads/MyFont-Regular.woff2 custom/fonts/ +cp ~/Downloads/MyFont-Bold.woff2 custom/fonts/ +``` + +### Step 2: Declare Font Faces + +In `custom/styles/base.css`: + +```css +@font-face { + font-family: 'MyFont'; + src: url('/custom/fonts/MyFont-Regular.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +@font-face { + font-family: 'MyFont'; + src: url('/custom/fonts/MyFont-Bold.woff2') format('woff2'); + font-weight: 700; + font-style: normal; + font-display: swap; +} + +:root { + --font-body: 'MyFont', sans-serif; +} +``` + +**Note**: Font files are automatically served by FolderWeb's static file handler. + +## Page-Specific Styling + +FolderWeb adds dynamic CSS classes to the `` element: + +```html + +``` + +Use these for targeted styling: + +```css +/* Style all blog pages */ +.section-blog { + --color-primary: oklch(0.60 0.15 150); /* Green for blog */ +} + +/* Style a specific page */ +.page-about { + font-size: 1.25rem; +} + +/* Combine for precision */ +.section-docs.page-installation { + background: var(--color-light); +} +``` + +## Responsive Design + +FolderWeb uses modern CSS features for responsiveness. Use `clamp()` for fluid typography: + +```css +:root { + --font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem); + --spacing-unit: clamp(1rem, 0.8rem + 1vw, 2rem); +} + +h1 { + font-size: clamp(2rem, 1.5rem + 2vw, 3.5rem); +} +``` + +Use container queries for component responsiveness: + +```css +.card-grid { + container-type: inline-size; + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: var(--spacing-unit); +} + +@container (min-width: 600px) { + .card { + padding: var(--spacing-large); + } +} +``` + +## Dark Mode + +Add a dark mode using CSS custom properties and media queries: + +```css +:root { + --color-bg: oklch(0.97 0.01 250); + --color-text: oklch(0.20 0 0); +} + +@media (prefers-color-scheme: dark) { + :root { + --color-bg: oklch(0.20 0 0); + --color-text: oklch(0.95 0 0); + --color-light: oklch(0.25 0 0); + --color-primary: oklch(0.70 0.15 250); + } +} + +body { + background: var(--color-bg); + color: var(--color-text); +} +``` + +## List Template Styling + +Style the different list templates: + +### Grid Layout + +```css +.list-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: var(--spacing-unit); +} + +.list-grid article { + border: 1px solid var(--color-light); + border-radius: var(--border-radius); + overflow: hidden; +} + +.list-grid img { + aspect-ratio: 16 / 9; + object-fit: cover; + width: 100%; +} +``` + +### Card Grid + +```css +.card-grid .card { + background: var(--color-light); + padding: var(--spacing-unit); + border-radius: var(--border-radius); + transition: transform 0.2s ease; +} + +.card-grid .card:hover { + transform: translateY(-4px); + box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1); +} +``` + +### FAQ Layout + +```css +.faq details { + border: 1px solid var(--color-light); + border-radius: var(--border-radius); + padding: var(--spacing-unit); + margin-block-end: var(--spacing-small); +} + +.faq summary { + cursor: pointer; + font-weight: 700; + user-select: none; +} + +.faq summary:hover { + color: var(--color-primary); +} +``` + +## Modern CSS Features + +FolderWeb encourages use of modern CSS: + +### CSS Nesting + +```css +.article { + padding: var(--spacing-unit); + + & h2 { + color: var(--color-primary); + margin-block-start: var(--spacing-large); + } + + & a { + color: var(--color-secondary); + text-decoration: none; + + &:hover { + text-decoration: underline; + } + } +} +``` + +### Logical Properties + +Use logical properties for internationalization: + +```css +/* Instead of: margin-left, margin-right */ +article { + margin-inline: auto; + padding-inline: var(--spacing-unit); + padding-block: var(--spacing-large); +} + +/* Instead of: text-align: left */ +.content { + text-align: start; +} +``` + +### Modern Color Functions + +```css +:root { + /* OKLCH: lightness, chroma, hue */ + --primary: oklch(0.65 0.15 250); + + /* Adjust lightness for hover */ + --primary-hover: oklch(0.55 0.15 250); + + /* Or use color-mix */ + --primary-light: color-mix(in oklch, var(--primary), white 20%); +} +``` + +## Performance Tips + +### Minimize Custom Styles + +Override only what's necessary. The default stylesheet is already optimized. + +### Use CSS Variables + +Variables reduce repetition and improve maintainability: + +```css +/* Good */ +:root { + --card-padding: var(--spacing-unit); +} + +.card { padding: var(--card-padding); } +.box { padding: var(--card-padding); } + +/* Less maintainable */ +.card { padding: 1.5rem; } +.box { padding: 1.5rem; } +``` + +### Avoid `!important` + +FolderWeb uses low-specificity selectors, so you shouldn't need `!important`. + +## Debugging Styles + +### Check Which Stylesheet is Loaded + +View source and look for: + +```html + +``` + +If you see `/app/styles/`, your custom stylesheet is being used. +If you see `/app/default-styles/`, the default is being used. + +### Browser DevTools + +1. Right-click element β†’ Inspect +2. Check "Computed" tab to see which properties are applied +3. Check "Sources" tab to verify your CSS file is loaded +4. Use "Network" tab to ensure CSS isn't cached with old version + +### Hard Refresh + +Always do a hard refresh after CSS changes: +- **Chrome/Firefox**: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac) +- **Safari**: Cmd+Option+R + +## Complete Override + +If you want complete control, you can replace the entire stylesheet. Copy the default: + +```bash +cp app/default/styles/base.css custom/styles/base.css +``` + +Then edit freely. Remember: you're responsible for all styles when you do this. + +## Related + +- [Custom Templates](custom-templates.md) +- [CSS Reference](../reference/css-variables.md) +- [File Structure Reference](../reference/file-structure.md) diff --git a/docs/how-to/custom-templates.md b/docs/how-to/custom-templates.md new file mode 100644 index 0000000..9aaec69 --- /dev/null +++ b/docs/how-to/custom-templates.md @@ -0,0 +1,288 @@ +# How to Create Custom Templates + +This guide shows you how to override default templates with your own custom designs. + +## Overview + +FolderWeb uses a template fallback system: +1. Check `/custom/templates/` for custom version +2. Fall back to `/app/default/templates/` if not found + +**Important**: Never modify files in `/app/default/` β€” always create custom versions in `/custom/`. + +## Available Templates + +- **base.php** - HTML wrapper (header, navigation, footer) +- **page.php** - Single page/article wrapper +- **list.php** - Simple list view (default) +- **list-grid.php** - Grid layout with images +- **list-card-grid.php** - Card grid (supports PDFs, external links) +- **list-faq.php** - Expandable FAQ/Q&A format + +## Customizing the Base Template + +The base template controls your entire site layout. + +### Step 1: Copy the Default + +```bash +mkdir -p custom/templates +cp app/default/templates/base.php custom/templates/base.php +``` + +### Step 2: Edit Your Copy + +The base template has access to these variables: + +```php +$content // The rendered page content (HTML) +$currentLang // Current language code (e.g., "en", "no") +$navigation // Array of navigation items +$homeLabel // Site title +$translations // Translation strings +$pageTitle // Current page title +$dirName // Parent directory name (for CSS classes) +$pageName // Current page name (for CSS classes) +``` + +### Example: Add a Custom Header + +```php + +``` + +## Customizing the Page Template + +The page template wraps individual articles and pages. + +### Step 1: Copy the Default + +```bash +cp app/default/templates/page.php custom/templates/page.php +``` + +### Step 2: Customize + +Available variables: + +```php +$content // Main content HTML +$pageMetadata // Array of metadata (tags, categories, etc.) +$translations // Translation strings +``` + +### Example: Add Author Information + +```php +
+ +
+

Written by

+
+ + + + + +
+ : + + + +
+ +
+``` + +## Creating a Custom List Template + +List templates control how directories with subdirectories are displayed. + +### Step 1: Create Your Template + +```bash +touch custom/templates/list-custom.php +``` + +### Step 2: Use List Template Variables + +All list templates receive: + +```php +$items // Array of subdirectories +$metadata // Directory metadata +$pageContent // Optional intro content +$translations // Translation strings +``` + +Each item in `$items` has: + +```php +[ + 'title' => 'Post Title', + 'date' => '2. november 2025', + 'url' => '/blog/2025-11-02-post/', + 'cover' => '/blog/2025-11-02-post/cover.jpg', + 'summary' => 'Brief description', + 'pdf' => '/blog/2025-11-02-post/document.pdf', + 'redirect' => 'https://external-site.com' +] +``` + +### Example: Timeline Template + +```php + +
+ +
+ + +
+ +
+ +

+ +

+ +

+ +
+ +
+``` + +### Step 3: Apply Your Template + +Create a `metadata.ini` in the directory: + +```ini +page_template = "list-custom" +``` + +## Template Best Practices + +### Always Escape Output + +Prevent XSS attacks by escaping user-generated content: + +```php + +``` + +### Use Short Echo Tags + +FolderWeb uses modern PHP, so short tags are always available: + +```php + // Good + // Also works, but verbose +``` + +### Check Before Using + +Always check if variables exist: + +```php + + + +``` + +### Leverage CSS Classes + +The base template adds dynamic classes to ``: + +```php + +``` + +Use these for page-specific styling without JavaScript. + +## Advanced: Accessing the Context Object + +Templates can access the full context object `$ctx`: + +```php +contentDir // Path to content directory +$ctx->currentLang // Current language +$ctx->defaultLang // Default language +$ctx->availableLangs // Array of available languages +$ctx->langPrefix // URL prefix (e.g., "/en" or "") +$ctx->requestPath // Current request path +$ctx->hasTrailingSlash // Boolean +$ctx->navigation // Navigation array (computed property) +$ctx->homeLabel // Site title (computed property) +$ctx->translations // Translation array (computed property) +?> +``` + +## Example: Breadcrumb Navigation + +Add breadcrumbs to your page template: + +```php + +``` + +## Testing Your Templates + +1. Clear your browser cache +2. Reload the page +3. Check browser console for errors +4. Validate HTML with W3C validator + +## Reverting Changes + +To revert to default templates, simply delete your custom version: + +```bash +rm custom/templates/base.php +``` + +FolderWeb will automatically fall back to the default. + +## Related + +- [Customizing Styles](custom-styles.md) +- [Template Reference](../reference/templates.md) +- [Metadata Reference](../reference/metadata.md) diff --git a/docs/how-to/multi-language.md b/docs/how-to/multi-language.md new file mode 100644 index 0000000..4464c8b --- /dev/null +++ b/docs/how-to/multi-language.md @@ -0,0 +1,425 @@ +# How to Create a Multi-Language Site + +This guide shows you how to set up and manage a multi-language website with FolderWeb. + +## Overview + +FolderWeb supports multiple languages through: +- Language prefixes in URLs +- Language-specific content files +- Translated slugs and metadata +- Translation files for UI strings + +## Configuration + +### Step 1: Configure Available Languages + +Create or edit `custom/config.ini`: + +```ini +[languages] +default = "en" +available = "en,no,fr" +``` + +- **default**: The primary language (no URL prefix) +- **available**: Comma-separated list of all supported languages + +### Step 2: Create Translation Files + +Create translation files for each language in `custom/languages/`: + +```bash +mkdir -p custom/languages +``` + +**English** (`custom/languages/en.ini`): +```ini +home = "Home" +read_more = "Read more" +categories = "Categories" +tags = "Tags" +footer_text = "Made with FolderWeb" +footer_handcoded = "Generated in" +footer_page_time = "ms" +``` + +**Norwegian** (`custom/languages/no.ini`): +```ini +home = "Hjem" +read_more = "Les mer" +categories = "Kategorier" +tags = "Stikkord" +footer_text = "Laget med FolderWeb" +footer_handcoded = "Generert pΓ₯" +footer_page_time = "ms" +``` + +**French** (`custom/languages/fr.ini`): +```ini +home = "Accueil" +read_more = "Lire la suite" +categories = "CatΓ©gories" +tags = "Γ‰tiquettes" +footer_text = "Créé avec FolderWeb" +footer_handcoded = "GΓ©nΓ©rΓ© en" +footer_page_time = "ms" +``` + +## URL Structure + +With the configuration above: + +- **English** (default): `yoursite.com/about/` +- **Norwegian**: `yoursite.com/no/about/` +- **French**: `yoursite.com/fr/about/` + +The default language never has a URL prefix. + +## Creating Language-Specific Content + +### Method 1: Separate Files Per Language + +Use language suffixes in filenames: `filename.{lang}.ext` + +**Example structure**: +``` +content/about/ +β”œβ”€β”€ index.md # Default language (English) +β”œβ”€β”€ index.no.md # Norwegian version +└── index.fr.md # French version +``` + +**Rules**: +- Language-specific files (`.lang.ext`) show only for that language +- Default files (no language suffix) show only if no language variant exists +- Files are automatically filtered based on current language + +### Example Content + +**content/about/index.md** (English): +```markdown +# About Us + +We are a company dedicated to simplicity. +``` + +**content/about/index.no.md** (Norwegian): +```markdown +# Om Oss + +Vi er et selskap dedikert til enkelhet. +``` + +**content/about/index.fr.md** (French): +```markdown +# Γ€ Propos + +Nous sommes une entreprise dΓ©diΓ©e Γ  la simplicitΓ©. +``` + +Now when users visit: +- `/about/` β†’ Shows English (index.md) +- `/no/about/` β†’ Shows Norwegian (index.no.md) +- `/fr/about/` β†’ Shows French (index.fr.md) + +### Method 2: Language-Specific Folders + +For blog posts and articles, you can create separate folders: + +``` +content/blog/ +β”œβ”€β”€ 2025-11-01-english-post/ +β”‚ └── index.md +β”œβ”€β”€ 2025-11-01-norsk-innlegg/ +β”‚ └── index.no.md +└── 2025-11-01-article-francais/ + └── index.fr.md +``` + +## Translated Slugs and Titles + +Use `metadata.ini` to provide translated slugs and metadata: + +**content/about/metadata.ini**: +```ini +; Default (English) +title = "About Us" +slug = "about" + +[no] +title = "Om Oss" +slug = "om-oss" + +[fr] +title = "Γ€ Propos" +slug = "a-propos" +``` + +Now URLs become: +- English: `/about/` +- Norwegian: `/no/om-oss/` +- French: `/fr/a-propos/` + +The actual folder is still named `about/`, but FolderWeb maps the translated slug to the real folder. + +## Blog Posts with Translations + +**Structure**: +``` +content/blog/ +└── 2025-11-02-my-post/ + β”œβ”€β”€ index.md + β”œβ”€β”€ index.no.md + β”œβ”€β”€ index.fr.md + β”œβ”€β”€ cover.jpg + └── metadata.ini +``` + +**metadata.ini**: +```ini +; Default language +title = "My First Post" +summary = "An introduction to multilingual blogging." +date = "2025-11-02" + +[no] +title = "Mitt FΓΈrste Innlegg" +summary = "En introduksjon til flersprΓ₯klig blogging." + +[fr] +title = "Mon Premier Article" +summary = "Une introduction au blogging multilingue." +``` + +**Important**: Date is global, cover image is shared across languages. + +## Navigation and Menus + +Navigation is automatically built with translations. In `metadata.ini` for each top-level directory: + +**content/blog/metadata.ini**: +```ini +menu = true +menu_order = 1 + +title = "Blog" +[no] +title = "Blogg" +[fr] +title = "Blog" +``` + +**content/about/metadata.ini**: +```ini +menu = true +menu_order = 2 + +title = "About" +[no] +title = "Om" +[fr] +title = "Γ€ Propos" +``` + +Navigation automatically includes language prefix in URLs. + +## Using Translations in Templates + +### In Default Templates + +Translations are automatically available as `$translations` array: + +```php + + + + +

+``` + +### In Custom Templates + +Access translations the same way: + +```php + +``` + +### Access Current Language + +```php +currentLang === 'no'): ?> +

Dette er norsk innhold.

+ +

This is English content.

+ +``` + +## Language Switcher + +Create a language switcher in your custom base template: + +```php + +``` + +Style it: + +```css +.language-switcher { + display: flex; + gap: 0.5rem; +} + +.language-switcher a { + padding: 0.25rem 0.75rem; + border-radius: var(--border-radius); + text-decoration: none; +} + +.language-switcher a.active { + background: var(--color-primary); + color: white; +} +``` + +## List Views with Multiple Languages + +When displaying blog listings, FolderWeb automatically filters items by language: + +``` +content/blog/ +β”œβ”€β”€ 2025-11-01-english-article/ +β”‚ └── index.md # Shows in English +β”œβ”€β”€ 2025-11-02-norsk-artikkel/ +β”‚ └── index.no.md # Shows only in Norwegian +└── 2025-11-03-universal/ + β”œβ”€β”€ index.md # Shows in English + β”œβ”€β”€ index.no.md # Shows in Norwegian + └── index.fr.md # Shows in French +``` + +When viewing `/blog/`: +- Shows "english-article" and "universal" + +When viewing `/no/blog/`: +- Shows "norsk-artikkel" and "universal" + +When viewing `/fr/blog/`: +- Shows only "universal" + +## Handling Missing Translations + +### Default Fallback + +If a translation is missing, FolderWeb uses the default language automatically. + +### Show Different Content + +You can use PHP in your content files: + +```php +currentLang === 'en'): ?> +# Welcome +This page is only in English. + +# Under Construction +This page is not yet translated. + +``` + +## SEO Considerations + +### Add hreflang Tags + +In your custom base template: + +```php + + + + availableLangs as $lang): ?> + defaultLang + ? 'https://yoursite.com/' . trim($ctx->requestPath, '/') + : 'https://yoursite.com/' . $lang . '/' . trim($ctx->requestPath, '/'); + ?> + + + + + +``` + +### Language-Specific Metadata + +Add language attributes: + +```php + +``` + +## Testing Your Multi-Language Site + +1. **Visit default language**: `http://localhost:8000/about/` +2. **Visit Norwegian**: `http://localhost:8000/no/about/` +3. **Visit French**: `http://localhost:8000/fr/about/` +4. **Check navigation**: Ensure links include language prefix +5. **Test translation strings**: Verify UI text changes per language +6. **Check blog listings**: Confirm language-specific posts appear correctly + +## Common Patterns + +### Blog in Multiple Languages + +Structure: +``` +content/blog/ +β”œβ”€β”€ metadata.ini # List template config +└── [date]-[slug]/ + β”œβ”€β”€ index.{lang}.md # One file per language + β”œβ”€β”€ cover.jpg # Shared assets + └── metadata.ini # Translated metadata +``` + +### Documentation in Multiple Languages + +Structure: +``` +content/docs/ +β”œβ”€β”€ metadata.ini # Template config +β”œβ”€β”€ 00-intro.md # Default language +β”œβ”€β”€ 00-intro.no.md # Norwegian +β”œβ”€β”€ 01-setup.md +β”œβ”€β”€ 01-setup.no.md +└── ... +``` + +### Mixed Content Strategy + +Not everything needs translation. You can have: +- English-only blog posts (no language suffix) +- Multi-language main pages (with language suffixes) +- Shared images and assets + +## Related + +- [Metadata Reference](../reference/metadata.md) +- [Configuration Reference](../reference/configuration.md) +- [Template Variables Reference](../reference/templates.md) diff --git a/docs/how-to/working-with-metadata.md b/docs/how-to/working-with-metadata.md new file mode 100644 index 0000000..ad224b5 --- /dev/null +++ b/docs/how-to/working-with-metadata.md @@ -0,0 +1,481 @@ +# How to Work with Metadata + +This guide shows you how to use `metadata.ini` files to control page behavior, appearance, and content. + +## What is Metadata? + +Metadata provides structured information about your content directories without cluttering your content files. It's stored in `metadata.ini` files using the INI format. + +## Basic Metadata File + +Create `metadata.ini` in any content directory: + +```ini +title = "My Page Title" +date = "2025-11-02" +summary = "A brief description of this page." +``` + +## Common Metadata Fields + +### Title + +Controls the displayed title (overrides automatic title extraction): + +```ini +title = "Custom Page Title" +``` + +If not provided, FolderWeb extracts the title from: +1. First H1 heading in content (`# Title` in Markdown) +2. Folder name (as fallback) + +### Date + +Set an explicit date (overrides folder name date extraction): + +```ini +date = "2025-11-02" +``` + +Format: `YYYY-MM-DD` + +FolderWeb automatically formats this in Norwegian style: "2. november 2025" + +### Summary + +Add a summary for list views: + +```ini +summary = "This appears in blog listings and card grids." +``` + +Summaries are displayed in: +- List views +- Grid layouts +- Card grids + +## Navigation Control + +### Adding to Menu + +```ini +menu = true +menu_order = 1 +``` + +- **menu**: Set to `true` to include in site navigation +- **menu_order**: Controls order (lower numbers appear first) + +**Example** - Setting up main navigation: + +**content/blog/metadata.ini**: +```ini +menu = true +menu_order = 1 +title = "Blog" +``` + +**content/about/metadata.ini**: +```ini +menu = true +menu_order = 2 +title = "About" +``` + +**content/contact/metadata.ini**: +```ini +menu = true +menu_order = 3 +title = "Contact" +``` + +Result: Navigation shows "Blog", "About", "Contact" in that order. + +## Template Control + +### Choosing List Template + +For directories with subdirectories, control which list template is used: + +```ini +page_template = "list-grid" +``` + +Available templates: +- `list` - Simple list (default) +- `list-grid` - Grid with cover images +- `list-card-grid` - Card-style grid (supports PDFs, external links) +- `list-faq` - Expandable FAQ format + +**Example** - Blog with grid layout: + +**content/blog/metadata.ini**: +```ini +title = "Blog" +page_template = "list-grid" +``` + +## External Redirects + +Make a directory item link externally (used with `list-card-grid`): + +```ini +redirect = "https://example.com" +``` + +**Example** - Portfolio with external links: + +**content/portfolio/project-live-site/metadata.ini**: +```ini +title = "Visit Live Site" +summary = "Check out the deployed project." +redirect = "https://myproject.com" +``` + +When using the `list-card-grid` template, this creates a card that links to the external URL instead of an internal page. + +## Multi-Language Metadata + +Use sections for language-specific overrides: + +```ini +; Default language values +title = "About Us" +summary = "Learn more about our company." + +[no] +title = "Om Oss" +summary = "LΓ¦r mer om vΓ₯rt selskap." +slug = "om-oss" + +[fr] +title = "Γ€ Propos" +summary = "DΓ©couvrez notre entreprise." +slug = "a-propos" +``` + +Language sections override base values for that language. + +### Translated Slugs + +The `slug` field in language sections changes the URL: + +```ini +[no] +slug = "om-oss" +``` + +Now the Norwegian version is accessible at `/no/om-oss/` instead of `/no/about/`. + +## Custom Metadata Fields + +You can add any custom fields you need: + +```ini +title = "Article Title" +author = "Jane Doe" +reading_time = "5 min" +difficulty = "intermediate" +featured = true +``` + +Access these in custom templates: + +```php + +

By

+ + + + + +``` + +## Arrays in Metadata + +INI format supports arrays using repeated keys: + +```ini +tags[] = "PHP" +tags[] = "Web Development" +tags[] = "Tutorial" + +categories[] = "Programming" +categories[] = "Backend" +``` + +Access in templates: + +```php + +
+ + + +
+ +``` + +## Boolean Values + +Use `true`, `false`, `1`, `0`, `yes`, `no`, `on`, `off`: + +```ini +menu = true +featured = yes +draft = false +``` + +## Comments + +Add comments with `;` or `#`: + +```ini +; This is a comment +title = "My Page" + +# This is also a comment +date = "2025-11-02" +``` + +## Metadata Inheritance + +Metadata does **not** inherit from parent directories. Each directory needs its own `metadata.ini`. + +## Metadata for List Items + +When a directory is displayed in a list view, FolderWeb loads its metadata: + +**content/blog/2025-11-01-first-post/metadata.ini**: +```ini +title = "My First Blog Post" +date = "2025-11-01" +summary = "An introduction to blogging with FolderWeb." +``` + +This metadata appears in the blog listing at `/blog/`. + +## Complete Example: Blog Setup + +### Blog Directory Metadata + +**content/blog/metadata.ini**: +```ini +title = "Blog" +menu = true +menu_order = 1 +page_template = "list-grid" + +[no] +title = "Blogg" +slug = "blogg" +``` + +### Individual Post Metadata + +**content/blog/2025-11-02-web-performance/metadata.ini**: +```ini +title = "Optimizing Web Performance" +date = "2025-11-02" +summary = "Learn techniques to make your website faster." +author = "Jane Developer" +reading_time = "8 min" + +tags[] = "Performance" +tags[] = "Web Development" +tags[] = "Optimization" + +categories[] = "Technical" +categories[] = "Tutorial" + +[no] +title = "Optimalisering av Nettsideytelse" +summary = "LΓ¦r teknikker for Γ₯ gjΓΈre nettsiden din raskere." +``` + +## Accessing Metadata in Templates + +### In Page Templates + +```php + + +
+
+

+

By

+
+ + + + +
+ + + +
+ +
+``` + +### In List Templates + +Each item in `$items` includes its metadata: + +```php + +
+

+ + + +

+ + + + + + +

+ + + + + + + + Download PDF + + + + + Visit External Link + + +
+ +``` + +## Debugging Metadata + +### Check if Metadata is Loaded + +In your template, dump the metadata: + +```php +
+``` + +Or for list items: + +```php +
+``` + +### Verify INI Syntax + +Use PHP to test your INI file: + +```bash +php -r "print_r(parse_ini_file('content/blog/metadata.ini', true));" +``` + +This shows parsed values and helps identify syntax errors. + +## Best Practices + +### Use Consistent Field Names + +Stick to standard fields for common data: +- `title` for titles +- `date` for dates +- `summary` for summaries +- `author` for authors + +### Escape Output + +Always escape metadata in templates: + +```php + +``` + +### Provide Defaults + +Use null coalescing for missing fields: + +```php +$author = $metadata['author'] ?? 'Anonymous'; +$date = $metadata['date'] ?? 'Unknown date'; +``` + +### Keep It Simple + +Only add metadata fields you actually use. Don't over-engineer. + +### Use Comments + +Document non-obvious metadata: + +```ini +; Featured articles appear at the top of the homepage +featured = true + +; Legacy field kept for backwards compatibility +old_url = "/blog/old-slug/" +``` + +## Common Patterns + +### Blog Post + +```ini +title = "Post Title" +date = "2025-11-02" +summary = "Brief description" +author = "Author Name" +tags[] = "tag1" +tags[] = "tag2" +``` + +### Documentation Page + +```ini +title = "API Reference" +menu = true +menu_order = 3 +page_template = "list" +``` + +### Portfolio Item + +```ini +title = "Project Name" +date = "2025-11-02" +summary = "Project description" +redirect = "https://live-demo.com" +``` + +### FAQ Section + +```ini +title = "Frequently Asked Questions" +menu = true +menu_order = 4 +page_template = "list-faq" +``` + +## Related + +- [Multi-Language Guide](multi-language.md) +- [Custom Templates](custom-templates.md) +- [Metadata Reference](../reference/metadata.md) +- [Template Variables Reference](../reference/templates.md) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..2a0e5b2 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,309 @@ +# FolderWeb Documentation + +Complete documentation for FolderWeb, a minimalist file-based PHP framework for content websites. + +## What is FolderWeb? + +FolderWeb is a file-based content publishing framework. Drop Markdown files in folders, and they become pages. No database, no build process, no JavaScript required. Just PHP, HTML, and CSS. + +**Core principle**: Your file system is your content management system. + +## Documentation Structure + +This documentation follows the [Diataxis framework](https://diataxis.fr/), organizing content into four types: + +### πŸŽ“ Tutorial + +**Learning-oriented**: Get started with FolderWeb + +- [Getting Started](tutorial/00-getting-started.md) - Build your first site in 10 minutes + +**Start here** if you're new to FolderWeb. + +### πŸ“‹ How-To Guides + +**Task-oriented**: Solve specific problems + +- [Custom Templates](how-to/custom-templates.md) - Override default templates +- [Custom Styles](how-to/custom-styles.md) - Customize appearance with CSS +- [Multi-Language Sites](how-to/multi-language.md) - Set up multiple languages +- [Working with Metadata](how-to/working-with-metadata.md) - Use metadata.ini files + +**Use these** when you need to accomplish a specific task. + +### πŸ“– Reference + +**Information-oriented**: Look up technical details + +- [File Structure](reference/file-structure.md) - Complete directory layout +- [Metadata](reference/metadata.md) - All metadata fields +- [Templates](reference/templates.md) - Template variables and usage +- [Configuration](reference/configuration.md) - Configuration options +- [CSS Variables](reference/css-variables.md) - Styling customization + +**Consult these** when you need precise technical information. + +### πŸ’‘ Explanation + +**Understanding-oriented**: Understand concepts and design + +- [Philosophy](explanation/philosophy.md) - Design principles and thinking +- [Architecture](explanation/architecture.md) - How FolderWeb works + +**Read these** to understand why FolderWeb works the way it does. + +## Quick Links + +### Common Tasks + +- **Create a page**: Drop `index.md` in a folder +- **Create a blog**: Make a folder with subdirectories +- **Add navigation**: Set `menu = true` in `metadata.ini` +- **Customize look**: Override `/custom/styles/base.css` +- **Use custom template**: Set `page_template = "template-name"` in metadata +- **Multi-language**: Configure languages and add `.{lang}.md` files + +### Key Concepts + +- **File-based routing**: `content/blog/post/` β†’ `yoursite.com/blog/post/` +- **Template fallback**: Custom templates override defaults +- **Language prefixes**: `/en/page/` for English, `/no/page/` for Norwegian +- **Metadata inheritance**: None - each directory has its own `metadata.ini` +- **Content types**: Single-file, multi-file, or list view + +## Quick Start + +```bash +# 1. Create project +mkdir my-site && cd my-site + +# 2. Copy framework files +cp -r /path/to/folderweb/app ./app + +# 3. Create content +mkdir content +echo "# Hello World" > content/index.md + +# 4. Start server +php -S localhost:8000 -t . app/router.php + +# 5. Visit http://localhost:8000 +``` + +## System Requirements + +- **PHP**: 8.4 or higher +- **Web server**: Apache, Nginx, or PHP built-in server +- **Extensions**: Standard PHP (no special extensions needed) + +## File Structure Overview + +``` +project/ +β”œβ”€β”€ app/ # Framework (never modify) +β”‚ β”œβ”€β”€ router.php # Entry point +β”‚ β”œβ”€β”€ content.php # Content discovery +β”‚ β”œβ”€β”€ rendering.php # Template rendering +β”‚ └── default/ # Default templates, styles, languages +β”œβ”€β”€ content/ # Your website content +β”‚ β”œβ”€β”€ index.md # Home page +β”‚ β”œβ”€β”€ about/ # About page +β”‚ └── blog/ # Blog with posts +└── custom/ # Your customizations + β”œβ”€β”€ templates/ # Custom templates + β”œβ”€β”€ styles/ # Custom CSS + β”œβ”€β”€ languages/ # Custom translations + └── config.ini # Configuration overrides +``` + +## Core Features + +### File-Based Routing + +Your folder structure defines your URLs: + +``` +content/blog/2025-11-02-post/ β†’ /blog/2025-11-02-post/ +``` + +No route configuration needed. + +### Multiple Content Types + +- **Single-file page**: One file in a directory +- **Multi-file page**: Multiple files combined into one page +- **List view**: Directory with subdirectories becomes a listing + +### Template System + +Six templates included: +- `base.php` - HTML wrapper +- `page.php` - Page wrapper +- `list.php` - Simple list +- `list-grid.php` - Grid with images +- `list-card-grid.php` - Card grid +- `list-faq.php` - Expandable FAQ + +Override any template in `/custom/templates/`. + +### Multi-Language Support + +Configure languages: +```ini +[languages] +default = "en" +available = "en,no,fr" +``` + +Create language-specific files: +``` +index.md # English (default) +index.no.md # Norwegian +index.fr.md # French +``` + +URLs automatically prefixed: `/`, `/no/`, `/fr/` + +### Metadata System + +Control behavior with `metadata.ini`: + +```ini +title = "My Page" +date = "2025-11-02" +summary = "Page description" +menu = true +menu_order = 1 +page_template = "list-grid" +``` + +### Modern CSS + +Default styles use: +- CSS custom properties (variables) +- CSS nesting +- OKLCH colors +- Grid layouts +- Fluid typography with `clamp()` +- Logical properties + +Override in `/custom/styles/base.css`. + +## Philosophy Highlights + +- **Just enough, nothing more**: Minimal, maintainable code +- **Longevity over novelty**: Works today, works in 2035 +- **Files are content**: Portable, version-controllable +- **No JavaScript required**: Pure HTML and CSS +- **No build process**: Immediate feedback + +Read the full [Philosophy](explanation/philosophy.md) for more. + +## Example Use Cases + +### Personal Blog + +``` +content/ +β”œβ”€β”€ index.md # About me +β”œβ”€β”€ blog/ # Blog posts +β”‚ β”œβ”€β”€ metadata.ini # page_template = "list-grid" +β”‚ β”œβ”€β”€ 2025-11-01-post/ +β”‚ └── 2025-11-02-post/ +└── contact/ # Contact page + └── index.md +``` + +### Documentation Site + +``` +content/ +β”œβ”€β”€ index.md # Introduction +β”œβ”€β”€ getting-started/ # Multi-file tutorial +β”‚ β”œβ”€β”€ 00-install.md +β”‚ β”œβ”€β”€ 01-setup.md +β”‚ └── 02-first-steps.md +└── reference/ # API reference + β”œβ”€β”€ metadata.ini # page_template = "list" + β”œβ”€β”€ functions/ + └── classes/ +``` + +### Portfolio + +``` +content/ +β”œβ”€β”€ index.md # Homepage +β”œβ”€β”€ projects/ # Project grid +β”‚ β”œβ”€β”€ metadata.ini # page_template = "list-card-grid" +β”‚ β”œβ”€β”€ project-1/ +β”‚ β”‚ β”œβ”€β”€ index.md +β”‚ β”‚ β”œβ”€β”€ cover.jpg +β”‚ β”‚ └── metadata.ini # redirect = "https://project.com" +β”‚ └── project-2/ +└── about/ + └── index.md +``` + +## When to Use FolderWeb + +### βœ… Ideal For + +- Blogs and content sites +- Documentation +- Portfolios +- Marketing sites +- Personal websites +- Projects requiring longevity + +### ❌ Not Ideal For + +- User-generated content (no database/auth) +- E-commerce (use dedicated platform) +- Social networks (need real-time features) +- JavaScript-heavy SPAs +- Sites with thousands of pages (consider static generators) + +## Getting Help + +### Documentation + +- Follow the [Tutorial](tutorial/00-getting-started.md) step-by-step +- Check [How-To Guides](how-to/) for specific tasks +- Consult [Reference](reference/) for technical details +- Read [Explanation](explanation/) for concepts + +### Common Issues + +**Styles not loading**: Hard refresh (Ctrl+Shift+R or Cmd+Shift+R) + +**404 errors**: Check folder exists and has content files + +**Language not working**: Ensure language is in `available` config + +**Metadata not appearing**: Verify INI syntax with `parse_ini_file()` + +**Templates not found**: Check file exists in `/custom/templates/` + +## Contributing + +FolderWeb prioritizes stability and simplicity. Contributions should: +- Maintain simplicity +- Avoid dependencies +- Solve real problems +- Be maintainable long-term + +## License + +Check the project repository for license information. + +## Next Steps + +1. **New user?** Start with the [Getting Started Tutorial](tutorial/00-getting-started.md) +2. **Need to do something specific?** Browse [How-To Guides](how-to/) +3. **Want technical details?** Explore [Reference Documentation](reference/) +4. **Curious about design?** Read [Philosophy](explanation/philosophy.md) and [Architecture](explanation/architecture.md) + +--- + +**FolderWeb**: Simple, file-based content publishing for the long term. diff --git a/docs/reference/configuration.md b/docs/reference/configuration.md new file mode 100644 index 0000000..038eeaa --- /dev/null +++ b/docs/reference/configuration.md @@ -0,0 +1,400 @@ +# Configuration Reference + +Complete reference for FolderWeb configuration options. + +## Configuration File + +**Location**: `/custom/config.ini` (or `/app/config.ini` for defaults) + +**Format**: INI format with sections + +**Override Behavior**: Custom config values override defaults + +## Configuration Sections + +### [languages] + +Controls language support and defaults. + +```ini +[languages] +default = "en" +available = "en,no,fr" +``` + +#### default + +**Type**: String +**Required**: Yes +**Default**: `"en"` + +The primary language of your site. This language: +- Has no URL prefix +- Is used as fallback for missing translations +- Should match a translation file in `/custom/languages/` or `/app/default/languages/` + +**Examples**: +```ini +default = "en" ; English +default = "no" ; Norwegian +default = "fr" ; French +default = "de" ; German +``` + +#### available + +**Type**: Comma-separated string +**Required**: Yes +**Default**: `"en,no"` + +List of all languages supported by your site. Must include the default language. + +**Examples**: +```ini +; English only +available = "en" + +; English and Norwegian +available = "en,no" + +; Multiple languages +available = "en,no,fr,de,es" +``` + +**URL Structure**: +- Default language: `yoursite.com/page/` +- Other languages: `yoursite.com/fr/page/`, `yoursite.com/de/page/` + +## File Structure + +### Default Configuration + +**Location**: `/app/config.ini` + +```ini +[languages] +default = "no" +available = "no,en" +``` + +**Note**: Never modify `/app/config.ini` directly. + +### Custom Configuration + +**Location**: `/custom/config.ini` + +Create this file to override defaults: + +```ini +[languages] +default = "en" +available = "en,fr,de" +``` + +Only include settings you want to override. + +## Configuration Loading + +Configuration is loaded in this order: + +1. Load `/app/config.ini` (defaults) +2. Load `/custom/config.ini` if exists +3. Merge, with custom values overriding defaults + +Example: + +**app/config.ini**: +```ini +[languages] +default = "no" +available = "no,en" +``` + +**custom/config.ini**: +```ini +[languages] +default = "en" +``` + +**Result**: +```ini +[languages] +default = "en" ; From custom +available = "no,en" ; From default (not overridden) +``` + +## Complete Configuration Examples + +### Single Language Site + +```ini +[languages] +default = "en" +available = "en" +``` + +URLs: All at root level (`/page/`, `/blog/`, etc.) + +### Bilingual Site (English/Norwegian) + +```ini +[languages] +default = "en" +available = "en,no" +``` + +URLs: +- English: `/page/`, `/blog/` +- Norwegian: `/no/page/`, `/no/blog/` + +### Multilingual Site + +```ini +[languages] +default = "en" +available = "en,no,fr,de,es" +``` + +URLs: +- English (default): `/page/` +- Norwegian: `/no/page/` +- French: `/fr/page/` +- German: `/de/page/` +- Spanish: `/es/page/` + +## Language Codes + +Use ISO 639-1 two-letter codes: + +| Code | Language | +|------|----------| +| `en` | English | +| `no` | Norwegian | +| `fr` | French | +| `de` | German | +| `es` | Spanish | +| `it` | Italian | +| `pt` | Portuguese | +| `nl` | Dutch | +| `sv` | Swedish | +| `da` | Danish | +| `fi` | Finnish | +| `pl` | Polish | +| `ru` | Russian | +| `ja` | Japanese | +| `zh` | Chinese | +| `ko` | Korean | +| `ar` | Arabic | + +Full list: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + +## Related Configuration + +### Translation Files + +For each language in `available`, create a translation file: + +**Pattern**: `/custom/languages/{lang}.ini` + +**Example** (with `available = "en,no,fr"`): +``` +custom/languages/ +β”œβ”€β”€ en.ini +β”œβ”€β”€ no.ini +└── fr.ini +``` + +See [Translation Reference](translations.md) for details. + +### Content Files + +Language-specific content uses the same codes: + +**Pattern**: `filename.{lang}.ext` + +**Examples**: +- `index.md` - Default language +- `index.no.md` - Norwegian +- `index.fr.md` - French + +See [Multi-Language Guide](../how-to/multi-language.md) for details. + +## Validation + +### Check Configuration + +Verify configuration is loaded correctly: + +**test-config.php**: +```php +defaultLang . "\n"; +echo "Available languages: " . implode(', ', $ctx->availableLangs) . "\n"; +echo "Current language: " . $ctx->currentLang . "\n"; +echo "Language prefix: " . $ctx->langPrefix . "\n"; +``` + +Run: +```bash +php test-config.php +``` + +### Common Errors + +**Missing default in available**: +```ini +; Wrong - default must be in available +[languages] +default = "en" +available = "no,fr" + +; Correct +[languages] +default = "en" +available = "en,no,fr" +``` + +**Invalid language codes**: +```ini +; Avoid - use ISO codes +available = "english,norwegian" + +; Correct - ISO 639-1 codes +available = "en,no" +``` + +**Typos in section names**: +```ini +; Wrong +[language] +default = "en" + +; Correct +[languages] +default = "en" +``` + +## Future Configuration Options + +FolderWeb is minimal by design. Currently, only language settings are configurable. + +Possible future additions: +- Date format preferences +- Timezone settings +- Content directory override +- Cache settings + +For now, these are handled through code or conventions. + +## Environment-Specific Configuration + +To use different configs per environment: + +**Option 1: Conditional Loading** + +**custom/config.ini**: +```ini +[languages] +default = "en" +available = "en,no" +``` + +**custom/config.dev.ini**: +```ini +[languages] +default = "en" +available = "en" +``` + +Modify `app/config.php` to load based on environment. + +**Option 2: Separate Deployments** + +Use different `custom/config.ini` files per deployment: +- Development: `/custom/config.ini` with dev settings +- Production: Different `/custom/config.ini` with prod settings + +## Configuration in Templates + +Access configuration through context object: + +```php + + + + + + + +currentLang === $ctx->defaultLang): ?> +

Viewing in default language

+ + + +About +``` + +## Best Practices + +### Keep It Simple + +Only configure what's necessary. FolderWeb embraces sensible defaults. + +### Match Translation Files + +Ensure translation files exist for all languages: + +```ini +[languages] +available = "en,no,fr" +``` + +Requires: +- `custom/languages/en.ini` +- `custom/languages/no.ini` +- `custom/languages/fr.ini` + +### Choose Appropriate Default + +Your default language should be: +- Your primary audience's language +- The language with most content +- The language you'll maintain long-term + +### Document Your Choices + +Add comments to explain configuration: + +```ini +; Site uses English as primary language (most content) +; Norwegian and French are secondary translations +[languages] +default = "en" +available = "en,no,fr" +``` + +## Testing Configuration Changes + +After changing configuration: + +1. **Clear browser cache** (Ctrl+Shift+R or Cmd+Shift+R) +2. **Test default language**: Visit `/` +3. **Test other languages**: Visit `/no/`, `/fr/`, etc. +4. **Check navigation**: Ensure menu links include language prefix +5. **Verify translations**: Check UI strings change per language +6. **Test language switcher**: Confirm switching works + +## Related + +- [Multi-Language Guide](../how-to/multi-language.md) +- [Translation Reference](translations.md) +- [Metadata Reference](metadata.md) +- [Context Object Reference](templates.md#context-object) diff --git a/docs/reference/css-variables.md b/docs/reference/css-variables.md new file mode 100644 index 0000000..135978d --- /dev/null +++ b/docs/reference/css-variables.md @@ -0,0 +1,538 @@ +# CSS Variables Reference + +Complete reference for all CSS custom properties available in FolderWeb. + +## Overview + +FolderWeb uses CSS custom properties (variables) for theming. Override these in `/custom/styles/base.css` to customize your site's appearance. + +## Color Variables + +### Primary Colors + +```css +:root { + --color-primary: oklch(0.65 0.15 250); + --color-secondary: oklch(0.50 0.12 250); + --color-light: oklch(0.97 0.01 250); + --color-grey: oklch(0.37 0 0); +} +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `--color-primary` | Blue (OKLCH) | Primary brand color, links, buttons | +| `--color-secondary` | Dark blue (OKLCH) | Secondary accents, hover states | +| `--color-light` | Off-white (OKLCH) | Background, light sections | +| `--color-grey` | Dark grey | Body text, headings | + +### OKLCH Color Space + +FolderWeb uses OKLCH for perceptually uniform colors: + +```css +oklch(lightness chroma hue) +``` + +- **Lightness**: 0 (black) to 1 (white) +- **Chroma**: 0 (grey) to ~0.4 (vibrant) +- **Hue**: 0-360 degrees + +**Examples**: +```css +/* Blue hues (250Β°) */ +--color-primary: oklch(0.65 0.15 250); + +/* Orange hues (30Β°) */ +--color-primary: oklch(0.65 0.20 30); + +/* Green hues (150Β°) */ +--color-primary: oklch(0.60 0.15 150); + +/* Red hues (0Β°) */ +--color-primary: oklch(0.60 0.20 0); + +/* Purple hues (300Β°) */ +--color-primary: oklch(0.60 0.18 300); +``` + +### Alternative Color Formats + +You can use hex, rgb, or hsl instead: + +```css +:root { + /* Hex */ + --color-primary: #4169E1; + --color-secondary: #1E3A8A; + + /* RGB */ + --color-primary: rgb(65, 105, 225); + --color-secondary: rgb(30, 58, 138); + + /* HSL */ + --color-primary: hsl(225, 73%, 57%); + --color-secondary: hsl(225, 64%, 33%); +} +``` + +## Typography Variables + +### Font Families + +```css +:root { + --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-heading: Georgia, "Times New Roman", serif; +} +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `--font-body` | System sans-serif stack | Body text, paragraphs | +| `--font-heading` | Serif stack | Headings (h1-h6) | + +**Custom Fonts**: +```css +@font-face { + font-family: 'MyFont'; + src: url('/custom/fonts/MyFont.woff2') format('woff2'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +:root { + --font-body: 'MyFont', sans-serif; +} +``` + +### Font Sizes + +```css +:root { + --font-size-base: 1.125rem; /* 18px */ + --font-size-small: 0.875rem; /* 14px */ +} +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `--font-size-base` | 1.125rem (18px) | Body text size | +| `--font-size-small` | 0.875rem (14px) | Small text, metadata | + +**Responsive Sizing**: +```css +:root { + /* Fluid typography */ + --font-size-base: clamp(1rem, 0.9rem + 0.5vw, 1.25rem); +} + +h1 { + font-size: clamp(2rem, 1.5rem + 2vw, 3.5rem); +} +``` + +### Line Heights + +```css +:root { + --line-height-base: 1.6; + --line-height-heading: 1.2; +} +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `--line-height-base` | 1.6 | Body text line height | +| `--line-height-heading` | 1.2 | Heading line height | + +## Spacing Variables + +```css +:root { + --spacing-unit: 1.5rem; /* 24px */ + --spacing-small: 0.75rem; /* 12px */ + --spacing-large: 3rem; /* 48px */ +} +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `--spacing-unit` | 1.5rem (24px) | Base spacing unit | +| `--spacing-small` | 0.75rem (12px) | Small gaps | +| `--spacing-large` | 3rem (48px) | Large gaps, section spacing | + +**Usage**: +```css +.card { + padding: var(--spacing-unit); + margin-block-end: var(--spacing-large); +} + +.tag { + padding: var(--spacing-small); +} +``` + +**Responsive Spacing**: +```css +:root { + --spacing-unit: clamp(1rem, 0.8rem + 1vw, 2rem); +} +``` + +## Layout Variables + +```css +:root { + --max-width: 70rem; /* 1120px */ + --border-radius: 4px; +} +``` + +| Variable | Default | Description | +|----------|---------|-------------| +| `--max-width` | 70rem (1120px) | Content max-width | +| `--border-radius` | 4px | Corner rounding | + +**Usage**: +```css +.contain { + max-inline-size: var(--max-width); + margin-inline: auto; +} + +.card { + border-radius: var(--border-radius); +} +``` + +## Complete Variable List + +```css +:root { + /* Colors */ + --color-primary: oklch(0.65 0.15 250); + --color-secondary: oklch(0.50 0.12 250); + --color-light: oklch(0.97 0.01 250); + --color-grey: oklch(0.37 0 0); + + /* Typography */ + --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-heading: Georgia, "Times New Roman", serif; + --font-size-base: 1.125rem; + --font-size-small: 0.875rem; + --line-height-base: 1.6; + --line-height-heading: 1.2; + + /* Spacing */ + --spacing-unit: 1.5rem; + --spacing-small: 0.75rem; + --spacing-large: 3rem; + + /* Layout */ + --max-width: 70rem; + --border-radius: 4px; +} +``` + +## Customization Examples + +### Orange Theme + +```css +:root { + --color-primary: oklch(0.65 0.20 30); + --color-secondary: oklch(0.50 0.18 30); + --color-light: oklch(0.97 0.01 30); +} +``` + +### Dark Mode + +```css +@media (prefers-color-scheme: dark) { + :root { + --color-primary: oklch(0.70 0.15 250); + --color-secondary: oklch(0.80 0.12 250); + --color-light: oklch(0.25 0 0); + --color-grey: oklch(0.90 0 0); + } +} +``` + +### Large Text + +```css +:root { + --font-size-base: 1.25rem; /* 20px */ + --line-height-base: 1.7; + --spacing-unit: 2rem; +} +``` + +### Tight Layout + +```css +:root { + --max-width: 50rem; /* 800px */ + --spacing-unit: 1rem; /* 16px */ + --spacing-large: 2rem; /* 32px */ +} +``` + +### Rounded Design + +```css +:root { + --border-radius: 12px; +} +``` + +## Using Variables + +### In Your Styles + +```css +.card { + background: var(--color-light); + color: var(--color-grey); + padding: var(--spacing-unit); + border-radius: var(--border-radius); +} + +.button { + background: var(--color-primary); + color: white; + padding: var(--spacing-small) var(--spacing-unit); + border-radius: var(--border-radius); +} + +.button:hover { + background: var(--color-secondary); +} +``` + +### With Fallbacks + +Provide fallbacks for older browsers: + +```css +.card { + background: #F5F5F5; /* Fallback */ + background: var(--color-light); /* Variable */ +} +``` + +### With calc() + +Combine with calculations: + +```css +.card { + padding: calc(var(--spacing-unit) * 2); + margin-block-end: calc(var(--spacing-large) - 1rem); +} +``` + +### With color-mix() + +Create variations: + +```css +.button { + background: var(--color-primary); +} + +.button:hover { + background: color-mix(in oklch, var(--color-primary), black 10%); +} + +.button-light { + background: color-mix(in oklch, var(--color-primary), white 80%); +} +``` + +## Adding Custom Variables + +Define your own variables: + +```css +:root { + /* Custom color palette */ + --color-accent: oklch(0.70 0.15 180); + --color-warning: oklch(0.70 0.20 60); + --color-danger: oklch(0.60 0.20 10); + --color-success: oklch(0.65 0.15 140); + + /* Custom spacing */ + --spacing-xs: 0.25rem; + --spacing-xl: 4rem; + --spacing-2xl: 6rem; + + /* Custom typography */ + --font-mono: 'Monaco', 'Courier New', monospace; + --font-size-large: 1.5rem; + --font-size-xlarge: 2rem; + + /* Custom layout */ + --sidebar-width: 20rem; + --header-height: 4rem; + --content-gap: 2rem; +} +``` + +Use them: + +```css +.sidebar { + width: var(--sidebar-width); + background: var(--color-light); +} + +code { + font-family: var(--font-mono); + background: var(--color-accent); + padding: var(--spacing-xs); +} +``` + +## Scoped Variables + +Override variables for specific sections: + +```css +/* Global defaults */ +:root { + --color-primary: oklch(0.65 0.15 250); +} + +/* Blog section uses green */ +.section-blog { + --color-primary: oklch(0.60 0.15 150); +} + +/* About page uses orange */ +.page-about { + --color-primary: oklch(0.65 0.20 30); +} + +/* Variables cascade to children */ +.section-blog .button { + background: var(--color-primary); /* Green in blog */ +} +``` + +## Responsive Variables + +Change variables at breakpoints: + +```css +:root { + --spacing-unit: 1rem; + --font-size-base: 1rem; + --max-width: 60rem; +} + +@media (min-width: 768px) { + :root { + --spacing-unit: 1.5rem; + --font-size-base: 1.125rem; + --max-width: 70rem; + } +} + +@media (min-width: 1200px) { + :root { + --spacing-unit: 2rem; + --font-size-base: 1.25rem; + --max-width: 80rem; + } +} +``` + +## Browser Support + +CSS custom properties are supported in all modern browsers: +- Chrome 49+ +- Firefox 31+ +- Safari 9.1+ +- Edge 15+ + +For older browsers, provide fallbacks or use PostCSS with custom properties plugin. + +## Debugging Variables + +Inspect variables in browser DevTools: + +1. Right-click element β†’ Inspect +2. Check "Computed" tab +3. Scroll to custom properties section +4. See resolved values + +Or log in console: + +```javascript +getComputedStyle(document.documentElement).getPropertyValue('--color-primary') +``` + +## Best Practices + +### Use Semantic Names + +```css +/* Good - semantic */ +--color-primary +--color-text +--color-background + +/* Avoid - non-semantic */ +--color-blue +--color-444 +``` + +### Group Related Variables + +```css +:root { + /* Colors */ + --color-primary: ...; + --color-secondary: ...; + + /* Typography */ + --font-body: ...; + --font-heading: ...; + + /* Spacing */ + --spacing-unit: ...; +} +``` + +### Document Your Variables + +```css +:root { + /* Brand colors from design system */ + --color-primary: oklch(0.65 0.15 250); /* Blue - primary CTA */ + --color-secondary: oklch(0.50 0.12 250); /* Dark blue - accents */ + + /* Layout constraints */ + --max-width: 70rem; /* 1120px - content max width */ +} +``` + +### Provide Fallbacks + +```css +.card { + background: #F5F5F5; + background: var(--color-light); +} +``` + +## Related + +- [Custom Styles Guide](../how-to/custom-styles.md) +- [Template Reference](templates.md) +- [File Structure Reference](file-structure.md) diff --git a/docs/reference/file-structure.md b/docs/reference/file-structure.md new file mode 100644 index 0000000..9e86833 --- /dev/null +++ b/docs/reference/file-structure.md @@ -0,0 +1,394 @@ +# File Structure Reference + +Complete reference for FolderWeb's file and directory structure. + +## Root Structure + +``` +project/ +β”œβ”€β”€ app/ # Framework core (never modify) +β”œβ”€β”€ content/ # Your website content +β”œβ”€β”€ custom/ # Your customizations +└── .htaccess # Web server configuration (optional) +``` + +## App Directory (Framework Core) + +``` +app/ +β”œβ”€β”€ router.php # Main entry point and request router +β”œβ”€β”€ content.php # Content discovery and parsing functions +β”œβ”€β”€ rendering.php # Template rendering engine +β”œβ”€β”€ context.php # Context object (readonly class) +β”œβ”€β”€ config.php # Configuration loader +β”œβ”€β”€ helpers.php # Utility functions +β”œβ”€β”€ constants.php # File extension constants +β”œβ”€β”€ static.php # Static file server +β”œβ”€β”€ config.ini # Default configuration +β”œβ”€β”€ default/ # Default files (fallback) +β”‚ β”œβ”€β”€ templates/ # Default templates +β”‚ β”‚ β”œβ”€β”€ base.php # HTML wrapper +β”‚ β”‚ β”œβ”€β”€ page.php # Page wrapper +β”‚ β”‚ β”œβ”€β”€ list.php # Simple list +β”‚ β”‚ β”œβ”€β”€ list-grid.php # Grid layout +β”‚ β”‚ β”œβ”€β”€ list-card-grid.php # Card grid +β”‚ β”‚ └── list-faq.php # FAQ layout +β”‚ β”œβ”€β”€ styles/ # Default CSS +β”‚ β”‚ └── base.css # Main stylesheet +β”‚ β”œβ”€β”€ languages/ # Default translations +β”‚ β”‚ β”œβ”€β”€ en.ini # English +β”‚ β”‚ └── no.ini # Norwegian +β”‚ └── content/ # Demo content (fallback) +└── vendor/ # Third-party libraries + └── Parsedown.php # Markdown parser +``` + +**Important**: Never modify files in `/app/`. All customization goes in `/custom/`. + +## Custom Directory + +``` +custom/ +β”œβ”€β”€ templates/ # Override templates +β”‚ β”œβ”€β”€ base.php # Custom base template +β”‚ β”œβ”€β”€ page.php # Custom page template +β”‚ β”œβ”€β”€ list-*.php # Custom list templates +β”‚ └── [custom].php # Your custom templates +β”œβ”€β”€ styles/ # Override styles +β”‚ └── base.css # Custom stylesheet +β”œβ”€β”€ languages/ # Override translations +β”‚ β”œβ”€β”€ en.ini # English translations +β”‚ β”œβ”€β”€ no.ini # Norwegian translations +β”‚ └── [lang].ini # Other languages +β”œβ”€β”€ fonts/ # Custom web fonts +β”‚ └── *.woff2 # Font files +β”œβ”€β”€ assets/ # Root-level assets +β”‚ β”œβ”€β”€ favicon.ico # Site favicon +β”‚ β”œβ”€β”€ robots.txt # Robots file +β”‚ β”œβ”€β”€ logo.svg # Logo +β”‚ └── [any file] # Served at root level +└── config.ini # Configuration overrides +``` + +## Content Directory + +Your content directory contains all your website pages and assets. + +### Basic Structure + +``` +content/ +β”œβ”€β”€ index.md # Home page +β”œβ”€β”€ about/ # About page +β”‚ β”œβ”€β”€ index.md # Page content +β”‚ β”œβ”€β”€ metadata.ini # Page metadata +β”‚ └── team-photo.jpg # Page asset +β”œβ”€β”€ blog/ # Blog (list view) +β”‚ β”œβ”€β”€ metadata.ini # Blog configuration +β”‚ β”œβ”€β”€ 2025-11-01-first-post/ +β”‚ β”‚ β”œβ”€β”€ index.md # Post content +β”‚ β”‚ β”œβ”€β”€ cover.jpg # Cover image +β”‚ β”‚ └── metadata.ini # Post metadata +β”‚ └── 2025-11-02-second-post/ +β”‚ β”œβ”€β”€ index.md +β”‚ β”œβ”€β”€ cover.webp +β”‚ └── metadata.ini +└── docs/ # Multi-file page + β”œβ”€β”€ 00-intro.md # Section 1 + β”œβ”€β”€ 01-setup.md # Section 2 + β”œβ”€β”€ 02-usage.md # Section 3 + └── metadata.ini # Page metadata +``` + +### Content Types + +#### Single-File Page + +``` +content/about/ +└── index.md +``` + +URL: `/about/` + +#### Multi-File Page + +``` +content/docs/ +β”œβ”€β”€ 00-intro.md +β”œβ”€β”€ 01-setup.md +└── 02-usage.md +``` + +URL: `/docs/` (all files render as one page) + +#### List View (Directory with Subdirectories) + +``` +content/blog/ +β”œβ”€β”€ metadata.ini +β”œβ”€β”€ 2025-11-01-post/ +β”‚ └── index.md +└── 2025-11-02-post/ + └── index.md +``` + +URL: `/blog/` (shows list of posts) + +## File Naming Conventions + +### Content Files + +Supported extensions: +- `.md` - Markdown (parsed with Parsedown) +- `.html` - HTML (included as-is) +- `.php` - PHP (executed with access to `$ctx`) + +### Language-Specific Files + +Format: `filename.{lang}.ext` + +Examples: +- `index.md` - Default language +- `index.no.md` - Norwegian +- `index.fr.md` - French +- `about.en.md` - English + +### Date Prefixes + +Format: `YYYY-MM-DD-slug` + +Examples: +- `2025-11-01-my-post` +- `2025-11-02-another-post` + +Dates are automatically extracted and formatted. + +### Cover Images + +Filename: `cover.{ext}` + +Supported formats: +- `cover.jpg` +- `cover.jpeg` +- `cover.png` +- `cover.webp` +- `cover.gif` + +Automatically detected in list views. + +### PDF Files + +Any `.pdf` file in a directory is automatically linked in grid layouts. + +### Metadata Files + +Filename: `metadata.ini` + +Format: INI with optional language sections. + +## File Discovery Order + +### Content File Priority + +For multi-file pages, files are rendered in alphanumerical order: + +``` +content/docs/ +β”œβ”€β”€ 00-intro.md # First +β”œβ”€β”€ 01-setup.md # Second +β”œβ”€β”€ 02-usage.md # Third +└── 99-appendix.md # Last +``` + +Use numerical prefixes to control order. + +### Template Resolution + +Templates are resolved with custom fallback: + +1. `/custom/templates/{name}.php` +2. `/app/default/templates/{name}.php` + +### CSS Resolution + +Stylesheets are resolved with custom fallback: + +1. `/custom/styles/base.css` +2. `/app/default/styles/base.css` + +### Translation Resolution + +Translations are resolved with custom fallback: + +1. `/custom/languages/{lang}.ini` +2. `/app/default/languages/{lang}.ini` + +### Configuration Resolution + +Configuration is merged: + +1. Load `/app/config.ini` +2. Merge with `/custom/config.ini` if exists + +Custom values override defaults. + +## URL Mapping + +### Basic Mapping + +``` +/content/about/index.md β†’ /about/ +/content/blog/ β†’ /blog/ +/content/docs/ β†’ /docs/ +``` + +### Language Prefixes + +Default language (no prefix): +``` +/content/about/index.md β†’ /about/ +``` + +Other languages (with prefix): +``` +/content/about/index.no.md β†’ /no/about/ +/content/about/index.fr.md β†’ /fr/about/ +``` + +### Translated Slugs + +With metadata slug overrides: +``` +content/about/metadata.ini: + [no] + slug = "om-oss" + + [fr] + slug = "a-propos" +``` + +URLs become: +- `/about/` (English) +- `/no/om-oss/` (Norwegian) +- `/fr/a-propos/` (French) + +### Trailing Slashes + +FolderWeb requires trailing slashes for directories. Missing slashes trigger 301 redirects: + +``` +/blog β†’ 301 redirect to β†’ /blog/ +``` + +## Special Files and Directories + +### System Files (Ignored) + +These files are automatically ignored: +- `.htaccess` +- `.git/` +- `.DS_Store` +- `node_modules/` +- Hidden files/directories (starting with `.`) + +### Index Files + +`index.md`, `index.html`, `index.php` are treated as directory content, not separate routes. + +### Metadata Files + +`metadata.ini` files are configuration, never rendered as content. + +## Asset Serving + +### Root-Level Assets + +Files in `/custom/assets/` are served at site root: + +``` +/custom/assets/robots.txt β†’ yoursite.com/robots.txt +/custom/assets/favicon.ico β†’ yoursite.com/favicon.ico +/custom/assets/logo.svg β†’ yoursite.com/logo.svg +``` + +### Content Assets + +Files in content directories are accessible at their directory URL: + +``` +/content/blog/2025-11-01-post/cover.jpg + β†’ yoursite.com/blog/2025-11-01-post/cover.jpg + +/content/about/team-photo.jpg + β†’ yoursite.com/about/team-photo.jpg +``` + +### CSS Files + +CSS is served with version hashing: + +``` +/custom/styles/base.css + β†’ yoursite.com/app/styles/base.css?v=abc123def456 +``` + +### Font Files + +Fonts in `/custom/fonts/` are accessible: + +``` +/custom/fonts/MyFont.woff2 + β†’ yoursite.com/custom/fonts/MyFont.woff2 +``` + +## File Permissions + +### Readable Files + +The web server must have read access to: +- All files in `/app/` +- All files in `/content/` +- All files in `/custom/` + +### Writable Files + +FolderWeb is read-only. No files require write access. + +### Security + +- Path validation prevents directory traversal +- Files must be within document root +- Realpath checks ensure proper resolution + +## Size Limits + +- **Read Tool**: Files larger than 50KB are truncated +- **No upload limits**: FolderWeb doesn't handle uploads +- **No execution limits**: Standard PHP limits apply + +## Caching + +### CSS Versioning + +CSS files are versioned with MD5 hash: + +```html + +``` + +Hash updates when file content changes. + +### No Built-in Cache + +FolderWeb doesn't implement content caching. Use: +- Web server caching (Apache, Nginx) +- Reverse proxy (Varnish, Cloudflare) +- PHP OPcache for code + +## Related + +- [Metadata Reference](metadata.md) +- [Configuration Reference](configuration.md) +- [How to Customize Templates](../how-to/custom-templates.md) +- [How to Customize Styles](../how-to/custom-styles.md) diff --git a/docs/reference/metadata.md b/docs/reference/metadata.md new file mode 100644 index 0000000..a99faa4 --- /dev/null +++ b/docs/reference/metadata.md @@ -0,0 +1,625 @@ +# Metadata Reference + +Complete reference for all metadata fields and their usage in `metadata.ini` files. + +## File Format + +Metadata files use INI format: + +```ini +; Comments start with semicolon +key = "value" +array[] = "value1" +array[] = "value2" + +[section] +key = "section value" +``` + +## Standard Fields + +### title + +**Type**: String +**Used in**: All content types +**Purpose**: Override automatic title extraction + +```ini +title = "Custom Page Title" +``` + +If not provided, FolderWeb extracts title from: +1. First H1 heading (`# Title` in Markdown) +2. Folder name (as fallback) + +**Multi-language**: +```ini +title = "English Title" + +[no] +title = "Norsk Tittel" + +[fr] +title = "Titre FranΓ§ais" +``` + +### date + +**Type**: Date string (YYYY-MM-DD) +**Used in**: Blog posts, articles +**Purpose**: Override automatic date extraction + +```ini +date = "2025-11-02" +``` + +If not provided, FolderWeb extracts date from folder names like `2025-11-02-post-title`. + +Dates are automatically formatted in Norwegian style: "2. november 2025" + +### summary + +**Type**: String +**Used in**: List views +**Purpose**: Brief description for cards and listings + +```ini +summary = "A concise description that appears in blog listings." +``` + +**Multi-language**: +```ini +summary = "English summary" + +[no] +summary = "Norsk sammendrag" +``` + +### menu + +**Type**: Boolean +**Used in**: Top-level directories +**Purpose**: Include in site navigation + +```ini +menu = true +``` + +Accepted values: `true`, `false`, `1`, `0`, `yes`, `no`, `on`, `off` + +### menu_order + +**Type**: Integer +**Used in**: Navigation items +**Purpose**: Control navigation order (lower numbers first) + +```ini +menu = true +menu_order = 1 +``` + +### page_template + +**Type**: String +**Used in**: Directories with subdirectories +**Purpose**: Choose list template + +```ini +page_template = "list-grid" +``` + +Available values: +- `list` - Simple list (default) +- `list-grid` - Grid with cover images +- `list-card-grid` - Card-style grid +- `list-faq` - Expandable FAQ format +- Any custom template name (without `.php` extension) + +### slug + +**Type**: String +**Used in**: Language sections +**Purpose**: Translate URL segments + +```ini +[no] +slug = "om-oss" + +[fr] +slug = "a-propos" +``` + +The actual folder is `about/`, but URLs become: +- `/about/` (English) +- `/no/om-oss/` (Norwegian) +- `/fr/a-propos/` (French) + +### hide_list + +**Type**: Boolean +**Used in**: Directories with subdirectories +**Purpose**: Hide list of subfolders and show only page content + +```ini +hide_list = true +``` + +When enabled, directories with subfolders display as regular pages instead of showing a list view. Useful when you need subfolders for organization but want to present a single page to visitors. + +Accepted values: `true`, `false`, `1`, `0`, `yes`, `no`, `on`, `off` + +### redirect + +**Type**: URL string +**Used in**: List items (with `list-card-grid` template) +**Purpose**: Link to external site instead of internal page + +```ini +redirect = "https://example.com" +``` + +Creates an external link card in card grid layouts. + +## Custom Fields + +You can add any custom fields for use in your templates: + +### Common Custom Fields + +```ini +; Author information +author = "Jane Doe" +author_email = "jane@example.com" +author_url = "https://janedoe.com" + +; Content metadata +reading_time = "5 min" +difficulty = "intermediate" +featured = true + +; Categorization +tags[] = "PHP" +tags[] = "Tutorial" +tags[] = "Web Development" + +categories[] = "Programming" +categories[] = "Backend" + +; SEO +meta_description = "Complete guide to FolderWeb metadata" +meta_keywords = "metadata, ini, folderweb" + +; Social sharing +og_image = "/blog/post/social-card.jpg" +twitter_card = "summary_large_image" + +; Version tracking +version = "1.2.0" +last_updated = "2025-11-02" + +; Display options +hide_date = true +hide_author = false +hide_list = true +show_toc = true + +; External references +github_url = "https://github.com/user/repo" +demo_url = "https://demo.example.com" +download_url = "/files/document.pdf" +``` + +## Array Fields + +Use `[]` syntax for array values: + +```ini +tags[] = "PHP" +tags[] = "Web Development" +tags[] = "Tutorial" + +authors[] = "Jane Doe" +authors[] = "John Smith" + +related_posts[] = "/blog/post-1/" +related_posts[] = "/blog/post-2/" +``` + +Access in templates: + +```php + + + +``` + +## Boolean Values + +Accepted boolean formats: + +```ini +; True values +featured = true +featured = 1 +featured = yes +featured = on + +; False values +draft = false +draft = 0 +draft = no +draft = off +``` + +## Language Sections + +Use `[lang]` sections for multi-language overrides: + +```ini +; Base values (default language) +title = "About Us" +summary = "Learn about our company" +slug = "about" + +; Norwegian overrides +[no] +title = "Om Oss" +summary = "LΓ¦r om vΓ₯rt selskap" +slug = "om-oss" + +; French overrides +[fr] +title = "Γ€ Propos" +summary = "DΓ©couvrez notre entreprise" +slug = "a-propos" + +; Fields not overridden inherit base values +``` + +Language-specific fields override base fields for that language. + +## Comments + +Use `;` or `#` for comments: + +```ini +; This is a comment +title = "My Page" + +# This is also a comment +date = "2025-11-02" + +; Comments can be on same line as values +menu = true ; Include in navigation +``` + +## Special Characters + +### Quotes + +Use quotes for values with special characters: + +```ini +; Optional for simple values +title = Simple Title +title = "Simple Title" + +; Required for values with spaces at start/end +title = " Padded Title " + +; Required for values with special characters +summary = "Use \"quotes\" for nested quotes" +summary = 'Single quotes work too' +``` + +### Escape Sequences + +Standard INI escape sequences: + +```ini +; Newline +text = "First line\nSecond line" + +; Tab +text = "Indented\ttext" + +; Quote +text = "He said \"Hello\"" +``` + +## Metadata Location + +### Directory Metadata + +Place `metadata.ini` in the directory it describes: + +``` +content/blog/metadata.ini # Blog configuration +content/about/metadata.ini # About page metadata +``` + +### Item Metadata + +Place `metadata.ini` in each subdirectory: + +``` +content/blog/2025-11-01-post/metadata.ini # Post metadata +content/blog/2025-11-02-post/metadata.ini # Post metadata +``` + +## Metadata Scope + +Metadata applies only to its directory. **No inheritance** from parent directories. + +## Complete Examples + +### Blog Configuration + +**content/blog/metadata.ini**: +```ini +; Display settings +title = "Blog" +page_template = "list-grid" + +; Navigation +menu = true +menu_order = 1 + +; Multi-language +[no] +title = "Blogg" +slug = "blogg" + +[fr] +title = "Blog" +slug = "blog" +``` + +### Blog Post + +**content/blog/2025-11-02-web-performance/metadata.ini**: +```ini +; Basic information +title = "Optimizing Web Performance" +date = "2025-11-02" +summary = "Learn techniques to make your website faster." + +; Author information +author = "Jane Developer" +author_url = "https://jane.dev" + +; Content metadata +reading_time = "8 min" +difficulty = "intermediate" +featured = true + +; Categorization +tags[] = "Performance" +tags[] = "Web Development" +tags[] = "Optimization" + +categories[] = "Technical" +categories[] = "Tutorial" + +; SEO +meta_description = "Complete guide to web performance optimization" + +; Multi-language versions +[no] +title = "Optimalisering av Nettsideytelse" +summary = "LΓ¦r teknikker for Γ₯ gjΓΈre nettsiden din raskere." + +[fr] +title = "Optimisation des Performances Web" +summary = "Apprenez Γ  accΓ©lΓ©rer votre site web." +``` + +### Documentation Page + +**content/docs/metadata.ini**: +```ini +title = "Documentation" +menu = true +menu_order = 2 +page_template = "list" + +; Custom fields +show_toc = true +github_url = "https://github.com/user/repo" + +[no] +title = "Dokumentasjon" +slug = "dokumentasjon" +``` + +### Portfolio Project + +**content/portfolio/project-name/metadata.ini**: +```ini +title = "Project Name" +date = "2025-11-02" +summary = "Brief project description" + +; External links +redirect = "https://project-demo.com" +github_url = "https://github.com/user/project" + +; Project details +client = "Company Name" +role = "Lead Developer" +technologies[] = "PHP" +technologies[] = "HTML" +technologies[] = "CSS" + +[no] +title = "Prosjektnavn" +summary = "Kort prosjektbeskrivelse" +``` + +### FAQ Section + +**content/faq/metadata.ini**: +```ini +title = "Frequently Asked Questions" +menu = true +menu_order = 4 +page_template = "list-faq" + +[no] +title = "Ofte Stilte SpΓΈrsmΓ₯l" +slug = "oss" + +[fr] +title = "Questions FrΓ©quemment PosΓ©es" +slug = "faq" +``` + +## Accessing Metadata in Templates + +### In Page Templates + +Variable: `$pageMetadata` + +```php + + +
+

+ + +

By

+ + + + + +
+ + + +
+ +
+``` + +### In List Templates + +Variable: `$metadata` (directory metadata), `$items` (item metadata) + +```php + +

+ + + +
+

+ + + +

+ + + + + + +

+ +
+ +``` + +## Validation + +### Check Syntax + +Test INI file parsing: + +```bash +php -r "print_r(parse_ini_file('content/blog/metadata.ini', true));" +``` + +### Common Errors + +**Unquoted special characters**: +```ini +; Wrong +title = Title with: special characters + +; Correct +title = "Title with: special characters" +``` + +**Missing array brackets**: +```ini +; Wrong (only last value kept) +tags = "PHP" +tags = "Tutorial" + +; Correct (array created) +tags[] = "PHP" +tags[] = "Tutorial" +``` + +**Invalid section names**: +```ini +; Wrong +[language.no] + +; Correct +[no] +``` + +## Best Practices + +### Always Escape Output + +```php + +``` + +### Provide Defaults + +```php +$author = $metadata['author'] ?? 'Anonymous'; +$tags = $metadata['tags'] ?? []; +``` + +### Check Before Using + +```php + +

By

+ +``` + +### Use Consistent Field Names + +Stick to standard names across your site: +- `author` not `writer` or `by` +- `tags` not `keywords` or `topics` +- `summary` not `description` or `excerpt` + +### Document Custom Fields + +Add comments explaining non-obvious fields: + +```ini +; Featured articles appear at top of homepage +featured = true + +; External demo link (overrides internal page) +demo_url = "https://demo.example.com" +``` + +## Related + +- [Multi-Language Guide](../how-to/multi-language.md) +- [Working with Metadata](../how-to/working-with-metadata.md) +- [Template Variables Reference](templates.md) +- [Configuration Reference](configuration.md) diff --git a/docs/reference/templates.md b/docs/reference/templates.md new file mode 100644 index 0000000..0fe7beb --- /dev/null +++ b/docs/reference/templates.md @@ -0,0 +1,608 @@ +# Template Reference + +Complete reference for all templates and available variables in FolderWeb. + +## Template System Overview + +FolderWeb uses a fallback template system: +1. Check `/custom/templates/{name}.php` +2. Fall back to `/app/default/templates/{name}.php` + +Templates are plain PHP files with access to specific variables and the context object. + +## Core Templates + +### base.php + +**Purpose**: HTML wrapper for all pages (header, navigation, footer) +**Used**: On every page render +**Customizable**: Yes + +**Available Variables**: + +| Variable | Type | Description | +|----------|------|-------------| +| `$content` | string | Rendered page content (HTML) | +| `$ctx` | Context | Full context object | +| `$currentLang` | string | Current language code (e.g., "en", "no") | +| `$navigation` | array | Navigation menu items | +| `$homeLabel` | string | Site title | +| `$translations` | array | UI translation strings | +| `$pageTitle` | string | Current page title | +| `$dirName` | string | Parent directory name | +| `$pageName` | string | Current page filename | + +**Example**: +```php + + + + + <?= htmlspecialchars($pageTitle) ?> | <?= htmlspecialchars($homeLabel) ?> + + + +
+ +
+ +
+ +
+ + + + +``` + +### page.php + +**Purpose**: Wrapper for single pages and articles +**Used**: For file and multi-file pages +**Customizable**: Yes + +**Available Variables**: + +| Variable | Type | Description | +|----------|------|-------------| +| `$content` | string | Rendered content (HTML) | +| `$pageMetadata` | array | Page metadata from metadata.ini | +| `$translations` | array | UI translation strings | + +**Example**: +```php +
+ + + + + +
+``` + +## List Templates + +### list.php + +**Purpose**: Simple list view (default) +**Used**: Directories with subdirectories +**Customizable**: Yes + +**Available Variables**: + +| Variable | Type | Description | +|----------|------|-------------| +| `$items` | array | Array of subdirectory items | +| `$metadata` | array | Directory metadata | +| `$pageContent` | string | Optional intro content (HTML) | +| `$translations` | array | UI translation strings | + +**Item Structure**: + +Each item in `$items` has: + +```php +[ + 'title' => 'Item Title', // From metadata or H1 + 'date' => '2. november 2025', // Formatted date + 'url' => '/blog/post-slug/', // Full URL with language prefix + 'cover' => '/path/to/cover.jpg', // Cover image path or null + 'summary' => 'Brief description', // From metadata or null + 'pdf' => '/path/to/file.pdf', // PDF file path or null + 'redirect' => 'https://...', // External URL or null +] +``` + +**Example**: +```php + +
+ +
+ + +
+ +
+

+ + + +

+ + + + +

+ +
+ +
+``` + +### list-grid.php + +**Purpose**: Grid layout with cover images +**Used**: Visual blog/portfolio listings +**Customizable**: Yes + +**Same variables as list.php** + +Features: +- Grid layout +- Cover images +- PDF download links +- "Read more" buttons + +**Example**: +```php +
+ +
+ + + + +

+ + + +

+ + + + + + +

+ + + +
+ +
+``` + +### list-card-grid.php + +**Purpose**: Card-style grid with external link support +**Used**: Portfolios, resource lists +**Customizable**: Yes + +**Same variables as list.php** + +Features: +- Card-style layout +- PDF download support +- External redirect support +- Cover images + +**Example**: +```php +
+ +
+ + + + +

+ + +

+ + + + + Visit Site + + + + Download PDF + + + + View Details + + +
+ +
+``` + +### list-faq.php + +**Purpose**: Expandable FAQ/Q&A format +**Used**: FAQ sections, documentation +**Customizable**: Yes + +**Same variables as list.php** + +Features: +- Collapsible `
` elements +- Semantic HTML +- Keyboard accessible + +**Example**: +```php + +
+ +
+ + +
+ +
+ + + +

+ + + + + +
+ +
+``` + +## Context Object + +All templates have access to `$ctx` (Context object): + +### Properties + +| Property | Type | Description | +|----------|------|-------------| +| `$ctx->contentDir` | string | Path to content directory | +| `$ctx->currentLang` | string | Current language code | +| `$ctx->defaultLang` | string | Default language code | +| `$ctx->availableLangs` | array | Available language codes | +| `$ctx->langPrefix` | string | URL language prefix (e.g., "/en" or "") | +| `$ctx->requestPath` | string | Current request path | +| `$ctx->hasTrailingSlash` | bool | Whether path has trailing slash | +| `$ctx->navigation` | array | Navigation menu items (computed) | +| `$ctx->homeLabel` | string | Site title (computed) | +| `$ctx->translations` | array | UI translations (computed) | + +### Example Usage + +```php + +availableLangs as $lang): ?> + defaultLang + ? '/' . trim($ctx->requestPath, '/') + : '/' . $lang . '/' . trim($ctx->requestPath, '/'); + ?> + currentLang ? 'aria-current="true"' : '' ?>> + + + + + + +``` + +## Navigation Array + +Structure of `$navigation` items: + +```php +[ + [ + 'title' => 'Blog', + 'url' => '/blog/', + 'order' => 1 + ], + [ + 'title' => 'About', + 'url' => '/about/', + 'order' => 2 + ], + // ... +] +``` + +Already sorted by `menu_order` field. + +## Translation Array + +Structure of `$translations`: + +```php +[ + 'home' => 'Home', + 'read_more' => 'Read more', + 'categories' => 'Categories', + 'tags' => 'Tags', + 'footer_text' => 'Made with FolderWeb', + 'footer_handcoded' => 'Generated in', + 'footer_page_time' => 'ms', + // ... custom translations +] +``` + +## Helper Functions Available in Templates + +### resolveTemplate() + +Find custom or default template: + +```php +$templatePath = resolveTemplate('base', 'templates'); +$cssPath = resolveTemplate('base.css', 'styles'); +``` + +### htmlspecialchars() + +Escape output (always use for user content): + +```php + +``` + +### Other PHP Functions + +All standard PHP functions are available: +- `isset()`, `empty()` +- `count()`, `array_filter()` +- `date()`, `time()` +- String functions +- etc. + +## Creating Custom Templates + +### Step 1: Create Template File + +```bash +touch custom/templates/my-custom-list.php +``` + +### Step 2: Use Standard Variables + +Custom list templates receive `$items`, `$metadata`, `$pageContent`, `$translations`. + +### Step 3: Apply in Metadata + +**content/my-section/metadata.ini**: +```ini +page_template = "my-custom-list" +``` + +Note: Omit `.php` extension. + +## Template Best Practices + +### Always Escape Output + +```php + + + + + +``` + +### Check Variables Before Use + +```php + + + +``` + +### Use Null Coalescing + +```php +$author = $metadata['author'] ?? 'Anonymous'; +``` + +### Semantic HTML + +```php + +
+

+ +
+ + +
+ + +
+``` + +### Accessibility + +```php + +<?= htmlspecialchars($item['title']) ?> + + +