Goal
Complete a clear, readable, maintainable local favicon.svgfor the site, and configure the <link rel="icon"> that the browser will actually read.
This skill prioritizes documentation sites and batch sites in a monorepo. It uses Iconify as a source of design inspiration, but the final asset should be a standalone SVG file in the repository, avoiding runtime dependencies, broken external links, and blurry, cluttered icons in browser tabs.
Default design direction
Unless the user explicitly requests otherwise, design according to the following direction:
- Iconify-first: First look for semantic and compositional inspiration from the Iconify icon library, preferring references such as
lucide:*simple linear icons like these. - Lucide style: Transparent background, linear strokes, rounded caps, rounded joins, few paths, ample whitespace.
- No background block: By default, do not use a black circular base, dark square, gradient base, shadow base, or decorative disc. The user has already expressed that “displaying the icon directly looks better.”
- At most 2 colors: Usually 1 primary color is enough; the second color is only for emphasis, and do not make rainbows, gradients, or multicolor illustrations.
- Readable at small sizes: Work backward on complexity using the browser’s 16 px favicon as the standard. Details that cannot be seen clearly should be deleted or merged.
- No text preferred: Avoid letters, abbreviations, and long text. Text in a favicon usually turns into a blur.
Workflow
1. Discover sites and configuration
First confirm whether the user wants to handle a single site or batch sites. For a monorepo, prefer discovering via the repository structure:
rg --files | rg '(^|/)\.vitepress/config\.(ts|mts|js|mjs)$'
rg --files | rg '(^|/)package\.json$'
Read each site’s:
- VitePress/VuePress configuration file
- Site title, package name, README, or homepage
- Existing
public/favicon.svg - Existing
headconfiguration - User-specified site list and exclusions
If the user explicitly provides a site list, use the user’s list as the source of truth; if additional sites are found, list them as candidates first, and do not expand the scope without authorization.
2. Confirm the favicon placement path
Do not place the file at .vitepress/public/favicon.svgbased on experience. The default public asset directory for VitePress is usually under the site source directory public:
<vitepress-source-root>/public/favicon.svg
For example:
packages/utils/src/public/favicon.svg
packages/claude-notifier/src/docs/public/favicon.svg
packages/vitepress-preset-config/src/docs/public/favicon.svg
Only when the project already has configuration or documentation that clearly changes the public directory to .vitepress/public , use .vitepress/public/favicon.svg。
How to determine:
- Look at the root in the VitePress command, for example
vitepress build srcmeans the source directory issrc, and the public directory is usuallysrc/public。 - Check whether the existing build output will copy
public/favicon.svgto.vitepress/dist/favicon.svg。 - Check whether the config has a custom
srcDir、outDiror public-related configuration.
Pure Nitro v3 API project
A pure Nitro API has no HTML head to inject into. Place favicon.svg in the project root’s public/favicon.svg, then add the browser default /favicon.ico fallback:
import { defineHandler, redirect } from "nitro/h3";
export default defineHandler(() => redirect("/favicon.svg", 302));
The file should be server/routes/favicon.ico.get.ts; Nitro v3 uses redirect, and do not continue to use the deprecated sendRedirect. For this route in tests/favicon-route.test.ts , add describe/test, asserting 302 and Location: /favicon.svg, and check after building .output/public/favicon.svg。
3. Configure head
Explicitly add the favicon in the site config:
head: [["link", { rel: "icon", type: "image/svg+xml", href: "/favicon.svg" }]],
Follow the project's existing config style:
- If there is already
head, only append the missing favicon entries to avoid duplication. - If the config is generated via
setUserConfig()、defineConfig()or a custom wrapper, reuse the existing wrapper and do not change the architecture. - If a png/ico favicon already exists, prefer keeping it and adding an SVG unless the user requests migration; do not delete old assets without reason.
4. Choose icon semantics
Choose a different icon for each site, but keep a consistent style. Selection logic:
| Site semantics | Iconify/Lucide inspiration |
|---|---|
| Toolkit, general tools | lucide:wrench, lucide:settings, lucide:blocks |
| Deployment, Vercel, release | lucide:cloud-upload, lucide:rocket, lucide:upload-cloud |
| Notifications, reminders | lucide:bell, lucide:message-circle |
| Domain, network, site | lucide:globe, lucide:network |
| Config, presets, templates | lucide:sliders-horizontal, lucide:badge-check, lucide:settings |
| Testing, experiments, samples | lucide:flask-conical, lucide:test-tube |
| Docs, knowledge base | lucide:book-open, lucide:file-text |
Avoid duplicate icons within the same batch of sites, unless two sites truly have the same semantics.
5. Write the SVG
The final SVG should be a local file, with the recommended structure:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" fill="none">
<path
d="..."
stroke="#2563eb"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2.6"
/>
</svg>
Design constraints:
viewBoxUse0 0 32 32to make it easier to bolden and add whitespace for the favicon.stroke-widthUsually between2.4and3; it must still be clear at 16 px.- Remove small details that are too thin, too dense, or have too many intersections.
- Do not use
filter, complex masks, gradients, large background areas, or embedded images. - Do not use external links,
<script>, event attributes, or unnecessary metadata. - If borrowing paths from Iconify/Lucide, you must re-simplify, bolden, and adjust whitespace for the favicon size, rather than copying the complex icon as-is.
6. Batch implementation strategy
When processing in batches, first produce a mapping table, then modify the files:
站点 | public 路径 | config 路径 | 图标语义 | 主色
Implementation order:
- Find all target sites.
- Define a semantic icon and color for each site.
- Write the local
public/favicon.svg。 - Add
head。 - Build or do a minimal validation.
- Spot-check the build output and the online paths.
7. Verification
At minimum, do these checks:
rg -n "favicon.svg|rel: \"icon\"|rel=\"icon\"" <目标配置和构建产物>
For VitePress, prioritize verifying:
public/favicon.svgexists.- After building,
.vitepress/dist/favicon.svgexists. - After building,
index.htmlcontains<link rel="icon" type="image/svg+xml" href="/favicon.svg">。
For a pure Nitro v3 API, additionally verify that GET /favicon.ico returns 302 and Location points to /favicon.svg,GET /favicon.svg returns 200 and image/svg+xml。
If the site is already deployed, additionally verify:
curl -I https://example.com/favicon.svg
curl -s https://example.com/ | rg 'favicon|rel="icon"|image/svg\+xml'
If the user requests a visual display, use an available browser MCP or generate a local preview page to show the candidates. When displaying, focus on the three sizes 16 px, 24 px, and 32 px, not just the enlarged image.
Content to output to the user
When finished, explain with a brief checklist:
- Which sites were processed.
- The icon semantics used for each site.
- The favicon file paths and config paths.
- Which verifications were run.
- If it does not take effect online, it means the issue is that the deployment was not updated, caching, or a domain alias problem. Do not conflate "configured locally" with "effective online."
Common mistakes
- Putting
favicon.svginto.vitepress/public, but the project's actual source directory issrcordocs, causing the build output to have no favicon. - Wrapping every icon in a black circular background or dark square makes it look heavy in browser tabs.
- Directly copying a complex Iconify SVG makes it look like a tangle of lines at 16 px.
- Only writing the SVG file without configuring
head, relying on the browser to guess the path. - A pure Nitro project only adds
public/favicon.svg, without providing a/favicon.icofallback, so the browser still shows the default icon. - Continuing to use the deprecated
sendRedirectin Nitro v3; it should be changed to returnredirect(location, status)。 - Batch sites use the same icon, losing project semantics.
- Not verifying after deployment
/favicon.svg, causing it to be correct locally but still 404 online.