Contributing Icons
Welcome! If you're contributing icons or symbols to the @adaptavant/eds-icons package, please follow the guidelines below to ensure consistency, quality, and smooth integration into the Earth Design System.
Overview
The package is a collection of icons, logos, and symbols that are used in the Earth Design System and has three main categories:
- Icons - Common UI icons (e.g., add, arrow, notifications).
- Logos - Rectangular product or brand logos with neutral and inverse tones.
- Symbols - Brand-related graphics, such as logos (e.g., Google, Zoom).
Pre-requisites
Using corepack is suggested to manage your pnpm version. Otherwise make sure you are using the node and pnpm versions listed under the engines key in the root package.json. Read them from that file rather than from here, so this page cannot go stale.
Installation
pnpm install
Sourcing Your Asset
Please make sure the icon/symbol is available in the Earth Figma Library. Do not use icons from other Figma files, personal drafts, or third-party sources.
This ensures design consistency and is a required step for contributing to EDS icon library.
Missing an Icon?
Reach out to your Product Designer if the icon you need isn't available in the Earth Figma Library. They can evaluate and include it, if it aligns with design system standards.
File Format
- Export the icon as an SVG from Figma.
.pngor other raster formats are not allowed.
Adding Your Asset
Name your files, drop them in the right folder, run one command. The same flow handles one asset and twenty, so there is nothing extra to learn for a batch.
- Export the asset as an SVG from the Earth Figma Library.
- Name the file for its category and move it into the matching folder under
packages/svg-to-component/. The folders and suffixes are in Naming Your Files below. - From the root of the repository, run
pnpm optimize:svg, thenpnpm build:svg. - Run
pnpm checkandpnpm check:test. - Add a changeset describing your changes, as in Adding a changeset file below.
- Push and open a pull request, working through the Pull Request Checklist below.
pnpm optimize:svg
pnpm build:svg
Nothing else needs updating. The Playroom snippets and the icon listings on this site are both derived from the package when the build runs, so a new asset appears in them without an edit.
If you'd like a walkthrough of the wider process, there is a video recording covering sourcing an icon through to opening a pull request.
Naming Your Files
This is the one step nothing can do for you, and the filename decides the
generated component name. pnpm optimize:svg checks every name and refuses a
file it cannot map to a component, but it cannot guess what you meant.
| Category | Folder | File name |
|---|---|---|
| Icon | icons/ | payment-history-icon.svg |
| Symbol | symbols/ | acme-color-icon.svg |
| Logo | logos/ | acme-logo.svg and acme-logo-inverse.svg |
Icons get a -icon suffix:
| SVG File Name | Auto-generated Component Name |
|---|---|
search-icon.svg | SearchIcon |
notifications-off-icon.svg | NotificationsOffIcon |
Symbols get a -color-icon suffix. Note that the word "symbol" never appears in the filename:
| SVG File Name | Auto-generated Component Name |
|---|---|
google-color-icon.svg | GoogleColorIcon |
google-calendar-color-icon.svg | GoogleCalendarColorIcon |
Logos get a -logo suffix, and ship as a pair. Add both files, the second one carrying -inverse:
| SVG Files | Auto-generated Component Name |
|---|---|
setmore-logo.svg and setmore-logo-inverse.svg | SetmoreLogo |
answerconnect-logo.svg and answerconnect-logo-inverse.svg | AnswerconnectLogo |
✅ Please ensure filenames are accurate, as these determine the final component name.
Every name is kebab-case. Anything else, PaymentHistory-icon.svg or
payment_history-icon.svg or Frame 2.svg, is reported and skipped.
What the Optimise SVG Command Does For You
It replaces cleaning the SVG up by hand in a web tool. Across every file in
icons/, symbols/ and logos/, it:
- Normalises. Strips the root
width/height, removes editor metadata and empty groups, rounds path precision, namespaces every gradient and clip-path id, and strips hardcoded colour from an icon while leaving a symbol's brand colours alone. - Checks the filename, so a missing
-color-iconis caught before it builds a component under the wrong name. - Reports what it cannot fix, naming the file, the rule, the value found and what to change in Figma.
A file it cannot fix is skipped and listed at the end; the rest are still written, so one asset needing a re-export does not hold up the others. The command exits non-zero when anything was skipped.
Already-normalised files are left untouched, so running it twice changes
nothing the second time, and running it when you have added nothing is a no-op.
It does not build; run pnpm build:svg after it.
Checking an Asset Without Changing It
To see whether files are already normalised, without writing anything:
pnpm check:svg packages/svg-to-component/icons/payment-history-icon.svg
It reports whether each file meets the requirements above, and exits non-zero if any does not. Paths outside icons/, symbols/ and logos/ are ignored, so you can pass a whole list of changed files.
Adding a Single Asset with the CLI
For one asset, pnpm svg-cli does the naming and the build for you. Pass the
bare name without a suffix, and it works out the filename, the folder and the
component.
On macOS you can paste the artwork straight from the clipboard, one command per asset:
pbpaste | pnpm svg-cli --type=icon --name=payment-history
pbpaste | pnpm svg-cli --type=symbol --name=acme
pbpaste | pnpm svg-cli --type=logo --name=acme
pbpaste | pnpm svg-cli --type=logo --name=acme --inverse
Run it in this order:
- Paste the command into your terminal and set
--name, but do not press Enter yet. - Switch to Figma and copy the SVG.
- Switch back and press Enter.
Copying the command is itself a clipboard write, so doing it the other way round
leaves the command on the clipboard instead of the artwork, and there is nothing
for pbpaste to pipe in.
pbpaste is macOS only. Everywhere else, save the SVG to a file and pass the
path instead. Piping is a convenience, not a separate code path:
pnpm svg-cli --type=icon --name=payment-history ~/Downloads/Frame\ 2.svg
pnpm svg-cli --type=symbol --name=acme ~/Downloads/acme.svg
pnpm svg-cli --type=logo --name=acme ~/Downloads/acme-logo.svg
pnpm svg-cli --type=logo --name=acme --inverse ~/Downloads/acme-logo-inverse.svg
| Flag | Meaning |
|---|---|
--type | icon, symbol or logo. Required. |
--name | The bare name in kebab-case, without the suffix. payment-history becomes payment-history-icon.svg. Required. |
--inverse | Logos only. Writes the inverse half of a logo pair. |
--force | Overwrites an asset that already exists. |
Unlike pnpm optimize:svg, this one runs the build itself, so there is no
separate pnpm build:svg step.
Colour: Icons and Symbols Differ
pnpm optimize:svg handles the mechanical half: it strips hardcoded colour from an icon and never touches a symbol's fills. Getting this backwards is the most-repeated correction in this package's review history, and the judgement call at the end is yours.
An icon ships with no colour of its own. tone sets a colour on the root <svg> and the paths inherit it. A path with its own colour ignores tone, which is how an icon ends up dark on a dark surface and invisible.
Read the diffs as a fix: - is the mistake, + is what lands.
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 480" fill-rule="evenodd">
- <path fill="#111" d="M240 12a228 228 0 1 0 0 456 228 228 0 0 0 0-456Z" />
+ <path d="M240 12a228 228 0 1 0 0 456 228 228 0 0 0 0-456Z" />
</svg>
Both of these look like exceptions. Only one is:
fill="none"on the root<svg>is fine, it does not affect child paths.- An icon needing two tones uses a token, not a hex:
fill="var(--icon-primary)".
A symbol keeps its brand colour. It is a third-party mark and its colours are not ours to change. Stripping them is as wrong as hardcoding a colour on an icon.
Stripping the fills is the mistake here:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 480 480" fill-rule="evenodd">
- <path d="M0 0h480v480H0z" />
+ <path fill="#0052CC" d="M0 0h480v480H0z" />
- <path d="M60 60h360v360H60z" />
+ <path fill="#2684FF" d="M60 60h360v360H60z" />
</svg>
One exception: a monochrome black mark should use fill="var(--symbol-primary)" rather than #000 or a near-black hex, so it inverts on a dark surface instead of disappearing. A mark with real brand colours keeps its hex untouched.
SVG Requirements
Read this when pnpm optimize:svg rejects your file. Each of these fails in a way that is easy to miss in a wall of path data, which is why they are enforced mechanically rather than left to review.
| Requirement | Why it matters |
|---|---|
Exported as SVG. .png and other raster formats are not allowed. | The component inlines the markup, and a raster file has none to inline. |
Icons and symbols use viewBox="0 0 480 480". | The system draws at 480 and scales down through the size prop. Another viewBox renders at the wrong optical weight next to its neighbours. |
| Logos keep their own aspect ratio. | Wordmarks differ in proportion, so there is no shared viewBox for them. |
No width or height on the root <svg>. | The size prop decides the rendered size, and fixed dimensions fight it. |
No stroke anywhere. Outline strokes in Figma before exporting. | A stroke keeps its width as the icon scales, so it thickens at small sizes. |
| Every gradient, mask and clip-path id is unique across the document. | Components share one DOM once inlined. Two carrying the same id resolve url(#...) to whichever came first, and the second renders with the other's colours. The commands namespace ids for you, so this only bites a file added by hand. |
| Icons and symbols are built from filled paths, not strokes or effects. | Anything the renderer interprets at draw time will not survive being inlined and scaled. |
Directory Structure
The source svg files live in packages/svg-to-component and will be auto-generated into the packages/icons directory for usage.
packages
- eds-svg-to-component
- icons
- icon-name-icon.svg
- logos
- logo-name-logo.svg
- logo-name-logo-inverse.svg
- symbols
- icon-name-color-icon.svg
Versioning
Package publishing has been configured using Changesets. Please review their documentation to familiarize yourself with the workflow.
View here for more information on how to add a changeset file.
Adding a changeset file
Run the following command:
pnpm changeset
Select eds-icons from the list of packages, choose the bump from the table below, and describe your changes.
Which bump
| Change | Bump | Why |
|---|---|---|
| A new icon, symbol or logo | minor | Adds to the public API without changing anything that exists. |
| A visual update to an asset that already ships | patch | The export stays the same, so no consumer has to change anything. |
| Renaming or removing an asset | major | The component name is part of the public API, so a rename breaks every consumer that imports it. |
Changeset Content Format
---
'@adaptavant/eds-icons': patch or minor or major
---
Describe your changes here, this will be used to generate the changelog content.
For example, you can describe your changes as follows:
---
'@adaptavant/eds-icons': minor
---
Icons:
- Added New icons
`AnnouncementsIcon, BirthdayIcon, BoldIcon, CampaignIcon, CashIcon, ChargeIcon, ChatIcon, CompanyIcon, CustomerFilledIcon, DesignIcon, ExpandIcon, GlobeIcon, InboxFilledIcon, ItalicIcon, JustifyLeftIcon, LinkDisconnectIcon, MessengerFilledIcon, MinimiseIcon, PaletteIcon, PauseIcon, PaymentApplepayPlainIcon, PaymentCashAppFilledIcon, PaymentCashAppIcon, PaymentCashAppPlainIcon, PaymentGooglepayPlainIcon, PaymentMastercardPlainIcon, PaymentPaypalPlainIcon, PaymentVisaPlainIcon, PlayIcon, PreviewIcon, ReductionIcon, SettingsFilledIcon, UnderlineIcon, WhatsappFilledIcon, XFilledIcon`
- Updated
`ConnectFilledIcon, CustomersFilledIcon, InstagramFilledIcon, LinkedinFilledIcon, MapPinIcon, MoreIcon, ServicesFilledIcon, ServicesIcon, TeleportIcon, VideoCameraIcon, YoutubeFilledIcon`
Pull Request Checklist
Copy this into your pull request description:
- [ ] Asset comes from the Earth Figma Library, not a personal draft or a third-party source
- [ ] Filename follows the convention for its category, in kebab-case with the right suffix
- [ ] Meets every requirement in "SVG Requirements" above
- [ ] Icon ships with no colour, or symbol keeps its brand colours
- [ ] `pnpm optimize:svg` run, and it reported no failures for your files
- [ ] `pnpm build:svg` run, and the generated output committed
- [ ] `pnpm check` and `pnpm check:test` pass
- [ ] Changeset added, with the bump chosen from the table above
- [ ] Not a duplicate of an asset that already exists
The last item is the one a script cannot check for you. Search the existing icons and symbols before adding, since near-duplicates are a recurring review finding.
Releases
The Earth Design System core team owns releases. There is no fixed calendar: merging your pull request into main adds your change to a pending Version Packages pull request, which Changesets keeps up to date automatically. When the core team merges that, @adaptavant/eds-icons publishes.
To confirm your asset has shipped, check either:
- The changelog on this site, which gets an entry per published version and includes your changeset description.
- The version of
@adaptavant/eds-iconsin the package registry, compared against the version your changeset was released in.
Every contribution you make helps make the system better for everyone. We appreciate your effort and care. Thank you!