1---
2/**
3 * The complete `<link rel="icon">` set, shared by every layout so the tags
4 * can't drift apart. All of these files are emitted by
5 * `scripts/generate-brand-assets.ts`.
6 *
7 * `favicon.svg` carries both themes in one file and scales to any surface;
8 * modern browsers pick it and ignore the rest. The PNGs are per-theme
9 * fallbacks selected with `media`, since a media query can't survive
10 * rasterization. 512 is listed so hi-DPI surfaces (retina bookmark bars,
11 * install prompts, Android home screens) have a large source rather than an
12 * upscaled 32. `apple-touch-icon` is opaque and light-only — iOS web clips
13 * have no dark variant and ignore `media`.
14 */
15
16interface Props {
17 /**
18 * Emit the SVG link. Starlight already injects one from its own `favicon`
19 * config, so the docs layout passes `false` to avoid a duplicate tag.
20 */
21 svg?: boolean;
22}
23
24const { svg = true } = Astro.props;
25
26/** [size, light href, dark href] — each expands into a media-switched pair. */
27const RASTERS = [
28 [16, "/favicon-16.png", "/favicon-16-dark.png"],
29 [32, "/favicon-32.png", "/favicon-32-dark.png"],
30 [512, "/icon-512.png", "/icon-512-dark.png"],
31] as const;
32---
33
34{svg && <link rel="icon" type="image/svg+xml" href="/favicon.svg" />}
35{
36 RASTERS.map(([size, light, dark]) => (
37 <>
38 <link
39 rel="icon"
40 type="image/png"
41 sizes={`${size}x${size}`}
42 href={light}
43 media="(prefers-color-scheme: light)"
44 />
45 <link
46 rel="icon"
47 type="image/png"
48 sizes={`${size}x${size}`}
49 href={dark}
50 media="(prefers-color-scheme: dark)"
51 />
52 </>
53 ))
54}
55<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png" />