# Icon

> Twenty Lucide glyphs as inline SVG, sized and labelled through props.

- **Slug:** `icon`
- **Kind:** atom · **Category:** Content
- **Source:** ported from [Lucide](https://lucide.dev) (ISC)
- **Docs page:** https://viewership.ai/design/components/icon
- **All files in one bundle:** https://viewership.ai/design/components/icon.bundle.txt
- **Structured JSON:** https://viewership.ai/design/components/icon.json

## Install

1. Import the foundation tokens once (download: https://viewership.ai/design/raw/foundation.css.txt):

```astro
---
// src/layouts/Layout.astro
import "../styles/foundation.css";
---
```

2. Copy these files into `src/components/viewership/icon/`:

   - `Icon.astro` (raw: https://viewership.ai/design/raw/icon/Icon.astro.txt)
   - `icons.ts` (raw: https://viewership.ai/design/raw/icon/icons.ts.txt)

npm packages: none.

## Usage

```astro
---
import Icon from "../components/viewership/icon/Icon.astro";
---

<Icon />
```

## Example

The gallery demo, verbatim. Use the import path from Usage instead of `./`.

```astro
---
import Icon from "./Icon.astro";
import { iconNames } from "./icons";
---

<ul class="grid">
  {
    iconNames.map((name) => (
      <li>
        <Icon name={name} size={22} />
        <span>{name}</span>
      </li>
    ))
  }
</ul>

<style>
  .grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(110px, 1fr));
    gap: 20px 12px;
    margin: 0;
    padding: 0;
    list-style: none;
    color: var(--vs-ink);
  }
  li {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 8px;
  }
  span {
    font-size: 11px;
    color: var(--vs-ink-3);
  }
</style>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `IconName` |  | Glyph key: arrow-right, check, zap, layers, shield and more. See `icons.ts`. |
| `size` | `number` | `20` | Width and height in pixels. |
| `strokeWidth` | `number` | `1.75` | Stroke weight. |
| `label` | `string` |  | Accessible name. Omit to mark the icon decorative. |

## Used by

[Feature Bento](https://viewership.ai/design/components/feature-bento.md), [Pricing Columns](https://viewership.ai/design/components/pricing-columns.md), [Pricing Rows](https://viewership.ai/design/components/pricing-rows.md), [Pricing Commission](https://viewership.ai/design/components/pricing-commission.md), [Pricing Toggle](https://viewership.ai/design/components/pricing-toggle.md)

## Source

### `src/components/viewership/icon/Icon.astro`

```astro
---
/**
 * Icon: a small set of Lucide glyphs (ISC) as inline SVG.
 *
 * Decorative by default (aria-hidden). Pass `label` when the icon carries
 * meaning on its own. Sections use it for feature icons and list ticks;
 * `name` accepts any key in `icons.ts`, so add glyphs there.
 *
 * @example
 * <Icon name="zap" />
 * <Icon name="check" size={16} label="Included" />
 */
import { iconPaths, type IconName } from "./icons";

interface Props {
  name: IconName;
  size?: number;
  strokeWidth?: number;
  /** Accessible name. Omit for decorative icons. */
  label?: string;
  class?: string;
}

const { name, size = 20, strokeWidth = 1.75, label, class: className } = Astro.props;


---

<svg
  class:list={["vs-icon", className]}
  width={size}
  height={size}
  viewBox="0 0 24 24"
  fill="none"
  stroke="currentColor"
  stroke-width={strokeWidth}
  stroke-linecap="round"
  stroke-linejoin="round"
  role={label ? "img" : undefined}
  aria-label={label}
  aria-hidden={label ? undefined : "true"}
  focusable="false"
  set:html={iconPaths[name]}
/>

<style>
  .vs-icon {
    flex: none;
    vertical-align: middle;
  }
</style>
```

### `src/components/viewership/icon/icons.ts`

```ts
/** Lucide (ISC) glyph bodies, 24x24 viewBox, stroke-based. Add keys here to extend Icon. */
export const iconNames = [
  "arrow-right",
  "arrow-up-right",
  "bot",
  "chart",
  "check",
  "clock",
  "code",
  "file",
  "globe",
  "layers",
  "lock",
  "mail",
  "minus",
  "plus",
  "search",
  "shield",
  "sparkles",
  "star",
  "users",
  "zap",
] as const;
export type IconName = (typeof iconNames)[number];

export const iconPaths: Record<IconName, string> = {
  "arrow-right": '<path d="M5 12h14"/><path d="m12 5 7 7-7 7"/>',
  "arrow-up-right": '<path d="M7 7h10v10"/><path d="M7 17 17 7"/>',
  bot: '<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>',
  chart: '<path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="M18 17V9"/><path d="M13 17V5"/><path d="M8 17v-3"/>',
  check: '<path d="M20 6 9 17l-5-5"/>',
  clock: '<circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/>',
  code: '<path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/>',
  file: '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 13H8"/><path d="M16 17H8"/><path d="M16 13h-2"/>',
  globe: '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>',
  layers: '<path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z"/><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65"/><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65"/>',
  lock: '<rect width="18" height="11" x="3" y="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/>',
  mail: '<rect width="20" height="16" x="2" y="4" rx="2"/><path d="m22 7-8.97 5.7a1.94 1.94 0 0 1-2.06 0L2 7"/>',
  minus: '<path d="M5 12h14"/>',
  plus: '<path d="M5 12h14"/><path d="M12 5v14"/>',
  search: '<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>',
  shield: '<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/>',
  sparkles: '<path d="M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z"/><path d="M20 3v4"/><path d="M22 5h-4"/><path d="M4 17v2"/><path d="M5 18H3"/>',
  star: '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>',
  users: '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
  zap: '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>',
};
```
