Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f34bc73
[docs][material-icons] Add generated keywords and visual descriptions…
Janpot Sep 24, 2026
be67210
[docs][material-icons] Search generated icon keywords and descriptions
Janpot Sep 24, 2026
8a1fc76
[docs][material-icons] Prototype semantic icon search with static emb…
Janpot Sep 24, 2026
c4fd5af
[docs][material-icons] Merge generated icon descriptions into synonyms
Janpot Sep 24, 2026
1b82655
Merge branch 'docs/icon-descriptions' into docs/icon-search-potion
Janpot Sep 24, 2026
1b7261d
[docs][material-icons] Build the semantic index from synonyms.js
Janpot Sep 24, 2026
7ea636b
[docs][material-icons] Show the visual description in the icon detail…
Janpot Sep 24, 2026
8f81545
[docs] Fix writing style in icon-descriptions skill
Janpot Sep 24, 2026
2d4c3ee
Merge branch 'docs/icon-descriptions' into docs/icon-search-potion
Janpot Sep 24, 2026
e400359
[docs] Exclude the generated icon search index from formatting
Janpot Sep 24, 2026
18176e9
Revert "[docs][material-icons] Show the visual description in the ico…
Janpot Sep 24, 2026
efe5e35
Merge branch 'docs/icon-descriptions' into docs/icon-search-potion
Janpot Sep 24, 2026
21431c3
[docs] Simplify description cleanup in icon-descriptions skill
Janpot Sep 24, 2026
ee3fc6d
[docs] Drop the context step from the icon-descriptions skill
Janpot Sep 24, 2026
ca3e656
Merge branch 'docs/icon-descriptions' into docs/icon-search-potion
Janpot Sep 24, 2026
80ed2b8
[docs][material-icons] Add the words of icon names to synonyms
Janpot Sep 24, 2026
d8a7f55
Merge branch 'docs/icon-descriptions' into docs/icon-search-potion
Janpot Sep 24, 2026
c603ee2
[docs][material-icons] Rebuild the icon search index
Janpot Sep 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions .agents/skills/icon-descriptions/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
name: icon-descriptions
description: 'Generate search keywords and visual descriptions for Material icons that are missing from docs/data/material/components/material-icons/iconDescriptions.json, for example after new icons are added to @mui/icons-material. Use when the user asks to update, fill in or regenerate icon descriptions or icon search keywords, or runs /icon-descriptions. Pass --all to regenerate every icon, or --icons=A,B for specific ones.'
---

# Icon descriptions

`docs/data/material/components/material-icons/iconDescriptions.json` holds, for every base icon name (no `Outlined`/`Rounded`/`TwoTone`/`Sharp` suffix):

- `keywords`: search words that are not already in the icon name or in `synonyms.js`
- `visual`: one sentence describing what the icon looks like

`pnpm docs:mdicons:synonyms` merges both into `synonyms.js`, the same way it merges the tags from Google's Material Symbols metadata, and the icon search on the docs site indexes `synonyms.js`. The JSON is generated by looking at rendered icons, in two passes run by subagents, with `docs/scripts/iconDescriptions.mjs` doing everything around them.

Argument hint: `[--all | --icons=A,B]`. Without arguments, only icons missing from the JSON are processed.

## Workflow

Use a work directory outside the repository, for example `$TMPDIR/mui-icon-descriptions`. Start from an empty directory.

### 1. Render sheets

```bash
pnpm docs:mdicons:descriptions prepare <workDir> [--all | --icons=A,B]
```

This renders the icons into 4×4 labeled sheets (`<workDir>/sheets/sheet_XXX.png`), writes `<workDir>/manifest.json`, and writes the icons' existing synonyms to `<workDir>/synonyms.json` for the keyword pass. If it reports 0 icons, there is nothing to do: tell the user and stop.

View one sheet yourself to confirm the icons render correctly before continuing.

### 2. Visual pass

Split the sheet ids into batches of about 14 sheets. For each batch, launch a subagent (model: Sonnet) whose prompt is the contents of [prompts/visual.md](prompts/visual.md) with `{{WORK_DIR}}` replaced by the absolute work directory and `{{SHEETS}}` by the batch's sheet ids. Run the batches in parallel and wait for all of them.

