# Illicon

Render saved dot-grid illustrations, convert icons in the browser, and export editable SVG.

`Illicon` renders saved dot-grid occupancy as SVG circles. Grid data contains only
equal-length, nonempty rows of `0`s and `1`s. Empty rows and columns, including
borders, are preserved. Height is the number of rows; width is the length of a row.

```tsx
import { Illicon, type DotGridData } from '@coreai-ds/react-components';

const plus: DotGridData = {
  rows: ['00000', '00100', '01110', '00100', '00000'],
};

<Illicon data={plus} radius={1} pitch={5} aria-label="Add" />;
```

## Pixel sizing and alignment

Radius and pitch are rendering settings in CSS pixels, not occupancy data. Pitch
is the center-to-center distance between cells. The default radius is 1 and pitch
is 5, producing 2px dots with 3px gaps. Both must be positive and finite; radius
cannot exceed half the pitch.

The SVG renders at `columns * pitch` by `rows * pitch` CSS pixels. There is no
independent `size` for grids: a 2px circle stays 2px when sampling resolution or
pitch changes. Grid SVGs do not stretch or flex-shrink to fit their wrapper.

When pitch and circle diameter are whole pixels, circle bounding edges align to
integer SVG coordinates. This works for even and odd pitches and diameters.
For example, 2px circles with pitch 5 have centers at 2, 7, 12, rather than
2.5, 7.5, 12.5. If the unused space in a cell is odd, one extra pixel remains on
the trailing side. The complete grid artboard, including empty borders, is retained.

Fractional pitch and diameter remain supported; those settings keep centered
geometry without snapping. Curves still need anti-aliasing. Pixel alignment is
local to the SVG: parent transforms, fractional positioning, zoom, and device
scale can affect the final physical pixels.

Omit accessible labels for decorative artwork; use `aria-label` or
`aria-labelledby` when the image conveys information. Ink follows the theme's
neutral foreground color and can be overridden with CSS.

## Convert an icon or SVG

Conversion is an asynchronous, browser-only preparation step, not part of rendering.
Import it from the main package alongside the renderer and SVG exporter:

### Recommended standard: native 20px at 2x

For new Fluent-based Illicons, use the actual native **20px icon at 2x**.
This produces a **40 x 40 canvas and occupancy grid**. Prefer Filled icons;
use Regular when the illustration needs an outline. Do not resize a 16px or
24px variant to imitate the 20px source.

Keep the default radius of 1px and pitch of 5px unless the design needs a
different rendering treatment. This gives a 200 x 200px illustration with 2px
dots. Save the occupancy after preparation; applications need not run conversion
each time the illustration renders.

```ts
import { Scales20Filled } from '@fluentui/react-icons';
import { iconToDotGrid } from '@coreai-ds/react-components';

const scales = await iconToDotGrid(Scales20Filled, { multiplier: 2 });
// multiplier: 2 is the default, so omitting the options gives the same grid.
// Save as JSON or reuse the result. Pass scales to <Illicon data={scales} />.
```

Source coordinates on half-pixels become whole pixels at 2x. Curves and other
fractional geometry can still anti-alias; this is not a guarantee of sharpness
for every source.

### Other sampling settings

The converter reads source dimensions from the SVG's `viewBox`, not its component
name or display size. Without a viewBox it uses numeric/px width and height.
`multiplier` defaults to 2 and can be any whole number from 1 to 256, provided
the resulting dimensions fit the 256-cell limit. Other native sizes and
multipliers remain supported for custom needs and visual testing; they do not
replace the recommended 20px-at-2x standard:

| Native icon | 1x grid | 2x grid | 3x grid |
| ----------- | ------- | ------- | ------- |
| 16 x 16     | 16 x 16 | 32 x 32 | 48 x 48 |
| 20 x 20     | 20 x 20 | 40 x 40 | 60 x 60 |
| 24 x 24     | 24 x 24 | 48 x 48 | 72 x 72 |

```ts
import { Heart16Regular } from '@fluentui/react-icons';

const outline = await iconToDotGrid(Heart16Regular, { multiplier: 3 });
// 48 x 48 cells.
```

For arbitrary grid sizes, use explicit `columns` and/or `rows` instead of
`multiplier`. These remain supported; do not combine the two sampling modes.
With explicit columns, omitted rows follow the source aspect ratio, rounded
to whole cells. With rows alone, width still defaults to source width times 2.
Fractional source dimensions times the multiplier are rounded to the nearest
whole pixel, with a minimum of 1. Each output dimension must be 1-256.

The Storybook recreation comparison uses native 20px icons at 2x by default,
with editable 16px/20px/24px sources and 1x-4x sampling for verification.
Regular icons usually draw outlines with filled paths, so the separate stroked
SVG example also tests actual SVG `stroke` geometry.

Sources may be Fluent icon components, React SVG elements, `SVGSVGElement`s, or
standalone SVG markup. A positive viewBox is required, or numeric/px width and
height. Fractional dimensions and nonzero viewBox origins are supported.

