From 078651112e17cf66d439d635b62ac0c19128a476 Mon Sep 17 00:00:00 2001 From: Rafael Fernandes Date: Thu, 24 Sep 2026 16:05:52 -0300 Subject: [PATCH] fix(brand): serve the R/ favicon reliably and fix the manifest link - add icon.svg built from the Caveat R/ glyphs: sharp at every size and switches to a dark tile with the system color scheme; the new URL also makes browsers drop the cached old favicon - rebuild favicon.ico from the same artwork and declare the modern set (ico 32x32, svg, apple-touch-icon); drop the upscaled 16px shortcut - the manifest link pointed at a nonexistent site.webmanifest (404), so home-screen icons never loaded; link the real manifest.webmanifest and serve it as application/manifest+json - nginx no longer answers missing .css/.js/.jpg/.svg/.json files on the main site with index.html (soft 404); Storybook keeps its fallback - icons spec checks every icon and manifest link loads as an image or manifest, and that missing assets return 404 Co-Authored-By: Claude Opus 5.5 --- nginx/mime.types | 1 + nginx/nginx.conf | 4 +- nginx/tests/mime.types | 1 + nginx/tests/nginx.conf | 4 +- public/favicon-16x16.png | Bin 616 -> 0 bytes public/favicon.ico | Bin 5060 -> 5951 bytes public/icon.svg | 9 +++++ src/app/_layout/metadata.tsx | 8 ++-- tests/icons.spec.ts | 76 +++++++++++++++++++++++++++++++++++ 9 files changed, 96 insertions(+), 7 deletions(-) delete mode 100644 public/favicon-16x16.png create mode 100644 public/icon.svg create mode 100644 tests/icons.spec.ts diff --git a/nginx/mime.types b/nginx/mime.types index b7ff2b4..45bd9e0 100644 --- a/nginx/mime.types +++ b/nginx/mime.types @@ -29,6 +29,7 @@ types { application/java-archive jar war ear; application/json json; + application/manifest+json webmanifest; application/mac-binhex40 hqx; application/msword doc; application/pdf pdf; diff --git a/nginx/nginx.conf b/nginx/nginx.conf index 3522f47..4654753 100644 --- a/nginx/nginx.conf +++ b/nginx/nginx.conf @@ -118,13 +118,13 @@ http { location ~* \.(?:css|js|jpg|svg)$ { gzip_static on; - try_files $uri $uri/ $uri.html /iframe.html /index.html; + try_files $uri =404; } location ~* \.(?:json)$ { gzip_static on; - try_files $uri $uri/ $uri.html /iframe.html /index.html; + try_files $uri =404; } location ~* (?:#.*#|\.(?:bak|conf|dist|fla|in[ci]|log|orig|psd|sh|sql|sw[op])|~)$ { diff --git a/nginx/tests/mime.types b/nginx/tests/mime.types index b7ff2b4..45bd9e0 100644 --- a/nginx/tests/mime.types +++ b/nginx/tests/mime.types @@ -29,6 +29,7 @@ types { application/java-archive jar war ear; application/json json; + application/manifest+json webmanifest; application/mac-binhex40 hqx; application/msword doc; application/pdf pdf; diff --git a/nginx/tests/nginx.conf b/nginx/tests/nginx.conf index 1749013..9c8d8e7 100644 --- a/nginx/tests/nginx.conf +++ b/nginx/tests/nginx.conf @@ -113,14 +113,14 @@ http { location ~* \.(?:css|js|jpg|svg)$ { gzip_static on; - try_files $uri $uri/ $uri.html /iframe.html /index.html; + try_files $uri =404; expires max; } location ~* \.(?:json)$ { gzip_static on; - try_files $uri $uri/ $uri.html /iframe.html /index.html; + try_files $uri =404; expires max; } diff --git a/public/favicon-16x16.png b/public/favicon-16x16.png deleted file mode 100644 index f938b9bd412c1b1bcb257c1d5a3cd5216d60d9ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 616 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 delta 5001 zcmV;46L##sF2pA^000310{{>Z00000AONlc001@s001B$00000AOK(n0086y001yB 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 + + + + diff --git a/src/app/_layout/metadata.tsx b/src/app/_layout/metadata.tsx index c3f1316..2f9c008 100644 --- a/src/app/_layout/metadata.tsx +++ b/src/app/_layout/metadata.tsx @@ -75,15 +75,17 @@ const buildMetadata = (locale: Locale): Metadata => ({ creator: '@raferdev', }, icons: { - icon: '/favicon.ico', - shortcut: '/favicon-16x16.png', + icon: [ + { url: '/favicon.ico', sizes: '32x32' }, + { url: '/icon.svg', type: 'image/svg+xml' }, + ], apple: '/apple-touch-icon.png', }, robots: { index: true, follow: true, }, - manifest: `${meta.url}/site.webmanifest`, + manifest: '/manifest.webmanifest', }) const viewport: Viewport = { diff --git a/tests/icons.spec.ts b/tests/icons.spec.ts new file mode 100644 index 0000000..820470a --- /dev/null +++ b/tests/icons.spec.ts @@ -0,0 +1,76 @@ +import { expect, test } from '@playwright/test' + +const PAGES = ['/', '/pt'] + +test.describe('Icons and manifest', () => { + for (const path of PAGES) { + test(`Expected - Every icon and manifest linked from ${path} to load`, async ({ + page, + request, + }) => { + await page.goto(path) + + const links = await page + .locator( + 'head link[rel~="icon"], head link[rel="apple-touch-icon"], head link[rel="manifest"]' + ) + .evaluateAll((elements) => + elements.map((element) => ({ + rel: element.getAttribute('rel'), + type: element.getAttribute('type'), + href: (element as HTMLLinkElement).href, + })) + ) + + expect(links.map(({ rel }) => rel)).toEqual( + expect.arrayContaining(['icon', 'apple-touch-icon', 'manifest']) + ) + + for (const { rel, type, href } of links) { + const response = await request.get(href) + const contentType = response.headers()['content-type'] + expect(response.status(), `${rel} ${href}`).toBe(200) + if (rel !== 'manifest') expect(contentType, href).toMatch(/^image\//) + if (type) expect(contentType).toContain(type) + + if (rel === 'manifest') { + expect(contentType).toContain('application/manifest+json') + const manifest = await response.json() + for (const icon of manifest.icons) { + const iconResponse = await request.get(new URL(icon.src, href).href) + expect(iconResponse.status(), icon.src).toBe(200) + expect(iconResponse.headers()['content-type'], icon.src).toMatch( + /^image\// + ) + } + } + } + }) + } + + for (const asset of [ + '/missing.svg', + '/missing.js', + '/missing.css', + '/missing.json', + ]) { + test(`Expected - A missing ${asset} to 404 instead of serving a page`, async ({ + request, + }) => { + const response = await request.get(asset) + + expect(response.status()).toBe(404) + }) + } + + test('Expected - The SVG icon to follow the color scheme', async ({ + request, + }) => { + const response = await request.get('/icon.svg') + const svg = await response.text() + + expect(response.headers()['content-type']).toContain('image/svg+xml') + expect(svg.startsWith('