Repository navigation
Icon Library
The PixlPrfct icon library ("pxlprfct icons") is an MIT-licensed set of 679 stroke icons. Each icon is a single 24×24 path, and every icon comes in three weights (Light 1, Regular 1.5, Bold 2.5). The same set serves two purposes. Interface-only directional panel variants live separately in interfaceIconPaths; they render through icon() and are not advertised as downloadable font glyphs. It is the editor's own UI icon set (icon() in web/icons.js). It is also a user-facing library that inserts icons into designs as editable vector layers (web/icon-library.js). The canonical source is the separate pixlprfct_icons repository. This repo vendors that source through scripts/sync-icon-library.mjs, so do not hand-edit iconPaths, icon-keywords.js or icon-library-assets/. Add icons upstream and sync them here.
| Key files | web/icons.js, web/icon-keywords.js, web/icon-library.js, web/icon-library.css, web/icon-library-assets/ |
| Entry points | Editor command PixlPrfct Icon Library (Actions panel, category Libraries), registered in web/app.js by installIconLibrary(...); icon(name, weight) for UI chrome; iconSvg(name, weight) for portable SVG |
| Depends on |
workspace-window.js (floating window), svg-import.js parseSvg and the engine's paste_layers (insertion), inspector-dropdown.js (restyles the weight <select>) |
| Used by | All editor chrome that renders icon(), control-inspector.js and segmented-control.js (icon pickers for starter controls), mcp/reference.mjs (icons topic), the SVG Animator, project home, and the product site's icon showcase |
| Tooling | scripts/sync-icon-library.mjs, scripts/territory-icon-inventory.mjs |
| Related docs | docs/TERRITORY_ICON_COVERAGE.md, site/assets/icons/README.md, docs/LICENSING.md |
Every icon exists in two forms in this repo, and they must stay in sync:
| Form | Where | Used for |
|---|---|---|
| SVG path data |
iconPaths in web/icons.js (one d string per name, 24×24 viewBox) |
icon() (inline SVG in the UI), iconSvg() (Insert, Copy and Download in the library), MCP reference, control icon pickers |
| Icon font |
web/icon-library-assets/css/icons.css plus fonts/pixlprfct-icons-{light,regular,bold}.woff2
|
The library gallery grid and preview (<span class="pxi pxi-<name> pxi-<weight>">) and external users of the font |
The gallery shows font glyphs, but Insert, Copy SVG and Download SVG produce SVG from iconPaths. If upstream fonts and paths drift apart, a user sees one drawing and inserts another. tests/icon-library.test.mjs checks that every iconPaths name has a .pxi-<name>::before rule. It does not compare the drawings.
flowchart LR
U["pixlprfct_icons repo<br/>icons.json · package.json · fonts/ · css/ · LICENSE · territory-track.json"]
S["scripts/sync-icon-library.mjs"]
U --> S
S --> P["web/icons.js<br/>iconPaths + filledIcons"]
S --> K["web/icon-keywords.js<br/>iconKeywords"]
S --> A["web/icon-library-assets/<br/>fonts · css/icons.css · LICENSE · territory-track.json"]
S --> H["web/index.html<br/>icons.css?v=<version>"]
P --> I["icon() → UI chrome"]
P --> L["icon-library.js iconSvg()"]
K --> L
A --> G["Gallery glyphs (.pxi classes)"]
L --> X["Insert → parseSvg → editor.paste_layers"]
L --> C["Copy SVG / Download SVG"]
T["scripts/territory-icon-inventory.mjs"] -->|--check| A
| File | Role |
|---|---|
| web/icons.js |
iconPaths (679 entries, generated), filledIcons (generated; currently pixlprfct, territory-track, apple-logo, facebook-logo, google-logo), iconWeights = {light:1, regular:1.5, bold:2.5}, icon(name, weight='regular'), installIcons() (fills [data-icon] and [data-tool] elements and the undo/redo buttons). It also exports the unrelated helper componentLabelType. The sync script replaces only the generated declarations, so hand-written code below them survives. |
| web/icon-keywords.js |
iconKeywords: search aliases per icon name (541 icons have keywords). Includes Territory Track names, SF Symbol names such as brain.head.profile, and category tags such as rte and ios. |
| web/icon-library.js |
iconSvg(name, weight) and installIconLibrary({insert, notify}). |
| web/icon-library.css | Gallery layout: an auto-fill grid of 108 px cards, 32 px glyphs, and a detail footer. |
| web/icon-library-assets/css/icons.css |
@font-face "PixlPrfct Icons" at weights 300/400/700, with cache-busting ?v=<hash> URLs. Defines .pxi (base), .pxi-light/.pxi-regular/.pxi-bold, and one .pxi-<name>::before{content:"\eXXX"} per icon. |
web/icon-library-assets/fonts/ |
pixlprfct-icons-{light,regular,bold}.{woff2,ttf} — six font files (WOFF2 + TTF per weight), referenced by the CSS. The sync copies the upstream fonts/ folder. |
| web/icon-library-assets/territory-track.json | Territory Track coverage manifest (see below). |
web/icon-library-assets/LICENSE |
MIT notice ("Copyright (c) 2026 the pixlprfct contributors"). |
| scripts/sync-icon-library.mjs | Vendors the upstream repository into the editor. |
| scripts/territory-icon-inventory.mjs | Scans the Territory Track web (TerritoryIQ) and iOS sources for named icon references. Its --check mode fails on unmapped names. |
site/assets/icons/ |
LICENSE.txt (MIT) and README.md covering attribution for the 18 inline SVG samples on the product site (site/index.html). The site links to the hosted gallery and ZIP instead of shipping a second copy. |
This function returns inline SVG markup. The <svg> has class app-icon, viewBox="0 0 24 24", aria-hidden="true", round caps and joins, stroke-width = iconWeights[weight] ?? 1.5, and a single <path d>. Icons named in filledIcons use fill="currentColor" stroke="none" instead. Unknown names silently fall back to iconPaths.rectangle, so a typo renders a rectangle instead of throwing. This is the function that all editor chrome uses (button(label, glyph, …) helpers across web/).
This is the portable export used by Insert, Copy and Download. It wraps icon(), adds xmlns, width="24" and height="24", and replaces currentColor with #222222 so the file has a concrete paint outside the app. Unlike icon(), it throws Unknown icon for names that are not in iconPaths.
This call builds a workspaceWindow('PixlPrfct Icon Library', …) that contains:
- a search field. Filtering is a substring match against
name + ' ' + keywords.join(' '). The query is lowercased, and spaces become hyphens, so "arrow up" matchesarrow-up-…. - a weight
<select>built fromObject.keys(iconWeights).installInspectorDropdownsrestyles it as the shared dropdown because it sits inside.workspace-window. - a live count
- a grid of
aria-pressedbuttons, one per matching name, sorted alphabetically - a detail footer with the preview glyph, the name, the CSS usage string (
pxi pxi-<name> pxi-<weight>) and three actions: Insert icon, Copy SVG (navigator.clipboard.writeText, with a fallback message when the clipboard is unavailable) and Download SVG (<name>-<weight>.svg).
The returned function re-renders the window, opens it and focuses the search field.
In web/app.js, inside bind():
const showIconLibrary=installIconLibrary({insert:(svg,name)=>safe(()=>placeSvg(parseSvg(svg,name))),notify:toast});
commands.push({label:'PixlPrfct Icon Library',category:'Libraries',run:showIconLibrary});Insertion takes this path:
-
Insert icon calls
insert(iconSvg(selected, weight), '<name> <weight>')and closes the window. -
parseSvg(text, name)in web/svg-import.js converts the SVG into an engine paste payload ({shapes:[…]}). This is the same importer used for pasted or dropped SVG files. -
placeSvg(payload)centers the root at the viewport center in canvas coordinates. It callseditor.paste_layers(json, x, y, frameAt(pageShapes(), start)), so an icon dropped over a frame becomes that frame's child. It then selects the new layers, switches to the Select tool and shows the toast "SVG imported as editable layers".
The result is ordinary editable layers. svgLayers creates a root frame named <name> <weight>, with fill disabled, that holds a vector built from the path, using the chosen stroke width and a #222222 stroke. No link to the library is kept. Changing the weight or the drawing later means inserting the icon again. Icon instances are not components.
Other consumers reference icons by name, not by inserting geometry:
-
Starter controls.
control-inspector.jsandsegmented-control.jsoffer everyiconPathsname in a picker and store the name in control or segment config. Renaming an icon upstream breaks those references. -
MCP.
pixlprfct_referencewithtopic: 'icons'returns the filterediconPaths(mcp/reference.mjs), so agents can discover valid icon names for controls.
- Codepoints are in the Private Use Area, starting at U+E900 and assigned densely. The 679 icons currently occupy U+E900 to U+EBA6. All 679 codepoints are unique; this was checked against the vendored
icons.css. -
The list is append-only. The first 546 entries (
align-bottom…grammar-check) keep their original codepoints. The 133 additions in 0.9.0 (fromalienonward) were appended after them, as TERRITORY_ICON_COVERAGE.md records: "All original 546 names, drawings and codepoints are preserved". Never renumber or reuse a codepoint, because existing font users would get the wrong glyph. - The three weights are separate font files that share one family name and one codepoint map. The family is
"PixlPrfct Icons", and the weights map tofont-weight300/400/700. They are selected through.pxi-light,.pxi-regularor.pxi-bold.font-synthesis: noneprevents the browser from faking weights. - The SVG stroke widths (1, 1.5, 2.5) correspond to those font weights. Each font glyph is the outlined stroke at that width. That is why the path data needs only one drawing while the font needs three files.
- Cache busting happens at two levels. Each
@font-faceURL carries a content hash (?v=1e99c62f9317, and so on).web/index.htmllinksicons.css?v=<package version>(currently0.9.0), and the sync script rewrites that query string. Desktop and browsers otherwise keep stale fonts that hide new glyphs. - The upstream package tests check that glyphs are drawable, that SVGs stay within bounds, that codepoints are unique, and that font rebuilds are byte-identical (per TERRITORY_ICON_COVERAGE.md). Those tests live in
pixlprfct_icons, not here.
-
In
pixlprfct_icons(the canonical repo), add the icon toicons.jsonas{ "<name>": { "path": "<24×24 d>", "keywords": [...], "filled": false } }. Use a lowercase, hyphenated name. Draw for a 1.5 stroke with round caps and joins, and stay inside the 24×24 box. Rebuild the fonts there, so that the new name gets the next codepoint after the current highest one, and bump the package version. -
In this repo, run:
node scripts/sync-icon-library.mjs /path/to/pixlprfct_icons # default: ../pixlprfct_iconsThe script reads
icons.jsonandpackage.jsonand then:- rewrites the
export const iconPaths = {…};block inweb/icons.js - regenerates
web/icon-keywords.jsfrom entries that havekeywords - rewrites (or inserts before
iconWeights) thefilledIconsdeclaration - copies
fonts/,css/,LICENSEandterritory-track.jsonintoweb/icon-library-assets/ - updates the
icons.css?v=query inweb/index.html
- rewrites the
-
Run
node --test tests/icon-library.test.mjs tests/territory-icon-coverage.test.mjs. -
Rebuild the editor (
npm run build:editor) or refresh the served assets (node scripts/prepare-editor.mjs), so thatpublic/editor/and the desktop bundle pick up the new files. See Build-System. -
If the product site's samples or the icon count changed, update
site/index.htmlandsite/assets/icons/README.mdby hand. The site pins a specific upstream commit.
Editor-only UI icons. Because icons.js is generated, an icon added only here is overwritten by the next sync. Add UI glyphs upstream too.
PixlPrfct is used to design Territory Track, so the library also acts as a semantic replacement for every named icon that the Territory Track web app (TerritoryIQ, Angular) and iOS app reference. The manifest maps referenced names to library entries. Mapping coverage and rendering tests do not establish the provenance or legal clearance of every drawing; preserve the published license and attribution notices.
territory-track.json (schemaVersion 1) contains:
-
sources: the web and iOS commit hashes the scan was run against -
ignoredReferences: five scanner hits classified as non-icons (web:stable,web:csv,web:pdf,web:down,web:up) -
references: 599 entries of the form{group, name, uses: ["file:line", …], target}.groupis one ofweb,lucide,avatar,customorios, andtargetis a PixlPrfct icon name. The doc reports that these 599 references resolve to 313 distinct symbols.
scripts/territory-icon-inventory.mjs exports inventory(webRoot, iosRoot). It needs the typescript package, which is a dev dependency. The scan covers:
-
Web (
client/src/app/**/*.ts|.html):appIcon/nfIconattributes and bindings,<app-icon>/<nf-icon name>,ph-*classes,iconSvg/nfIconSvg/iconHtmlcalls, properties named likeicon/leadingIcon, methods whose names containicon, and*ICON*registries.NF_ICON_REGISTRYmaps tolucide,ICON_SVGmaps toavatar, and inline path literals map tocustom. The scan uses the TypeScript AST to follow conditional expressions and drop||/??fallbacks. -
iOS (Swift):
systemName:,systemImage:,systemImageName:,icon:andsymbol:arguments; tab tuples; and computedicon/symbolproperties and functions. Comparison literals andcase "x":labels are skipped.
CLI:
node scripts/territory-icon-inventory.mjs <TerritoryIQ> <TerritoryTrackIOS> # print inventory JSON
node scripts/territory-icon-inventory.mjs <TerritoryIQ> <TerritoryTrackIOS> out.json # write it
node scripts/territory-icon-inventory.mjs <TerritoryIQ> <TerritoryTrackIOS> --check # fail on unmapped namesAfter an icon change in Territory Track, run --check. Any unmapped reference needs an upstream mapping, a new icon or keyword, or an ignoredReferences entry.
-
Generated code. Edits to
iconPaths,filledIconsoriconKeywordsare lost on the next sync. The sync uses regular expressions to replace those blocks, so keep their exact declaration forms:export const iconPaths = {…\n};andexport const filledIcons = …;. -
Searchability is a test requirement for Territory Track names.
territory-icon-coverage.test.mjsasserts thaticonKeywords[target]includes each reference'sname, that every target exists, and that every target exports a<path d>at all three weights. -
icon()versusiconSvg()error behavior differs: one falls back to a rectangle, the other throws. -
Filled icons ignore the weight. Logos in
filledIconsrender as fills, so the weight has no visible effect. -
Licensing. The icons are MIT, separate from the editor's custom Community License. Keep
web/icon-library-assets/LICENSEandsite/assets/icons/LICENSE.txtwith any redistribution. docs/LICENSING.md lists this file among the third-party notices. -
Gallery depends on the font files. If
fonts/is missing or stale in a checkout, the gallery shows blank or wrong glyphs, but Insert still works because it usesiconPaths.
| Test | Checks |
|---|---|
| tests/icon-library.test.mjs | Every iconPaths name has a CSS class. iconSvg produces xmlns and the right stroke-width for all three weights. icon('asterisk') defaults to 1.5. Unknown names throw. |
| tests/territory-icon-coverage.test.mjs | Manifest integrity (no duplicates, targets exist, names are searchable, uses evidence is present, all five groups appear, specific required icons exist). A synthetic source tree checks that the scanner catches template branches, indirect maps, avatars, custom paths, Swift badges, shortcuts and tab tuples, and ignores conditional operands and case labels. |
- Canvas-Tools-and-Editing: paste and SVG placement on the canvas
-
Import-Design-Files:
parseSvg, the importer that turns inserted icons into layers - Components-Variants-and-Slots: starter controls that store icon names
-
MCP-Server: the
iconsreference topic - Web-Editor-Architecture, Build-System
- Code:
web/icons.js,web/icon-keywords.js,web/icon-library.js,web/icon-library.css,web/icon-library-assets/,scripts/sync-icon-library.mjs,scripts/territory-icon-inventory.mjs,web/app.js(installIconLibrarywiring andplaceSvg) - Docs: docs/TERRITORY_ICON_COVERAGE.md (coverage numbers and local verification on 2026-10-30), site/assets/icons/README.md
- Upstream:
github.com/ninefingerwoodshop-ops/pixlprfct_icons(gallery atninefingerwoodshop-ops.github.io/pixlprfct_icons/)
Start
Core
- Rust Document Engine
- Document Model & File Format
- Web Editor Architecture
- Rendering & Export
- Build System
Editor features
- Canvas Tools & Editing
- Layout, Auto Layout & Grid
- Components, Variants & Slots
- Styles, Variables & Paints
- Prototyping, Behaviors & Controls
- Import: Design Files & SVG
- SVG Animator
- Panel Workspace — tear-outs, icons, side flyouts and saved layouts
- Icon Library
Platforms
- Hosting Modes
- Self-Hosted Server
- Cloud API & Database
- Teams, Libraries & Collaboration
- Desktop App
- MCP Server
Practice