Icons
Iconify + Phosphor icon reference. Inline SVGs via @iconify/react, no CDN calls.
BDS uses Phosphor icons via @iconify/react. Icons render as inline SVGs — no CDN, no flash-of-unstyled-icon, no bundle bloat from importing all icons.
Setup
| Package | Purpose |
|---|---|
@iconify/react | Renders any Iconify icon as a React <Icon> component |
@iconify-json/ph | Phosphor icon data (bundled for offline / SSR) |
BDS registers the Phosphor collection at startup in .storybook/preview.tsx via addCollection(). No CDN calls needed.
Use it for
- Inline icons in buttons, badges, tooltips, fields
- Decorative icons in cards and headers
- Status / state markers paired with text
- Anywhere a small (≤ 32px) glyph carries meaning
For larger illustrations and brand marks, use a static SVG asset directly. For non-icon visual primitives (ServiceTag, avatar fallbacks), use the dedicated component.
Import
import { Icon } from '@iconify/react';For BDS-internal usage, prefer the named constants in components/icons.ts:
import { Star } from '@brikdesigns/bds/icons';
<Icon icon={Star} />Usage
Direct string reference
The icon prop accepts a collection:name string. Phosphor icons use the ph: prefix.
<Icon icon="ph:star-fill" />
<Icon icon="ph:check" />
<Icon icon="ph:x-circle" />Find available icons at phosphoricons.com.
BDS icon constants
For commonly-used icons in BDS components, prefer the named constants — protects against typos and centralizes the icon vocabulary.
import { Star, Check, XCircle } from '@brikdesigns/bds/icons';
<Icon icon={Star} />
<Icon icon={Check} />
<Icon icon={XCircle} />Constants follow the pattern export const Name = 'ph:icon-name'.
Sizing
Iconify icons inherit font-size from their parent or accept explicit width / height props.
{/* Inherits parent font-size — recommended */}
<span style={{ fontSize: 24 }}>
<Icon icon="ph:star-fill" />
</span>
{/* Explicit size */}
<Icon icon="ph:star-fill" width={24} height={24} />For inline icons inside text, use 1em sizing so the icon scales with the surrounding text:
<button>
<Icon icon="ph:plus" style={{ fontSize: '1em' }} />
Add item
</button>Phosphor weights
Phosphor provides six weights per icon. BDS primarily uses the regular and fill variants.
| Weight | Suffix | Use |
|---|---|---|
| Regular | ph:name | Default. Most contexts. |
| Bold | ph:name-bold | Emphasis, IconButton labels at sm size |
| Fill | ph:name-fill | Active/selected states, status badges |
| Light | ph:name-light | Decorative or quiet contexts |
| Thin | ph:name-thin | Rare; very specific design intent |
| Duotone | ph:name-duotone | Empty states, illustrations |
<Icon icon="ph:star" /> {/* outline, default weight */}
<Icon icon="ph:star-fill" /> {/* solid */}
<Icon icon="ph:star-bold" /> {/* heavier outline */}Color / theming
Iconify icons render with fill="currentColor", so they inherit color from the CSS color of any ancestor — no per-icon fill prop needed. To recolor an icon, set color on it or a parent, ideally to a BDS text token rather than a raw hex:
{/* inherits the surrounding text color */}
<span style={{ color: 'var(--text-muted)' }}>
<Icon icon="ph:info" /> Quiet hint
</span>
{/* status icon */}
<Icon icon="ph:check-circle-fill" style={{ color: 'var(--text-positive)' }} />Text tokens commonly used for icon fill:
| Token | Use |
|---|---|
--text-primary | Default icon next to body text |
--text-secondary / --text-muted | Quiet / secondary icons |
--text-link | Icons inside links |
--text-positive / --text-negative / --text-info | Status icons |
--text-inverse | Icons on dark / inverted surfaces |
BDS text tokens are mode-aware, so an icon colored with one retones automatically in brik-dark — prefer them over hardcoded colors.
Service-line glyphs (ServiceTag)
ServiceTag's per-service glyphs are not Phosphor icons — they're custom SVG assets under components/ui/ServiceTag/icons/. They render via CSS mask-image with background-color: currentColor (not <img src>), so the fill is CSS-recolorable and inherits the tag's service text token:
.bds-service-tag--marketing { color: var(--text-service-marketing-on-light); }
.bds-service-tag__icon { background-color: currentColor; /* masked glyph */ }Each service line carries a mode-aware --text-service-{line}-on-light / -on-dark pair (brand, marketing, information, product, back-office), so the glyph retones per theme. Reach for ServiceTag rather than a raw <img> whenever you need a service-line glyph — Phosphor does not cover every icon need.
Adding icons to BDS
- Find the icon name at phosphoricons.com — copy the kebab-case name.
- Add a named constant to
components/icons.tsusing theph:icon-nameformat. - (Optional) Add the icon to a category in the Storybook stories so other contributors can find it.
// components/icons.ts
export const Heart = 'ph:heart';
export const HeartFill = 'ph:heart-fill';When not to use
Don't use raw SVG inline. Iconify provides 200,000+ icons across multiple collections — you almost never need a one-off SVG. If the design calls for a non-Phosphor icon, evaluate whether a Phosphor alternative is acceptable before committing a custom asset.
- Don't use Icons for brand marks. Logos and brand-specific illustrations are static SVG assets, not icon-system components.
- Don't use Icons without semantic context. An icon-only button needs an
aria-label; an icon next to text doesn't.
Accessibility
- Iconify icons render
<svg aria-hidden="true">by default — appropriate for decorative use next to text. - For icon-only triggers (icon-only buttons), pair with IconButton which adds the required
aria-label. - For meaningful icons that aren't paired with text, set
aria-labeldirectly on the surrounding element.
Related
- IconButton — accessible wrapper for icon-only buttons
- Avatar — for person identity
- ServiceTag — for Brik service-line icons (use
variant="icon") - Storybook icon catalog
- External: phosphoricons.com — find icon names