# Icons (Caseroom component)

Twenty-four icons on a 16px grid with a 1.5px stroke, decorative unless labelled. Black and white, dark mode, shadcn-compatible; React, Astro and plain HTML.

0.39% of the 2,062 sites in The UI Index have a icon (8 sites).

## Install

- React: `npx shadcn@latest add https://www.themes.dev/caseroom/r/icon.json`
- Astro: `npx shadcn@latest add https://www.themes.dev/caseroom/r/astro/icon.json`
- HTML: https://www.themes.dev/caseroom/r/html/icon.html (with https://www.themes.dev/caseroom/r/html/caseroom.css)

Page: https://www.themes.dev/caseroom/components/icon/ · Rules for agents: https://www.themes.dev/caseroom/AGENT.md

## Code

### icon.astro

```astro
---
import { cn } from '@/lib/utils';
import { ICONS, iconClasses, type IconName } from './icon.variants';

interface Props {
  name: IconName;
  /** Only when the icon means something on its own; otherwise it is hidden from screen readers. */
  label?: string;
  class?: string;
}
const { name, label, class: className } = Astro.props;
---

<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" role={label ? 'img' : undefined} aria-label={label} aria-hidden={label ? undefined : 'true'} class={cn(iconClasses, className)}><path d={ICONS[name]} /></svg>
```

### icon.tsx

```tsx
import { cn } from '@/lib/utils';
import { ICONS, iconClasses, type IconName } from './icon.variants';

export { ICONS, type IconName };

export interface IconProps {
  name: IconName;
  /** Only when the icon means something on its own; otherwise it is hidden from screen readers. */
  label?: string;
  className?: string;
}

/** One of Caseroom's icons: 16px grid, 1.5px stroke, decorative unless it has a label. */
export function Icon({ name, label, className }: IconProps) {
  return (
    <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" role={label ? 'img' : undefined} aria-label={label} aria-hidden={label ? undefined : 'true'} className={cn(iconClasses, className)}>
      <path d={ICONS[name]} />
    </svg>
  );
}
```

### icon.variants.ts

```tsx
/** Caseroom's icons: drawn on a 16px grid with a 1.5px stroke, round caps and joins, so they sit with the text.
 *  Decorative by default (aria-hidden); give a label when the icon is the only content of a control or carries
 *  meaning. Sizes follow the text: 16px with text-sm, 20px for larger controls. */
export const ICONS = {
  'arrow-right': 'M3 8h10M9 4l4 4-4 4',
  'arrow-up-right': 'M5 11l6-6M6 5h5v5',
  check: 'M3.5 8.5l3 3 6-7',
  x: 'M4 4l8 8M12 4l-8 8',
  plus: 'M8 3v10M3 8h10',
  minus: 'M3.5 8h9',
  'chevron-down': 'M4 6l4 4 4-4',
  'chevron-right': 'M6 3.5l4.5 4.5L6 12.5',
  'chevron-left': 'M10 3.5L5.5 8l4.5 4.5',
  search: 'M7 12.5a5.5 5.5 0 1 0 0-11 5.5 5.5 0 0 0 0 11zM11 11l3.5 3.5',
  menu: 'M2.5 4.5h11M2.5 8h11M2.5 11.5h11',
  copy: 'M5.5 5.5h8v8h-8zM10.5 3.5V3A1.5 1.5 0 0 0 9 1.5H3A1.5 1.5 0 0 0 1.5 3v6A1.5 1.5 0 0 0 3 10.5h.5',
  play: 'M5 3.5v9l7-4.5z',
  'map-pin': 'M8 14.5s4.5-4.2 4.5-8a4.5 4.5 0 0 0-9 0c0 3.8 4.5 8 4.5 8zM8 8a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3z',
  calendar: 'M2.5 4h11v9.5h-11zM2.5 7h11M5.5 2.5v3M10.5 2.5v3',
  settings: 'M8 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4zM8 1.5v2M8 12.5v2M1.5 8h2M12.5 8h2M3.4 3.4l1.4 1.4M11.2 11.2l1.4 1.4M3.4 12.6l1.4-1.4M11.2 4.8l1.4-1.4',
  user: 'M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM2.5 14.5a5.5 5.5 0 0 1 11 0',
  mail: 'M2 3.5h12v9H2zM2.5 4l5.5 4.5L13.5 4',
  bell: 'M4 11V7a4 4 0 0 1 8 0v4l1.5 1.5h-11zM6.5 14h3',
  trash: 'M2.5 4h11M6 4V2.5h4V4M4 4l.7 9.5h6.6L12 4',
  edit: 'M10.5 2.5l3 3-8 8h-3v-3z',
  download: 'M8 2.5v8M4.5 7L8 10.5 11.5 7M2.5 13.5h11',
  info: 'M8 14.5a6.5 6.5 0 1 0 0-13 6.5 6.5 0 0 0 0 13zM8 7.5V11M8 5h.01',
  alert: 'M8 2l6.5 11.5h-13zM8 6.5v3M8 11.5h.01',
} as const;

export type IconName = keyof typeof ICONS;
export const iconClasses = 'inline-block size-4 shrink-0';
```
