<!-- Protocore Design System — Icon -->
# Icon

- **Category:** Media (`media`)
- **Slug:** `media/icon`
- **Status:** stable
- **Platforms:** web
- **Import:** `import { Icon } from "@protocore/pds";`
- **Docs:** https://pds.protocore.io/components/media/icon

> Sizing wrapper that scales any inline SVG icon to a token box.

## When to use it

**Icon** is a *sizing primitive*, not an icon set — the PDS ships no glyphs of its own. Bring your own SVG (lucide-react in these docs) and wrap it so it snaps to the token scale and inherits `currentColor`. Use Icon for **inline, non-interactive** glyphs: a leading mark on a list row, a status affordance beside text. If the glyph is clickable, use **IconButton** (it owns the hit target, focus ring, and required `aria-label`). Component slots like Button's `startIcon` already size their icons — you don't need to wrap those.

## Props

| Prop | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| `children` | `ReactNode` | no | — | The SVG icon element to size (drawn at 1em). |
| `className` | `string` | no | — | — |
| `label` | `string` | no | — | Accessible label. When set the icon becomes `role="img"`; omit it for decorative icons (then `aria-hidden`). |
| `size` | `enum` | no | `md` | Box + font-size: sm 14 · md 16 · lg 20 · xl 24. |
| `style` | `CSSProperties` | no | — | — |

## Examples

### Basics

Wrap any SVG icon child — lucide-react here. The wrapper sets `font-size`, and because the icon draws at `1em` it takes the box size. Icons are **decorative by default** (`aria-hidden`).

```tsx
import { Icon } from "@protocore/pds";
import { Activity } from "lucide-react";

export default function Demo() {
  return (
    <Icon>
      <Activity />
    </Icon>
  );
}
```

### Sizes

Four boxes tuned to the type scale: `sm` 14, `md` 16 (default), `lg` 20, `xl` 24. Match the icon size to the text or control it sits beside.

```tsx
import { Icon, HStack } from "@protocore/pds";
import { Server } from "lucide-react";

export default function Demo() {
  return (
    <HStack gap={4} align="center">
      <Icon size="sm">
        <Server />
      </Icon>
      <Icon size="md">
        <Server />
      </Icon>
      <Icon size="lg">
        <Server />
      </Icon>
      <Icon size="xl">
        <Server />
      </Icon>
    </HStack>
  );
}
```

## Do & don't

**Do**

- Give the icon an accessible `label` whenever it conveys information not present in nearby text.
- Leave `label` off for purely decorative icons so screen readers skip them.
- Let the icon inherit color via `currentColor` from its container.
- Size icons to the adjacent text or control using the four presets.

**Don't**

- Don't use Icon for clickable glyphs — that's IconButton, which supplies the hit target and focus ring.
- Don't hardcode width/height on the SVG; the wrapper's `font-size` does the scaling.
- Don't double-wrap icons that a component slot (e.g. `startIcon`) already sizes.
- Don't rely on an unlabelled icon as the only signal of state or meaning.

## Accessibility

**Notes**

- Decorative icons (no `label`) render `aria-hidden` so they are never announced.
- Passing `label` promotes the wrapper to `role="img"` with `aria-label`, giving the glyph an accessible name.
- Icon is not focusable and has no interactive behavior — for keyboard operability wrap the glyph in a Button or IconButton.
- Color contrast still applies: labelled informational icons must meet non-text contrast against their background.

## Related

`icon-button`, `avatar`, `logo`, `button`

---

© Protocore. All rights reserved. Use of the Protocore Design System requires prior written authorization from Protocore (contact@protocore.io). These machine-readable materials must not be ingested into ML-training datasets or derivative design systems. See https://pds.protocore.io/legal/
