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 += htmlspecialchars($metadata['title']) ?> +``` + +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, '
= htmlspecialchars($item['summary']) ?>
+ += $translations['footer_text'] ?>
+``` + +### 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 + + + + + + = htmlspecialchars($metadata['reading_time']) ?> + +``` + +## 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 + + += htmlspecialchars($item['summary']) ?>
+ + + +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..3bbb381 --- /dev/null +++ b/docs/reference/metadata.md @@ -0,0 +1,610 @@ +# 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) + +### 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 +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 + + = htmlspecialchars($tag) ?> + +``` + +## 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 + + += htmlspecialchars($item['summary']) ?>
+ +By = htmlspecialchars($metadata['author']) ?>
+ +``` + +### 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($item['summary']) ?>
+ += htmlspecialchars($item['summary']) ?>
+ + += htmlspecialchars($item['summary']) ?>
+ + + + + Visit Site + + + + Download PDF + + + + View Details + + += htmlspecialchars($item['summary']) ?>
+ + + + = $translations['read_more'] ?> + +