Help us improve
Share bugs, ideas, or general feedback.
From cutify-that-tab
Add a cute, hand-authored SVG favicon to a web app — the tiny icon that shows in the browser tab. Use when the user asks to add a favicon, browser tab icon, tab logo, page icon, site icon, or "cute thing in the tab," or says the current tab shows a blank/default square. Covers 3 techniques in increasing effort: (A) emoji-as-favicon via inline SVG data URI — zero assets; (B) gradient mascot blob — a soft blob shape with a face built from circles + a quadratic-curve smile; (C) 16×16 pixel-art sticker using SVG <rect> on an integer grid with shape-rendering="crispEdges" (bunny, bear, chick, mochi cat, ghost, frog, strawberry, cloud, dango, bee, ...). Includes the modern `<link rel="icon" type="image/svg+xml">` recipe (no more .ico files, no PNG fallbacks needed for modern browsers), tab-size verification, and which technique fits which vibe. Skip if the user wants a corporate/photographic logo — this skill is for hand-authored, cute, low-effort icons.
npx claudepluginhub wan-huiyan/cutify-that-tab --plugin cutify-that-tabHow this skill is triggered — by the user, by Claude, or both
Slash command
/cutify-that-tab:cutify-that-tabThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Put something cute in the browser tab. Three techniques, all SVG, all
examples/blob_brand_grad.svgexamples/blob_heart_eyes.svgexamples/blob_kissy.svgexamples/blob_mint.svgexamples/blob_sleepy.svgexamples/blob_smug.svgexamples/blob_starry.svgexamples/blob_surprise.svgexamples/blob_wide_eye.svgexamples/pixel_bear.svgexamples/pixel_bee.svgexamples/pixel_bunny.svgexamples/pixel_chick.svgexamples/pixel_cloud.svgexamples/pixel_dango.svgexamples/pixel_frog.svgexamples/pixel_ghost.svgexamples/pixel_ghost_silhouette.svgexamples/pixel_ghost_silhouette_dark.svgexamples/pixel_ghost_tile.svgProvides Playwright E2E testing patterns including Page Object Model, test organization, configuration, CI/CD integration, and flaky test strategies.
Share bugs, ideas, or general feedback.
Put something cute in the browser tab. Three techniques, all SVG, all hand-authorable in a single file. Pick whichever vibe — a smiling gradient blob, a 16×16 pixel-art mascot (bunny, ghost, bear, mochi cat, ...), or just an emoji. The tab is small real estate; spend ten minutes making it delightful.
User has a web app with a blank/default favicon (the empty square in the browser tab) and wants something cute in there. They're not looking for a corporate logo treatment — they want personality. Most online "favicon generators" produce a heavy 12-file pack (favicon.ico, apple-touch-icon variants, manifest.json, ...) for a problem that needs one SVG file.
Use this skill when any of:
Skip if the user wants a polished brand mark, photographic logo, or a multi-platform icon pack (apple-touch-icon, Android maskable, etc.) — those are different problems requiring different tooling.
Three techniques. Pick before authoring; switching mid-way is wasted work.
| # | Technique | Effort | When to pick it |
|---|---|---|---|
| A | Emoji favicon | 0 min — one <link> tag | User wants instant-cute, zero asset files, OK with rendering as the OS's emoji font (slightly different on Mac vs Windows vs Linux). Examples: 🦊 🦦 🍡 🌈 🪼 🐸 🌷 |
| B | Gradient mascot blob | 5-10 min | App has a brand gradient and the favicon should rhyme with it. Soft, friendly. ~12 SVG elements. |
| C | 16×16 pixel-art sticker | 10-15 min | Indie / "internal tool with personality" vibe. Web 1.0 charm. Renders pixel-perfect at native tab size. ~10 <rect> elements per icon. |
When in doubt, present the user options A-C with example renders before authoring (the AskUserQuestion preview is great for this). Don't pick for them on first ask — vibe matters.
No file. Inline the emoji as a <text> element inside a data URI on the
<link rel="icon"> tag:
<link rel="icon" href='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">🦊</text></svg>' />
y=".9em" aligns the emoji to its full height inside the viewBox.
font-size="90" fills the 100-unit viewBox with margin for descenders.
Use single quotes around href= so the SVG can use double quotes
internally. Otherwise escape every quote with " and lose your
mind.
Swap emojis later by changing one character.
Static file. Reuses the app's brand gradient via <linearGradient>. Eyes
are two <circle>s, smile is a quadratic <path>:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<defs>
<linearGradient id="bg" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" stop-color="#f59e0b"/>
<stop offset="35%" stop-color="#ef4444"/>
<stop offset="70%" stop-color="#ec4899"/>
<stop offset="100%" stop-color="#6366f1"/>
</linearGradient>
</defs>
<rect x="2" y="2" width="28" height="28" rx="8" fill="url(#bg)"/>
<circle cx="12" cy="14" r="2" fill="#fff"/>
<circle cx="20" cy="14" r="2" fill="#fff"/>
<path d="M12 21 Q16 24 20 21" stroke="#fff" stroke-width="1.8" fill="none" stroke-linecap="round"/>
</svg>
Key design parameters:
viewBox="0 0 32 32" — chosen because 32×32 gives integer coordinates
for cx/cy while still scaling cleanly to 16×16 tabs. Don't use 16×16
directly — sub-pixel coordinates compound rounding errors.rx="8" on the rect = 25% corner radius. Higher = more pill-shaped.
rx="14" = full circle.cy=14 (a bit above middle), smile at y=21 (lower third).
Don't center vertically — faces read cuter when the eyes are above
middle.Q control point: Q16 24 20 21 — the 24 is the y of the curve
pull. Larger = bigger smile. Cap around 26 before it looks demonic.Variants (use radial gradients, different palettes, sleepy ~~ eyes
instead of dots, blush ellipses) under examples/blob_*.svg.
Static file. SVG <rect> elements on an integer grid. The critical
attribute is shape-rendering="crispEdges" — without it, browsers
antialias the rectangles into a mushy mess at every zoom level.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">
<!-- ears -->
<rect x="3" y="3" width="2" height="2" fill="#fda4af"/>
<rect x="11" y="3" width="2" height="2" fill="#fda4af"/>
<!-- head -->
<rect x="3" y="5" width="10" height="9" fill="#fff"/>
<!-- eyes -->
<rect x="5" y="8" width="1" height="2" fill="#1f2937"/>
<rect x="10" y="8" width="1" height="2" fill="#1f2937"/>
<!-- blush -->
<rect x="4" y="10" width="1" height="1" fill="#fda4af"/>
<rect x="11" y="10" width="1" height="1" fill="#fda4af"/>
<!-- mouth -->
<rect x="7" y="10" width="1" height="1" fill="#1f2937"/>
<rect x="8" y="10" width="1" height="1" fill="#1f2937"/>
</svg>
Authoring approach: think of it as drawing on graph paper. Each
<rect> is a "pixel" at integer coords. A typical face = 10-15 rects.
Plan the grid before writing SVG:
. . . X . . . . . . X . . . ← ears (rows 3-4)
. . . X . . . . . . X . . .
. . . H H H H H H H H H H . ← head (rows 5-13)
. . . H . E . . . . E . H . ← eyes
. . . H B E . M M . E B H . ← mouth + blush
...
Where H=head fill, E=eye, M=mouth, B=blush, X=ear, .=blank.
Margin matters at tab size — leave ≥2px gap from the viewBox edge so browsers' tab-bar rounding/clipping doesn't shave the icon. Don't paint to the edge. See "Margin & tab-bar clipping" in Notes.
Sometimes you want the pixel art to feel like a "sticker" — high-contrast
mascot on a colored rounded square — rather than a free-floating shape on
the browser-tab background. Adds 1 extra <rect> (the tile) and a
clearer visual boundary.
The two-margin rule. A tiled-sticker favicon has two margins to size separately, and BOTH matter:
Canonical recipe (16×16 viewBox):
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">
<!-- 14×14 tile with 1-cell OUTER margin on all sides. rx=3 ≈ 21% of tile width. -->
<rect x="1" y="1" width="14" height="14" rx="3" fill="#121216"/>
<!-- Mascot drawn on top. Body width up to 12 (cols 2-13). -->
...
</svg>
Why this geometry:
Visible cells inside the 14×14 tile (rx=3), in viewBox coords:
| viewBox row (y) | Tile-internal row | Visible cols (viewBox) | Width |
|---|---|---|---|
| 1 | top (rounded) | 2-13 | 12 |
| 2 | row 1 | 1-14 | 14 (full tile) |
| 3-12 | body | 1-14 | 14 (full tile) |
| 13 | bottom-1 | 1-14 | 14 (full tile) |
| 14 | bottom (rounded) | 2-13 | 12 |
So features at viewBox y=1 and y=14 (the rounded tile rows) must stay within cols 2-13.
Sizing rule of thumb for tiled pixel art:
See examples/pixel_ghost_tile.svg for the worked example
(post-margin-fix): 14×14 inset tile, 12-wide
ghost body (cols 2-13), 3-row dome (4→8→12) at viewBox y=1/2/3,
3 symmetric bottom bumps × 2 wide at y=13, eyes at col 5+10, pink
cheeks at col 5-6+9-10.
The other end of the spectrum from C-variant 1. Drop the tile entirely and render the mascot as a single-color silhouette that floats freely against the tab. Eyes (and any other interior details) become cut-outs — gaps in the body shape that let the tab background show through. This is the GitHub octocat treatment, and it's the visual equivalent of "the mascot emerges from the tab."
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">
<!-- Body in solid color. Split into multiple <rect>s with gaps where -->
<!-- the eyes (or other cut-outs) should be — the gaps stay transparent. -->
<rect class="ghost" x="6" y="1" width="4" height="1"/> <!-- dome top -->
<rect class="ghost" x="2" y="3" width="12" height="3"/> <!-- top of body -->
<rect class="ghost" x="2" y="6" width="3" height="2"/> <!-- left of eyes -->
<rect class="ghost" x="6" y="6" width="4" height="2"/> <!-- between eyes -->
<rect class="ghost" x="11" y="6" width="3" height="2"/> <!-- right of eyes -->
<rect class="ghost" x="2" y="8" width="12" height="5"/> <!-- bottom of body -->
...
</svg>
Eye cut-out technique: instead of drawing the eye as a darker <rect>
on top of the body, split the body rect into multiple smaller rects
that flow around the eye position, leaving a gap. The gap = transparent
= the tab background shows through, same as the cat's eyes in
github.com's favicon.
This pattern only works for single-color mascots. If your design needs additional accent colors (e.g. pink cheek blush), those can sit on top of the body as separately-filled rects — they don't break the silhouette aesthetic if used sparingly (1-2 accent dots max).
When to pick C-variant 2 over C-variant 1 (tile background):
| Pick this | If… |
|---|---|
| C-variant 1 (tile background) | The favicon should feel like a "sticker" with a defined boundary. Brand color demands a colored background. Want maximum control over what surrounds the mascot. |
| C-variant 2 (silhouette + cut-outs) | The favicon should feel like the mascot emerges from the tab. Want it to integrate visually with the browser chrome (light or dark). User explicitly references icons like the GitHub octocat. |
Pairs naturally with the dark-mode CSS-in-SVG pattern below — a
charcoal silhouette in light mode + a light-grey silhouette in dark
mode reads on both tab themes. See examples/pixel_ghost_silhouette.svg.
<link media> (Chrome compatible)For favicons that need to read on both light and dark browser-tab
backgrounds, use two SVG files and two <link rel="icon"> tags,
with a media="(prefers-color-scheme: dark)" attribute on the dark
variant:
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
<link rel="icon" type="image/svg+xml" href="/static/favicon-dark.svg"
media="(prefers-color-scheme: dark)" />
Each SVG hardcodes its body fill (no <style> block, no @media):
<!-- favicon.svg (light mode default) -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">
<rect x="2" y="3" width="12" height="3" fill="#24292f"/>
...
<!-- accent rects can have a separate hardcoded fill -->
<rect x="5" y="9" width="2" height="1" fill="#f4a8b8"/>
</svg>
<!-- favicon-dark.svg (dark mode) — same geometry, light fill -->
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" shape-rendering="crispEdges">
<rect x="2" y="3" width="12" height="3" fill="#e5e7eb"/>
...
<rect x="5" y="9" width="2" height="1" fill="#f4a8b8"/>
</svg>
Why two files, not one file + CSS-in-SVG (correction of v1.3):
The v1.3 recipe used a single SVG with an embedded <style> block
containing @media (prefers-color-scheme: dark). This does not work
in Chrome for favicons. Chrome treats favicon SVGs as image-type
resources (loaded via the favicon pipeline, not the document
pipeline) and does NOT evaluate CSS media queries inside them.
@media inside favicons.Conversely, media on <link rel="icon"> does work in Chrome —
since Chrome 92 (2021). It also works in Firefox and Safari. So the
two-file <link media> approach is the only path that works in all
three modern engines for favicons specifically.
(The v1.3 claim that "Chrome picks the first <link rel="icon">
regardless of the media attribute" was wrong — it was based on
outdated reports from pre-Chrome-92. Confirmed empirically in
field testing 2026-05-27: user reported the v1.3 single-file recipe
stayed charcoal in Chrome dark mode; switching to two files fixed it
immediately.)
Browser support summary for favicon dark-mode techniques:
| Approach | Chrome 92+ | Firefox | Safari |
|---|---|---|---|
Two files + <link media> | ✅ Works | ✅ Works | ✅ Works |
One file + CSS-in-SVG @media | ❌ Ignored | ✅ Works | ✅ Works |
Page-level JS swap of <link href> based on matchMedia | ✅ Works | ✅ Works | ✅ Works |
The two-file approach is the simplest cross-browser path. JS swap is the most flexible (e.g. live-toggle without page reload) but adds a script dependency.
Color guidance:
#24292f
(GitHub), #1f2937 (Tailwind gray-800), or your app's primary dark.#e5e7eb (Tailwind
gray-200) is a soft choice; #ffffff is max contrast but glaring at
favicon size; warm cream #fce7d4 works if the mascot needs a
friendlier feel.Accents (cheeks, freckles, hat colors) can stay hardcoded — they read on both light and dark body fills as long as they sit in the medium-saturation range (avoid pure-white/pure-black accents).
Caveat — file-level previews can't show both modes simultaneously.
The <img src="favicon.svg"> tag inherits the system color scheme,
so a preview HTML page on a system in light mode will render the
light-mode variant in every <img>, regardless of the page's own
background. To preview dark-mode rendering without toggling the system
theme: inline the SVG twice into the preview HTML with hardcoded fills
(one charcoal, one light grey) so each pane shows its variant
correctly. See examples/pixel_ghost_silhouette.svg and the
/tmp/favicon_preview.html pattern used in field testing.
Save the SVG to your project's static dir (e.g. static/favicon.svg) and
add a <link> to the page <head>:
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
That's it. You don't need:
.ico fallback (every browser shipped since 2020 supports SVG
favicons — Chrome 80+, Firefox 41+, Safari 12+, Edge 80+).16x16, 32x32, 64x64, ...). SVG is resolution-
independent; the browser scales the same file.manifest.json (PWA territory, also different problem).For Option A (emoji), put the data URI directly in href=. No file
needed; the <link> tag itself is the asset.
This is the validation step people skip — and the one where pixel-art choices break. Always test the favicon at actual tab size (~14-16px wide), not just the enlarged SVG.
Quick verification:
shape-rendering="crispEdges" for pixel art?Iterate-with-preview loop (recommended during authoring, not just at the end). For pixel art especially, the design choices that matter (body width, bump count, eye spacing) only become obvious at real tab size. Don't author end-to-end then verify — interleave.
Write a tiny /tmp/favicon_preview.html that renders the SVG at 16, 32,
64, 128 px stacked vertically:
<!DOCTYPE html><html><body style="background:#0a0a0d;color:#aaa;font:13px system-ui;padding:40px">
<div><img src="file:///abs/path/to/favicon.svg" width="16" height="16" style="image-rendering:pixelated"> 16×16 (real tab size)</div>
<div><img src="file:///abs/path/to/favicon.svg" width="32" height="32" style="image-rendering:pixelated"> 32×32</div>
<div><img src="file:///abs/path/to/favicon.svg" width="64" height="64" style="image-rendering:pixelated"> 64×64</div>
<div><img src="file:///abs/path/to/favicon.svg" width="128" height="128" style="image-rendering:pixelated"> 128×128 (sketch scale)</div>
</body></html>
Then: open /tmp/favicon_preview.html. Edit the SVG → it's already
re-rendered on the next reload. image-rendering:pixelated matches how
browsers actually render pixel art at the favicon's true 16×16.
This was used in a field-testing session — 5 iterations (8-wide → 10 → 12 → 16 → 14) converged with the user in ~10 minutes. Each iteration: write the SVG, refresh the preview, ask "ship / tweak / different design" via AskUserQuestion.
Caveat — file preview does NOT catch tab-edge blending. The
/tmp/favicon_preview.html mock above renders the favicon against a
flat dark body { background:#0a0a0d }. That hides one class of bug:
when the tile sits flush against the viewBox edge and there's no outer
margin (see C-variant), the tile blends into the browser tab's own
rounded chrome — visible only against a real browser-tab background,
not against a flat dark <body>. After ship, always verify with one of:
<body> mock — Chrome's light theme tab is roughly #f0eee9,
not #0a0a0d).Caught in field testing 2026-05-27: the v1.1-shipped flush 14-wide tile looked clean in the file preview but blended visibly with Chrome's tab edge. Live screenshot prompted the v1.2 inset-tile fix.
Browser-tab mock for previews (when the user can't view a deployed URL yet — useful for sharing options):
<div style="display:flex; align-items:flex-end; padding:8px; background:#1c1c22; border-radius:8px 8px 0 0;">
<div style="display:flex; align-items:center; gap:8px; padding:8px 14px;
background:#0f0f12; border-radius:6px 6px 0 0; color:#d4d4d8; font-size:11.5px;">
<span style="width:14px; height:14px; display:inline-flex;">
<!-- your favicon SVG goes here, sized 14×14 -->
</span>
Your App Name
</div>
</div>
This shows the favicon at real tab size next to a tab label, in a tab-strip context. Far more informative than just looking at the SVG file.
Diagnose: silhouette disappeared into the tab background (v1.4.0).
A separate failure mode from tab-edge blending (v1.2) and dark-mode
mismatch (v1.3): the mascot has high contrast with the page <body>
but disappears into the tab strip color, which is often a light grey
or warm-grey distinct from pure white.
Chrome on macOS uses roughly:
#f0eee9 (warm off-white) or #ffffff#c2c5be to #d6d6d3 (grey-sage to warm grey)#202124#2f3033A pure-white mascot body (#ffffff) reads great on #0a0a0d mock
backgrounds and on dark-mode active tabs but completely vanishes
on a light-mode inactive tab — only the colored accents (ears, blush)
remain visible, plus the eye dots floating in apparent nothingness.
The prefers-color-scheme recipe in the Dark-mode section does NOT
fix this: Chrome is technically in light mode while displaying the
grey-sage inactive tab, so the CSS-in-SVG light-mode variant fires —
which is the white body that disappears.
When the user reports "I can see the ears and eyes but no body" or shares a screenshot with most of the mascot invisible:
/tmp/favicon_preview.html mock as
the background: of the tab-strip wrapper so subsequent variants
render against the real failure environment, not against an
idealized off-white.| # | Path | What changes | Trade-off |
|---|---|---|---|
| R1 | Tint the body color | fill="#fff" → mid/saturated color (#f9a8d4 pink-300, #fde68a cream, #bbf7d0 mint) | Recommended for cute mascots. Keeps the floating-silhouette charm. Critical: the tint must be ≥2-3 shades darker than the lightest tab background it has to survive — #fbcfe8 (pink-50) and similar *-50 tier pastels are TOO PALE and vanish on cream/white active tabs. Tailwind reference: aim for *-200 to *-300 for soft-cute, *-400 to *-500 for kawaii-sticker. ~30 sec edit. |
| R2 | Add a colored tile background | Wrap with <rect x=0 y=0 w=16 h=16 rx=3 fill="#fbcfe8"/> first; keep body white | Best contrast in any tab color. Loses the "floating" charm — becomes a sticker icon. Same authoring as C-variant 1 with the two-margin rule (v1.2). |
| R3 | Add a 1px dark pixel outline | Add charcoal <rect>s tracing the body silhouette | "Lineart" feel. Keeps body white. Doubles the rect count. Survives light tabs because the outline reads even when the fill blends. |
| R4 | Add prefers-color-scheme CSS-in-SVG | Use the v1.3 dark-mode pattern with a charcoal body in light mode + light body in dark mode | Only fixes the dark-mode case, NOT the grey-light-tab case (see above). Combine with R1 or R3, don't substitute. |
When to pick which:
*-200 to *-300 tint (not *-50 — see contrast rule above).R1 contrast threshold (v1.6.0). "Tint the body" only works if the
tint is meaningfully darker than the tab background. A common
first-iteration failure: pick a *-50 Tailwind pastel (e.g. #fbcfe8
pink-50) because it looks "cute" in the file preview against a dark
#0a0a0d body — then ship and discover it vanishes on Chrome's cream
active tab (~#f0f2e8) because pink-50 and cream are within ~5%
luminance of each other.
Working rule: pick a tint whose lightness is ≥30 points (Tailwind
luminance) below the brightest tab background you need to survive.
For Chrome's brightest tab (~#f0f2e8, lightness ~95%), that puts the
body color around lightness ~65% — i.e. Tailwind *-300 territory
(#f9a8d4 pink-300, #fde047 yellow-300, #86efac green-300, etc.).
For accents (ears, blush, mouth) that need to differentiate from the
body: bump 2 shades further (*-500 against a *-300 body).
Field testing walked this two-iteration loop:
#fbcfe8 pink-50 head with #fda4af rose-300 ears.
Reads on grey-sage inactive tab but vanishes on cream active tab.#f9a8d4 pink-300 head, #ec4899 pink-500 ears,
#db2777 pink-600 blushes. Reads on cream + grey + dark.The lesson: if the user reports "still can't see it" after R1, the fix is rarely "switch to R2" — it's "go 2 shades darker on R1." Don't abandon R1 prematurely.
Preview the recolor options side-by-side. Build a single HTML preview with the tab-strip mock at the user's real tab background color, render all four R-paths inline (one tab per variant). Each variant: tab-size on the left (14×14), 56×56 enlarged on the right with hex labels. User can pick by eye in <1 min instead of ship → screenshot → "hmm" → re-ship loop.
This was a real field-testing experience: white-headed mochi-cat
shipped, user reported "the cat disappears into the tab" with a
screenshot of grey-sage inactive tab (#c2c5be). Sampled background,
rendered 4 variants (R1 pink head, R1 cream head, R2 pink tile, R3
outlined), user picked R1 pink. One-line fill="#fff" → fill="#fbcfe8"
edit shipped the fix.
After wiring up:
curl /static/favicon.svg (or whatever path) returns 200 + the SVG<link rel="icon" type="image/svg+xml" href="..."If the icon looks crisp on retina but blurry at 1× and you're using
pixel art: you forgot shape-rendering="crispEdges". Add it; refresh.
User says: "the browser tab is empty, can we put a cute logo on it?"
preview field or a tab-strip mock HTML file).static/favicon.svg with the 10-rect pixel art.<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
to the page template.Total: ~10 minutes including option preview + render verification.
There are two regimes — pick the one that matches your icon:
1. Transparent-background pixel art (the standard examples — mochi- cat, dango, frog, the simpler pixel_ghost). The shape floats on the browser-tab background, and browser tab-bars crop the favicon slightly — usually 1-2px of edge gets shaved by the tab's rounded corners or hover overlay.
2. Tiled-background "sticker" pixel art (see Technique C-variant and
examples/pixel_ghost_tile.svg). Here there are two margins — see
the "two-margin rule" in Technique C-variant. Both regimes apply:
the browser tab still crops something, but what it crops is now the
tile, not the mascot.
(x=1, y=1, w=14, h=14, rx=3) inside a 16×16 viewBox. Skip this
margin and the tile blends with the tab chrome at live render.examples/pixel_ghost_tile.svg for the corrected
recipe..ico alongside:
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
<link rel="icon" type="image/png" href="/static/favicon.png" sizes="32x32">
But — most internal tools, modern SaaS, and dev tooling drop IE11
support entirely. Don't add a fallback unless you have concrete users
on those browsers.<text> inside SVG favicons uses the system font, not your
webfonts. Webfonts load after the favicon does. Pick a font-family
that resolves immediately: -apple-system, system-ui, sans-serif.width= and height= on the SVG root are optional when viewBox
is set — the browser scales the icon to the tab's native size (14-32px
depending on browser/OS/zoom). Don't hardcode.<link rel="icon" href="/favicon-light.svg" media="(prefers-color-scheme: light)">
<link rel="icon" href="/favicon-dark.svg" media="(prefers-color-scheme: dark)">
But this is overkill for cute mascot work — pick colors that read on
both backgrounds (white/cream fills work for both).blob_mint.svg's generic happy face with a wink (kept mint gradient).
Added 5 new emotion blobs: blob_heart_eyes.svg (in love),
blob_smug.svg (smirk), blob_surprise.svg (gasping with O mouth),
blob_starry.svg (amazed with 4-point star eyes), blob_kissy.svg
(wink + puckered lips). Skill now ships 9 gradient-blob variants
covering 9 distinct emotions. Useful for picker UIs where the user
selects mood/personality before color palette.shape-rendering="crispEdges", ~10-15 <rect> elements.*-50 tier Tailwind pastels (e.g. #fbcfe8 pink-50) sit
within ~5% luminance of Chrome's cream active tab (~#f0f2e8) and
vanish on it, even though they read fine on grey-sage inactive tabs
and on dark mocks. New explicit threshold: body tint should be ≥30
lightness points below the brightest tab background it has to
survive (Tailwind *-300 for cream/white active tabs). Added the
"if user still can't see it after R1, go 2 shades darker, don't
abandon R1 for R2" heuristic. Source: field testing iter 1 (pink-50,
failed) → iter 2 (pink-300, shipped).@media (prefers-color-scheme: dark) for favicons (it treats favicon SVGs
as image-type resources, which don't see system color-scheme inside).
Firefox and Safari do. The v1.3 claim that "Chrome ignores media
on <link rel="icon">" was based on outdated reports — Chrome HAS
supported it since Chrome 92 (2021). New canonical recipe: two
hardcoded-fill SVG files + two <link rel="icon"> tags, the dark
one with media="(prefers-color-scheme: dark)". Added a browser-
support matrix table. Source: field testing (user reported the v1.3
recipe stayed charcoal in Chrome dark mode; switching to two files
fixed it immediately).#c2c5be grey-sage). The dark-mode prefers-color-scheme
recipe does NOT fix this because Chrome is technically in light mode.
New decision tree (R1 tint body / R2 colored tile / R3 dark outline
/ R4 stack with prefers-color-scheme) with when-to-pick guidance.
Sample-the-tab-background workflow + side-by-side preview pattern
for all 4 variants in one HTML mock. Source: field testing follow-up.@media (prefers-color-scheme: dark) inside SVG works in Chrome /
Firefox / Safari, sidesteps Chrome's broken <link media> honoring.
Added file-preview caveat: <img src=*.svg> inherits system theme,
so dark-mode rendering can't be previewed from a light-mode system
without inlining the SVG with hardcoded fills. New example:
pixel_ghost_silhouette.svg. Resolved "Dark-mode-aware favicons"
open extension. Source: field testing.<body> miss the
tab-edge blending failure mode; live-browser verification is
mandatory for tiled stickers. Updated examples/pixel_ghost_tile.svg
to the post-fix design. Source: field testing.pixel_ghost_tile.svg. Superseded by v1.2 for the sizing rule.
Source: field testing.The examples/ folder ships starter SVGs you can copy and edit:
examples/blob_brand_grad.svg — Option B, sunset gradient + happy faceexamples/blob_mint.svg — Option B, mint gradient + winkexamples/blob_wide_eye.svg — Option B, anime/excited (big eyes + blushes)examples/blob_sleepy.svg — Option B, sleepy closed-eye arcsexamples/blob_heart_eyes.svg — Option B, in-love (heart eyes + open smile)examples/blob_smug.svg — Option B, smug (half-lidded eyes + smirk)examples/blob_surprise.svg — Option B, gasping (round eyes + O mouth)examples/blob_starry.svg — Option B, amazed (star eyes + big grin)examples/blob_kissy.svg — Option B, kissy (wink + puckered lips + blush)examples/pixel_mochi_cat.svg — Option C, the canonical mochi-catexamples/pixel_bunny.svg — Option C, peach bunny with pink earsexamples/pixel_bear.svg — Option C, brown bear with cream snoutexamples/pixel_chick.svg — Option C, yellow chick with orange beak + feetexamples/pixel_bee.svg — Option C, striped bee with sky-blue wingsexamples/pixel_strawberry.svg — Option C, red strawberry with seeds + faceexamples/pixel_cloud.svg — Option C, sky-blue cloud with pink blushexamples/pixel_ghost.svg — Option C, friendly ghost (transparent bg)examples/pixel_ghost_tile.svg — Option C-variant 1, the same ghost
on a dark rounded tile (14×14 inset, 12-wide body)examples/pixel_ghost_silhouette.svg — Option C-variant 2, single-color
GitHub-octocat-style silhouette with cut-out eyes + pink cheek accent
prefers-color-scheme dark-mode variantexamples/pixel_dango.svg — Option C, dango skewerexamples/pixel_frog.svg — Option C, frog faceCopy + rename + edit the colors and grid positions. They're 13-20 lines each.
frontend-design — broader frontend / UI design guidanceclaude-design-handoff-bundle — when the favicon comes from a Claude
Design handoff (different problem: implementing a designer's specific
mark, not authoring a cute one from scratch)<text> and font-family timing