The visual pass must not see the existing synonyms: they describe meaning, and in testing they made the descriptions vaguer.

### 3. Check and clean up the descriptions

```bash
pnpm docs:mdicons:descriptions check <workDir> visual
```

- Missing or invalid files: rerun the visual pass for those sheets only.
- Descriptions using style or filler words ("outline", "filled", ...): subagents use these in roughly 1 in 8 descriptions despite the instructions. Edit those sentences directly in `<workDir>/out/visual_XXX.json` with a minimal change: drop the style word ("house outline with a badge" becomes "house with a badge"), or rephrase when it carries meaning ("one dashed and one solid line" becomes "one dashed and one continuous line").

Run the check again. It must pass before continuing.

### 4. Keyword pass

Launch subagents (Sonnet) in the same batches with [prompts/keywords.md](prompts/keywords.md), placeholders replaced as in step 2, and wait for all of them. They read the existing synonyms and the checked visual descriptions from the work directory.

```bash
pnpm docs:mdicons:descriptions check <workDir> keywords
```

Rerun the keyword pass for any sheet that fails.

### 5. Merge

```bash
pnpm docs:mdicons:descriptions merge <workDir>
pnpm docs:mdicons:synonyms
```

`merge` adds or replaces the processed icons in `iconDescriptions.json`, drops keywords that are filler or repeat the name or synonyms, removes icons that no longer exist in `@mui/icons-material`, and sorts the file. `docs:mdicons:synonyms` then merges the new words into `synonyms.js` and formats the changed files with Prettier.

`docs:mdicons:synonyms` also fetches the latest tags from Google's Material Symbols metadata, so the diff can include changes that are not from this workflow. Mention them when reporting.

Then rebuild the semantic search index, which embeds the icon texts:

```bash
pnpm docs:mdicons:search-index
```

### 6. Review

- Pick two or three sheets and compare the new entries against the images. Look for invented details (parts that are not drawn) and keywords that belong to a neighboring icon.
- Report to the user: how many icons were added or updated, how many descriptions needed the cleanup pass, and anything that looked wrong.

## Notes

- Why sheets of 16 with names: tested against one icon per image and against unlabeled grids. Labeled 4×4 sheets at this size were as specific as single images and more accurate, because the name tells the model what an ambiguous shape represents.
- Do not edit `synonyms.js` by hand from this workflow. It only ever gains words: `docs:mdicons:synonyms` keeps existing words, so removing a keyword from the JSON does not remove it from `synonyms.js`.
42 changes: 42 additions & 0 deletions .agents/skills/icon-descriptions/prompts/keywords.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Search keywords

You are writing search keywords for Material UI icons, shown on an icon search page. People type short queries (1–3 words) to find an icon, either by what it means ("delete", "settings", "log out") or by what it looks like ("trash can", "three dots", "eye with slash").

## Input

- Sheet images in `{{WORK_DIR}}/sheets/`: each `sheet_XXX.png` is a 4×4 grid of up to 16 icons, each cell labeled `<number>. <IconName>`. The exact icon names per sheet are in `{{WORK_DIR}}/manifest.json` (array of `{ id, file, names }`).
- `{{WORK_DIR}}/synonyms.json` maps each icon name to the search keywords that already exist for it (chosen by people, may be empty or noisy).
- For each sheet, `{{WORK_DIR}}/out/visual_XXX.json` maps each icon name to a short description of what the icon looks like, written earlier.

Read the manifest and `synonyms.json` once. Then, for each sheet, read its visual descriptions and view the sheet image with the Read tool. Do not crop, zoom or otherwise process the images, and do not open icon source files. Only use Read and Write.

Your sheets: {{SHEETS}}

## What to write for every icon

An array of 8–15 lowercase strings, single words or short phrases (at most 3 words), that someone might type to find this icon and that are NOT already covered by the icon name or the existing synonyms. Add what is missing:

