Improvements to Icons for the Website service (#21)
Reviewed-on: rock-n-code/loud-amsterdam#21 Co-authored-by: Javier Cicchelli <javier@rock-n-code.com> Co-committed-by: Javier Cicchelli <javier@rock-n-code.com>
@@ -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 |
|
||||
|
||||
|
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 700 B |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 3.9 KiB |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 192 192"><path fill="#e08524" d="M75.3 73.4H18.4l45.3 34.3L48.3 163l46.1-32.3 48.2 34.6-16.9-58.3 44.9-33.6H115l-20.5-55-19.2 55z"/><path d="m96.7 18.8 18.2 8.2 16.5 44.3h-15.1L96.7 18.8zm-47 146 18.7 9.9 42.6-29.9-16.5-11.4-44.8 31.4zm79.1-56.8 17.4 9.4 18.6 60.1-19.7-11.3-16.3-58.2z"/><path d="m173.1 74.3 17.8 9.2-44.7 34-17.4-9.4 44.3-33.8z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 192 192"><style>.accent{fill:#000}@media (prefers-color-scheme:dark){.accent{fill:#f3ecf5}}</style><path fill="#e08524" d="M75.3 73.4H18.4l45.3 34.3L48.3 163l46.1-32.3 48.2 34.6-16.9-58.3 44.9-33.6H115l-20.5-55-19.2 55z"/><path class="accent" d="m96.7 18.8 18.2 8.2 16.5 44.3h-15.1L96.7 18.8zm-47 146 18.7 9.9 42.6-29.9-16.5-11.4-44.8 31.4zm79.1-56.8 17.4 9.4 18.6 60.1-19.7-11.3-16.3-58.2z"/><path class="accent" d="m173.1 74.3 17.8 9.2-44.7 34-17.4-9.4 44.3-33.8z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 549 B |
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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"))
|
||||
|
||||