diff --git a/Services/Website/README.md b/Services/Website/README.md
index 5f85d3a..7e410c7 100644
--- a/Services/Website/README.md
+++ b/Services/Website/README.md
@@ -213,6 +213,27 @@ Preview the optimized output locally — requires only Docker and writes to the
make ast-minify
```
+### Icons
+All icons are renditions of the star mark in `icon.svg`, which is the canonical source — there is no external design file to regenerate from.
+
+| File | Size | Used by | Dark mode |
+| --- | --- | --- | --- |
+| `icon.svg` | vector | Tab icon in modern browsers (preferred over the ICO) | Adapts: an embedded `prefers-color-scheme` style flips the accent paths from `#000` to `#f3ecf5`. |
+| `favicon.ico` | 32×32 | Tab icon in browsers without SVG favicon support (Safari) | Theme-neutral **by design**: it is the orange star *without* the accent paths, so one raster reads on both themes. Keep it accent-free when regenerating. |
+| `icon-192.png`, `icon-512.png` | 192/512 | `site.webmanifest` install icons and splash screens | None (no platform mechanism); full artwork, light rendering, on a transparent background. |
+| `apple-touch-icon.png` | 180×180 | iOS home-screen bookmarks | None (fetched once, outside any page context); full artwork, deliberately opaque — iOS fills transparent regions with black. |
+
+The landing page pairs the icons with two `theme-color` metas: `#0c0710` (media-qualified for dark, listed first — the first matching entry wins) and `#fafafa` as the fallback.
+
+The raster icons are committed binaries, regenerated from `icon.svg` on demand via a throwaway container (no local toolchain needed) — e.g. for the 512px rendition:
+```sh
+docker run --rm -v "$PWD/Resources/Static:/work" alpine sh -c '
+ apk add --no-cache imagemagick librsvg oxipng &&
+ magick -background none -density 256 /work/icon.svg -depth 8 PNG32:/work/icon-512.png &&
+ oxipng --opt max --strip safe /work/icon-512.png'
+```
+(`-density` scales the 192px viewBox: `96 × target ÷ 192`. For `favicon.ico`, rasterize a star-only copy of the SVG at 32px and pack it with `icotool -c --raw`.)
+
### Required variables
The Makefile and Compose files read these from a `.env` file (or the environment). Provide your own values — do **not** commit secrets.
| Variable | Used for |
diff --git a/Services/Website/Resources/Static/favicon.ico b/Services/Website/Resources/Static/favicon.ico
index be74abd..2b5bcda 100644
Binary files a/Services/Website/Resources/Static/favicon.ico and b/Services/Website/Resources/Static/favicon.ico differ
diff --git a/Services/Website/Resources/Static/icon-192.png b/Services/Website/Resources/Static/icon-192.png
new file mode 100644
index 0000000..d94836c
Binary files /dev/null and b/Services/Website/Resources/Static/icon-192.png differ
diff --git a/Services/Website/Resources/Static/icon-512.png b/Services/Website/Resources/Static/icon-512.png
new file mode 100644
index 0000000..6412cb0
Binary files /dev/null and b/Services/Website/Resources/Static/icon-512.png differ
diff --git a/Services/Website/Resources/Static/icon.png b/Services/Website/Resources/Static/icon.png
deleted file mode 100644
index 8a42581..0000000
Binary files a/Services/Website/Resources/Static/icon.png and /dev/null differ
diff --git a/Services/Website/Resources/Static/icon.svg b/Services/Website/Resources/Static/icon.svg
index f232922..68e7d38 100644
--- a/Services/Website/Resources/Static/icon.svg
+++ b/Services/Website/Resources/Static/icon.svg
@@ -1 +1 @@
-
+
diff --git a/Services/Website/Resources/Static/site.webmanifest b/Services/Website/Resources/Static/site.webmanifest
index 222ae16..e5e517f 100644
--- a/Services/Website/Resources/Static/site.webmanifest
+++ b/Services/Website/Resources/Static/site.webmanifest
@@ -2,9 +2,13 @@
"short_name": "",
"name": "",
"icons": [{
- "src": "icon.png",
+ "src": "icon-192.png",
"type": "image/png",
"sizes": "192x192"
+ }, {
+ "src": "icon-512.png",
+ "type": "image/png",
+ "sizes": "512x512"
}],
"start_url": "/?utm_source=homescreen",
"background_color": "#fafafa",
diff --git a/Services/Website/Sources/Library/Internal/Enumerations/StaticFile.swift b/Services/Website/Sources/Library/Internal/Enumerations/StaticFile.swift
index aed99f7..69afb22 100644
--- a/Services/Website/Sources/Library/Internal/Enumerations/StaticFile.swift
+++ b/Services/Website/Sources/Library/Internal/Enumerations/StaticFile.swift
@@ -10,8 +10,12 @@ enum StaticFile: CaseIterable, Sendable {
case error
/// The `favicon.ico` icon.
case favicon
- /// The `icon.png` and `icon.svg` icons.
+ /// The `icon.svg` icon.
case icon
+ /// The `icon-192.png` icon for the web manifest.
+ case icon192
+ /// The `icon-512.png` icon for the web manifest.
+ case icon512
/// The `css/index.css` stylesheet and `js/index.js` script for the landing page.
case index
/// The `robots.txt` crawler directives.
@@ -57,12 +61,14 @@ extension StaticFile {
/// The file extensions the file is available with.
var fileExtensions: [Extension] {
switch self {
- case .appleTouchIcon: [.png]
+ case .appleTouchIcon,
+ .icon192,
+ .icon512: [.png]
case .error,
.index,
.shared: [.css, .js]
case .favicon: [.ico]
- case .icon: [.png, .svg]
+ case .icon: [.svg]
case .robots: [.txt]
case .site: [.webmanifest]
case .sitemap: [.xml]
@@ -76,6 +82,8 @@ extension StaticFile {
case .error: "error"
case .favicon: "favicon"
case .icon: "icon"
+ case .icon192: "icon-192"
+ case .icon512: "icon-512"
case .index: "index"
case .robots: "robots"
case .shared: "shared"
diff --git a/Services/Website/Sources/Library/Internal/Pages/IndexPage.swift b/Services/Website/Sources/Library/Internal/Pages/IndexPage.swift
index 7f8bf19..6a9b6e2 100644
--- a/Services/Website/Sources/Library/Internal/Pages/IndexPage.swift
+++ b/Services/Website/Sources/Library/Internal/Pages/IndexPage.swift
@@ -74,6 +74,14 @@ struct IndexPage: HTMLDocument, Sendable {
.rel("manifest"),
.href(StaticFile.site.urlPath(for: .webmanifest))
)
+ meta(
+ .name("theme-color"),
+ .content("#0c0710"),
+ .custom(
+ name: "media",
+ value: "(prefers-color-scheme: dark)"
+ )
+ )
meta(
.name("theme-color"),
.content("#fafafa")
diff --git a/Services/Website/Tests/Library/Cases/Internal/Enumerations/StaticFileTests.swift b/Services/Website/Tests/Library/Cases/Internal/Enumerations/StaticFileTests.swift
index 4cb579f..24bde11 100644
--- a/Services/Website/Tests/Library/Cases/Internal/Enumerations/StaticFileTests.swift
+++ b/Services/Website/Tests/Library/Cases/Internal/Enumerations/StaticFileTests.swift
@@ -114,7 +114,7 @@ struct StaticFileTests {
@Test
func `all cases`() {
- #expect(File.allCases.count == 9)
+ #expect(File.allCases.count == 11)
}
}
@@ -159,7 +159,9 @@ private extension StaticFileTests {
[.png],
[.css, .js],
[.ico],
- [.png, .svg],
+ [.svg],
+ [.png],
+ [.png],
[.css, .js],
[.txt],
[.css, .js],
@@ -171,6 +173,8 @@ private extension StaticFileTests {
"error",
"favicon",
"icon",
+ "icon-192",
+ "icon-512",
"index",
"robots",
"shared",
@@ -181,7 +185,9 @@ private extension StaticFileTests {
["apple-touch-icon.png"],
["css/error.css", "js/error.js"],
["favicon.ico"],
- ["icon.png", "icon.svg"],
+ ["icon.svg"],
+ ["icon-192.png"],
+ ["icon-512.png"],
["css/index.css", "js/index.js"],
["robots.txt"],
["css/shared.css", "js/shared.js"],
diff --git a/Services/Website/Tests/Library/Cases/Internal/Pages/IndexPageTests.swift b/Services/Website/Tests/Library/Cases/Internal/Pages/IndexPageTests.swift
index a754d77..fc308d2 100644
--- a/Services/Website/Tests/Library/Cases/Internal/Pages/IndexPageTests.swift
+++ b/Services/Website/Tests/Library/Cases/Internal/Pages/IndexPageTests.swift
@@ -23,6 +23,7 @@ struct IndexPageTests {
#expect(html.contains("/icon.svg"))
#expect(html.contains("/apple-touch-icon.png"))
#expect(html.contains("/site.webmanifest"))
+ #expect(html.contains(#"media="(prefers-color-scheme: dark)""#))
#expect(html.contains("Hello world!"))
#expect(html.contains("/js/shared.js"))
#expect(html.contains("/js/index.js"))