- Synonyms for the concept (delete → erase, discard).
- Everyday and informal names for the drawn object (gear → cog; three vertical dots → kebab menu; trash can → bin, dustbin; three horizontal lines → hamburger menu).
- Short appearance phrases people might type, based on the drawing and the visual description ("eye with slash", "arrow in circle", "house with chimney").
- British and American variants where they differ (trash / rubbish, elevator / lift).
- What the icon is typically used for in an app UI (settings page, upload file, log out, mark as favorite).
- The expansion of abbreviations in the name (Hdr → high dynamic range, Sip → session initiation protocol, Mp → megapixel).
- For brand logos, the brand and company name.

Check warning on line 25 in .agents/skills/icon-descriptions/prompts/keywords.md

View workflow job for this annotation

GitHub Actions / test-dev (ubuntu-latest)

[vale] reported by reviewdog 🐶 Use the Oxford comma in ', the brand and company name.'. Raw Output: {"message":"Use the Oxford comma in ', the brand and company name.'.","location":{"path":".agents/skills/icon-descriptions/prompts/keywords.md","range":{"start":{"line":25,"column":18},"end":{"line":25,"column":47}}},"severity":"WARNING","code":{"value":"Google.OxfordComma","url":"https://developers.google.com/style/commas"}}

Do not include:

- Words already in the icon name or the existing synonyms (plurals and small spelling variants of them count as already covered).
- Generic filler: icon, symbol, button, sign, shape, graphic, image, filled, outlined, solid, black, simple, material.
- Style words: filled, outlined, rounded, sharp, two tone.
- Keywords that belong to a different icon on the sheet: neighboring icons are often variants (Mic, MicOff, MicNone). Keywords must fit this specific icon, for example "mute" for MicOff, not for Mic.

If the name and synonyms already cover an icon well, it is fine to return fewer (at least 5), but never pad with weak words.

## Output

For each of your sheets, write `{{WORK_DIR}}/out/kw_XXX.json` (same `XXX` as the sheet) with the Write tool: one JSON object mapping each exact icon name to its keyword array, for example `{"Mail": ["envelope", "..."], "MailLock": ["..."]}`. Every icon on the sheet must appear exactly once. The file must be valid JSON.

Work through your sheets one at a time. Do not write anywhere else.

When done, reply with one line only: the sheet ids you completed and the total number of icons written.
38 changes: 38 additions & 0 deletions .agents/skills/icon-descriptions/prompts/visual.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Visual descriptions

You are writing short visual descriptions of UI icons for an icon search page. People sometimes search by what an icon looks like ("eye with slash", "three dots", "person with backpack"), so each description must say what is drawn.

## Input

Sheet images are in `{{WORK_DIR}}/sheets/`. Each `sheet_XXX.png` is a 4×4 grid of up to 16 icons, each cell labeled `<number>. <IconName>`. The exact icon names per sheet are in `{{WORK_DIR}}/manifest.json` (array of `{ id, file, names }`). Read the manifest once, then view each of your sheets with the Read tool.

Look at the sheet images as they are. Do not crop, zoom, re-render or otherwise process them, and do not open icon source files or `{{WORK_DIR}}/synonyms.json`. Only use Read and Write.

Your sheets: {{SHEETS}}

## What to write for every icon

One sentence, 8–20 words:

