/ head tags from the CMS instead of * hard-coding them. The icon files live under public/fileadmin/icons/ and are * referenced root-relative — identical convention to the image URLs emitted by * UsecaseSerializer::image() (getImageUri also returns /fileadmin/... paths). * If the frontend ever runs on a separate origin from the CMS, it must prefix * these hrefs with the CMS base URL, exactly as it does for image URLs. * * Static by nature (same for every page) — no DB access, exception-free. */ final class FaviconsJsonRenderer { private const BASE = '/fileadmin/icons'; /** Browser UI / address-bar tint (VITEC navy). */ private const THEME_COLOR = '#26358C'; #[AsAllowedCallable] public function render(string $content, array $conf): string { $payload = [ 'themeColor' => self::THEME_COLOR, 'manifest' => self::BASE . '/site.webmanifest', // Ready-to-render descriptors, ordered most- to least-specific. 'links' => [ ['rel' => 'icon', 'type' => 'image/x-icon', 'href' => self::BASE . '/favicon.ico'], ['rel' => 'icon', 'type' => 'image/png', 'sizes' => '32x32', 'href' => self::BASE . '/favicon-32.png'], ['rel' => 'icon', 'type' => 'image/png', 'sizes' => '16x16', 'href' => self::BASE . '/favicon-16.png'], ['rel' => 'apple-touch-icon', 'sizes' => '180x180', 'href' => self::BASE . '/apple-touch-icon.png'], ['rel' => 'manifest', 'href' => self::BASE . '/site.webmanifest'], ], ]; return json_encode($payload) ?: ''; } }