From 1aa4d6a83b980298e4b389582f0b95a67dfef470 Mon Sep 17 00:00:00 2001 From: Ruben Date: Sat, 1 Nov 2025 16:11:20 +0100 Subject: [PATCH 1/2] Add modern CSS reset and styling system Implement responsive grid layout Add CSS variables for consistent theming Create button component with states Improve header and footer structure Add page load time measurement Enhance list template with styling Update template structure with semantic HTML Implement dynamic CSS file loading Add favicon support Improve navigation with active state Add page and section class names to body Implement conditional date display in list items --- app/default/styles/base.css | 202 ++++++++++++++++++++++++++++----- app/default/templates/base.php | 78 +++++++++---- app/default/templates/list.php | 41 ++++++- 3 files changed, 268 insertions(+), 53 deletions(-) diff --git a/app/default/styles/base.css b/app/default/styles/base.css index dcaff10..ff51983 100644 --- a/app/default/styles/base.css +++ b/app/default/styles/base.css @@ -1,47 +1,191 @@ -/* MINIMAL RESET */ -* { margin: 0; padding: 0; box-sizing: border-box; } +/* MINIMAL CSS RESET*/ +* { margin-bottom: 0; } -/* GLOBAL */ -body { - font-family: system-ui, sans-serif; - line-height: 1.6; - color: #333; - max-width: 800px; - margin: 0 auto; - padding: 1rem; +/* 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) } +} -a { color: #0066cc; text-decoration: none; } -a:hover { text-decoration: underline; } +.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: 2px solid #eee; - padding-bottom: 1rem; - margin-bottom: 2rem; -} + 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); + } + } -header h1 { font-size: 1.5rem; } + 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; + a { + margin-left:0.4rem; + margin-top:0.4rem; + } + } + } + } +} /* MAIN */ -main { margin-bottom: 2rem; } +main { + grid-area: main; + background-color: var(--color-light); + padding-bottom: 2rem; -article { margin-bottom: 2rem; } - -h1 { - font-size: 1.8rem; - margin-bottom: 0.5rem; + aside { margin-top: 1.3em } + article { + .intro { + font-size: 1.2rem; + line-height: 1.35em; + } + } + .button { + margin-top: 1.3rem; + justify-self: start; + } } -p { margin-bottom: 1rem; } +/* 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 */ footer { - border-top: 2px solid #eee; - padding-top: 1rem; + 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; - font-size: 0.9rem; - color: #666; + .generated { font-size: .6rem } + } } diff --git a/app/default/templates/base.php b/app/default/templates/base.php index 3782607..074445e 100644 --- a/app/default/templates/base.php +++ b/app/default/templates/base.php @@ -1,29 +1,63 @@ + + - + - - <?= htmlspecialchars($pageTitle ?? 'Site') ?> + + + + <?= htmlspecialchars($pageTitle ?? 'Site Title') ?> - -
-

Webfolder demo

- - - -
-
- -
- + + +
+
+
+ + +
+
+ +
+ +
+ +
+
+

+ +

+
+
+
diff --git a/app/default/templates/list.php b/app/default/templates/list.php index 40dcffe..8d7ab48 100644 --- a/app/default/templates/list.php +++ b/app/default/templates/list.php @@ -1,18 +1,55 @@ + +
+ +
+ +
+ + From 36a3221dbb05d3d9dc4ba3413599950ff7d2d483 Mon Sep 17 00:00:00 2001 From: Ruben Date: Sat, 1 Nov 2025 16:11:33 +0100 Subject: [PATCH 2/2] Add default templates for list views Add default language files for English and Norwegian Add list-card-grid template for card-based content display Add list-faq template for FAQ-style content display Add list-grid template for grid-based content display --- app/README.md | 274 +++++++++++++++++++++++ app/default/languages/en.ini | 12 + app/default/languages/no.ini | 12 + app/default/templates/list-card-grid.php | 82 +++++++ app/default/templates/list-faq.php | 141 ++++++++++++ app/default/templates/list-grid.php | 94 ++++++++ 6 files changed, 615 insertions(+) create mode 100644 app/README.md create mode 100644 app/default/languages/en.ini create mode 100644 app/default/languages/no.ini create mode 100644 app/default/templates/list-card-grid.php create mode 100644 app/default/templates/list-faq.php create mode 100644 app/default/templates/list-grid.php diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..be1102d --- /dev/null +++ b/app/README.md @@ -0,0 +1,274 @@ +# Framework Documentation + +This directory contains the core framework for the file-based CMS. It provides a minimal, extensible foundation that can be customized via the `/custom/` directory. + +## Architecture + +The framework is a lightweight PHP-based routing system that: +- Converts URLs to filesystem paths +- Resolves language-specific content and slugs +- Applies templates to render content +- Serves static assets (styles, fonts, images) + +## Directory Structure + +``` +app/ +├── router.php # Main routing logic +├── static.php # Static asset server for /app/* resources +├── config.ini # Default framework configuration +├── vendor/ # Third-party dependencies +│ └── Parsedown/ # Markdown parser +└── default/ # Default templates and styles (fallback) + ├── templates/ + │ ├── base.php # Base HTML structure + │ ├── page.php # Single page/article template + │ └── list.php # Directory listing template + └── styles/ + └── base.css # Default base styles +``` + +## Core Components + +### `router.php` + +The main request router that handles all content requests. Key responsibilities: + +1. **Language Detection**: Extracts language from URL path (`/no/`, `/en/`) +2. **Slug Resolution**: Converts language-specific slugs to directory names using metadata +3. **Path Resolution**: Maps URLs to filesystem paths in `/content/` +4. **Content Loading**: Reads content files (`.md`, `.html`, `.php`) +5. **Template Application**: Applies appropriate template based on content type +6. **Rendering**: Outputs final HTML with base template + +**Key Functions:** +- `resolveLanguageSlugToName()` - Translates slugs to directory names +- `findContentFile()` - Locates content files with language variants +- `renderPage()` - Renders content with templates +- `parseMetadata()` - Parses INI metadata files + +### `static.php` + +Serves static assets from `/app/` directory. Handles requests for: +- `/app/styles/*` - Framework CSS files +- `/app/fonts/*` - Framework fonts +- `/app/default-styles/*` - Default stylesheet aliases + +Includes security checks to prevent path traversal attacks. + +### `config.ini` + +Default configuration for the framework: + +```ini +[languages] +default = "no" # Default language +available = "no,en" # Available languages +``` + +Can be overridden by `/custom/config.ini`. + +### `vendor/` + +Third-party dependencies: +- **Parsedown**: Markdown-to-HTML parser library + +### `default/` + +Fallback templates and styles used when custom templates are not provided. + +## Request Flow + +``` +1. User requests URL (e.g., /no/artikler/pasientinfo) + ↓ +2. Apache routes to /content/index.php + ↓ +3. index.php includes router.php + ↓ +4. router.php: + - Extracts language: "no" + - Resolves slug "artikler" → "artikler" directory + - Resolves slug "pasientinfo" → "pasientinfo" directory + - Checks if path exists in /content/ + - Determines content type (file or directory) + ↓ +5. For single article: + - Loads metadata.ini + - Loads article.no.md (or article.md) + - Applies /custom/templates/page.php (or default) + - Wraps with /custom/templates/base.php + ↓ +6. For directory listing: + - Scans directory for subdirectories + - Loads metadata.ini for each item + - Applies list template specified in metadata + - Wraps with base.php + ↓ +7. Outputs final HTML to browser +``` + +## Template System + +Templates can be overridden by placing files in `/custom/templates/`: + +### Base Template (`base.php`) +Wraps all content with HTML structure, header, footer, navigation. + +**Variables available:** +- `$title` - Page title +- `$content` - Rendered page content +- `$language` - Current language code +- `$metadata` - Page metadata array + +### Page Template (`page.php`) +Renders single articles/pages. + +**Variables available:** +- `$contentHtml` - Parsed HTML content +- `$metadata` - Article metadata +- `$language` - Current language code +- `$parentMetadata` - Parent directory metadata + +### List Templates +Render directory listings. Multiple variants: +- `list.php` - Simple list +- `list-grid.php` - Grid layout +- `list-card-grid.php` - Card grid with images +- `list-faq.php` - Expandable FAQ view + +**Variables available:** +- `$items` - Array of child items with metadata +- `$metadata` - Directory metadata +- `$language` - Current language code + +## Customization + +The framework is designed to be minimal and extensible. All customization should happen in `/custom/`: + +### Override Templates +Create files in `/custom/templates/` with the same name as default templates. + +### Override Configuration +Create `/custom/config.ini` to override default settings. + +### Add Custom Styles +Place CSS in `/custom/styles/` and reference in custom templates. + +### Add Custom Fonts +Place font files in `/custom/fonts/` and reference in CSS. + +### Add Translations +Create or edit `/custom/languages/[lang].ini` files. + +## Metadata System + +Content is configured via `metadata.ini` files placed in each directory. + +### Common Metadata Fields + +```ini +[metadata] +title[no] = "Norwegian Title" +title[en] = "English Title" +slug[no] = "norwegian-slug" +slug[en] = "english-slug" +summary[no] = "Norwegian summary" +summary[en] = "English summary" +date = "2024-10-15" +category = "Category Name" +tags = "tag1,tag2,tag3" +template = "list-card-grid" # For directories +show_in_menu = true +menu_order = 10 +``` + +### Metadata Inheritance +Child items inherit parent metadata when not specified. + +## Content File Resolution + +The router looks for content files in this order: + +1. `article.[language].md` (e.g., `article.no.md`) +2. `article.[language].html` +3. `article.[language].php` +4. `article.md` (fallback) +5. `article.html` (fallback) +6. `article.php` (fallback) +7. `page.[language].md` (for directory index) +8. `page.md` (fallback) + +## Language System + +### URL Structure +- Norwegian: `/no/artikler/pasientinfo` +- English: `/en/articles/patient-info` + +### Slug Translation +Slugs are resolved using metadata: +```ini +slug[no] = "pasientinfo" +slug[en] = "patient-info" +``` + +The router automatically translates URLs to filesystem paths regardless of language. + +### Translation Files +Located in `/custom/languages/`: +- `no.ini` - Norwegian translations +- `en.ini` - English translations + +Format: +```ini +key = "Translated text" +another_key = "More text" +``` + +Access in templates: `$translations['key']` + +## Security Considerations + +- **Path Traversal Protection**: `static.php` validates paths to prevent directory traversal +- **Input Sanitization**: URLs are sanitized before filesystem operations +- **Template Isolation**: Templates run in controlled scope with specific variables +- **No Database**: File-based system eliminates SQL injection risks + +## Performance + +- **No Caching**: Content is rendered on each request (suitable for low-traffic sites) +- **Performance Tracking**: Page generation time is measured and displayed +- **Static Assets**: Served directly by Apache when possible + +## Extending the Framework + +To add new features: + +1. **Custom Functions**: Add helper functions in custom templates +2. **New Template Types**: Create new template files in `/custom/templates/` +3. **Metadata Fields**: Add new fields to `metadata.ini` files +4. **Custom Routes**: Extend `router.php` (requires modifying framework) + +## Debugging + +Enable error reporting in `content/index.php`: +```php +ini_set('display_errors', 1); +error_reporting(E_ALL); +``` + +View page generation time at bottom of each page. + +## Requirements + +- PHP 8.3 or higher +- Apache with mod_rewrite enabled +- Write permissions on content directories (for future admin features) + +## Limitations + +- No built-in caching (regenerates pages on each request) +- No built-in admin interface (content edited via filesystem) +- No user authentication system +- No built-in search functionality +- Performance may degrade with very large content libraries diff --git a/app/default/languages/en.ini b/app/default/languages/en.ini new file mode 100644 index 0000000..761b54b --- /dev/null +++ b/app/default/languages/en.ini @@ -0,0 +1,12 @@ +; English translations +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_text = "Footer content goes here" +footer_handcoded = "This page was generated in" +footer_page_time = "ms" diff --git a/app/default/languages/no.ini b/app/default/languages/no.ini new file mode 100644 index 0000000..514bf0a --- /dev/null +++ b/app/default/languages/no.ini @@ -0,0 +1,12 @@ +; Norwegian translations +home = "Hjem" +categories = "Kategorier" +tags = "Stikkord" +read_more = "Les mer" +read_article = "Les artikkel" +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" 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-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 new file mode 100644 index 0000000..0aad44c --- /dev/null +++ b/app/default/templates/list-grid.php @@ -0,0 +1,94 @@ + +
+ +
+ + +
+
+ + + +
+
+ +