### Raster and SVG behavior

Explicit rows use the SVG's `preserveAspectRatio` behavior to fit the artwork
into that viewport. The browser
renders directly into a canvas of `columns` by `rows` pixels, one pixel per cell:
a 20px icon at 1x uses a 20 x 20 raster; at 2x it uses 40 x 40. There is no larger
intermediate raster or center-point subsampling. A cell is occupied when its
pixel alpha reaches `alphaThreshold`, which defaults to 128 (approximately 50%
coverage/opacity) and accepts integers from 1 to 255. Anti-aliasing and source
opacity both affect alpha. This sampling is unrelated to rendering pitch or
circle diameter. The output always retains every requested row and column,
even when entirely empty.

This differs from the earlier fixed-24-cell default and supersampled center-point
approach; regenerating data can change grid dimensions and dots near edges or
thin features. Previously saved data is unchanged. Rasterizing an exported
dotted SVG is not a lossless grid-data import: small dots may cover less than
the threshold of a pixel. Reuse saved occupancy
when exact grid preservation is required.

The browser renders static vector shapes, paths, strokes, transforms, and local
definitions such as gradients, masks, and clipping paths. External styles are not
captured. Local masks work as SVG attributes or inline CSS (`mask` or its longhands);
all mask references must resolve within the source SVG.
Scripts, event handlers, animation, stylesheets, text/fonts, images,
external references, and unsupported SVG elements/styles are rejected. Sources are
limited to 1 MiB of UTF-8 text and 10,000 elements. Conversion errors reject the promise;
callers must surface them. During server rendering, use saved grid data instead.

## Export SVG for Figma

```ts
import { dotGridToSvg } from '@coreai-ds/react-components';

const markup = dotGridToSvg(plus, { radius: 1, pitch: 5 });
```

The result is a standalone SVG document with editable circles, explicit dark ink,
and the complete artboard. It uses the same circle positions as `Illicon`. Save or
copy this markup into design tools; there is no need to maintain a second drawing
by hand.

## Preserve custom SVG artwork

Use the separate children mode for trusted inline SVG or an SVG component that
forwards SVG props. Do not pass raw markup strings to `Illicon`.

```tsx
<Illicon size={120} aria-label="Custom illustration">
  <svg viewBox="0 0 40 20" fill="currentColor">
    <circle cx="4.25" cy="5.75" r="1.25" />
    <path d="M10 2h12v10H10z" fill="none" stroke="currentColor" />
  </svg>
</Illicon>
```

Geometry is preserved, including irregular dots and hybrid illustrations.
Use `currentColor` for theme-controlled fills and strokes; explicit colors stay
unchanged. Supply either `data` or SVG children, not both. Radius and pitch apply
only to grid data. Optional `size` applies only to custom SVG and preserves its
aspect ratio; it does not apply to grids. Custom SVG is trusted React content, not a sanitized upload;
authors must handle internal IDs correctly when artwork is rendered more than once.

## Reviewed artwork

The `illicons` package export provides artwork metadata and original SVG image
sources, not saved dot-grid data. There is no `Illicon name` prop. Download the
original SVGs below for design tools; existing artwork is not resampled.

### Agents

![Agents](/coreai-design-system/assets/illicons/agents.svg)

[Download original SVG](/coreai-design-system/assets/illicons/agents.svg)

Tags: none.

### Workflows

![Workflows](/coreai-design-system/assets/illicons/workflows.svg)

[Download original SVG](/coreai-design-system/assets/illicons/workflows.svg)

Tags: none.

### Models

![Models](/coreai-design-system/assets/illicons/models.svg)

[Download original SVG](/coreai-design-system/assets/illicons/models.svg)

Tags: none.

### Fine-tune

![Fine-tune](/coreai-design-system/assets/illicons/beaker.svg)

[Download original SVG](/coreai-design-system/assets/illicons/beaker.svg)

Tags: beaker, lab, test.

### Database

![Database](/coreai-design-system/assets/illicons/database.svg)

[Download original SVG](/coreai-design-system/assets/illicons/database.svg)

Tags: data, storage.

### Documents

![Documents](/coreai-design-system/assets/illicons/documents.svg)

[Download original SVG](/coreai-design-system/assets/illicons/documents.svg)

Tags: file, pages.

### Evaluations

![Evaluations](/coreai-design-system/assets/illicons/evaluations.svg)

[Download original SVG](/coreai-design-system/assets/illicons/evaluations.svg)

Tags: compare, scale.

### Shield

![Shield](/coreai-design-system/assets/illicons/shield.svg)

[Download original SVG](/coreai-design-system/assets/illicons/shield.svg)

Tags: security, defence.

### Lock closed

![Lock closed](/coreai-design-system/assets/illicons/lock-closed.svg)

[Download original SVG](/coreai-design-system/assets/illicons/lock-closed.svg)

Tags: lock, access, permissions.

### Warning

![Warning](/coreai-design-system/assets/illicons/warning.svg)

