feat(metadata|docs|site): reuse metadata from a base icon with $extends: (#4360)

* feat(metadata): reuse metadata from a base icon with `$extends:`

Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.

- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
  unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
  already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(metadata): reuse metadata from a base icon with `$extends:`

Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.

- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
  unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
  already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix comments

* Format code

* Update tools/build-helpers/src/resolveMetadataExtends.ts

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Fix resolving icon metadata

* feat(metadata): reuse metadata from a base icon with `$extends:`

Icon metadata can reference a base icon with an inline `$extends:<icon-name>`
marker in `tags`, `categories` or `contributors`. The marker is replaced in
place with the base icon's resolved values at build time.

- Resolve markers in `readAllMetadata` and `getIconMetaData`, failing on
  unknown icons, self references and circular chains
- Accept markers in `icon.schema.json`
- `pnpm checkIcons` fails on duplicate markers and on literals a marker
  already provides, and runs in CI
- Docs read the resolved metadata index instead of the raw icon JSON
- Use `$extends:alarm-clock` in `alarm-clock-minus`

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Fix comments

* Format code

* Update tools/build-helpers/src/resolveMetadataExtends.ts

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* Fix resolving icon metadata

* Fix comments

* Format code

* Update tools/build-helpers/src/resolveMetadataExtends.ts

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Eric Fennis
2026-10-29 17:29:58 +01:00
committed by GitHub
co-authored by Claude Opus 5.5 github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
parent e2c3360976
commit 7bf67d3b1c
14 changed files with 417 additions and 131 deletions
@@ -19,6 +19,33 @@ Provide tag suggestions based on the name of the icon and the use cases provided
The `categories` property is an array of strings that specify the categories to which the icon belongs, such as "devices", "interface", "media", etc. See the `categories` property in the `icon.schema.json` for more details on the allowed values. The categories should be chosen based on the use cases provided in the PR description and the existing categories in the repository. The categories should be relevant to the icon and should help users find the icon when searching for specific types of icons. The name of icon should not be included in the categories, as it is already specified. Suggest categories based on the name of the icon and the use cases provided in the PR description. Use the existing categories in the repository as a reference for consistency and to avoid duplicates. Categories should always be in lowercase and should not contain spaces.
## Extending metadata from other icons
Some icons are variants of another icon (for example `alarm-clock-minus` is an `alarm-clock`). Instead of repeating the same values, an icon can inherit `tags`, `categories`, or `contributors` from its **base icon** using an inline `"$extends:<icon-name>"` marker placed directly inside the relevant array. At build time each marker is replaced, in place, with the referenced icon's fully resolved values for that same field.
```jsonc
{
"tags": ["$extends:alarm-clock", "remove"],
"categories": ["$extends:alarm-clock"],
"contributors": ["$extends:alarm-clock", "some-user"]
}
```
When checking or adding a `$extends:` marker:
- A marker must be exactly `"$extends:"` followed by the name of an **existing** icon — one that has its own `icons/<icon-name>.json` and `icons/<icon-name>.svg`. The icon name is the file name without the `.json` extension, written in lowercase kebab-case. Referencing a non-existent icon is invalid.
- Markers are only supported inside the `tags`, `categories`, and `contributors` arrays. They are not a separate property.
- Only use `$extends:` for the icon's **base icon** — the thing the icon _is_ (`file-plus` is a `file`, `alarm-clock-minus` is an `alarm-clock`). Never extend a **modifier** icon such as `plus`, `minus`, `x` or `check`: their tags describe that glyph on its own (`minus` carries `divider`, `hr`, `markdown`, `separator`…), which makes no sense on an alarm clock. Add the modifier's meaning as literal tags instead.
- An icon may not extend itself, and circular chains (A extends B, B extends A) are not allowed.
- Ordering is preserved: inherited values are inserted at the marker's position, so place `$extends:` markers where the inherited values should appear (conventionally first, before the icon's own literal values).
- Run `pnpm checkIcons` to validate all references. It fails on missing icons, self-references, and circular dependencies. Do not assume a marker is valid without confirming the target icon exists.
## There should not be duplicates
Never list a value that a `$extends:` marker already provides. Before adding a literal `tag`, `category`, or `contributor`, look at the referenced icon's metadata: if that icon already contains the value, do not also add it as a literal entry in the same array — the inherited value already covers it. For example, if `alarm-clock` already has the tag `morning`, an icon that uses `"$extends:alarm-clock"` in its `tags` must **not** also list `"morning"`.
Likewise, do not reference the same icon more than once in a single field (no duplicate `$extends:` markers), and do not repeat literal values. The `tags`, `categories`, and `contributors` arrays must contain only unique entries (`uniqueItems` in `icon.schema.json`), and after extends resolution the final arrays must not contain any duplicates.
## Use Cases
The `use-cases` property is an array of strings that describe the specific scenarios in which the icon would be used. Each use case should be concise and clearly convey the context in which the icon is applicable. Use the existing use cases in the repository as a reference for consistency and to avoid duplicates.
+19
View File
@@ -42,6 +42,25 @@ jobs:
env:
CHANGED_FILES: ${{ steps.changed-files.outputs.all_changed_files }}
check-icons:
name: Check Icons & Categories
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: 'pnpm'
node-version-file: 'package.json'
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Check icons and categories
run: pnpm checkIcons
check-lab-icons:
name: Check Lab Icons
runs-on: ubuntu-latest