@orvain/icons
v0.1.3
Published
Orvain Design System — shared SVG icons (one module per icon) consumed by Vue and React
Maintainers
Readme
@orvain/icons
The shared SVG icons behind the Orvain Design System — outline icons (one
style, stroked in currentColor), consumed by both
@orvain/react and @orvain/vue. Every icon is its own module, so a bundler
ships only the icons you import.
Icons are one-colour glyphs that follow currentColor. Multi-colour artwork
(brand logos, country flags, emoji, file-type badges and the empty-state
illustrations) lives in @orvain/assets.
Commercial software. See LICENSE — use is governed by your agreement with Orvain Labs.
Install
npm install @orvain/iconsMost people never install this directly — it ships as a dependency of
@orvain/react and @orvain/vue, and you render icons through their <Icon>
component:
import { Icon } from '@orvain/react';
import { iconClockAdd, iconTrash } from '@orvain/icons';
<Icon icon={iconClockAdd} size={20} />
<Icon icon={iconTrash} title="Delete" /> {/* labelled, exposed to assistive tech */}
<Icon name="clock-add" /> {/* by name: draws the icon registered as "clock-add" */}Names need the icon to be registered once at start-up (see Icons by name).
Install it on its own when you need the raw path data — for a canvas renderer, a PDF exporter, or an icon picker UI.
Usage
import { iconClockAdd } from '@orvain/icons';
// { name: 'clock-add', viewBox: '0 0 24 24', body: '<path …/>' }
import { iconRegistry, iconNames, type IconName } from '@orvain/icons';
const def = iconRegistry['clock-add']; // the same object as iconClockAdd| Export | What it is |
|---|---|
| iconChevronDown, iconClockAdd, … | one IconDefinition per icon: icon + the name in PascalCase (arrow-up-01 → iconArrowUp01). Only the ones you import are bundled. |
| registerIcons(…) | makes icons renderable by name (see below) |
| lookupIcon(name) | the registered icon for a name, or undefined |
| iconRegistry | Record<IconName, IconDefinition>: every icon, keyed by name. Using it includes every icon. |
| iconNames | the list of built-in names (includes every icon) |
| IconName | a string-literal union of those names, so typos fail to compile |
| IconSource | IconName \| IconDefinition: what the components' icon props accept (needs TypeScript ≥ 5.4) |
| isIconDefinition, iconNameOf | tell a definition from other values; get an icon's name from a name or definition |
| defaultIconViewBox, defaultIconStrokeWidth | rendering defaults |
| getIconsVersion, subscribeIcons | low level, for renderers: re-render when icons are registered |
body is a raw SVG fragment intended to be injected inside an
<svg> element with the matching viewBox. It uses currentColor, so icons
inherit the colour of their container.
Icons by name
<Icon name="…"> and every component prop that takes an icon name draw the icon
registered under that name. Nothing is registered by default. The icons the
components draw on their own (chevrons, close, check marks, spinners, status icons,
defaults) are imported as definitions inside each component, so they always render
and need no registration; only names an app passes in do. Register once,
in the app's entry file, before the first render:
import { registerIcons, iconBellRing, iconCheckCircle } from '@orvain/icons';
registerIcons(iconBellRing, iconCheckCircle); // also takes arrays and objects: registerIcons({ iconBellRing }, [iconCheckCircle])
import '@orvain/icons/all'; // or: every icon (~100 KB gzipped)| Setup | In the bundle | Names render |
|---|---|---|
| definitions only (icon={iconBellRing}) | those icons | no names needed |
| registerIcons(…) with the icons used by name | those icons (about 0.2 KB gz each) | synchronously, server-side too |
| import '@orvain/icons/all' | every icon, ~100 KB gz | synchronously, server-side too |
| import('@orvain/icons/all') (lazy) | a ~100 KB gz chunk, loaded after start-up | once the chunk lands; not in server HTML |
| nothing | no icons | not at all: an empty <svg> and a development warning per name |
Custom icons. A definition is plain data: { name: 'acme-logo', viewBox:
'0 0 24 24', body: '<path d="…" stroke="currentColor"/>' }. Pass it directly
(<Icon icon={acmeLogo} />, icon={acmeLogo} on any icon prop), which needs no
registration. For names that arrive as data (a CMS, a config file),
registerIcons(acmeLogo) makes 'acme-logo' resolvable like a built-in name;
registering a built-in name replaces that icon. IconName lists only the
built-in names, so a custom name from typed code needs a cast; prefer the
definition there.
Why registration and not automatic lookup. A component only sees a name
string at runtime, so no bundler can tell which icons a name will need: either
every icon ships with any component that accepts a name (the old default,
~100 KB gz for a Tooltip), or names are resolved asynchronously (a flash, and
missing from server-rendered HTML), or the app says which names it uses. This
package does the last: explicit, synchronous and SSR-safe, and it needs no
bundler configuration. The library never loads icons on its own: a dynamic
import() of the full set inside the package would land in the initial bundle
under esbuild (inlined without code splitting, pulled into a shared chunk with
it). An SVG sprite was not used either: it needs the app to serve an asset file,
and the shadow tree of <use> would stop components from styling individual
icon paths.
@orvain/icons/all
A side-effect import that registers every icon (listed in sideEffects).
Storybook and the landing page use it.
@orvain/icons/resolve
resolveIcon(nameOrDefinition) returns the definition: definitions pass through,
names resolve when registered. withIconNames(component) returns the component;
the component packages export their name-taking components through it.
Accessibility
Icon bodies carry no <title> and no roles — labelling belongs to whatever
renders them. The <Icon> components in @orvain/react and @orvain/vue mark
icons aria-hidden="true" unless given title, aria-label or
aria-labelledby. Do the same if you render the definitions yourself.
Adding icons (maintainers)
Icons are authored in src/registry.ts, the single source of truth.
scripts/gen-icons.mjs (run by pnpm build, or pnpm gen:icons) generates the
per-icon modules, the barrel and the registry built from them into
src/generated/ (gitignored). It fails when two names map to the same export
name.
Known limitations
iconRegistry,iconNamesand@orvain/icons/allinclude every icon (~410 KB raw, ~100 KB gzipped). Import definitions, or register only the icons used by name, to avoid that.IconSizeTokenandiconSizeMapare deprecated and will be removed in the next major.
Related packages
@orvain/react,@orvain/vue— the component libraries
