Back to home
Prelude DS Icons preview

Drawing the icon in Figma is just the beginning

Prelude DS Icons. An icon catalog that weighs 10.9 KB, has zero production dependencies, and updates itself by dropping an SVG into a folder.

Design EngineerPersonal project2026

A library nobody can find isn't a library, it's a folder

I've been building the iconography for a design system that runs in production, and that's where I learned where the real bottleneck is. It's not in the drawing. It's in everything that happens after.

An icon truly exists when someone who didn't draw it can find it by name or category, see it at the exact size they'll use it, inspect its code, copy it, and drop it into their screen without opening Figma, without installing anything, and without asking the design person. Until that point, the library is a directory of files with a nice name.

The options on the market always failed in the same place. They were either tied to a framework, or bundled inside a full UI library dragging its own visual identity, or they were pretty galleries with no SVG inspection or real export. None of them solved the distribution problem, which is the only one that matters when the team consuming the icons isn't the team producing them.

That's where Prelude comes from. It's not an icon set with a website on top. It's the distribution infrastructure for an icon set, and the set is what goes inside.

Zero dependencies isn't a technical constraint, it's the thesis

The decision that orders the entire project is that an icon catalog is infrastructure, and infrastructure has to survive the framework decisions of the team that uses it.

If the catalog needs React to open, it dies the day the team migrates. If it needs an npm install of 40 KB of runtime to render a grid of SVGs and open a modal, it's paying a toll for something that document.createElement and <dialog> solve natively. The entire application logic fits in 255 lines of JavaScript, and that number isn't an optimization anecdote: it's proof that the problem didn't require a framework.

The result is 10.9 KB of minified JS and CSS for the entire catalog, and zero production dependencies. Vite stays in the development layer, where it provides HMR and a build with hashed assets, without leaking into what the end user downloads.

Dropping a file is the entire API

The icon registry isn't maintained by hand. A Node script traverses /public/icons/, extracts name, category, tags, and sizes from each SVG, and generates icons.json. In development, chokidar watches the folder and regenerates the manifest on the fly. The reasoning is the same one I apply to any design system: any process that forces someone to register the same data in two places ends up out of sync. Always. The manifest is derived data, not maintained data. Adding an icon to the catalog is a single-step action, and that's the condition for the library to grow without me being the intermediary.

Normalize on consumption, not at the source

Each SVG is processed right before rendering: width and height attributes are stripped so it scales with context, and aria-hidden="true" and focusable="false" are injected so it doesn't announce to screen readers or enter the tab order. What matters is where this happens. Normalization runs at load time and the source files stay untouched. The SVG in the repository remains the pure design artifact, exportable to an icon font, a package, or back to Figma without carrying decisions made for a specific website. That's the difference between a source of truth and a source already contaminated by its first consumer.

Tokens here too

Spacing, typography, color, radii, shadows, and blurs live as custom properties on :root. Components consume tokens, never literal values. Changing the theme or adding dark mode means overriding properties, not rewriting stylesheets. It's exactly the same principle I apply in a product design system, applied to a tool with 794 lines of CSS, because scale doesn't change the argument.

The details you don't see in a screenshot

Each icon's detail view opens in a native <dialog>, which already brings modal behavior, backdrop, and Escape-to-close without any library. On top of that, focus enters the modal on open and returns to the trigger element on close, which is the part almost no custom implementation gets right.

The SVG code syntax highlighting doesn't use a library either. It's a regex-based tokenizer that wraps tags, attributes, and values in <span> elements with CSS classes. It saves an entire highlighting package for the single language this application needs to display.

The copy button uses the Clipboard API with a fallback to execCommand for older browsers, because a design system tool ends up being opened by someone in whatever corporate browser they've been given, not the one you have.

And all transitions respect prefers-reduced-motion. Accessibility here isn't an audit I passed at the end: it's semantic HTML (<dialog>, <nav>, <aside>, <section>), ARIA labels on every interactive element, full keyboard navigation, and a skip link, decided at the same moment each component was written.

Measurable Impact

10.9 KB

total catalog weight in production, minified JS and CSS

0

production dependencies, with only 4 in development

6

export sizes per icon, generated from a single source SVG

1 step

to add an icon to the catalog: drop the file and the manifest regenerates

What's next: from gallery to pipeline

The machine is built and now the content scales. Icon coverage grows by category (actions, navigation, media, shapes, and logos) and every new addition inherits normalization, six-size export, and accessibility for free.

Next is closing the full design-to-code cycle. First, automated vector quality control in CI: checking that each icon respects the grid, stroke weight, and optical size before entering the catalog, so consistency stops depending on my eye. Then, distribution as a package, so the same manifest that feeds the gallery also feeds product code. And finally, connection with Figma variables, so the name decided in design is the same one that reaches code, without manual translation.

The goal isn't to have more icons. It's to make adding icon number two hundred cost exactly the same as adding number six.