[Download original SVG](/coreai-design-system/assets/illicons/warning.svg)

Tags: alert, error.

### Globe

![Globe](/coreai-design-system/assets/illicons/globe.svg)

[Download original SVG](/coreai-design-system/assets/illicons/globe.svg)

Tags: networking, network, world.

### Foundry

![Foundry](/coreai-design-system/assets/illicons/foundry.svg)

[Download original SVG](/coreai-design-system/assets/illicons/foundry.svg)

Tags: none.

### Home

![Home](/coreai-design-system/assets/illicons/home.svg)

[Download original SVG](/coreai-design-system/assets/illicons/home.svg)

Tags: none.

### AI Gateway

![AI Gateway](/coreai-design-system/assets/illicons/ai-gateway.svg)

[Download original SVG](/coreai-design-system/assets/illicons/ai-gateway.svg)

Tags: none.

## Contributing artwork

This directory is the source of truth for finished Illicon SVGs packaged with
`@coreai-ds/react-components`. Storybook's **Components / Illicon / Catalog** makes
the assets searchable and copies their original SVG markup for Figma or further
editing. Its previews are 80px; original SVG artboards remain 140px.

The completed `Illicon` component accepts saved dot-grid data or custom inline SVG,
not an artwork name. See the [component guide](https://github.com/microsoft/coreai-design-system/blob/main/packages/react-components/src/components/Illicon.md) for
usage and sizing. Saved-data catalog integration is a separate step; the current
`illicons` registry retains artwork metadata and original image sources only.

## Standard for Fluent-based Illicons

Use the actual native **20px Fluent icon at 2x** for new generated artwork.
Prefer Filled, or Regular when an outline is intended. This produces a
40 x 40 canvas and grid. The converter's default multiplier is 2. At the
default 1px radius and 5px pitch, the grid renders at 200 x 200px with 2px dots.
See the [component guide](https://github.com/microsoft/coreai-design-system/blob/main/packages/react-components/src/components/Illicon.md#recommended-standard-native-20px-at-2x)
for conversion and saved-data usage.

This standard applies to new source-based generation, not to finished designer
SVGs. Keep existing artwork and custom logos unchanged unless a replacement has
been reviewed. The saved-data catalog and its generation workflow remain separate work.

## Source verification

Storybook's **Components / Illicon / Source Recreation Dev** compares the original
SVGs with candidate Fluent icons and freshly sampled grids. It is a verification
story, excluded from the component MCP manifest. No original files are replaced.
It starts with the recommended 20px-at-2x settings. Use the editable
native-size selector to switch between the actual 16px, 20px, and 24px Fluent
variants, not scaled copies of one icon. At 2x these produce 32, 40, and 48 cells.
For the default 20px variant, the 1x-4x controls sample 20, 40, 60, or 80 cells.
These alternatives are testing controls, not changes to the standard.
The converter reads the SVG's
own viewBox and applies the multiplier; the component name is not parsed.
Radius and pitch affect rendering only; originals stay at their authored 140px
size. Sampling renders directly at the grid resolution: a 20 x 20 canvas at 1x
and a 40 x 40 canvas at 2x. Each pixel becomes one dot when its alpha reaches 128; no
supersampled center point is used.
The comparison also displays the actual sampling canvas before thresholding,
enlarged without smoothing to match the output artboard. A fixed white backdrop
makes the unchanged raster visible in both themes; gray pixels show partial alpha.

Sources use the selected native size and Filled variant, except Cube Regular for Models:
Agents, Database, Document One Page Multiple, Scales, Globe, Home, Lock Closed,
Shield, Warning, and the new Code illustration. Models and Shield approximate
designer-modified originals. `Beaker20Filled` is a temporary Fine-tune candidate
because Beaker Empty is not available in the installed package. `Fluid20Filled`
is an unconfirmed Workflows candidate from silhouette matching. AI Gateway and
Foundry are custom logos and remain original-only.

## Add artwork

1. Check the catalog for an existing concept.

2. Export a transparent, monochrome SVG at `width="140"`, `height="140"` and
   `viewBox="0 0 140 140"`. Keep finished dot geometry unchanged. Check centering,
   visual weight, and family consistency; do not re-sample through a converter.
   When using an export scale control, select 100%, with background and
   construction-grid options disabled.

3. Save it here as a lowercase kebab-case filename, such as `trash.svg`.

4. Add an entry to `manifest.json`:

   ```json
   {
     "title": "Trash",
     "file": "trash.svg",
     "tags": ["delete", "remove"],
     "author": "your-github-handle"
   }
   ```

5. From the repository root, run:

   ```bash
   npm run storybook
   ```

6. Open **Components / Illicon / Catalog**. Check search, preview, and Copy SVG.
   Include light/dark screenshots in your PR.

Storybook startup validates and generates the catalog automatically. If Storybook
is already running, regenerate after saving the SVG and manifest:

```bash
node scripts/generate-illicons.mjs
```

See the [repository contribution policy](https://github.com/microsoft/coreai-design-system/blob/main/CONTRIBUTING.md) for setup and
review and CLA requirements. Assets reach consumers in a published package release.

## Manifest contract (version 1)

- `title`: unique, trimmed display title.
- `file`: unique lowercase kebab-case `.svg` filename in this directory.
- `tags`: an array of unique, trimmed, lowercase search terms; use `[]` when no
  additional keywords are useful. Do not repeat the illustration's title.
- `author`: the artwork creator's GitHub handle or credited name.

The filename determines the registry ID: `trash.svg` becomes `trash`.
Keep filenames stable so existing registry lookups continue to work.

## Suggest tags

Edit an existing entry's `tags` in `manifest.json` without changing its filename or SVG.
Use relevant search terms beyond the title, or leave the array empty. Run the
same checks and submit the update in a pull request.

## SVG contract

The validator parses XML and rejects malformed documents, executable content,
event handlers, CSS, embedded images, remote references, DOCTYPE, and processing
instructions. It accepts static SVG shapes, groups, definitions, text-only
titles/descriptions, and local `use` references directly to shapes. References
to groups or other `use` elements are not supported. Files must be regular files,
at most 1 MiB, and at most 20,000 elements.

Supported paints are `none`, `currentColor`, `black`, `white`, and hex colors.
Transforms and original path/circle geometry are preserved. Export unsupported
effects as static vector shapes before submitting; the validator never rewrites
artwork. Automated checks are not a visual or accessibility review: maintainers
still verify dot spacing, size, contrast, centering, meaningful names/tags, and
consistency with the illustration family.

The manifest and all listed SVGs are validated before generating the typed
artwork registry. Missing files, duplicate filenames/titles, missing authors,
invalid tags, unsafe SVGs, and unlisted SVG files fail validation. The registry
embeds byte-identical SVGs as image sources, avoiding asset-path configuration
and keeping SVG IDs isolated when an illustration appears more than once.

## Default

Source: CoreAI.

Samples Agents20Filled at 2x its native resolution, then renders the resulting
40 x 40 grid at 200 x 200 CSS pixels with 2px dots and 5px pitch.
Native 20px at 2x is the recommended standard for new Fluent-based Illicons.
The helper shows asynchronous browser preparation with explicit loading and
error states. Applications can save the returned data and render it directly
without converting again. An accessible label identifies the standalone image.

[Download source](/coreai-design-system/source/examples/components-illicon--default.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { DotGridRenderOptions } from "@coreai-ds/react-components";
import { iconToDotGrid } from "@coreai-ds/react-components";
import type { FluentIcon } from "@fluentui/react-icons";
import * as React from 'react';
import { Text } from "@coreai-ds/react-components";
import { Illicon } from "@coreai-ds/react-components";
import { Agents20Filled } from "@fluentui/react-icons";

type SamplingProps = DotGridRenderOptions & {
    multiplier: number;
};

function ConvertedIllicon({ icon, multiplier, convert = iconToDotGrid, ...props }: DotGridRenderOptions & {
    icon: FluentIcon;
    multiplier?: number;
    'aria-label'?: string;
    convert?: typeof iconToDotGrid;
}) {
    const [sample, setSample] = React.useState<{
        icon: FluentIcon;
        multiplier?: number;
        convert: typeof iconToDotGrid;
        value: Awaited<ReturnType<typeof iconToDotGrid>> | Error;
    }>();
    React.useEffect(() => {
        let active = true;
        void convert(icon, { multiplier }).then((value) => {
            if (active)
                setSample({ icon, multiplier, convert, value });
        }, (error: unknown) => {
            if (active) {
                setSample({
                    icon,
                    multiplier,
                    convert,
                    value: error instanceof Error ? error : new Error(String(error)),
                });
            }
        });
        return () => {
            active = false;
        };
    }, [icon, multiplier, convert]);
    if (!sample ||
        sample.icon !== icon ||
        sample.multiplier !== multiplier ||
        sample.convert !== convert) {
        return <Text role="status">Converting illustration...</Text>;
    }
    if (sample.value instanceof Error) {
        return <Text role="alert">{sample.value.message}</Text>;
    }
    return <Illicon {...props} data={sample.value}/>;
}

export const Default = (args: SamplingProps) => (<ConvertedIllicon {...args} icon={Agents20Filled} aria-label="Agents illustration"/>);

export default function Example() { return <Default {...{ multiplier: 2, radius: 1, pitch: 5 }} />; }
```

## API

Generated CoreAI prop summaries, not replacement type declarations. Most native DOM/event and ARIA props are omitted. `?` means optional; JSDoc shows defaults, deprecations, and conditional requirements. Defaults assume the standard CoreAI provider.

### Illicon API

Renders saved occupancy as SVG circles, or preserves custom inline SVG artwork.
Supply aria-label/aria-labelledby for meaningful images; otherwise it is decorative.
Convert source icons separately with iconToDotGrid before rendering.

```ts
interface IlliconProps {
  /**
   * Trusted inline SVG. Use currentColor for theme-aware paint; geometry is not rewritten.
   */
  children?: ReactElement<SVGProps<SVGSVGElement>, string | JSXElementConstructor<any>>;

  className?: string;

  data?: DotGridData;

  /**
   * Center-to-center distance between cells. Defaults to 5.
   */
  pitch?: number;

  /**
   * Circle radius. Defaults to 1; must fit within half the pitch.
   */
  radius?: number;

  ref?: Ref<HTMLSpanElement>;

  /**
   * Display width for custom SVG artwork only. Grids use cells times pitch.
   */
  size?: number;

  style?: CSSProperties;
}
```

### iconToDotGrid API

Browser-only conversion of standalone, static SVG artwork into occupancy.
The browser renders shapes, strokes, transforms, and local SVG references.
By default, grid dimensions are the SVG viewBox dimensions times 2.
For Fluent-based Illicons, prefer native 20px variants at the default 2x.
The canvas has one pixel per output cell; each pixel's alpha determines occupancy.
Empty rows and columns are never trimmed. No radius or pitch is stored.

```ts
declare function iconToDotGrid(source: IconToDotGridSource, options?: IconToDotGridOptions): Promise<DotGridData>;
```

### dotGridToSvg API

Export standalone, circle-based SVG with explicit ink for Figma and other tools.

```ts
declare function dotGridToSvg(data: DotGridData, options?: DotGridRenderOptions): string;
```

## Implementation

CoreAI 0.12.0; Fluent 9.74.9.

[Setup and dependencies](/coreai-design-system/guides/Package.md).

Import components from `@coreai-ds/react-components`. CoreAI guidance above takes precedence over API demonstration examples.

Note: CustomSvgDev is excluded: Verification-only story marked !manifest.

Note: EmptyGridDev is excluded: Verification-only story marked !manifest.

Note: SourceRecreationDev is excluded: Verification-only source comparison marked !manifest.

Note: Catalog is excluded: Local searchable gallery marked !manifest; reviewed artwork is listed below.

## In Empty State

Source: CoreAI.

Uses an Illicon as the dotted illustration in an empty state.

[Download source](/coreai-design-system/source/examples/components-illicon--in-empty-state.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import * as React from 'react';
import { makeStyles } from "@coreai-ds/react-components";
import { tokens } from "@coreai-ds/react-components";
import { Text } from "@coreai-ds/react-components";
import { iconToDotGrid } from "@coreai-ds/react-components";
import type { DotGridRenderOptions } from "@coreai-ds/react-components";
import type { FluentIcon } from "@fluentui/react-icons";
import { Illicon } from "@coreai-ds/react-components";
import { Agents20Filled } from "@fluentui/react-icons";
import { Button } from "@coreai-ds/react-components";
import { CodeRegular } from "@fluentui/react-icons";
import { Link } from "@coreai-ds/react-components";

interface EmptyStateProps {
    /** Optional Illicon or custom artwork displayed above the message. */
    illustration?: React.ReactNode;
    /** Main empty-state heading. */
    header: React.ReactNode;
    /** Optional supporting text explaining the state or the next step. */
    description?: React.ReactNode;
    /** Optional row of value propositions, metrics, or highlights. */
    metrics?: React.ReactNode;
    /** Optional row of cards, choices, integrations, or detailed content. */
    cards?: React.ReactNode;
    /** Optional row of primary and secondary actions. */
    actions?: React.ReactNode;
    /** Optional supporting links displayed after the actions. */
    links?: React.ReactNode;
}

const useEmptyStateStyles = makeStyles({
    root: {
        boxSizing: 'border-box',
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        justifyContent: 'center',
        rowGap: tokens.spacingVerticalXL,
        width: '100%',
        minHeight: '400px', // no dimension token; preserves the empty-state canvas height
        paddingBlock: tokens.spacingVerticalXXXL,
        paddingInline: tokens.spacingHorizontalXXL,
        textAlign: 'center',
    },
    illustration: {
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        color: tokens.colorNeutralForeground3,
        fontSize: tokens.fontSizeHero800,
        lineHeight: 1,
    },
    copy: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        rowGap: tokens.spacingVerticalXS,
        maxWidth: '600px', // no dimension token; matches the Figma copy width
    },
    header: {
        marginBlock: 0,
    },
    description: {
        marginBlock: 0,
    },
    collection: {
        display: 'flex',
        flexWrap: 'wrap',
        justifyContent: 'center',
        gap: tokens.spacingHorizontalL,
        width: '100%',
    },
    actions: {
        display: 'flex',
        flexWrap: 'wrap',
        alignItems: 'center',
        justifyContent: 'center',
        gap: tokens.spacingHorizontalS,
        width: '100%',
    },
    links: {
        display: 'flex',
        flexWrap: 'wrap',
        alignItems: 'center',
        justifyContent: 'center',
        gap: tokens.spacingHorizontalS,
    },
});

function EmptyState({ illustration, header, description, metrics, cards, actions, links, }: EmptyStateProps) {
    const styles = useEmptyStateStyles();
    return (<div className={styles.root}>
      {illustration ? <div className={styles.illustration}>{illustration}</div> : null}

      <div className={styles.copy}>
        <Text as="h2" preset="heading4" foreground={1} className={styles.header}>
          {header}
        </Text>
        {description ? (<Text as="p" preset="body" foreground={2} className={styles.description}>
            {description}
          </Text>) : null}
      </div>

      {metrics ? <div className={styles.collection}>{metrics}</div> : null}
      {cards ? <div className={styles.collection}>{cards}</div> : null}
      {actions ? <div className={styles.actions}>{actions}</div> : null}
      {links ? <div className={styles.links}>{links}</div> : null}
    </div>);
}

function ConvertedIllicon({ icon, multiplier, convert = iconToDotGrid, ...props }: DotGridRenderOptions & {
    icon: FluentIcon;
    multiplier?: number;
    'aria-label'?: string;
    convert?: typeof iconToDotGrid;
}) {
    const [sample, setSample] = React.useState<{
        icon: FluentIcon;
        multiplier?: number;
        convert: typeof iconToDotGrid;
        value: Awaited<ReturnType<typeof iconToDotGrid>> | Error;
    }>();
    React.useEffect(() => {
        let active = true;
        void convert(icon, { multiplier }).then((value) => {
            if (active)
                setSample({ icon, multiplier, convert, value });
        }, (error: unknown) => {
            if (active) {
                setSample({
                    icon,
                    multiplier,
                    convert,
                    value: error instanceof Error ? error : new Error(String(error)),
                });
            }
        });
        return () => {
            active = false;
        };
    }, [icon, multiplier, convert]);
    if (!sample ||
        sample.icon !== icon ||
        sample.multiplier !== multiplier ||
        sample.convert !== convert) {
        return <Text role="status">Converting illustration...</Text>;
    }
    if (sample.value instanceof Error) {
        return <Text role="alert">{sample.value.message}</Text>;
    }
    return <Illicon {...props} data={sample.value}/>;
}

export const InEmptyState = () => (<EmptyState illustration={<ConvertedIllicon icon={Agents20Filled}/>} header="Benefit from Action Artifact" description="Disambiguate with text or exclude entirely." actions={<>
          <Button appearance="primary">Create agent</Button>
          <Button icon={<CodeRegular />}>Code</Button>
        </>} links={<Link href="#">How to make the coolest artifact ever</Link>}/>);

export default InEmptyState;
```

## Original vs. Illicon

Source: CoreAI.

Compare Filled and Regular icons at native-size sampling multiples.
Starts with the recommended native 20px source at 2x; other settings are
available for visual testing.
The multiplier changes occupancy resolution. Radius and pitch only change
rendering; a 2px dot stays 2px at every multiplier. Regular icons usually
represent outlines using filled paths rather than SVG stroke attributes.

[Download source](/coreai-design-system/source/examples/components-illicon--original-vs-illicon.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { DotGridRenderOptions } from "@coreai-ds/react-components";
import { Agents20Filled } from "@fluentui/react-icons";
import { Agents20Regular } from "@fluentui/react-icons";
import { Brain24Filled } from "@fluentui/react-icons";
import { Brain24Regular } from "@fluentui/react-icons";
import { Cube24Filled } from "@fluentui/react-icons";
import { Cube24Regular } from "@fluentui/react-icons";
import { Database24Filled } from "@fluentui/react-icons";
import { Database24Regular } from "@fluentui/react-icons";
import { Document24Filled } from "@fluentui/react-icons";
import { Document24Regular } from "@fluentui/react-icons";
import { FolderAdd20Filled } from "@fluentui/react-icons";
import { FolderAdd20Regular } from "@fluentui/react-icons";
import { FolderAdd24Filled } from "@fluentui/react-icons";
import { FolderAdd24Regular } from "@fluentui/react-icons";
import { Heart16Filled } from "@fluentui/react-icons";
import { Heart16Regular } from "@fluentui/react-icons";
import { Heart20Filled } from "@fluentui/react-icons";
import { Heart20Regular } from "@fluentui/react-icons";
import { Heart24Filled } from "@fluentui/react-icons";
import { Heart24Regular } from "@fluentui/react-icons";
import { Rocket16Filled } from "@fluentui/react-icons";
import { Rocket16Regular } from "@fluentui/react-icons";
import { Rocket24Filled } from "@fluentui/react-icons";
import { Rocket24Regular } from "@fluentui/react-icons";
import { Scales20Filled } from "@fluentui/react-icons";
import { Scales20Regular } from "@fluentui/react-icons";
import { Search24Filled } from "@fluentui/react-icons";
import { Search24Regular } from "@fluentui/react-icons";
import { Shield24Filled } from "@fluentui/react-icons";
import { Shield24Regular } from "@fluentui/react-icons";
import { Star24Filled } from "@fluentui/react-icons";
import { Star24Regular } from "@fluentui/react-icons";
import type { FluentIcon } from "@fluentui/react-icons";
import { makeStyles } from "@coreai-ds/react-components";
import { tokens } from "@coreai-ds/react-components";
import { Text } from "@coreai-ds/react-components";
import { iconToDotGrid } from "@coreai-ds/react-components";
import * as React from 'react';
import { Illicon } from "@coreai-ds/react-components";

type SamplingProps = DotGridRenderOptions & {
    multiplier: number;
};

const iconOptions = {
    Agents20: { filled: Agents20Filled, regular: Agents20Regular, nativeSize: 20 },
    Brain24: { filled: Brain24Filled, regular: Brain24Regular, nativeSize: 24 },
    Cube24: { filled: Cube24Filled, regular: Cube24Regular, nativeSize: 24 },
    Database24: { filled: Database24Filled, regular: Database24Regular, nativeSize: 24 },
    Document24: { filled: Document24Filled, regular: Document24Regular, nativeSize: 24 },
    FolderAdd20: { filled: FolderAdd20Filled, regular: FolderAdd20Regular, nativeSize: 20 },
    FolderAdd24: { filled: FolderAdd24Filled, regular: FolderAdd24Regular, nativeSize: 24 },
    Heart16: { filled: Heart16Filled, regular: Heart16Regular, nativeSize: 16 },
    Heart20: { filled: Heart20Filled, regular: Heart20Regular, nativeSize: 20 },
    Heart24: { filled: Heart24Filled, regular: Heart24Regular, nativeSize: 24 },
    Rocket16: { filled: Rocket16Filled, regular: Rocket16Regular, nativeSize: 16 },
    Rocket24: { filled: Rocket24Filled, regular: Rocket24Regular, nativeSize: 24 },
    Scales20: { filled: Scales20Filled, regular: Scales20Regular, nativeSize: 20 },
    Search24: { filled: Search24Filled, regular: Search24Regular, nativeSize: 24 },
    Shield24: { filled: Shield24Filled, regular: Shield24Regular, nativeSize: 24 },
    Star24: { filled: Star24Filled, regular: Star24Regular, nativeSize: 24 },
};

type ComparisonProps = SamplingProps & {
    icon: keyof typeof iconOptions;
    variant: 'Filled' | 'Regular';
};

const useComparisonStyles = makeStyles({
    root: {
        width: '100%',
        overflowX: 'auto',
    },
    comparison: {
        display: 'grid',
        gridTemplateColumns: 'repeat(2, max-content)',
        columnGap: tokens.spacingHorizontalXXL,
        marginInline: 'auto',
        width: 'max-content',
        color: tokens.colorNeutralForeground1,
    },
    example: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        rowGap: tokens.spacingVerticalM,
    },
});

function ConvertedIllicon({ icon, multiplier, convert = iconToDotGrid, ...props }: DotGridRenderOptions & {
    icon: FluentIcon;
    multiplier?: number;
    'aria-label'?: string;
    convert?: typeof iconToDotGrid;
}) {
    const [sample, setSample] = React.useState<{
        icon: FluentIcon;
        multiplier?: number;
        convert: typeof iconToDotGrid;
        value: Awaited<ReturnType<typeof iconToDotGrid>> | Error;
    }>();
    React.useEffect(() => {
        let active = true;
        void convert(icon, { multiplier }).then((value) => {
            if (active)
                setSample({ icon, multiplier, convert, value });
        }, (error: unknown) => {
            if (active) {
                setSample({
                    icon,
                    multiplier,
                    convert,
                    value: error instanceof Error ? error : new Error(String(error)),
                });
            }
        });
        return () => {
            active = false;
        };
    }, [icon, multiplier, convert]);
    if (!sample ||
        sample.icon !== icon ||
        sample.multiplier !== multiplier ||
        sample.convert !== convert) {
        return <Text role="status">Converting illustration...</Text>;
    }
    if (sample.value instanceof Error) {
        return <Text role="alert">{sample.value.message}</Text>;
    }
    return <Illicon {...props} data={sample.value}/>;
}

function IconComparison({ icon: Icon, label, nativeSize, multiplier, radius = 1, pitch = 5, }: SamplingProps & {
    icon: FluentIcon;
    label: string;
    nativeSize: number;
}) {
    const styles = useComparisonStyles();
    const columns = nativeSize * multiplier;
    const width = columns * pitch;
    return (<div className={styles.root}>
      <div className={styles.comparison}>
        <div className={styles.example}>
          <Text>Original</Text>
          <Icon style={{ width, height: width }} aria-label={`Original ${label}`}/>
          <Text>
            {label}: native {nativeSize} x {nativeSize}
          </Text>
        </div>
        <div className={styles.example}>
          <Text>Illicon</Text>
          <ConvertedIllicon icon={Icon} multiplier={multiplier} radius={radius} pitch={pitch} aria-label={`Illicon ${label}`}/>
          <Text>
            {columns} x {columns} cells; {width} x {width}px; {radius * 2}px dots
          </Text>
        </div>
      </div>
    </div>);
}

export const OriginalVsIllicon = ({ icon, variant, ...args }: ComparisonProps) => {
    const source = iconOptions[icon];
    return (<IconComparison {...args} icon={variant === 'Filled' ? source.filled : source.regular} label={`${icon}${variant}`} nativeSize={source.nativeSize}/>);
};

export default function Example() { return <OriginalVsIllicon {...{
    icon: 'Scales20',
    variant: 'Filled',
    multiplier: 2,
    radius: 1,
    pitch: 5,
  }} />; }
```

## Stroked SVG

Source: CoreAI.

Real 1px SVG strokes, rather than outline-shaped filled paths. Compare
rectangles, curves, and a diagonal at 1x, 2x, 3x, or 4x sampling.

[Download source](/coreai-design-system/source/examples/components-illicon--stroked-svg.tsx)

```tsx
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

import type { DotGridRenderOptions } from "@coreai-ds/react-components";
import type { FluentIcon } from "@fluentui/react-icons";
import { makeStyles } from "@coreai-ds/react-components";
import { tokens } from "@coreai-ds/react-components";
import { Text } from "@coreai-ds/react-components";
import { iconToDotGrid } from "@coreai-ds/react-components";
import * as React from 'react';
import { Illicon } from "@coreai-ds/react-components";

type SamplingProps = DotGridRenderOptions & {
    multiplier: number;
};

const useComparisonStyles = makeStyles({
    root: {
        width: '100%',
        overflowX: 'auto',
    },
    comparison: {
        display: 'grid',
        gridTemplateColumns: 'repeat(2, max-content)',
        columnGap: tokens.spacingHorizontalXXL,
        marginInline: 'auto',
        width: 'max-content',
        color: tokens.colorNeutralForeground1,
    },
    example: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
        rowGap: tokens.spacingVerticalM,
    },
});

function ConvertedIllicon({ icon, multiplier, convert = iconToDotGrid, ...props }: DotGridRenderOptions & {
    icon: FluentIcon;
    multiplier?: number;
    'aria-label'?: string;
    convert?: typeof iconToDotGrid;
}) {
    const [sample, setSample] = React.useState<{
        icon: FluentIcon;
        multiplier?: number;
        convert: typeof iconToDotGrid;
        value: Awaited<ReturnType<typeof iconToDotGrid>> | Error;
    }>();
    React.useEffect(() => {
        let active = true;
        void convert(icon, { multiplier }).then((value) => {
            if (active)
                setSample({ icon, multiplier, convert, value });
        }, (error: unknown) => {
            if (active) {
                setSample({
                    icon,
                    multiplier,
                    convert,
                    value: error instanceof Error ? error : new Error(String(error)),
                });
            }
        });
        return () => {
            active = false;
        };
    }, [icon, multiplier, convert]);
    if (!sample ||
        sample.icon !== icon ||
        sample.multiplier !== multiplier ||
        sample.convert !== convert) {
        return <Text role="status">Converting illustration...</Text>;
    }
    if (sample.value instanceof Error) {
        return <Text role="alert">{sample.value.message}</Text>;
    }
    return <Illicon {...props} data={sample.value}/>;
}

function IconComparison({ icon: Icon, label, nativeSize, multiplier, radius = 1, pitch = 5, }: SamplingProps & {
    icon: FluentIcon;
    label: string;
    nativeSize: number;
}) {
    const styles = useComparisonStyles();
    const columns = nativeSize * multiplier;
    const width = columns * pitch;
    return (<div className={styles.root}>
      <div className={styles.comparison}>
        <div className={styles.example}>
          <Text>Original</Text>
          <Icon style={{ width, height: width }} aria-label={`Original ${label}`}/>
          <Text>
            {label}: native {nativeSize} x {nativeSize}
          </Text>
        </div>
        <div className={styles.example}>
          <Text>Illicon</Text>
          <ConvertedIllicon icon={Icon} multiplier={multiplier} radius={radius} pitch={pitch} aria-label={`Illicon ${label}`}/>
          <Text>
            {columns} x {columns} cells; {width} x {width}px; {radius * 2}px dots
          </Text>
        </div>
      </div>
    </div>);
}

const StrokedSvgIcon: FluentIcon = (props) => (<svg {...props} role="img" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1">
    <rect x="4.5" y="4.5" width="15" height="15" rx="2"/>
    <circle cx="12" cy="12" r="4.5"/>
    <path d="M6.5 17.5 17.5 6.5"/>
  </svg>);

export const StrokedSvg = (args: SamplingProps) => (<IconComparison {...args} icon={StrokedSvgIcon} label="Stroked SVG" nativeSize={24}/>);

export default function Example() { return <StrokedSvg {...{ multiplier: 2, radius: 1, pitch: 5 }} />; }
```