--- name: bm-favicon-creator description: Generate a complete favicon set (SVG, PNGs, multi-resolution .ico, Apple touch icon) from a Lucide icon (default) or another source SVG, then wire the favicon meta tags into the codebase. Use when the user wants to set up a favicon, replace a placeholder favicon, or brand the app icon. user_invocable: true --- # Favicon Creator Generate a complete, cross-device favicon set and wire it into the project's layout so it shows up correctly in every browser, tab, home screen, and pinned shortcut. The favicon is always rendered as a **square with tight rounded corners**, with the chosen icon **centered** inside on a solid background color. ## Prerequisites Requires `rsvg-convert` and ImageMagick's `magick` on the system. If either is missing, stop and ask the user to install them (`brew install librsvg imagemagick`). Do not attempt a workaround. ## Step 1 — Gather inputs from the user Ask each question in order. Wait for an answer before moving on. Do not assume defaults beyond what is specified. ### 1.1 Icon source Ask: "What icon do you want to use? You can: - Name a [Lucide](https://lucide.dev/icons) icon (e.g. `rocket`, `sparkles`, `book-open`) — this is the default - Name an icon from a different icon library (and which library) - Point me to an existing `.svg` file in the repo - Describe the icon you want and I'll generate the SVG" Resolve the answer: - **Default — Lucide icon by name.** If the user names an icon without naming a library, treat it as a Lucide icon. Fetch the **exact SVG from lucide.dev** for that icon and use it as the source. Lucide icons use `stroke="currentColor"` and `fill="none"`. **Always set `stroke-width="1.5"`** on the root `` element (override whatever value Lucide ships with). Do not redraw, simplify, or recreate the paths — use the exact paths from Lucide. - **Other icon library.** If the user names a different library (e.g. Heroicons, Phosphor, Tabler, Material Symbols), fetch the exact SVG from that library's official source. - **Existing SVG path.** Read the file and confirm the `viewBox` / intrinsic size. If there is no `viewBox`, derive one from `width`/`height`. - **Described icon.** Generate a minimal SVG matching the description. Keep it geometric and legible at 16×16. ### 1.2 Background color Ask: "What background color should the favicon square use? (hex, e.g. `#FCF8F2`)" This fills the rounded square and also becomes the page `theme-color` meta tag value. ### 1.3 Icon color Ask: "What color should the icon itself be? (hex, e.g. `#22D3EE`)" For Lucide / stroke-based icons, this becomes the `stroke` color. For filled icons, this becomes the `fill` color. Confirm both colors back to the user before generating. ## Step 2 — Locate the layout file Find where favicon `` tags should live. First, read `CLAUDE.md` and/or `AGENTS.md` if they exist — they may declare a convention for where head/meta tags go. If no convention is declared, check in this order: - Next.js (app router): `app/layout.tsx` / `app/layout.jsx` — favicons are typically handled via file conventions (`app/icon.*`, `app/apple-icon.*`) and/or a `metadata` export - Next.js (pages router): `pages/_document.tsx` / `pages/_app.tsx` - Rails: `app/views/layouts/application.html.erb` (or `.haml`, `.slim`) - Vite/React (Inertia, SPA): `index.html` or `app/views/layouts/application.html.erb` - SvelteKit: `src/app.html` - Astro: `src/layouts/*.astro` or individual page layouts - Plain HTML: `index.html` If you can't find the layout, grep for `rel="icon"` or `apple-touch-icon` across the repo. If still ambiguous, ask the user. ## Step 3 — Determine the public asset directory **Always look for a `public/` folder first.** Most modern frameworks use it, and the four canonical files (`favicon.ico`, `icon.png`, `icon.svg`, `apple-touch-icon.png`) must always land there if it exists. If the project also has another conventional asset location (e.g. Next.js app router's `app/` directory for file-convention icons), write there in addition — but `public/` is always required. If neither exists, ask the user where assets should go, then ensure a `public/` equivalent. ## Step 4 — Generate the SVG for browser tabs (rounded square with centered icon) Write `public/icon.svg` as a **square with tight rounded corners**, the background color filling the square, and the source icon centered inside. After writing it, also copy the same file to `public/favicon.svg` (some browsers and link-preview crawlers look for `favicon.svg` at the site root by name). Canvas: `80 × 80`. Corner radius: `16` (tight rounded corners — 20% of the canvas). The inner icon should occupy ~60% of the canvas (safe zone for iOS rounded corner masking). Round offsets to whole pixels. **Stroke-based icon (Lucide, etc.)** — keep `fill="none"` and apply the icon color via `stroke`. Always use `stroke-width="1.5"` for Lucide: ```svg ``` **Filled icon** — apply the icon color via `fill`: ```svg ``` **Centering math** — if the source viewBox is `W × H`, target inner size `T = 48` (60% of 80): - `N = T / max(W, H)` - `Xoff = (80 - W * N) / 2` - `Yoff = (80 - H * N) / 2` ## Step 5 — Generate the raster source SVG Write a second, scaled-up SVG to `/tmp/favicon-source.svg` — same composition, but at `512 × 512` with corner radius `102` (~20% of 512). This is the source for all PNGs and the ICO. ```svg ``` For Lucide, multiply `stroke-width` by `N` if needed so it renders proportionally at large sizes, OR keep `stroke-width="1.5"` outside the transform and apply `vector-effect="non-scaling-stroke"` on the paths. Simpler: keep the icon's stroke crisp by scaling its stroke-width with `N` (e.g. `stroke-width="${1.5 * N}"`). Inner-icon target: `T = 308` (~60% of 512). Same offset math as Step 4. ## Step 6 — Rasterize all sizes Run from the project root, with `$PUBLIC` set to the public directory (usually `public`): ```bash rsvg-convert -w 512 -h 512 /tmp/favicon-source.svg -o $PUBLIC/icon.png rsvg-convert -w 192 -h 192 /tmp/favicon-source.svg -o $PUBLIC/icon-192.png rsvg-convert -w 180 -h 180 /tmp/favicon-source.svg -o $PUBLIC/apple-touch-icon.png rsvg-convert -w 48 -h 48 /tmp/favicon-source.svg -o /tmp/fav-48.png rsvg-convert -w 32 -h 32 /tmp/favicon-source.svg -o /tmp/fav-32.png rsvg-convert -w 16 -h 16 /tmp/favicon-source.svg -o /tmp/fav-16.png magick /tmp/fav-16.png /tmp/fav-32.png /tmp/fav-48.png $PUBLIC/favicon.ico ``` This produces, in `public/`: - `favicon.ico` — multi-resolution 16/32/48 for Windows/legacy - `icon.svg` and `favicon.svg` — the rounded square mark for modern browser tabs - `icon.png` — 512×512 PNG - `icon-192.png` — 192×192 PNG for Android - `apple-touch-icon.png` — 180×180 PNG for iOS home screen ## Step 7 — Wire up the meta tags Edit the layout file. Inside ``, ensure these tags exist (add or replace — do not duplicate): ```html ``` Notes: - `theme-color` uses the background color from Step 1.2. - `favicon.ico` gets `sizes="32x32"` (not `any`) so modern browsers prefer the SVG. - If the project already has a `` or any `` / `apple-touch-icon`, replace them — don't stack duplicates. - **Next.js app router:** prefer the file convention. Drop the generated files at `app/icon.svg`, `app/icon.png`, `app/apple-icon.png`, `app/favicon.ico` (rename accordingly) and let Next emit the tags. Still write the same four files to `public/` as well. Only hand-write `` tags if the project already does. ## Step 8 — Report what changed Summarize for the user: - The icon source used (Lucide name, library, file path, or description) - The background and icon colors - Which files were written - Which head tags were added or replaced - A reminder to hard-refresh the browser (or close and reopen the tab) to bust the favicon cache ## Rules - Do not leave behind a half-generated set. If any step fails, stop and report. - Do not add a `manifest.json` unless the project already has one. - Do not commit the changes. This skill only generates assets and edits the layout. - Do not use emojis in file contents. - Keep the generated SVGs minimal — no decorative comments, no unused attributes. - Always preserve the exact Lucide path data when a Lucide icon is requested. Only the stroke width and color may change.