overlay-svg-text — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited overlay-svg-text (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Two layers of brand identity on every share image. The base image — generated viagenerate-consistent-og-images— already follows the empty-region-first composition rule, leaving the top 1/3 of the frame deliberately clean. This skill paints that clean zone with three text elements: a thin sans eyebrow, a Hack-Bold gradient-filled h1, and a Poor Story handwritten note. Everything else (layout, colors, font weights) is locked at the project'sDESIGN.mdlevel.
Three text elements, in canonical vertical order from top of frame to bottom of overlay zone:
| Element | Font | Weight | Color | Notes |
|---|---|---|---|---|
| Eyebrow | Project's display sans (or Inter as fallback) | Thin (200) or Light (300) | white at 70–80% alpha, OR --color-text-soft | Often uppercase with wide tracking (0.18em–0.24em); the project's typography.eyebrow token wins if present |
| H1 (gradient) | Hack Bold (Hack Nerd Font Mono Bold is what most Lossless dev machines have installed) | Bold (700) | gradient-filled from the project's imagery.gradient or components.gradient-text.background in DESIGN.md | Monospaced + gradient is the brand's "data" aesthetic — short noun-phrase or shell-command-style; never sentence-case prose |
| Note (handwritten) | Poor Story (Google Fonts, Korean handwriting family) — preferred. Any cartoonish handwriting font is acceptable as fallback (e.g. Caveat, Patrick Hand, Indie Flower) | Regular | white (#ffffff) — almost always | The handwriting/Hack contrast is the brand move; never serif, never another mono |
The three elements together compose the brand's signature: clean thin sans label → monospaced gradient title → cartoonish white handwritten phrase. The handwriting/mono contrast is the most-defining axis; don't lose it by going all-sans or all-mono.
Not from trademark__The-Lossless-Group--Vibrant.svg or gradient__The-Lossless-Group--Rounded-Rectangle.svg. Those files are raster-baked exports (each is a <clipPath> of glyph/shape paths with a base64 JPEG as the fill) and the gradient inside them isn't an editable <linearGradient> element — it's pixel data. Treat them as visual reference only.
The editable gradient for any given project lives in that project's DESIGN.md. Look in this order:
accent, accent-warm, thread — or primary, secondary, tertiary) at 0% / 55% / 100% stopsFor the lossless-agent-skills splash, that resolves to:
linear-gradient(90deg, #5eead4 0%, #fbbf24 55%, #e879f9 100%)
(teal) (amber) (magenta)For the ai-labs splash:
linear-gradient(95deg, #ffb733 0%, #5cc8ff 55%, #d96fff 100%)
(sodium) (cyan) (plum)Per-project gradient + brand-wide type system = images that look like family with a project on each member. Use Lossless Group's brand gradient (teal/violet/etc. — the JPEG-baked one) only when the overlay belongs to The Lossless Group itself, e.g. a corporate splash, an "About" page, a consulting-firm asset. Site-specific imagery uses site-specific gradients.
Two compatible patterns. The first is the default; the second is for the rare cases where you need the gradient to extend past the letterforms or to be clipped by a more complex shape.
fill="url(#gradient)" on <text> (default)<svg width="1312" height="736" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="brand" x1="0" y1="0" x2="1" y2="0">
<stop offset="0%" stop-color="#5eead4"/>
<stop offset="55%" stop-color="#fbbf24"/>
<stop offset="100%" stop-color="#e879f9"/>
</linearGradient>
</defs>
<text x="64" y="120"
font-family="Hack Nerd Font Mono, Hack, ui-monospace, monospace"
font-weight="700"
font-size="96"
fill="url(#brand)">
Agent Skills
</text>
</svg>The gradient renders inside each glyph's bounding box. Simple, portable across librsvg / resvg / browser / sharp. This is what 95% of overlays should use.
<defs>
<linearGradient id="brand" x1="0" y1="0" x2="1" y2="0">...</linearGradient>
<clipPath id="text-clip">
<text x="64" y="120"
font-family="Hack Nerd Font Mono"
font-weight="700"
font-size="96">
Agent Skills
</text>
</clipPath>
</defs>
<rect x="0" y="0" width="1312" height="200"
fill="url(#brand)"
clip-path="url(#text-clip)"/>Use this when the gradient should sweep across a region wider than the text itself (e.g., the gradient should flow as if it were on a banner behind the text but be visible only through the letterforms). For all simple cases, Pattern A is shorter and renders identically.
The base OG image is a JPEG; the overlay is an SVG; the output is a JPEG with the SVG painted on top. The cleanest tool for this is sharp (Node, librsvg under the hood). The canonical script lives at templates/compose-overlay.mjs in this skill.
# One-time install at the splash project root:
pnpm add -D sharp
# Then run:
node templates/compose-overlay.mjs \
--base public/ogimage__Lossless-Agent-Skills--BannerImage.jpg \
--overlay overlays/banner.svg \
--out public/ogimage__Lossless-Agent-Skills--BannerImage--overlaid.jpg \
--quality 90The script:
width/height match the base (or scales it)open-graph-share-seo-geo skill targets ~85–95 for OG).ogimage-archive/<name>--<YYYY-MM-DD>.jpg first if the output path already existsrsvg-convert overlay.svg -o overlay.png then composite with ffmpeg -i base.jpg -i overlay.png -filter_complex "[0][1]overlay=0:0" out.jpgresvg overlay.svg overlay.png + same ffmpeg stepconvert base.jpg overlay.svg -composite out.jpg — works but text rendering is less reliable than librsvgSharp is the documented default because it's already in the Astro Knots ecosystem and produces byte-identical output to librsvg-CLI without the two-step PNG→JPEG dance.
Sharp's renderer (librsvg under the hood) reads fonts via fontconfig on Linux/macOS and via the system font store on Windows. For the local-dev case where fonts are installed in ~/Library/Fonts/ (macOS) or ~/.fonts/ (Linux), no extra step is needed — just reference the family name in the SVG's font-family attribute.
For CI builds (GitHub Actions, Vercel build container, etc.) where the host doesn't have Hack or Poor Story installed, embed the WOFF2 binary as a data URL inside the SVG:
<defs>
<style type="text/css">
@font-face {
font-family: 'Hack Bold';
src: url(data:font/woff2;base64,d09GMgABAAAA...) format('woff2');
font-weight: 700;
}
@font-face {
font-family: 'Poor Story';
src: url(data:font/woff2;base64,d09GMgABAAAA...) format('woff2');
font-weight: 400;
}
</style>
</defs>Recipe to generate the data URL:
# Convert TTF/OTF to WOFF2 (smaller, faster decode)
# pnpm dlx fonttools (or use python -m fontTools.ttLib if installed)
fonttools ttLib --woff2 ~/Library/Fonts/HackNerdFontMono-Bold.ttf
# Then base64-encode:
base64 -i HackNerdFontMono-Bold.woff2 | tr -d '\n' > hack-bold.b64A WOFF2-embedded SVG balloons by ~80–250 KB (Hack Bold is ~200 KB WOFF2; Poor Story is ~80 KB WOFF2). Acceptable for a one-time composited PNG/JPEG output — the resulting raster is the only thing that ships, the SVG is intermediate.
The base OG image was generated with Top 1/3 of frame is empty negative space as the locked composition. The overlay's safe zone is therefore the top third of the canvas. Within that:
┌────────────────────────────────────────────────┐ ← y = 0
│ ┌─ padding 6% of height ─┐ │
│ │ │ │
│ ┃ EYEBROW · THIN · ALLCAPS ← ~12% down (eyebrow baseline)
│ │ │ │
│ ┃ Gradient H1 Title ← ~22% down (h1 baseline; bold mono)
│ │ │ │
│ ┃ ✎ a handwritten phrase, white ← ~30% down │
│ │ │ │
└──┴─────────────────────────┴────────────────────┘ ← y = floor(height / 3)
↑ ↑
left padding 5–6% right edge stays cleanVertical positions are expressed as percentages of the canvas height so the same SVG template re-aspects cleanly when fed different dimensions. For the four canonical aspect ratios from generate-consistent-og-images:
| Format | Dimensions | Top-zone height | Eyebrow y | H1 y | Note y |
|---|---|---|---|---|---|
BannerImage | 1312×736 | 245 px | ~88 px | ~162 px | ~221 px |
BannerImageTall | 896×1120 | 373 px | ~134 px | ~246 px | ~336 px |
PortraitImage | 736×1312 | 437 px | ~157 px | ~289 px | ~394 px |
SquareImage | 1024×1024 | 341 px | ~123 px | ~225 px | ~307 px |
The compose script computes these from the base image dimensions; never hard-code pixel positions for a specific aspect ratio in the template SVG. Author the SVG once with percentage-based y-values; the script substitutes them per output.
Five steps. Steps 1–2 are setup; steps 3–5 are the per-image work.
The base JPEGs must already exist (output of generate-consistent-og-images). If they don't, hand off to that skill first. The overlay paints onto what's there; it can't conjure a base.
Read the project's DESIGN.md:
imagery.gradient (an extension key on some projects)components.gradient-text.backgroundlinear-gradient(<angle>, <accent>, <accent-warm>, <thread>) from the project's brand-spine semantic colorsFor the lossless-agent-skills splash:
linear-gradient(90deg, #5eead4 0%, #fbbf24 55%, #e879f9 100%)For ai-labs:
linear-gradient(95deg, #ffb733 0%, #5cc8ff 55%, #d96fff 100%)Translate to SVG <linearGradient> stops; x1/y1/x2/y2 derives from the CSS angle.
Use templates/overlay.svg as the starting point. Fill in:
width and height (match the base image exactly)For multi-aspect runs (BannerImage + BannerImageTall + PortraitImage + SquareImage), author one SVG template with percentage-based positions and let the compose script re-render it per output. Don't hand-author four separate SVGs.
Run node compose-overlay.mjs --base <jpg> --overlay <svg> --out <jpg> (or the per-format loop variant in the same script).
The script archives any existing canonical at .ogimage-archive/<name>--<YYYY-MM-DD>.jpg before writing the new one (per the generate-consistent-og-images preservation discipline).
Open the output JPEG. Check:
If the overlay clips the subject (text overlapping the robots / bench / terminal in the base), the base image's empty-region zone wasn't large enough. Re-generate the base via generate-consistent-og-images with a longer "Top 1/3" clause or Top 40% in the prompt; don't shrink the overlay text below readable size to work around it.
open-graph-share-seo-geo skill: JPEG-over-WebP for unfurler compatibility. Sharp's .jpeg({ quality: 90 }) is the canonical output step.generate-consistent-og-images — archive the prior bytes to .ogimage-archive/<name>--<YYYY-MM-DD>.jpg before writing the new one. The unfurler URL stays stable; byte history survives.~/Library/Fonts/. Embed only for CI / cross-machine portability or when the SVG itself is the deliverable.templates/overlay.svg — the canonical SVG scaffold with placeholders for gradient stops, eyebrow, h1, and handwritten notetemplates/compose-overlay.mjs — the canonical Node script that composites SVG onto JPEG via sharp, with multi-aspect loop + archive disciplinegenerate-consistent-og-images skill — the upstream skill that produces the base JPEGs this skill overlays text onto; defines the empty-region-first composition that creates the overlay safe zonemaintain-design-md skill — where the gradient stops actually live (components.gradient-text.background or imagery.gradient); this skill is a consumer of those tokens, not an authoropen-graph-share-seo-geo skill — the delivery-side discipline (JPEG-over-WebP, cache headers, the og:image:type invariant); cares that the overlaid JPEG ends up on the right URL, not how the text was painted~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.