Skip to content
AstroCraft Docs
On this theme

Icons

8-BitQuest ships two icon systems, and the split is deliberate. <Icon> is a large, general-purpose line-icon registry — 571 icons behind one typed component. <PixelIcon> is a small hand-ported pixel-art set used where the retro identity needs a chunky, crisp-edged glyph. Both are inlined at build time, so no icon bytes reach client JavaScript, and both follow the same primitive contract as the rest of the UI library.

<Icon> — the main registry

src/components/svg/icons/ holds one auto-generated registry of 571 icons (~450 KB of markup) behind a single component. The bulk are Stratis UI line icons across a dozen categories, with an 18-mark filled social/brand set on top. icons.ts is generated and marked “do not edit by hand”; the component’s own README.md is the regeneration runbook.

---
import { Icon } from "@components/svg/icons";
---

<Icon name="arrow-right" />
<Icon name="mail" size="lg" class="text-primary" />
<Icon name="check" title="Complete" />

Four things govern its use:

  • Typed names. name is the generated IconName union, so an illegal name fails astro check and every icon autocompletes. There is no way to reference an icon that does not exist.
  • Sizes. size is sm / md / lg / xl, mapping to size-4 through size-8, defaulting to md. Or pass a size-* class directly for an off-scale size.
  • Colour is currentColor. Recolour with token utilities — text-primary, text-error, text-success — so dark mode is free. The brand marks are filled silhouettes with knockout negative space, so they theme the same way (one tone, not brand colours).
  • Accessibility. An icon is decorative by default (aria-hidden). Pass a title and it flips to role="img" with a <title>, giving it an accessible name.

The barrel exports Icon, the bare icon recipe, iconNames (for iterating) and the IconName type.

<PixelIcon> — the retro sibling

src/components/svg/pixel-icons/ is a second, much smaller registry of 18 pixel-art glyphs — socials, brand marks and tech-stack icons (arrow-left, arrow-right, code, component, email, flash, git-branch, github, globe, linkedin, meteor, moon, package, rss, server, twitter, type, youtube). It renders on real pages — the home Tech Stack chips, the About skill tree, the footer socials, the ThemeToggle’s meteor/moon flip — so it is not dev-only and not redundant with <Icon>.

The two registries differ in a way that matters:

  • <Icon> is stroke-based on a fixed 24×24 viewBox and sizes by size-* (equal width and height).
  • <PixelIcon> is fill-based (fill="currentColor" plus shape-rendering="crispEdges" to keep the pixels sharp), and each glyph sits on its own native, often non-square viewBox. Because the glyphs are not square, it sizes by height plus w-autosm/md/lg/xl map to h-4 through h-8 — so the aspect ratio is preserved. Merging the two would squash the non-square glyphs like youtube and email, which is exactly why they stay separate.

Otherwise it follows the same contract: an exported pixelIcon recipe, data-slot="pixel-icon", typed PixelIconName names, and the same decorative-by-default accessibility (title promotes it to role="img"). The barrel exports PixelIcon, the pixelIcon recipe, pixelIconNames and the PixelIconName type.

Build-time only

Both registries map each name to inline SVG markup, which the component inlines with <Fragment set:html>. Everything happens at build — no icon bytes reach client JavaScript. The registry’s own note names the ceiling: importing it from a client <script> would ship the whole ~450 KB set, so if you ever need icons at runtime, the upgrade path is per-file .svg imports or a sprite, not a client import of the registry.

Adding an icon

For a pixel glyph, add a hand-authored entry to pixelIcons.ts following the existing shape (the source note at the top of the file records the community set it was ported from) — the PixelIconName type picks it up automatically.

For a line icon, the icons.ts registry is generated from Figma via an MCP pipeline documented in svg/icons/README.md; regenerate rather than hand-editing the generated file. If you only need one or two extra line icons, the simplest path is to add them to that pipeline’s manifest and regenerate.

Seeing them all

/examples/ui has an Icons section that iterates the registry and shows every icon with its name and a live count — the fastest way to find the name you want rather than guessing.

NEXT STEPMotion & Animation