- Start with what the drawing represents when you can recognize it: say "door", "face", "person", "hand", "house", "speech bubble", not "rectangle with a circle". Use the icon name to understand what it represents.
- Then add the details that set it apart from similar icons: modifiers (diagonal slash through it, small plus badge, circle around it), arrow direction, position of parts, and any text or letters shown. Neighboring icons are often variants of each other (Man, Man2, Man3); make sure each sentence captures what is specific to that icon.
- Only mention parts you can actually see. Do not add parts that are not drawn (a doorknob, window, face or person that isn't there).
- Give counts only when they are small and obvious at a glance ("three dots", "two arrows"). Otherwise say "several" or leave the count out.
- Do not say "filled", "outline", "outlined", "solid", "hollow" or "black": the same description is used for the filled, outlined, rounded, sharp and two-tone versions of the icon.
- The sentence must stand on its own. Do not compare with other icons ("like the previous one", "same as Man but").
- No filler: icon, depicting, image of, showing, simple.
- Lowercase start, no trailing period.

Examples of the style:

- "gear with rounded teeth around the rim and a round hole in the center"
- "three dots stacked vertically in a single column"
- "microphone on a stand with a diagonal slash through it"

## Output

For each of your sheets, write `{{WORK_DIR}}/out/visual_XXX.json` (same `XXX` as the sheet) with the Write tool: one JSON object mapping each exact icon name from the manifest to its sentence, for example `{"Mail": "...", "MailLock": "..."}`. Every icon on the sheet must appear exactly once. The file must be valid JSON.

Work through your sheets one at a time: view the sheet, write its file, then move to the next. Do not write anywhere else.

When done, reply with one line only: the sheet ids you completed and the total number of icons written.
10 changes: 10 additions & 0 deletions .claude/skills/icon-descriptions/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: icon-descriptions
description: 'Generate search keywords and visual descriptions for Material icons that are missing from docs/data/material/components/material-icons/iconDescriptions.json, for example after new icons are added to @mui/icons-material. Use when the user asks to update, fill in or regenerate icon descriptions or icon search keywords, or runs /icon-descriptions. Pass --all to regenerate every icon, or --icons=A,B for specific ones.'
---

# Icon descriptions

This is the Claude Code entrypoint for the shared repo skill.

Read `.agents/skills/icon-descriptions/SKILL.md` completely and follow that workflow. Pass through any user arguments such as `--all` or `--icons=A,B`.
2 changes: 2 additions & 0 deletions .lintignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/.git
/coverage
/docs/export
# Generated by docs/scripts/buildIconSearchIndex.mjs
/docs/public/static/material-icons
/docs/pages/playground/
/examples/material-ui-cra*/src/serviceWorker.js
/examples/material-ui-gatsby/public/
Expand Down
34 changes: 29 additions & 5 deletions docs/data/material/components/material-icons/SearchIcons.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import useQueryParameterState from 'docs/src/modules/utils/useQueryParameterStat
// import DeleteForeverSharp from '@mui/icons-material/DeleteForeverSharp';
import { HighlightedCode } from '@mui/internal-core-docs/HighlightedCode';
import synonyms from './synonyms';
import { loadSemanticIndex, rankIcons } from './semanticSearch';

const FlexSearchIndex = flexsearch.Index;

Expand Down Expand Up @@ -565,6 +566,12 @@ const allIcons = Object.keys(mui)
return icon;
});

const allIconsByTheme = {};
for (const icon of allIcons) {
allIconsByTheme[icon.theme] ??= {};
allIconsByTheme[icon.theme][icon.name] = icon;
}

/**
* Returns the last defined value that has been passed in [value]
*/
Expand Down Expand Up @@ -594,12 +601,29 @@ export default function SearchIcons() {
setSelectedIcon('');
}, [setSelectedIcon]);

const [semanticIndex, setSemanticIndex] = React.useState(null);
React.useEffect(() => {
if (query !== '' && !semanticIndex) {
// Keyword search keeps working if the index fails to load.
loadSemanticIndex().then(setSemanticIndex, () => {});
}
}, [query, semanticIndex]);

const icons = React.useMemo(() => {
const keys = query === '' ? null : searchIndex.search(query, { limit: 3000 });
return (keys === null ? allIcons : keys.map((key) => allIconsMap[key])).filter(
(icon) => theme === icon.theme,
);
}, [query, theme]);
if (query === '') {
return allIcons.filter((icon) => theme === icon.theme);
}
const keywordMatches = [
...new Set(
searchIndex
.search(query, { limit: 3000 })
.map((key) => allIconsMap[key].name),
),
];
return rankIcons(query, keywordMatches, semanticIndex)
.map((name) => allIconsByTheme[theme][name])
.filter(Boolean);
}, [query, theme, semanticIndex]);

const deferredIcons = React.useDeferredValue(icons);

Expand Down
Loading
Loading