# Theme management ## Managing themes 1. Go to **Theme** in the admin menu 2. **Edit** — Click the pencil icon to edit theme files (see [Theme editor](#theme-editor)) 3. **Activate** — Click the checkmark to activate a theme (stored in `config.json`) 4. **Compile SCSS** — Click the palette icon to force-compile SCSS to `assets/css_compiled/theme.css` 5. **Delete** — Trash icon (only non-active, non-default themes) 6. **New theme** — Create a custom theme, optionally based on an existing theme ## Theme status badges In the theme overview you see per theme: - **Active** (green) — this theme is selected in `config.json` - **SCSS ok** (green) — `assets/css_compiled/theme.css` is newer than `assets/scss/theme.scss` - **SCSS stale** (yellow) — SCSS source was changed after the last compile; click the palette icon to compile ## Theme editor Via **Edit** (pencil icon) in the theme overview you open the theme editor (`/admin/theme-edit?theme=`). It works the same as the plugin editor: ### File browser sidebar - Shows the nested file tree of the theme - `assets/css_compiled/` is hidden (runtime artefact, read-only) - Click a file to open it in the CodeMirror editor - Folders containing the active file are auto-expanded ### Editable file types `.twig`, `.json`, `.scss`, `.css`, `.js`, `.html`, `.md`, `.php` ### Create a new file - Click **New file** - Enter a path within the theme (e.g. `partials/header.twig` or `assets/scss/_variables.scss`) - Subfolders are created automatically - Allowed: twig, json, scss, css, js, html, md, php - A stub is auto-generated (e.g. `{% extends 'base.twig' %}` for `.twig`) ### Upload a file - Click **Upload** to upload files to the theme's `assets/` - Allowed: images, video, audio, PDF, ZIP, CSS, SCSS, JS, JSON, HTML, MD, TWIG, fonts - Path-traversal protection: target dir must stay within `assets/` ### Move / delete a file - In the file tree each file has a move button (arrows icon) and a delete button (trash) - **Move**: choose a target folder from the dropdown listing all folders in the theme - **Delete**: with confirmation; `theme.json` cannot be deleted ### Compile SCSS from the editor - At the top of the editor there is a **Compile SCSS** button (only if `assets/scss/theme.scss` exists) - Shows the compile status: **up-to-date** (green) or **stale** (yellow) - Forces compilation via `ThemeManager::compileCss(true)` ### Insert media in the editor - The media button in the editor toolbar opens the media modal - In theme context it scans `themes//assets/` (via `/admin/media-list?theme=`) - Snippet format depends on file type: markdown → `![alt](url)`, html/php → ``, others → raw URL ### Security - All actions require a CSRF token - Path-traversal protection via `realpath()` + prefix check on the theme dir - `theme.json` can be edited but not deleted/moved - Default theme can be edited but not deleted - Active theme cannot be deleted (activate another theme first) ## Theme structure ``` themes/default/ ├── theme.json # { title, config.default_template, template: layout→.twig } ├── README.md # Theme documentation (per theme) ├── base.twig # Main layout (head, header, nav, breadcrumb, footer) ├── full_content.twig # Layout: full width ├── left_sidebar.twig # Layout: sidebar on the left ├── right_sidebar.twig # Layout: sidebar on the right ├── custom1.twig # Layout: custom ├── guide.twig # Layout: guide with sidebar (Navigation plugin) ├── partials/ # header.twig, navigation.twig, footer.twig └── assets/ ├── scss/theme.scss # SCSS source (only CSS source — manual css/theme.css must not exist) ├── css_compiled/ # Generated by scssphp (read-only, do not edit manually, hidden in editor) ├── css/ # External CSS (bootstrap.min.css, bootstrap-icons.css, mobile.css) ├── js/ # app.js, bootstrap.bundle.min.js ├── fonts/ # bootstrap-icons.woff, woff2 └── img/ # favicon, icon, world-map ``` New themes created via admin automatically get this uniform structure (with `README.md`, `base.twig`, `full_content.twig`, `partials/header.twig`, `partials/footer.twig`, `assets/scss/theme.scss`, and all assets subfolders). ## theme.json ```json { "title": "default", "config": { "default_template": "full_content" }, "template": { "full_content": "full_content.twig", "left_sidebar": "left_sidebar.twig", "right_sidebar": "right_sidebar.twig", "custom1": "custom1.twig", "guide": "guide.twig" } } ``` - `title` — Display name in admin - `config.default_template` — Default layout for pages without a `layout:` frontmatter - `template` — Mapping from layout name to `.twig` file ## SCSS compilation - `ThemeManager` compiles `assets/scss/theme.scss` at runtime to `assets/css_compiled/theme.css` via scssphp - `css_compiled/` is **read-only** — do not edit manually (hidden in the theme editor) - `assets/css/theme.css` must **not** exist; otherwise `ThemeManager::getCssUrl()` ignores the SCSS - Force compilation via the **Compile SCSS** button in admin (or remove `assets/css_compiled/theme.css` and `.mtime`) ## Layouts Layouts are chosen via the frontmatter `layout:` key in content files. Unknown layouts fall back to `config.default_template` from `theme.json`. ## guide.twig Special layout for guides. Injects the Navigation plugin into the sidebar for sidebar navigation. Automatically used for pages in the `guide/` folder.