Guides
Icons
Cynosure ships two kinds of icon, both drawn by the same Icon component.
- Facet icons (
@arshad-shah/cynosure/icons): Cynosure's own set, 274 icons in 17 categories. - Brand marks (
@arshad-shah/cynosure/brands): the official logos of 98 companies and projects, exactly as their owners publish them.
Facet iconsLink to "Facet icons"
One grammar: a 24 grid, a 1.75 round stroke, softened-square geometry, strokes only. The categories (ICON_CATEGORIES) are arrows, navigation, actions, editing, files, data, media, communication, status, time, security, people, devices, design, layout, maths and faith. Search them at the top of the Icons page, or browse them in Storybook: Foundations → Icon → Gallery (hover a tile to see its motion).
import { Icon } from "@arshad-shah/cynosure";
import { search } from "@arshad-shah/cynosure/icons";
<Icon icon={search} /> // decorative (hidden from screen readers)
<Icon icon={search} label="Search" /> // role="img" with a name
<Icon icon={search} size="lg" tone="accent" /> // sm 14 · md 16 · lg 20 · or a number of px
<Icon icon={search} color="#14b8a6" /> // a one-off colourICON_KEYWORDS (from the same entry) maps each icon's name to other words it is found by (trash: delete, remove, bin), for building your own icon picker. Import icons by name so unused ones are left out of your bundle. Names are camelCase versions of the icon's kebab-case name (arrow-up-right → arrowUpRight); reserved words get an Icon suffix (packageIcon).
By nameLink to "By name"
When all you have is a name as a string (a plugin manifest that says icon: "database", a setting saved as text), look the icon up on its own entry:
import { Icon } from "@arshad-shah/cynosure";
import { iconByName, type IconName, icons } from "@arshad-shah/cynosure/icons/by-name";
import { puzzle } from "@arshad-shah/cynosure/icons";
<Icon icon={iconByName(plugin.icon) ?? puzzle} />;iconByName(name)takes the icon's kebab-casename("arrow-up-right", as in the gallery), and answersundefinedfor anything else (a camelCase export name, another set's name,"toString"), so keep a fallback.iconsis the frozen record behind it, keyed by name (IconNameis the union of the names), for listing or validating names up front.- This entry brings every icon with it (about 13.5 kB gzipped once bundled), which is why it is separate: the main entry and
@arshad-shah/cynosure/iconsnever import it, so importing icons one by one stays tree-shaken. Use it only where names arrive at run time.
Adding one: draw it on a 24 grid with strokes only, export the SVG, then run pnpm cy new icon <name> --from icon.svg --category <category>. In the generated file, set motion and mark the moving part animate: true. See src/icons/AGENTS.md. The by-name entry's budget is measured as one bundle by tools/icons-size.ts, which pnpm size runs after size-limit (size-limit's file plugin gzips each module on its own and overstated it about five times): when new icons push it over, raise ICONS_BY_NAME_BUDGET there to what it prints.
Brand marksLink to "Brand marks"
import { Icon } from "@arshad-shah/cynosure";
import { github, npm } from "@arshad-shah/cynosure/brands";
<Icon icon={github} label="GitHub" /> // in the text colour (a one-colour mark)
<Icon icon={npm} label="npm" color="brand" size="lg" /> // in npm's own colourBrands let their logos be used only as they publish them, so these are never redrawn in Facet's style:
- Don't change a mark's shape, proportions or colours (other than one colour throughout, or
color="brand"). - Don't combine a mark with other shapes, or use it to suggest the brand endorses your product.
- Do read the brand's own guidelines when you use its mark prominently. Each mark carries
brand.guidelines(where published) andbrand.source.
The marks come from Simple Icons (opens in new tab) (CC0). The trademarks remain their owners'. The list is tools/brands.json; regenerate with node --import tsx tools/brands.ts <unpacked simple-icons package>.
Not included, at the owners' request: some companies ask icon sets not to carry their marks. Use their own brand kits instead:
- LinkedIn: https://brand.linkedin.com/ (opens in new tab)
- Slack: https://slack.com/media-kit (opens in new tab)
- OpenAI: https://openai.com/brand (opens in new tab)
- CodePen: https://blog.codepen.io/documentation/brand-assets/ (opens in new tab)
- AWS: https://aws.amazon.com/co-marketing/ (opens in new tab)
- Microsoft and Azure: https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks (opens in new tab)
Without ReactLink to "Without React"
iconToSvg returns the same markup as Icon, as a string, for Astro components, static HTML or plain TypeScript. Load @arshad-shah/cynosure/styles/icon.css (or the full styles.css) so it sizes and colours the same way.
import { iconToSvg, search } from "@arshad-shah/cynosure/icons";
import { github } from "@arshad-shah/cynosure/brands";
element.innerHTML = iconToSvg(search, { label: "Search" });
const mark = iconToSvg(github, { size: 20, color: "brand", label: "GitHub" });Options: size (sm, md, lg or px), label (gives role="img"; omit for decorative icons), color (any CSS colour, or brand) and className. Labels and classes are escaped.