apx-dsv0.1Local renderer · live
K
Foundations4
  • Getting started
  • Theming
  • Templates
  • Icons
  • Avatar
  • Badge
  • DataGrid
  • Scheduler
  • Stat
  • Table
  • Timeline
  • TreeView
  • Accordion
  • Alert
  • EmptyState
  • Progress
  • Skeleton
  • Spinner
  • SplashScreen
  • Toast
  • ColorPicker
  • FileUpload
  • Form
  • Rating
  • TagsInput
  • Combobox
  • Field
  • Select
  • Toggle
  • Button
  • Calendar
  • Checkbox
  • DatePicker
  • Input
  • NumberInput
  • Radio
  • Slider
  • Switch
  • Textarea
  • AppShell
  • Div
  • Divider
  • Sidebar
  • Stack
  • Typography
  • Image
  • Breadcrumbs
  • Carousel
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Toolbar
  • CommandPalette
  • Confirm
  • Drawer
  • HoverCard
  • Menu
  • Modal
  • Popover
  • Tooltip
  • Icon
  • Card
  • PricingCard
60 componentsapx-ds/renderer
Icon
Variant↳ other

Primitives

Icon

Library-agnostic icon primitive. Three render modes (

Icon

<Icon> is the canonical DS icon primitive — library-agnostic, with three render modes (as / name / children), DS color + size tokens, decorative-by-default a11y, and utility props for rotate / flip / spin. Ships with <IconProvider> + createIconRegistry so your app's icon library wires into every shipped DS component through a stable string-name contract.

Overview — sizes and semantic colors at a glance

Loading preview…
Overview.tsx

Why a primitive at all?

Every DS component up to this point inlined raw SVGs or accepted ReactNode for icon slots. That meant:

  • Every example used a different icon library / inline SVG.
  • No central swap point if you switch from lucide-react → heroicons → iconify.
  • A11y bugs leaked through (icons stuck in the tab order, no aria-hidden defaults, etc.).
  • currentColor cascade was inconsistent (fill vs stroke).

<Icon> standardizes all of it without locking you to a library.

Quick start

1. Inline static (zero-config)

tsx
import { Mail } from 'lucide-react';
import { Icon } from 'apx-ds';

<Icon as={Mail} />
<Icon as={Mail} size="lg" color="danger" />
<Icon as={Mail} label="Inbox" />   {/* meaningful: role=img + aria-label */}
import { Mail } from 'lucide-react';
import { Icon } from 'apx-ds';

<Icon as={Mail} />
<Icon as={Mail} size="lg" color="danger" />
<Icon as={Mail} label="Inbox" />   {/* meaningful: role=img + aria-label */}

2. Registry (the 80% case)

Wire your icon set once at app boot, then reference by name everywhere:

tsx
import { createIconRegistry, IconProvider, Icon } from 'apx-ds';import * as Lucide from 'lucide-react';

const icons = createIconRegistry({
  mail: Lucide.Mail,
  user: Lucide.User,
  settings: Lucide.Settings,
});

<IconProvider value={icons}>
  <App />
</IconProvider>;

<Icon name="mail" />
<Icon name="user" size="lg" />
<Icon name="settings" label="Settings" />
import { createIconRegistry, IconProvider, Icon } from 'apx-ds';import * as Lucide from 'lucide-react';

const icons = createIconRegistry({
  mail: Lucide.Mail,
  user: Lucide.User,
  settings: Lucide.Settings,
});

<IconProvider value={icons}>
  <App />
</IconProvider>;

<Icon name="mail" />
<Icon name="user" size="lg" />
<Icon name="settings" label="Settings" />

3. Inline children (escape hatch)

tsx
<Icon size="md" label="Custom logo">
  <svg viewBox="0 0 24 24"><path d="..." /></svg>
</Icon>
<Icon size="md" label="Custom logo">
  <svg viewBox="0 0 24 24"><path d="..." /></svg>
</Icon>

Render-source precedence

When multiple sources are passed, the runtime picks in this order:

  1. children wins.
  2. as runs when children is absent.
  3. name runs when both are absent.

Missing name (no registry / unregistered key) → fires onMissing, then renders the provider fallback (or an empty placeholder when no fallback is set).

Examples

Basic — inline lucide glyph via as

Loading preview…
BasicAsLucide.tsx

Basic — registry lookup by name

Loading preview…
BasicNameRegistry.tsx

Basic — inline SVG children

Loading preview…
BasicChildrenInline.tsx

Sizes — xs through xl tokens

Loading preview…
Sizes.tsx

Sizes — numeric and CSS lengths

Loading preview…
SizesNumeric.tsx

Colors — palette tokens + arbitrary values

Loading preview…
Colors.tsx

Rotate — quarter-turn utility

Loading preview…
Rotate.tsx

Flip — horizontal / vertical mirror

Loading preview…
Flip.tsx

Spin — loading animation

Loading preview…
Spin.tsx

A11y — decorative vs meaningful

Loading preview…
MeaningfulVsDecorative.tsx

Missing name — provider fallback

Loading preview…
MissingNameFallback.tsx

asChild — compose with Tooltip

Loading preview…
AsChildWithTooltip.tsx

In Button — leading / trailing slots

Loading preview…
InButton.tsx

In Badge — status icons

Loading preview…
InBadge.tsx

In Alert — intent icons

Loading preview…
InAlert.tsx

In Toast — intent icons

Loading preview…
InToastIntents.tsx

DS icon catalog — stable string names

Loading preview…
DSIconCatalog.tsx

A11y

IntentPass to IconResulting ARIA
Decorative (paired with visible text)(default — no props)aria-hidden="true"
Meaningful (icon stands alone)label="…"role="img" + aria-label="…"
Explicit decorative overridedecorativearia-hidden="true" (even with label)

focusable="false" is always applied on the underlying <svg> to neutralize legacy IE/Edge tab-stop behavior.

Sizes

Token classes for xs | sm | md | lg | xl. Numeric (size={20} → 20px) and arbitrary CSS lengths (size="1.5rem") apply via inline style.

Tokenpx
xs12
sm14
md16 (default)
lg20
xl24

Colors

DS palette tokens map to recipe classes; arbitrary CSS colors ('#abc', 'var(--my-c)') apply via inline style.color. Default color="current" inherits currentColor, so dropping an Icon inside a colored button "just works."

current | inherit | default | muted | subtle | accent | success | warning | danger | info

DS Icon Catalog

The DS internally references icons by these stable string names. Register them in your provider once and every shipped component (Checkbox, Select, Toast, Alert, NavigationMenu, …) lights up:

tsx
import { DS_ICON_NAMES, createIconRegistry } from 'apx-ds';import * as Lucide from 'lucide-react';

const icons = createIconRegistry(
  {
    check: Lucide.Check,
    'check-circle': Lucide.CheckCircle2,
    x: Lucide.X,
    'chevron-down': Lucide.ChevronDown,
    // …see DS_ICON_NAMES for the full set
  },
  { strict: true }, // optional — runtime warns if a DS name is missing
);
import { DS_ICON_NAMES, createIconRegistry } from 'apx-ds';import * as Lucide from 'lucide-react';

const icons = createIconRegistry(
  {
    check: Lucide.Check,
    'check-circle': Lucide.CheckCircle2,
    x: Lucide.X,
    'chevron-down': Lucide.ChevronDown,
    // …see DS_ICON_NAMES for the full set
  },
  { strict: true }, // optional — runtime warns if a DS name is missing
);

Utility props

  • rotate={0 | 90 | 180 | 270} — quarter-turn CSS rotation.
  • flip="horizontal" | "vertical" | "both" — CSS scale mirror.
  • spin — applies the spinner ring animation; honors prefers-reduced-motion.
  • asChild — render via <Slot>; child element receives the merged className + ARIA wiring.

More examples

_glyphs

Loading preview…
_glyphs.tsx

Props

No documented props found. Add JSDoc to the component's prop interface to populate this table.