Snapshot of main at 9f39a6e1e (2026-11-02)

This commit is contained in:
Ronald Huynen committed 2026-11-02 10:41:47 +01:00
commit 6ccf1792d7
2580 files changed
+1040202

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
# Theme System
How the look of an installation is chosen and applied. Setting up a platform's theme and branding step by step:
[MULTI_PLATFORM_INSTANCES.md](MULTI_PLATFORM_INSTANCES.md), sections 3, 5 and 6.
Last checked against the code: 2026-10-19 (branch `laravel-11`).
## Where the active theme comes from
1. `platform/theme.php` in the installation's platform folder: one theme definition with its own `id`. When it
exists, `App\Providers\PlatformServiceProvider` registers it in `themes.themes.<id>` and makes it the active theme.
2. Otherwise `config('themes.active')`, which is `TIMEBANK_THEME` in `.env` (default `timebank_cc`), one of the themes
in `config/themes.php`: `timebank_cc`, `uuro`, `vegetable`, `yellow` (template `config/themes.php.example`, from
which `deploy.sh` creates the file and `config:merge` adds new keys).
A theme definition has `name`, `description`, `logos`, `colors` and `typography`. `colors` needs every key the
templates have (`primary`, `secondary`, `button-secondary`, `button-secondary-text`, `accent`, `success`, `warning`,
`danger-dark`, `danger`, `danger-light`, `info`, `brand`, `background`, `border`, `surface`, `reaction`,
`reaction-hover`, `text`, `logo`): a missing key leaves its CSS variable empty. Copy a whole block from
`config/themes.php.example` when you make a theme.
## How it is applied: at runtime, one build for all themes
- `theme_css_vars()` turns the active theme's colours and typography into CSS custom properties
(`--color-primary-500: 37 99 235;`, `--font_family_body: ...`). The layouts (`layouts/app`, `layouts/guest`) put
them in a `<style>` block on every page.
- `tailwind.config.js` defines the theme colours as `rgb(var(--color-...) / <alpha-value>)`, so classes such as
`bg-theme-brand`, `text-theme-primary`, `border-theme-border` and `bg-primary-500` follow the active theme.
- So switching themes needs no `npm run build`: one asset build serves every theme. Only `VITE_*` values in `.env`
are compiled into the build.
- `<html data-theme="@themeId">` carries the theme id; no CSS depends on it.
## Logos, favicons and the preview image
Each asset is looked up in the platform folder first, then through the theme's `logos` settings:
| Asset | Platform folder | Theme setting | Helper |
|---|---|---|---|
| Website logo (inline SVG) | `platform/views/logos/<theme>.blade.php` | `logos.svg_inline`, a view name such as `logos.uuro` (`resources/views/logos/`) | `theme_logo('svg_inline')` |
| Email and PDF logo | `platform/public/mail-logo.png` | `logos.email_logo`, relative to `storage/app/public/` | `theme_email_logo_url()` (emails), `theme_email_logo_file()` (PDFs, inline images) |
| Email logo for dark headers | `platform/public/mail-logo-inverted.png` | `logos.email_logo_inverted` | the same with `true` |
| Favicons | `platform/public/` | `logos.favicon_dir`, relative to `public/` | `theme_favicon('favicon.ico')`; `theme_favicon_optional()` for `favicon.svg`, `favicon-96x96.png`, `site.webmanifest` |
| Preview image for shared links | `platform/public/og-image.png` | `logos.og_image`, relative to `public/` | `theme_og_image()` |
Without any of these, an email gets a generated PNG with the platform name (`App\Services\GeneratedLogo`; the
`timebank_cc` theme falls back to the tracked `app_mail_logo.png`), favicons come from `public/`, and there is no
preview image. The title, the icons and the Open Graph tags are in one partial that both layouts include:
`resources/views/layouts/partials/brand-head.blade.php`.
## Helpers and Blade directives
| PHP (`app/Helpers/ThemeHelper.php`) | Blade (`app/Providers/ThemeServiceProvider.php`) | Returns |
|---|---|---|
| `theme($key = null, $default = null)` | `@theme('key')` | the active theme's definition, or one key of it |
| `theme_id()` | `@themeId` | the active theme's id |
| `theme_name()` | `@themeName` | its display name |
| `theme_color('primary.500')` | `@themeColor('primary.500')` | a colour |
| `theme_font('font_family_body')` | `@themeFont('font_family_body')` | a typography value |
| `is_theme('uuro')` | `@isTheme('uuro') ... @endIsTheme` | whether that theme is active |
| `theme_css_vars()` | `@themeCssVars` | the CSS custom properties |
| `theme_logo($type)`, `theme_email_logo($inverted)` and the helpers in the table above | | logos, favicons, preview image |
Prefer theme classes and helpers over theme checks: code that asks `is_theme('uuro')` breaks for a theme of a new
platform. Only the generated email logo checks the id (`timebank_cc` keeps its own PNG).
In views always use theme classes, never fixed colours. UI patterns: [STYLE_GUIDE.md](STYLE_GUIDE.md),
[THEME_CLASS_CONVERSION_GUIDE.md](THEME_CLASS_CONVERSION_GUIDE.md).
## Adding a theme
- **For one platform**: in its `platform/theme.php`, with its logo in `platform/views/logos/<id>.blade.php`. Nothing
in the code changes. `php artisan platform:init --from-current` writes the active theme there as a start.
- **For every installation**: a block in `config/themes.php.example` and a logo view in `resources/views/logos/`,
committed to git. `config:merge themes` then adds it to each installation's `config/themes.php`.
## Testing
- `tests/Feature/ThemeButtonColorsTest.php`, `PlatformBrandingTest.php`, `PlatformFolderTest.php` (theme from the
platform folder) and `PlatformHeadTest.php` (icons and Open Graph).
- By hand: switch `TIMEBANK_THEME` in `.env` and reload; with a `platform/theme.php` present, that one wins.