Skip to main content
BM

Icon

One icon from a typed registry, so the icon family can be swapped in one file.

View source on GitHub

  • Email
  • GitHub
  • LinkedIn
IconExample.tsx
import { Icon } from "@/components/Icon";
import { Stack } from "@/components/Stack";

export function IconExample() {
  return (
    <Stack as="ul" direction="horizontal" gap="md">
      <li>
        <Icon name="mail" /> Email
      </li>
      <li>
        <Icon name="github" /> GitHub
      </li>
      <li>
        <Icon name="linkedin" /> LinkedIn
      </li>
    </Stack>
  );
}

Examples

Registry

  • sun
  • moon
  • external-link
  • arrow-left
  • arrow-right
  • arrow-up
  • chevron-down
  • check
  • copy
  • linkedin
  • github
  • mail
  • warning
  • search
  • close
  • palette
  • download
  • upload
  • reset
  • trash
  • menu
  • pause
  • play

Sizes

  • inherit (1em)
  • sm (1rem)
  • md (1.25rem)
  • lg (1.5rem)

Weights

  • light
  • regular
  • bold
  • fill
  • duotone

Inherits text size

Heading-size text, heading-size icon

Small text, small icon

Takes text color

Design tokens Accent-colored text and icon

Decorative, inline in running text

Deployed through GitHub Actions.

Props

Any native prop not listed here goes to the root element unchanged.

SVG attributes (fill, stroke, viewBox, width, height, transform) and children also reach the <svg> as escape hatches: fill replaces currentColor; viewBox and children depend on the icon family's 256-unit grid; width and height are overridden by the size classes, so size an icon with the size prop or a class. Children must not be focusable or include <title> or <desc>: the name comes only from label.

Icon props
PropTypeDefaultDescription
name"sun" | "moon" | "external-link" | "arrow-left" | "arrow-right" | "arrow-up" | "chevron-down" | "check" | "copy" | "linkedin" | "github" | "mail" | "warning" | "search" | "close" | "palette" | "download" | "upload" | "reset" | "trash" | "menu" | "pause" | "play"—A registry name. Anything else is a type error.
labelstring—Accessible name. Without one the icon is decorative and hidden.
size"inherit" | "sm" | "md" | "lg""inherit"inherit follows the text size (1em); the others use size.icon.*.
weight"light" | "regular" | "bold" | "fill" | "duotone""regular"Normalized weight, mapped onto the icon family's own weights.

Component tokens

None. Size comes from the size prop (size.icon.*), color from currentColor, and the default weight is a registry constant.

Accessibility

  • Decorative by default: aria-hidden and not focusable.
  • Never focusable, and named only by label: aria-labelledby and tabIndex are blocked.
  • With label: role="img", named by the label.
  • Colored by currentColor unless fill is passed, so it matches its text's contrast. A custom fill on a labelled icon must meet 3:1 against its background.
  • Where a visible caption names an icon, the icon stays decorative so the name isn't announced twice.