@spark-web/icon
Version:
--- title: Icon isExperimentalPackage: false ---
139 lines (103 loc) • 5.99 kB
Markdown
# @spark-web/icon — AI Context
## What this package is
A library of ~230 single-color, stroke-based SVG icons plus the `createIcon`
factory used to build them. Every icon is imported by name (e.g. `SearchIcon`,
`ChevronDownIcon`) and shares one props interface (`IconProps`): `size`, `tone`,
and `data`. Icons render a 24×24 `viewBox` `<svg>` with `aria-hidden="true"`.
## What this is NOT
- Not an ad-hoc SVG drop point — never hand-write `<svg>` markup in product
code; use a named icon, or `createIcon` if a glyph is genuinely missing
- Not a colored/filled illustration system — icons are single-tone strokes
driven by theme foreground tones
- Not interactive — icons carry no click handling or label; wrap them in
`@spark-web/button` / `@spark-web/icon-button` for actions
- Not a logo or brand-mark component
## Props interface
### `IconProps` (shared by every icon)
| Prop | Type | Default | Notes |
| ------ | ---------------------------------------------------------------------------------- | ----------- | ------------------------------------------------- |
| `size` | `'xxsmall' \| 'xsmall' \| 'small' \| 'medium' \| 'large' \| 'xlarge' \| 'xxlarge'` | `'small'` | Maps to `theme.sizing[size]` for width and height |
| `tone` | `ForegroundTone` | `'neutral'` | Stroke color via `theme.color.foreground[tone]` |
| `data` | `DataAttributeMap` | — | Test/analytics attributes |
`size` is `Exclude<keyof theme.sizing, 'full' | 'none'>`. `ForegroundTone` is
the same tone union used by `Text`
(`'neutral' | 'muted' | 'link' | 'disabled' | 'field' | 'fieldAccent' | 'placeholder' | 'accent' | 'primary' | 'primaryHover' | 'primaryActive' | 'secondary' | 'secondaryHover' | 'secondaryActive' | 'caution' | 'critical' | 'info' | 'positive' | 'dark' | 'brandSubtle'`).
Each icon is a `forwardRef<SVGSVGElement, IconProps>`, so a `ref` passes through
to the underlying `<svg>`.
### `createIcon(children, name)`
| Arg | Type | Notes |
| ---------- | ----------- | -------------------------------------------------- |
| `children` | `ReactNode` | The SVG path(s), rendered inside a 24×24 `viewBox` |
| `name` | `string` | Sets the component `displayName` |
Returns a fully-styled icon component. Use only when a needed glyph does not
already exist in the package.
## Token usage
All values from `useTheme()` from `@spark-web/theme`. Never hard-code px or hex.
| Property | Token / Value |
| --------------- | -------------------------------------------------------- |
| Width/height | `theme.sizing[size]` (e.g. `xxsmall` = 16, `small` = 32) |
| Stroke color | `theme.color.foreground[tone]` via `useForegroundTone` |
| Stroke width | `2` (fixed in `createIcon`) |
| Fill | `none` (stroke-based glyphs) |
| Line caps/joins | `round` (fixed) |
## Import-by-name convention
Import each icon individually by its named export from the package root:
```tsx
import { SearchIcon, ChevronDownIcon, XIcon } from '@spark-web/icon';
```
There is no default export and no dynamic name-to-icon lookup. To find an exact
export name, search the exports list in `packages/icon/src/index.ts` (each icon
is `export { FooIcon } from './icons/foo-icon';`). Names are PascalCase and end
in `Icon` (e.g. `SearchIcon`, `InformationCircleIcon`, `ExclamationIcon`,
`CheckCircleIcon`, `XIcon`).
Canonical icons used in the internal-admin patterns: `SearchIcon` (list-page
search field adornment). `Alert` selects `ExclamationIcon`,
`InformationCircleIcon`, `CheckCircleIcon`, and `XIcon` internally by tone.
## Composition
- Icons are leaf components — they accept no children beyond what `createIcon`
bakes in
- Commonly nested inside `InputAdornment` (`@spark-web/text-input`), `Button`
(`@spark-web/button`), and `Alert` (`@spark-web/alert`)
- List page — Section 4 uses `<SearchIcon size="xxsmall" tone="muted" />` inside
a start `InputAdornment`. See
`node_modules/@spark-web/design-system/patterns/internal-admin/list-page.md`.
## Correct usage
### Search field adornment (from internal-admin list-page pattern)
```tsx
import { SearchIcon } from '@spark-web/icon';
import { InputAdornment, TextInput } from '@spark-web/text-input';
<TextInput placeholder="Search by...">
<InputAdornment placement="start">
<SearchIcon size="xxsmall" tone="muted" />
</InputAdornment>
</TextInput>;
```
### Tone-colored status icon
```tsx
<CheckCircleIcon size="small" tone="positive" />
```
### Creating a missing glyph
```tsx
import { Fragment } from 'react';
import { createIcon } from '@spark-web/icon';
export const SparkleIcon = createIcon(
<Fragment>
<path d="M12 3v18M3 12h18" />
</Fragment>,
'SparkleIcon'
);
```
## Do NOTs
- NEVER hand-write raw `<svg>` markup in product code — import a named icon
- NEVER set `size` or `tone` with raw px/hex — use the `size` and `tone` props
- NEVER use a `size` outside the seven named values, or a `tone` outside the
`ForegroundTone` union
- NEVER rely on a default icon export or string-keyed lookup — every icon is a
named import
- NEVER add an `onClick` to an icon for interactivity — wrap it in a button
component; icons are `aria-hidden`
## Component gaps / known limitations
- Single stroke color only — no multi-color or filled icons
- Stroke width is fixed at `2` and not configurable per icon
- Icons are decorative (`aria-hidden="true"`); meaning must come from an
accessible label on the surrounding interactive element