Repository
Design surface · paired with the executable repository standard.
Every repo in the estate ships three brand artifacts:
avatar.svg, hero.svg, and a derived
brand.png (the combined sheet: avatar over hero on
the surface canvas, for social cards and README banner spots).
Avatar is a metaphorical logo. Hero is a data-sheet with the
metaphor embedded. The hero exists because GitHub doesn't render
the repo avatar; it carries the brand on mirrored views.
Avatar
256 × 256 viewBox. Metaphorical glyph; reads at favicon and avatar scale.
Hero
1200 × 430 viewBox. Big title left, embedded metaphor below it, four fact tiles right.
The rules
The design language, stated tersely. Lift the templates, replace the placeholders, keep the rest.
Two artifacts, two voices
Avatar answers what is this like? with a metaphor. Hero answers what does this promise? with principles. Don't mix: a flow diagram in the avatar or a metaphorical hero is the failure mode.
The metaphor goes in both
GitHub strips the repo avatar, so the hero embeds an elaborated version of the same glyph beneath the title. The hero version has more endpoints, more lines, more density; it's the avatar with room to breathe.
One tagline, two surfaces
The italic line on the hero (under the title) and the muted tagline on the avatar (under the title) are the same phrase. It's a tagline, not a stage direction: evocative about the project's posture, not a literal description of the visual ("a basket catching the day's photographs as they fall from the feed" is the failure mode; "collected moments of early learning" is the right register).
One phrase across both artifacts keeps the brand coherent on the two surfaces a viewer hits (avatar in the repo header, hero in the README). Pick a phrase that fits the avatar's narrower budget; the hero has more room than it'll ever need.
Glyph stays in its band
The hero metaphor mark sits in y 230-380 (150 actual units;
~75 local at the template's scale(2)). The bottom rule line
at y 386 is hard: a glyph that overlaps it looks like a
layout bug. If a glyph won't fit at scale(2), drop a row of
elements before scaling down: a less-full layout reads as "the project's
still small / still growing," which is usually on-brand. Scaling below
1.7 makes the strokes too thin against the canvas.
The avatar glyph sits in y 90-200 (110 units) at
scale(1). Similar discipline: photo + caption + a binding
line beats four photos crammed in.
One glyph, three sizes
A brand kit ships three renderings of one composition: the favicon (64-unit viewBox), the avatar's inline glyph band inside the 256 canvas, and the mark in the project site's header, its only place on the site. Drifting between them, different grid densities, different trail shapes, different stroke widths, reads as three competing brand marks.
Design the glyph once at the favicon's 64-unit viewBox. Render the
avatar's band by wrapping the same path data in
<g transform="translate(...) scale(N)">, picking N
so it fills y 90-200. The site glyph is usually the
favicon re-saved under another name. When the metaphor evolves, edit
one and copy to the others.
The favicon is re-authored, not cropped
Favicons render at 16-32px, where the avatar's glyph shrunk from a 256 viewBox loses every fine detail. The 64 viewBox is the chance to bump stroke widths, drop details that vanish at small size, and re-balance the composition without the surrounding title, tagline and output blocks. Same metaphor, different artifact: an avatar showing a conductor calling out to renderers ships a favicon with the same conductor and fan, simplified.
- viewBox
0 0 64 64, no outer rect; the browser tab provides the background. - Stroke and fill hardcode the moss accent
#4a6741, nevercurrentColor: browser-UI text colour inherits as black on light tab strips and washes out on dark. - No text. The glyph carries the metaphor alone.
- Strict XML. Some browsers refuse an SVG favicon that fails well-formedness even when the same file renders fine inline; the common trap is a double hyphen inside a comment. Validate with
rsvg-convert favicon.svg -o /dev/null.
A repo with no metaphor of its own keeps the dunn.dev rune from
repository/templates/favicon.svg until its glyph lands.
Lines start and end at edges
Connecting lines start at the conductor's edge and end at the renderer's edge; they never pass through circle centres. When the metaphor has both call and response, draw them as parallel lanes (perpendicular offset, same magnitude on both ends), not converging on the same point.
Tile content
Each of the four hero tiles carries four layers: a small-caps mono label, a big bold moss word, a body subtitle that grounds the word, and a small mono spec line at the bottom. Top row covers flow (in / out), bottom row covers discipline (posture / lifetime). The pattern is fixed; the labels can vary per project.
Territory tags name concepts, not stack
The dotted line at the hero's bottom left carries territory
tags: stable concepts saying what kind of project this is. Domain
words (audio, auth), form words
(cli, library), protocol names when they are
stable and central (UPnP, OAuth).
What does not go here: language names, library names, framework names, version numbers, LOC counts, file counts, CLI flag names. Anything that might be renamed or swapped is a maintenance detail and belongs in the repo's badge row, not in the brand.
Every fill and stroke is a published token
The whitelist is the estate's SVG palette on
Colors, hardcoded as hex because
sanitisers strip var() from the surfaces that matter
most. In the brand artifacts it maps: the avatar's canvas is
--bg; the hero's canvas is --surface with
--bg tile fills; --border draws the avatar's
outer border and the hero's dividers and tile borders;
--accent carries the title, the big tile word and the
primary stroke. A colour outside that set makes one repo's artwork
read as a different estate's.
Type is a system stack
The avatar uses Helvetica, Arial, sans-serif so the SVG
renders identically everywhere it is embedded. The hero uses
IBM Plex Sans, Helvetica, Arial, sans-serif with
IBM Plex Mono, Menlo, monospace for small-caps headers
and spec lines. Set font-family on both the
<svg> root and the wrapping <g>
so it inherits everywhere.
Required SVG attributes
role="img"on the<svg>root.- A non-empty
aria-labeldescribing the artwork in a sentence. - An outer
<rect>filling the canvas with paper (avatar) or paper-warm (hero), so the artwork carries its own background. <defs>marker IDs prefixed with the repo slug, so nothing collides when SVGs are inlined together.
Generation
avatar.png is derived from avatar.svg via
rsvg-convert -w 256 -h 256. brand.png is
derived by stacking the avatar (rendered at 800×800, padded to 1600×800
on a surface canvas) above the hero (rendered at 1600×573), with a
24px surface border around the whole thing. The hero stays SVG-only;
GitHub renders SVG natively.
Each repo's justfile adds a brand target that
regenerates both PNGs. Run it after editing either SVG.
Character budgets
The only ceiling you can break visibly. SVG text does not wrap; overflow looks unprofessional and hides content.
| Field | Where | Budget |
|---|---|---|
| title | avatar + hero | ~10 chars (one short word) |
| tagline | avatar + hero italic | ~36 chars |
| title sub-line | hero, two lines | ~70 chars per line |
| top metadata | hero, mono caps | ~58 chars |
| tile big word | hero, font-size 34 | ~12 chars |
| tile subtitle | hero, font-size 11 | ~46 chars |
| tile spec line | hero, mono caps | ~30 chars |
| territory tags | hero bottom-left | ~38 chars |
| output line | avatar, font-size 9 | ~50 chars |
Treat these as worst-they-still-fit ceilings. Render at canvas size (avatar 256, hero 1200) before shipping, then look at a 3× PNG (avatar 768, hero 3600): some overflow is only obvious there.
Files
Lift, rename, replace placeholders.
repository/templates/avatar.svg→ copy to your repo asavatar.svg; replace project, tagline, glyph, summary line.repository/templates/hero.svg→ copy ashero.svg; replace project, tagline, glyph, four tile contents, territory tags.brand.png→ derived;just brandregenerates it from the two SVGs.repository/templates/favicon.svg→ copy asfavicon.svg; re-author the glyph for the 64 viewBox.repository/templates/glyphs/→ a reusable 64-unit glyph per recurring domain (registry, server stack, network, auth, GPU, config, routing, ideation, tooling, extension, site index, test probe). Its README names the domain each one answers to;brand-init.shreads them by name. Project-specific metaphors stay in the project's own favicon.- Both templates ship the dunn.dev mark as the placeholder glyph. A repo without its own metaphor yet can leave it as-is and the artifact still carries the estate identity.
Consumers can also npm-install the package and reference templates
directly: @dunn.dev/reference/repository/templates/avatar.svg.