dotfiles/.claude/skills/brand-assets/SKILL.md
Create and export project logo/banner SVG assets to light/dark PNG variants. Covers the full lifecycle from initial design exploration through SVG creation to themed PNG export. Use when creating logos, banners, or regenerating PNG exports from SVG source files.
npx skillsauth add kdeldycke/dotfiles brand-assetsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Create, maintain, and export project logo and banner assets as SVGs with light/dark PNG variants.
Every project produces four SVG variants, each with light and dark PNG exports:
Favicon (favicon.svg): The project icon only, no text, no margins. Tight-cropped to the icon's bounding box. Used as html_favicon in Sphinx and as the browser tab icon. Transparent background. Does not need PNG exports (browsers handle SVG favicons natively).
Square logo (logo-square.svg): The project icon with the project name centered below it. Used as the Sphinx sidebar logo (html_logo). Transparent background. The viewBox is taller than the icon to accommodate the text below.
Banner (logo-banner.svg): Horizontal layout with the icon on the left, project name and tagline to the right. Transparent background. Used in the GitHub readme.
Social banner (banner-{style}.svg): Same layout as the banner but with a decorative opaque background (e.g., marble veins, gradients, wave patterns). Used for OpenGraph/social previews. Opaque background.
Each SVG produces two PNGs: {name}-light.png and {name}-dark.png. The favicon is SVG-only (no PNG exports needed).
When creating assets for a new project, start with a broad exploration phase to find a visual direction before refining.
Prompt pattern:
Create several PNG versions of {base-svg} but with different abstract backgrounds (curvy, bitmap, slopes, splines, gradients, noise, halftone, topographic, marble, waves, geometric, etc). Generate 30 of them, all singularly different, so I can choose a direction. Place them in {assets-dir}.
Use rsvg-convert or a Python script (Pillow) to composite the base SVG over programmatically generated backgrounds. Number each output banner-{nn}-{descriptor}.png (e.g., banner-12-wind-lines.png, banner-27-marble-veins.png).
The user reviews the candidates and picks one or more to refine. Delete the rest.
Recreate the chosen design as a clean, hand-authored SVG:
style attributes).@media queries).{name}.svg is the canonical source (always renders in light mode).{name}-light.png is the light-theme PNG export.{name}-dark.png is the dark-theme PNG export.SVGs use CSS classes for themed properties (fills, strokes). To export a themed PNG:
<style> block with dark-mode colors swapped in.rsvg-convert.Typical light/dark color pairs (Tailwind Slate palette):
| Role | Light | Dark |
| ---------- | --------- | --------- |
| Frame/ring | #334155 | #94A3B8 |
| Handle out | #334155 | #94A3B8 |
| Handle in | #475569 | #CBD5E1 |
| Title text | #1E293B | #F1F5F9 |
| Tagline | #64748B | #CBD5E1 |
| Background | #F8FAFC | #0F172A |
| Vein light | #e2e8ef | #1a2535 |
| Vein dark | #c6cfda | #253040 |
<rect> background. The PNG will have alpha transparency, suitable for overlaying on any surface.<rect> with a .bg class. Swap its fill color for the target theme. Both light and dark PNGs get their respective solid background.Use rsvg-convert (from librsvg):
$ rsvg-convert -o output.png input.svg
If rsvg-convert is unavailable, fall back to inkscape --export-type=png --export-filename=output.png input.svg.
Discover SVGs. If $ARGUMENTS is a directory, find all .svg files in it. If it's a specific file, use that. Default to docs/assets/.
For each SVG, read it and identify:
<rect> with .bg class exists (opaque) or not (transparent).Generate light PNG. The SVG already has light-mode styles, so convert directly:
rsvg-convert -o {name}-light.png {name}.svg.bg rect has the light fill).Generate dark PNG. Create a temporary SVG with dark-mode colors:
<style> block with dark-mode values..bg fill is swapped to the dark background color.Report the generated files and their sizes.
.svg files. Only create temporary copies for baking.@media (prefers-color-scheme: dark) blocks. Light-mode styles are the only styles in the SVG. Dark mode is handled exclusively through baked PNG exports.'Inter', 'Segoe UI', system-ui, -apple-system, 'Helvetica Neue', Arial, sans-serif.radialGradient for the lens glass.docs/conf.pyWire the assets into the Furo theme:
html_logo = "assets/logo-square.svg"
html_favicon = "assets/favicon.svg"
html_theme_options = {
"sidebar_hide_name": True,
# ...
}
html_logo: Points to the square logo (with project name baked in). Combined with "sidebar_hide_name": True to avoid a duplicate auto-generated name below the SVG.html_favicon: Points to the icon-only favicon (no text, tight crop).The readme includes a centered banner image (logo-banner.svg) for GitHub. When the readme is included in the Sphinx front page via {include}, the banner is redundant with the sidebar logo. Hide it with custom CSS:
docs/_static/custom.css:
/* Hide the readme banner on the Sphinx front page (logo already in sidebar). */
article p[align="center"]:has(img[alt="Project Name"]) {
display: none;
}
Wire it in conf.py:
html_static_path = ["_static"]
html_css_files = ["custom.css"]
Replace "Project Name" with the actual alt text of the banner <img> in the readme.
When a design exists only as a raster image (PNG/JPEG) and needs to be reproduced as a clean SVG, use pixel analysis to extract geometry and colors.
Identify the background color. Sample pixels in a known empty region. This establishes the threshold for separating foreground elements from background.
Scan for foreground features. Using Pillow + NumPy, iterate over the image in slices (vertical columns for horizontal features, horizontal rows for vertical features). At each slice, threshold grayscale values to find pixels that differ from the background.
Cluster pixels into distinct elements. Group adjacent foreground pixels within a slice. A gap larger than 3-5px indicates a separate element. For each cluster, record:
Trace paths across slices. Match clusters across adjacent slices by proximity to build continuous paths. Each path becomes a series of (x, y) sample points.
Fit SVG paths. Convert the sampled points into SVG cubic bezier curves:
C (cubic bezier) for the initial segment.S (smooth cubic bezier) for continuations. Each S needs 4 coordinates (control point + endpoint); fewer causes the path to terminate early.x=-20 and x=1300 for a 1280-wide image) so lines reach the borders cleanly.Assign colors. Group paths by sampled RGB values. Create CSS classes for each distinct color and assign them to the corresponding paths.
tools
Create or update an upstream contributions page (docs/upstream.md) tracking the project's relationship with its dependencies. Discovers merged PRs, reported issues, workarounds, and declined features.
documentation
Detect stale translations in readme.*.md and contributing.*.md files by comparing structure and content against the English source, then draft updated translations for changed sections.
testing
Two-way comparison and synchronization of Sphinx documentation across sibling projects. Discovers discrepancies in conf.py, install.md, index.md toctree, pyproject.toml docs dependencies, extra-deps sections, readme badges, and static assets. Use when you want to align documentation structure, catch stale dependencies, or push improvements across your Sphinx-enabled repositories.
tools
Optimize GitHub topics for discoverability by analyzing competition on topic pages.