OpenIcon 0.1: an icon set as a folder, with terminal glyphs (#198)

One openicon.json naming every icon (kebab-case keys, unique aliases,
shared keywords), 24x24 currentColor SVGs with a stated grid, and three
terminal glyphs per icon: a Nerd Font character with its codepoint and
glyph name, a Unicode symbol and a 1-4 character ASCII spelling, picked
nerd > unicode > ascii. Brand logos carry brand: true, a trademark note,
source and licence. made_by + W3C AI disclosure per set and per icon.
Mapping from Lucide, Tabler, Font Awesome, Nerd Fonts and Simple Icons.
Landing page shows 47 icons from the reference set (profullstack/openicon).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Anthony Ettinger 2026-09-23 22:27:15 -07:00 • committed by GitHub
parent f80488ce92
commit 3883ee7a2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 423 additions and 0 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Add</title><path d="M12 5v14M5 12h14"/></svg>

After

Width:  |  Height:  |  Size: 211 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>At</title><circle cx="12" cy="12" r="4"/><path d="M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"/></svg>

After

Width:  |  Height:  |  Size: 265 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Bell</title><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.9 1.9 0 0 0 3.4 0"/></svg>

After

Width:  |  Height:  |  Size: 277 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>Bluesky</title><g transform="translate(2 2) scale(0.83333)"><path d="M5.202 2.857C7.954 4.922 10.913 9.11 12 11.358c1.087-2.247 4.046-6.436 6.798-8.501C20.783 1.366 24 .213 24 3.883c0 .732-.42 6.156-.667 7.037-.856 3.061-3.978 3.842-6.755 3.37 4.854.826 6.089 3.562 3.422 6.299-5.065 5.196-7.28-1.304-7.847-2.97-.104-.305-.152-.448-.153-.327 0-.121-.05.022-.153.327-.568 1.666-2.782 8.166-7.847 2.97-2.667-2.737-1.432-5.473 3.422-6.3-2.777.473-5.899-.308-6.755-3.369C.42 10.04 0 4.615 0 3.883c0-3.67 3.217-2.517 5.202-1.026"/></g></svg>

After

Width:  |  Height:  |  Size: 624 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Calendar</title><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg>

After

Width:  |  Height:  |  Size: 270 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Chat</title><path d="M21 11.5a8.4 8.4 0 0 1-9 8.4 8.8 8.8 0 0 1-3.8-.9L3 21l1.9-5.2A8.4 8.4 0 0 1 12 3a8.4 8.4 0 0 1 9 8.5z"/></svg>

After

Width:  |  Height:  |  Size: 298 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Cloud</title><path d="M17.5 19H7a5 5 0 1 1 1.3-9.8A6 6 0 0 1 19.6 11 4 4 0 0 1 17.5 19z"/></svg>

After

Width:  |  Height:  |  Size: 262 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Code</title><path d="m16 18 6-6-6-6M8 6l-6 6 6 6"/></svg>

After

Width:  |  Height:  |  Size: 223 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Copy</title><rect x="8" y="8" width="14" height="14" rx="2"/><path d="M4 16a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2"/></svg>

After

Width:  |  Height:  |  Size: 297 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Database</title><path d="M3 5v14a9 3 0 0 0 18 0V5M3 12a9 3 0 0 0 18 0"/><ellipse cx="12" cy="5" rx="9" ry="3"/></svg>

After

Width:  |  Height:  |  Size: 283 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Delete</title><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6M10 11v6M14 11v6"/></svg>

After

Width:  |  Height:  |  Size: 304 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>Discord</title><g transform="translate(2 2) scale(0.83333)"><path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Download</title><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>

After

Width:  |  Height:  |  Size: 262 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Edit</title><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4z"/><path d="m15 5 4 4"/></svg>

After

Width:  |  Height:  |  Size: 259 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Eye</title><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7z"/><circle cx="12" cy="12" r="3"/></svg>

After

Width:  |  Height:  |  Size: 274 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Filter</title><path d="M22 3H2l8 9.5V19l4 2v-8.5z"/></svg>

After

Width:  |  Height:  |  Size: 224 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Git branch</title><circle cx="6" cy="18" r="3"/><circle cx="18" cy="6" r="3"/><path d="M6 3v12M18 9a9 9 0 0 1-9 9"/></svg>

After

Width:  |  Height:  |  Size: 288 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Git pull request</title><circle cx="18" cy="18" r="3"/><circle cx="6" cy="6" r="3"/><path d="M13 6h3a2 2 0 0 1 2 2v7M6 9v12"/></svg>

After

Width:  |  Height:  |  Size: 298 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>GitHub</title><g transform="translate(2 2) scale(0.83333)"><path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"/></g></svg>

After

Width:  |  Height:  |  Size: 881 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Globe</title><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 4 10 15 15 0 0 1-4 10 15 15 0 0 1-4-10 15 15 0 0 1 4-10z"/></svg>

After

Width:  |  Height:  |  Size: 311 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Home</title><path d="m3 10 9-7 9 7v10a2 2 0 0 1-2 2h-4v-7h-6v7H5a2 2 0 0 1-2-2z"/></svg>

After

Width:  |  Height:  |  Size: 254 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Key</title><circle cx="7.5" cy="15.5" r="5.5"/><path d="m11.5 11.5 9-9M15.5 7.5l3 3L22 7l-3-3"/></svg>

After

Width:  |  Height:  |  Size: 268 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Link</title><path d="M10 13a5 5 0 0 0 7.5.5l3-3a5 5 0 0 0-7-7l-1.7 1.7"/><path d="M14 11a5 5 0 0 0-7.5-.5l-3 3a5 5 0 0 0 7 7l1.7-1.7"/></svg>

After

Width:  |  Height:  |  Size: 307 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Lock</title><rect x="4" y="11" width="16" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>

After

Width:  |  Height:  |  Size: 270 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Mail</title><rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 6L2 7"/></svg>

After

Width:  |  Height:  |  Size: 259 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Map pin</title><path d="M20 10c0 6-8 12-8 12s-8-6-8-12a8 8 0 0 1 16 0z"/><circle cx="12" cy="10" r="3"/></svg>

After

Width:  |  Height:  |  Size: 276 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>Mastodon</title><g transform="translate(2 2) scale(0.83333)"><path d="M23.268 5.313c-.35-2.578-2.617-4.61-5.304-5.004C17.51.242 15.792 0 11.813 0h-.03c-3.98 0-4.835.242-5.288.309C3.882.692 1.496 2.518.917 5.127.64 6.412.61 7.837.661 9.143c.074 1.874.088 3.745.26 5.611.118 1.24.325 2.47.62 3.68.55 2.237 2.777 4.098 4.96 4.857 2.336.792 4.849.923 7.256.38.265-.061.527-.132.786-.213.585-.184 1.27-.39 1.774-.753a.057.057 0 0 0 .023-.043v-1.809a.052.052 0 0 0-.02-.041.053.053 0 0 0-.046-.01 20.282 20.282 0 0 1-4.709.545c-2.73 0-3.463-1.284-3.674-1.818a5.593 5.593 0 0 1-.319-1.433.053.053 0 0 1 .066-.054c1.517.363 3.072.546 4.632.546.376 0 .75 0 1.125-.01 1.57-.044 3.224-.124 4.768-.422.038-.008.077-.015.11-.024 2.435-.464 4.753-1.92 4.989-5.604.008-.145.03-1.52.03-1.67.002-.512.167-3.63-.024-5.545zm-3.748 9.195h-2.561V8.29c0-1.309-.55-1.976-1.67-1.976-1.23 0-1.846.79-1.846 2.35v3.403h-2.546V8.663c0-1.56-.617-2.35-1.848-2.35-1.112 0-1.668.668-1.67 1.977v6.218H4.822V8.102c0-1.31.337-2.35 1.011-3.12.696-.77 1.608-1.164 2.74-1.164 1.311 0 2.302.5 2.962 1.498l.638 1.06.638-1.06c.66-.999 1.65-1.498 2.96-1.498 1.13 0 2.043.395 2.74 1.164.675.77 1.012 1.81 1.012 3.12z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Menu</title><path d="M4 6h16M4 12h16M4 18h16"/></svg>

After

Width:  |  Height:  |  Size: 219 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>npm</title><g transform="translate(2 2) scale(0.83333)"><path d="M1.763 0C.786 0 0 .786 0 1.763v20.474C0 23.214.786 24 1.763 24h20.474c.977 0 1.763-.786 1.763-1.763V1.763C24 .786 23.214 0 22.237 0zM5.13 5.323l13.837.019-.009 13.836h-3.464l.01-10.382h-3.456L12.04 19.17H5.113z"/></g></svg>

After

Width:  |  Height:  |  Size: 376 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Package</title><path d="M21 16V8a2 2 0 0 0-1-1.7l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.7l7 4a2 2 0 0 0 2 0l7-4a2 2 0 0 0 1-1.7z"/><path d="M3.3 7 12 12l8.7-5M12 22V12M7.5 4.2l9 5.2"/></svg>

After

Width:  |  Height:  |  Size: 371 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Phone</title><path d="M22 16.9v3a2 2 0 0 1-2.2 2 19.8 19.8 0 0 1-8.6-3.1 19.5 19.5 0 0 1-6-6A19.8 19.8 0 0 1 2.1 4.2 2 2 0 0 1 4.1 2h3a2 2 0 0 1 2 1.7c.1 1 .4 1.9.7 2.8a2 2 0 0 1-.5 2.1L8 9.9a16 16 0 0 0 6 6l1.3-1.3a2 2 0 0 1 2.1-.4c.9.3 1.8.6 2.8.7a2 2 0 0 1 1.7 2z"/></svg>

After

Width:  |  Height:  |  Size: 441 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Refresh</title><path d="M21 12a9 9 0 0 1-15.4 6.4L3 16M3 12a9 9 0 0 1 15.4-6.4L21 8M21 3v5h-5M3 21v-5h5"/></svg>

After

Width:  |  Height:  |  Size: 278 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Rocket</title><path d="M4.5 16.5c-1.5 1.3-2 5-2 5s3.7-.5 5-2c.7-.8.7-2.1-.1-2.9a2.2 2.2 0 0 0-2.9-.1z"/><path d="m12 15-3-3a22 22 0 0 1 2-3.9A12.9 12.9 0 0 1 22 2c0 2.7-.8 7.5-6 11a22.4 22.4 0 0 1-4 2z"/><path d="M9 12H4s.6-3 2-4c1.6-1.1 5 0 5 0M12 15v5s3-.6 4-2c1.1-1.6 0-5 0-5"/></svg>

After

Width:  |  Height:  |  Size: 453 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Rss</title><path d="M4 11a9 9 0 0 1 9 9M4 4a16 16 0 0 1 16 16"/><circle cx="5" cy="19" r="1"/></svg>

After

Width:  |  Height:  |  Size: 266 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Search</title><circle cx="11" cy="11" r="7"/><path d="m21 21-5-5"/></svg>

After

Width:  |  Height:  |  Size: 239 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Settings</title><path d="M9.37 4.98L10.71 4.61L10.79 2.07L13.21 2.07L13.29 4.61L15.11 5.17L16.31 5.86L18.17 4.13L19.87 5.83L18.14 7.69L19.02 9.37L19.39 10.71L21.93 10.79L21.93 13.21L19.39 13.29L18.83 15.11L18.14 16.31L19.87 18.17L18.17 19.87L16.31 18.14L14.63 19.02L13.29 19.39L13.21 21.93L10.79 21.93L10.71 19.39L8.89 18.83L7.69 18.14L5.83 19.87L4.13 18.17L5.86 16.31L4.98 14.63L4.61 13.29L2.07 13.21L2.07 10.79L4.61 10.71L5.17 8.89L5.86 7.69L4.13 5.83L5.83 4.13L7.69 5.86Z"/><circle cx="12" cy="12" r="3"/></svg>

After

Width:  |  Height:  |  Size: 680 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Share</title><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 13.5 6.8 4M15.4 6.5l-6.8 4"/></svg>

After

Width:  |  Height:  |  Size: 319 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>Signal</title><g transform="translate(2 2) scale(0.83333)"><path d="M12 0q-.934 0-1.83.139l.17 1.111a11 11 0 0 1 3.32 0l.172-1.111A12 12 0 0 0 12 0M9.152.34A12 12 0 0 0 5.77 1.742l.584.961a10.8 10.8 0 0 1 3.066-1.27zm5.696 0-.268 1.094a10.8 10.8 0 0 1 3.066 1.27l.584-.962A12 12 0 0 0 14.848.34M12 2.25a9.75 9.75 0 0 0-8.539 14.459c.074.134.1.292.064.441l-1.013 4.338 4.338-1.013a.62.62 0 0 1 .441.064A9.7 9.7 0 0 0 12 21.75c5.385 0 9.75-4.365 9.75-9.75S17.385 2.25 12 2.25m-7.092.068a12 12 0 0 0-2.59 2.59l.909.664a11 11 0 0 1 2.345-2.345zm14.184 0-.664.909a11 11 0 0 1 2.345 2.345l.909-.664a12 12 0 0 0-2.59-2.59M1.742 5.77A12 12 0 0 0 .34 9.152l1.094.268a10.8 10.8 0 0 1 1.269-3.066zm20.516 0-.961.584a10.8 10.8 0 0 1 1.27 3.066l1.093-.268a12 12 0 0 0-1.402-3.383M.138 10.168A12 12 0 0 0 0 12q0 .934.139 1.83l1.111-.17A11 11 0 0 1 1.125 12q0-.848.125-1.66zm23.723.002-1.111.17q.125.812.125 1.66c0 .848-.042 1.12-.125 1.66l1.111.172a12.1 12.1 0 0 0 0-3.662M1.434 14.58l-1.094.268a12 12 0 0 0 .96 2.591l-.265 1.14 1.096.255.36-1.539-.188-.365a10.8 10.8 0 0 1-.87-2.35m21.133 0a10.8 10.8 0 0 1-1.27 3.067l.962.584a12 12 0 0 0 1.402-3.383zm-1.793 3.848a11 11 0 0 1-2.345 2.345l.664.909a12 12 0 0 0 2.59-2.59zm-19.959 1.1L.357 21.48a1.8 1.8 0 0 0 2.162 2.161l1.954-.455-.256-1.095-1.953.455a.675.675 0 0 1-.81-.81l.454-1.954zm16.832 1.769a10.8 10.8 0 0 1-3.066 1.27l.268 1.093a12 12 0 0 0 3.382-1.402zm-10.94.213-1.54.36.256 1.095 1.139-.266c.814.415 1.683.74 2.591.961l.268-1.094a10.8 10.8 0 0 1-2.35-.869zm3.634 1.24-.172 1.111a12.1 12.1 0 0 0 3.662 0l-.17-1.111q-.812.125-1.66.125a11 11 0 0 1-1.66-.125"/></g></svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>Slack</title><g transform="translate(3.25 2) scale(0.03906)"><path d="M94.1 315.1c0 25.9-21.2 47.1-47.1 47.1S0 341 0 315.1 21.2 268 47.1 268l47.1 0 0 47.1zm23.7 0c0-25.9 21.2-47.1 47.1-47.1S212 289.2 212 315.1l0 117.8c0 25.9-21.2 47.1-47.1 47.1s-47.1-21.2-47.1-47.1l0-117.8zm47.1-189c-25.9 0-47.1-21.2-47.1-47.1S139 32 164.9 32 212 53.2 212 79.1l0 47.1-47.1 0zm0 23.7c25.9 0 47.1 21.2 47.1 47.1S190.8 244 164.9 244L47.1 244C21.2 244 0 222.8 0 196.9s21.2-47.1 47.1-47.1l117.8 0zm189 47.1c0-25.9 21.2-47.1 47.1-47.1S448 171 448 196.9 426.8 244 400.9 244l-47.1 0 0-47.1zm-23.7 0c0 25.9-21.2 47.1-47.1 47.1S236 222.8 236 196.9l0-117.8C236 53.2 257.2 32 283.1 32s47.1 21.2 47.1 47.1l0 117.8zm-47.1 189c25.9 0 47.1 21.2 47.1 47.1S309 480 283.1 480 236 458.8 236 432.9l0-47.1 47.1 0zm0-23.7c-25.9 0-47.1-21.2-47.1-47.1S257.2 268 283.1 268l117.8 0c25.9 0 47.1 21.2 47.1 47.1s-21.2 47.1-47.1 47.1l-117.8 0z"/></g></svg>

After

Width:  |  Height:  |  Size: 998 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Sparkles</title><path d="M9.9 15.5A2 2 0 0 0 8.5 14l-6.1-1.6a.5.5 0 0 1 0-1L8.5 9.9A2 2 0 0 0 9.9 8.5l1.6-6.1a.5.5 0 0 1 1 0l1.6 6.1a2 2 0 0 0 1.4 1.4l6.1 1.6a.5.5 0 0 1 0 1l-6.1 1.6a2 2 0 0 0-1.4 1.4l-1.6 6.1a.5.5 0 0 1-1 0z"/><path d="M20 3v4M22 5h-4"/></svg>

After

Width:  |  Height:  |  Size: 427 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Terminal</title><path d="m4 17 6-6-6-6M12 19h8"/></svg>

After

Width:  |  Height:  |  Size: 221 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Upload</title><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M17 8l-5-5-5 5M12 3v12"/></svg>

After

Width:  |  Height:  |  Size: 261 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>User</title><circle cx="12" cy="8" r="4"/><path d="M4 21v-1a6 6 0 0 1 6-6h4a6 6 0 0 1 6 6v1"/></svg>

After

Width:  |  Height:  |  Size: 266 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>Users</title><circle cx="9" cy="7" r="4"/><path d="M2 21v-1a6 6 0 0 1 6-6h2a6 6 0 0 1 6 6v1M16 3.1a4 4 0 0 1 0 7.8M22 21v-1a6 6 0 0 0-4-5.7"/></svg>

After

Width:  |  Height:  |  Size: 314 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>WhatsApp</title><g transform="translate(2 2) scale(0.83333)"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>X</title><g transform="translate(2 2) scale(0.83333)"><path d="M14.234 10.162 22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299-.929-1.329L3.076 1.56h3.182l5.965 8.532.929 1.329 7.754 11.09h-3.182z"/></g></svg>

After

Width:  |  Height:  |  Size: 358 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><title>YouTube</title><g transform="translate(2 2) scale(0.83333)"><path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/></g></svg>

After

Width:  |  Height:  |  Size: 518 B

View file

@ -0,0 +1,248 @@
import Link from "next/link";
import type { ReactNode } from "react";
import type { Metadata } from "next";
import { SiteShell } from "@/components/site-shell";
import { mono, pre, table, td, th } from "../openontology/ui";
export const metadata: Metadata = {
title: "OpenIcon · LogicSRC",
description:
"OpenIcon is an icon set as a folder: one openicon.json naming every icon with aliases and keywords, 24x24 currentColor SVGs, and three terminal glyphs per icon (Nerd Font, Unicode, ASCII) so a TUI draws the best one it can. Brand logos say they are brands.",
alternates: { canonical: "/openicon" }
};
/** From the reference set, profullstack/openicon; copied into public/openicon. */
const SAMPLE = [
"mail", "phone", "chat", "at", "link", "search", "settings", "filter", "menu", "add", "edit", "delete",
"copy", "share", "download", "upload", "refresh", "bell", "calendar", "user", "users", "lock", "key", "eye",
"home", "map-pin", "globe", "cloud", "database", "package", "terminal", "code", "git-branch", "git-pull-request",
"rocket", "sparkles", "rss", "github", "x", "bluesky", "mastodon", "discord", "slack", "signal", "whatsapp",
"youtube", "npm"
];
/** key, Nerd Font codepoint, Unicode, ASCII: straight from openicon.json. */
const GLYPHS: Array<[string, string, string, string]> = [
["mail", "U+F01F0", "✉", "@"],
["phone", "U+F0DF0", "☎", "tel"],
["link", "U+F0339", "🔗", "~"],
["settings", "U+F0493", "⚙", "*"],
["terminal", "U+F018D", "⌨", ">_"],
["git-branch", "U+F418", "⎇", "Y"],
["warning", "U+F002A", "⚠", "!"],
["github", "U+F02A4", "🐙", "gh"]
];
const EXAMPLE = `{
"openicon": "0.1",
"name": "OpenIcon",
"license": "MIT",
"made_by": "ai",
"grid": { "size": 24, "stroke": 2, "padding": 2 },
"sizes": [16, 24, 32, 64, 128],
"icons": [
{ "key": "mail", "category": "communication",
"aliases": ["email", "envelope"], "keywords": ["message", "inbox"],
"svg": "svg/mail.svg", "png": "png/{size}/mail.png",
"tui": { "nerd": "\\u{f01f0}", "nerd_code": "f01f0",
"nerd_name": "md-email_outline", "unicode": "✉", "ascii": "@" } },
{ "key": "github", "category": "brand", "brand": true,
"trademark": "GitHub and its logo are trademarks of their owner. …",
"source": "simple-icons:github", "license": "CC0-1.0", "made_by": "human",
"svg": "svg/github.svg",
"tui": { "nerd_code": "f02a4", "unicode": "🐙", "ascii": "gh" } }
]
}`;
const RULES: Array<[string, string]> = [
["A set is a folder", "openicon.json at the root, every path relative to it; unknown keys are kept."],
["The key is the name", "Kebab case: mail, git-pull-request. The key is the file name; a brand's key is its own name."],
["Aliases find one icon, keywords find many", "email is mail everywhere, so every key and alias is unique; keywords like money are shared search terms."],
["Files take the text colour", "24x24 view box, currentColor, and a grid block stating size, stroke and padding so sets can be matched."],
["Every icon has terminal glyphs", "tui.nerd (with its codepoint and Nerd Fonts name), tui.unicode (one character), tui.ascii (1 to 4 printable characters)."],
["A terminal picks the best it can draw", "Nerd, then Unicode, then ASCII, from OPENICON_GLYPHS, NERD_FONT=1 or the locale. Pad to the cell width: an emoji can be two columns."],
["A brand says it is one", "brand: true, a trademark note, its source and its own licence. Logos are taken from their owners or a set that publishes them, never redrawn."],
["The set says what made it", "made_by and the W3C AI disclosure vocabulary, per set and per icon: the reference set's drawn icons are ai, its logos human."]
];
const ABSENT: Array<[string, string]> = [
["No colour", "Icons take the text colour. A brand's own colour is optional and ignorable."],
["No style variants", "Outline and filled are two sets, or two keys. One key, one drawing."],
["No webfont format", "A font is a build output a set may ship; SVG and the tui glyphs are the interchange."],
["No name registry", "Keys are the set's own; aliases are how it answers to another set's names."],
["No animation", "A spinner is a component, not an icon."]
];
export default function OpenIconPage(): ReactNode {
return (
<SiteShell active="OpenIcon">
<div className="band">
<div className="section-head">
<p className="eyebrow">LogicSRC standards surface</p>
<h2>OpenIcon</h2>
<p>
An icon set as a folder, and the first one that knows what to draw in a terminal: SVG in a browser, a
Nerd Font glyph in a patched terminal, a Unicode symbol in a plain one, ASCII over a serial line.
</p>
</div>
<p style={{ color: "#41505d" }}>
Every interface needs the same few hundred icons, and every set names them differently: Lucide&apos;s mail
is Font Awesome&apos;s envelope is Material&apos;s email. Brand logos sit beside drawn icons as if a
trademark were just another shape. And no set says what an icon should be when there are no pixels, so
TUIs hard-code a Nerd Font codepoint and show a box everywhere else.
</p>
<p style={{ color: "#5b6b7a" }}>
Status: 0.1. A sibling of <Link href="/openemoji">OpenEmoji</Link>.
</p>
</div>
<div className="band">
<div className="section-head">
<h2>The reference set</h2>
<p>
370 icons: 259 drawn on a 24x24 grid with 2px strokes, 111 brand logos from Simple Icons and Font Awesome
Free. <a href="https://github.com/profullstack/openicon">profullstack/openicon</a>.
</p>
</div>
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(76px, 1fr))", gap: "0.45rem" }}>
{SAMPLE.map((key) => (
<figure
key={key}
style={{
margin: 0,
padding: "0.75rem 0.25rem 0.5rem",
border: "1px solid #e3e6e0",
borderRadius: "0.6rem",
background: "#fff",
textAlign: "center"
}}
>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src={`/openicon/${key}.svg`} alt="" width={28} height={28} />
<figcaption style={{ fontSize: "0.7rem", color: "#5b6b7a", marginTop: "0.35rem" }}>{key}</figcaption>
</figure>
))}
</div>
</div>
<div className="band">
<div className="section-head">
<h2>Three glyphs per icon</h2>
<p>What a TUI draws, best first. Your browser probably has no Nerd Font, so that column shows the codepoint.</p>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>Icon</th>
<th style={th}>Nerd Font</th>
<th style={th}>Unicode</th>
<th style={th}>ASCII</th>
</tr>
</thead>
<tbody>
{GLYPHS.map(([key, nerd, unicode, ascii]) => (
<tr key={key}>
<td style={td}>
<code style={mono}>{key}</code>
</td>
<td style={td}>
<code style={mono}>{nerd}</code>
</td>
<td style={{ ...td, fontSize: "1.2rem" }}>{unicode}</td>
<td style={td}>
<code style={mono}>{ascii}</code>
</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>The shape</h2>
<p>openicon.json, trimmed to a drawn icon and a brand logo.</p>
</div>
<pre style={pre}>{EXAMPLE}</pre>
</div>
<div className="band">
<div className="section-head">
<h2>The eight rules</h2>
<p>Every one of them degrades rather than fails.</p>
</div>
<table style={table}>
<thead>
<tr>
<th style={th}>Rule</th>
<th style={th}>What it means</th>
</tr>
</thead>
<tbody>
{RULES.map(([rule, meaning], index) => (
<tr key={rule}>
<td style={td}>
<strong>
{index + 1}. {rule}
</strong>
</td>
<td style={td}>{meaning}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>What is deliberately absent</h2>
</div>
<table style={table}>
<tbody>
{ABSENT.map(([what, why]) => (
<tr key={what}>
<td style={td}>
<strong>{what}</strong>
</td>
<td style={td}>{why}</td>
</tr>
))}
</tbody>
</table>
</div>
<div className="band">
<div className="section-head">
<h2>Use it</h2>
<p>
<code style={mono}>icon</code> in <a href="https://github.com/profullstack/cli-tools">profullstack/cli-tools</a>{" "}
builds the set; <a href="https://hqtui.com">hqtui</a> ships it as its default icon pack.
</p>
</div>
<pre style={pre}>{`icon build --out ./openicon # openicon.json, svg/, png/, sprite.svg
icon glyph mail # the best glyph this terminal can draw
// hqtui
import { icon } from "@profullstack/hqtui";
icon("mail"); // "\\u{f01f0}", "✉" or "@"`}</pre>
</div>
<div className="band">
<div className="section-head">
<h2>Where everything lives</h2>
</div>
<ul style={{ color: "#41505d", lineHeight: 1.9, paddingLeft: "1.1rem" }}>
<li>
<Link href="/docs/openicon">Specification</Link>: the shape, eight rules, discovery, the mapping from Lucide,
Tabler, Font Awesome, Nerd Fonts and Simple Icons
</li>
<li>
<a href="https://github.com/profullstack/openicon">profullstack/openicon</a>: the reference set
</li>
<li>
<Link href="/openemoji">OpenEmoji</Link>, the sibling format for emoji
</li>
</ul>
</div>
</SiteShell>
);
}

View file

@ -97,6 +97,7 @@ export const FAMILIES: Family[] = [
s("openrecipe", "OpenRecipe.md", "One Markdown file that is a recipe, with schema.org derived from it and never the reverse"),
s("opensong", "OpenSong", "One plain-text file that is a song: title, style, exclusions and lyrics as the blocks a generator takes, kept beside the audio"),
s("openemoji", "OpenEmoji", "An emoji set as a folder: one file that states coverage, licence and whether a person or a model drew it, and glyphs named by the codepoints they draw"),
s("openicon", "OpenIcon", "An icon set as a folder: every icon named, with aliases, 24x24 currentColor SVGs and a Nerd Font, Unicode and ASCII glyph each, so a terminal draws the best one it can"),
s("openthreat", "OpenThreat", "One file a security tool serves about what it found in the open: public subjects only, secrets never located"),
s("openrental", "OpenRental", "One file an operator serves about the agents and file swarms it rents out: members, metadata and rates through CoinPay", { landing: undefined, status: "draft" }),
s("opensite", "OpenSite", "One record about a page or a site: the card a reader would draw, declared by the site or read from it, kept by an index"),

127
docs/openicon.md Normal file
View file

@ -0,0 +1,127 @@
# OpenIcon
OpenIcon is an icon set as a folder: one `openicon.json` at the top that names every icon, says where its files are and who or what drew it, and gives each icon three terminal glyphs, so the same `mail` icon is an SVG in a browser, a PNG in an email, 󰇰 in a terminal with a Nerd Font, ✉ in one without, and `@` over a serial line. It is maintained by Profullstack, Inc. as part of the LogicSRC open-standards surface.
Status: **0.1**. A sibling of [OpenEmoji](/openemoji): files first, one small descriptor, `made_by` on the work.
Slug: `openicon`
## The problem
Every interface needs the same few hundred icons: mail, phone, link, search, settings, a trash can, a GitHub logo. Every icon set ships them under its own names (Lucide's `mail` is Font Awesome's `envelope` is Material's `email`), in its own folder layout, with its licence in a README, and with brand logos mixed in beside drawn icons as if a trademark were just another shape.
Terminals are worse. A TUI that wants an icon has three choices, all bad: hard-code a Nerd Font codepoint and show a box on every terminal without one, hard-code an emoji and break the column width, or draw nothing. No icon set says what an icon should be when there are no pixels at all.
## The shape
```
openicon.json the descriptor (rule 1)
svg/<key>.svg 24x24, currentColor
png/<size>/<key>.png rendered from the SVG
sprite.svg every icon as a <symbol>, optional
```
```json
{
"openicon": "0.1",
"name": "OpenIcon",
"version": "2026-09-24",
"license": "MIT",
"made_by": "ai",
"disclosure": "ai-generated",
"ai_model": "claude-opus-5-5",
"ai_provider": "Anthropic",
"grid": { "size": 24, "stroke": 2, "padding": 2 },
"sizes": [16, 20, 24, 32, 48, 64, 128, 256],
"sprite": "sprite.svg",
"icons": [
{
"key": "mail",
"name": "Mail",
"category": "communication",
"aliases": ["email", "envelope"],
"keywords": ["message", "inbox"],
"svg": "svg/mail.svg",
"png": "png/{size}/mail.png",
"tui": { "nerd": "󰇰", "nerd_code": "f01f0", "nerd_name": "md-email_outline", "unicode": "✉", "ascii": "@" }
},
{
"key": "github",
"name": "GitHub",
"category": "brand",
"brand": true,
"trademark": "GitHub and its logo are trademarks of their owner. Use them to refer to GitHub, not to imply endorsement.",
"source": "simple-icons:github",
"license": "CC0-1.0",
"made_by": "human",
"svg": "svg/github.svg",
"png": "png/{size}/github.png",
"tui": { "nerd": "󰊤", "nerd_code": "f02a4", "nerd_name": "md-github", "unicode": "🐙", "ascii": "gh" }
}
]
}
```
Two entries from the reference set: a drawn icon and a brand logo.
## The rules
There are eight, and every one of them degrades rather than fails.
**1. A set is a folder with `openicon.json` at its root.** Every path is relative to that file. Top-level keys a reader should understand: `openicon` (the spec version, required), `name` (required), `version`, `license` (SPDX, covering every icon without its own), `homepage`, `grid`, `sizes`, `sprite` and `icons` (required). Unknown keys are kept and ignored.
**2. An icon's key is its name, in kebab case.** Lowercase letters and digits joined by single hyphens: `mail`, `git-pull-request`, `arrow-up-right`. The key is also the file name. A brand's key is the brand's own name in the same form (`github`, `stack-overflow`), never a product code.
**3. Aliases find one icon; keywords find many.** `aliases` are other names people type for the icon (`email` for `mail`, `trash` for `delete`), and every key and alias in a set is unique, so `icon('email')` is never ambiguous. `keywords` are search terms and may be shared (`money` finds `wallet`, `dollar` and `coins`). A reader resolves a name against keys first, then aliases.
**4. Files are monochrome and take the text colour.** An SVG uses `currentColor` for every stroke and fill and a `0 0 24 24` view box. `grid` states the drawing rules the set keeps (`size`, `stroke` width, `padding`), so a reader mixing sets can scale one to match another. `png` may contain `{size}`, filled from `sizes`; PNGs are rendered in one colour, which the set's README states. A set without PNGs or without a sprite is still a set.
**5. Every icon has terminal glyphs.** `tui.unicode` is one character or emoji that says the same thing, and `tui.ascii` is 1 to 4 printable ASCII characters, spaces only inside (`[ ]`, `>_`, `@`). `tui.nerd` is the Nerd Font character when there is one, with `nerd_code` (hex) and `nerd_name` (the Nerd Fonts glyph name, without `nf-`), so a reader can check it against the Nerd Fonts release it has. An icon with no Nerd Font glyph leaves the three `nerd` fields out.
**6. A terminal picks the best glyph it can draw.** Nerd first, then Unicode, then ASCII. A Nerd Font cannot be detected from inside a terminal, so a reader takes it from configuration: `OPENICON_GLYPHS=nerd|unicode|ascii` wins, `NERD_FONT=1` means nerd, a UTF-8 locale means unicode, anything else means ascii. A reader that reserves a fixed cell width pads to it, because an emoji may be two columns wide where a Nerd glyph is one.
**7. A brand says it is one.** A logo carries `brand: true`, a `trademark` note, its `source` (`simple-icons:<slug>`, `font-awesome:<name>`, or a URL) and its own `license`, because the licence of a drawing is not permission to use a mark. Brand logos are taken from their owners or from a set that publishes them; a set does not redraw them.
**8. The set says what made it.** `made_by` is `human`, `ai` or `both`, the [OpenWebring](/docs/openwebring) vocabulary, and `disclosure`, `ai_model`, `ai_provider` and `ai_prompt_url` are the W3C AI Content Disclosure vocabulary, as in [OpenEmoji](/docs/openemoji). Any of them may be repeated on an icon that differs from the set: in the reference set the drawn icons are `ai` and every brand logo is `human`.
## Discovery
- A `<link rel="openicon" href="/icons/openicon.json">` in a page's head: this page draws its icons from that set.
- `/.well-known/openicon.json` on a site: a set, or a list of sets, `{"openicon": "0.1", "sets": [{"name": "…", "url": "…/openicon.json"}]}`.
- A package: a set published to npm, PyPI or a git repository keeps `openicon.json` at the package root. Served as JSON with CORS open.
## Mapping from what exists
| OpenIcon | Lucide | Tabler | Font Awesome | Nerd Fonts | Simple Icons |
|---|---|---|---|---|---|
| `key` | icon name | icon name | icon name | glyph name after the prefix | slug |
| `aliases` | `aliases` in the icon's JSON | none | `aliases.names` | none | `aliases.aka` |
| `keywords` | `tags` | `tags` | `search.terms` | none | none |
| `category` | `categories` | `category` | `categories` | the font the glyph came from | none |
| `grid` | 24, stroke 2 | 24, stroke 2 | 512 tall, filled | a font cell | 24, filled |
| `tui.nerd` | none | none | the codepoint, in a patched font | the codepoint | none |
| `brand` | none (no brands) | a few, unmarked | the `brands` style | some, unmarked | every icon |
| `license` per icon | none | none | per style | none | one for all (CC0) |
An importer from any of these is a rename and a copy. Only `tui.unicode` and `tui.ascii` need a person or a model to write them, because none of the sets carries them.
## What is deliberately absent
- **No colour.** Icons are monochrome and take the text colour. A brand's own colour is the brand's business; a set may add `hex` to a brand entry and a reader may ignore it.
- **No style variants.** Outline, filled and duotone versions of one icon are three sets, or three icons with three keys. One key, one drawing.
- **No icon font format.** A webfont is a build output a set may ship, not part of the spec; the SVGs and the `tui` glyphs are the interchange.
- **No registry of names.** Keys are the set's own. Aliases are how one set answers to another's names.
- **No animation.** A spinner is a component, not an icon.
## Reference implementation
The reference set is [profullstack/openicon](https://github.com/profullstack/openicon): 370 icons, 259 drawn on a 24x24 grid with 2px strokes and 111 brand logos from Simple Icons and Font Awesome Free. 357 of them have a Nerd Font glyph. It is built by `icon` in [profullstack/cli-tools](https://github.com/profullstack/cli-tools):
```sh
icon build --out ./openicon # openicon.json, svg/, png/, sprite.svg
icon glyph mail # 󰇰, ✉ or @, whichever this terminal can draw
```
[hqtui](https://hqtui.com) ships the set as its default icon pack, so `icon('mail')` in a TUI draws the best glyph the terminal has.
Related: [OpenEmoji](/docs/openemoji) for the sibling format; [OpenWebring](/docs/openwebring) for `made_by`.