Illicon
View .mdRender saved dot-grid illustrations, convert icons in the browser, and export editable SVG.
Default
Loading interactive example. Full source is available below.
View example source
// 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 }} />; }
Illicon renders saved dot-grid occupancy as SVG circles. Grid data contains only
equal-length, nonempty rows of 0s and 1s. Empty rows and columns, including
borders, are preserved. Height is the number of rows; width is the length of a row.
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.
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 |
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, SVGSVGElements, 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
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.
<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
Tags: none.
Workflows
Tags: none.
Models
Tags: none.
Fine-tune
Tags: beaker, lab, test.
Database
Tags: data, storage.
Documents
Tags: file, pages.
Evaluations
Tags: compare, scale.
Shield
Tags: security, defence.
Lock closed
Tags: lock, access, permissions.
Warning
Tags: alert, error.
Globe
Tags: networking, network, world.
Foundry
Tags: none.
Home
Tags: none.
AI Gateway
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 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 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
-
Check the catalog for an existing concept.
-
Export a transparent, monochrome SVG at
width="140",height="140"andviewBox="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. -
Save it here as a lowercase kebab-case filename, such as
trash.svg. -
Add an entry to
manifest.json:{ "title": "Trash", "file": "trash.svg", "tags": ["delete", "remove"], "author": "your-github-handle" } -
From the repository root, run:
npm run storybook -
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:
node scripts/generate-illicons.mjs
See the repository contribution policy 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.svgfilename 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.
API referenceGenerated CoreAI prop and type summaries
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.
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.
declare function iconToDotGrid(source: IconToDotGridSource, options?: IconToDotGridOptions): Promise<DotGridData>;
dotGridToSvg API
Export standalone, circle-based SVG with explicit ink for Figma and other tools.
declare function dotGridToSvg(data: DotGridData, options?: DotGridRenderOptions): string;
Implementation
CoreAI 0.12.0; Fluent 9.74.9.
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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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
Loading interactive example. Full source is available below.
// 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 }} />; }