docs: drop upstream-only icon guidance from icon-jetbrains skill

This commit is contained in:
kirillk
2026-07-30 17:37:07 -04:00
parent 55ea1c2765
commit 5b0a958870
2 changed files with 43 additions and 69 deletions
+28 -52
View File
@@ -5,66 +5,44 @@ description: Create or review IntelliJ New UI SVG icons, theme variants, sizes,
# IntelliJ Platform New UI Icons
Guidance for authoring SVG icons for the New UI. This skill is the single source of truth for icon sizing, palette, dark variants, composition rules, and placement. Other docs (including `packages/kilo-jetbrains/AGENTS.md`) defer here for SVG authoring details.
Guidance for authoring SVG icons for the JetBrains plugin. This skill is the single source of truth for icon sizing, palette, dark variants, composition rules, and placement. Other docs (including `packages/kilo-jetbrains/AGENTS.md`) defer here for SVG authoring details.
Background: the IDE maintains Classic UI and New UI in parallel. Code branches with `ExperimentalUI.isNewUI()`; icons follow the same split. In upstream IntelliJ Community sources, old icons stay in their original resource folders for Classic-UI compatibility and New-UI icons live under `expui/` folders, substituted for the old ones at runtime by icon mappers. **In this repo there is no `expui/` tree or `*IconMappings.json` file** — see [Where the SVGs live](#where-the-svgs-live) for where icons actually go.
Icons follow IntelliJ New UI conventions: a fixed canvas per role, a strict light/dark palette, and explicit per-shape colors (the IntelliJ SVG loader recolors by matching literal hex values, so `currentColor` and CSS do not work). The plugin loads icons directly from a flat resource folder — see [Where the SVGs live](#where-the-svgs-live).
## Golden rules
1. **Always ship two SVGs** — one for the light theme (e.g. `addFile.svg`) and one for the dark theme with the `_dark` suffix (`addFile_dark.svg`). Geometry must be identical between them; only the palette swaps.
- **Tool-window icons ship as a quartet, not a pair.** When a tool window has both a 16×16 base (`name.svg` / `name_dark.svg`) and a 20×20 New-UI stripe variant (`name@20x20.svg` / `name@20x20_dark.svg`), they must share the same metaphor. The stripe is only one surface — the 16×16 base also appears in **Search Everywhere**, **Find Action**, context menus, the Services tool window, recent locations, and the View ▸ Tool Windows menu. Changing only the @20x20 leaves users seeing two different icons for the same tool window depending on where they encounter it. Always update all four files together (and the same applies to plugin-local icons like `tool-window-chat.svg` + `tool-window-chat@20x20.svg`).
1. **Always ship two SVGs** — one for the light theme (e.g. `add-file.svg`) and one for the dark theme with the `_dark` suffix (`add-file_dark.svg`). Geometry must be identical between them; only the palette swaps.
- **Tool-window icons ship as a quartet, not a pair.** When a tool window has both a 16×16 base (`name.svg` / `name_dark.svg`) and a 20×20 stripe variant (`name@20x20.svg` / `name@20x20_dark.svg`), they must share the same metaphor. The stripe is only one surface — the 16×16 base also appears in **Search Everywhere**, **Find Action**, context menus, the Services tool window, recent locations, and the View ▸ Tool Windows menu. Changing only the @20x20 leaves users seeing two different icons for the same tool window depending on where they encounter it. Always update all four files together (e.g. `tool-window-chat.svg` + `tool-window-chat@20x20.svg`).
2. **Only use colors from the canonical palette.** See [palette.md](./palette.md). Picking a one-off color breaks theming and contrast.
3. **One canvas size per icon role.** See [Icon roles](#icon-roles). Do not invent new sizes or pad with empty space — IntelliJ scales the canvas as a single unit.
4. **No raster, no gradients, no filters, no embedded fonts.** Path geometry only (`<path>`, `<rect>`, `<circle>`, `<line>`, `<polyline>`, `<polygon>`). Text must be converted to outlines.
5. **Use `fill="none"` on the root `<svg>`** and set `fill` / `stroke` explicitly per shape — never rely on CSS or `currentColor`.
6. **Strokes use `stroke-width="1"`, `stroke-linecap="round"`, `stroke-linejoin="round"`** (or `stroke-miterlimit="10"` for hard joins). Heavier strokes are reserved for hero glyphs inside a circle badge (e.g. status checkmarks) and use `stroke-width="1.5"` or `"2"`.
7. **Pixel-grid align**: keep stroke axes on half-pixel centers (`x.5`) and fills on whole pixels so the icon stays crisp at 1× rendering.
8. **File names match the naming convention of sibling icons in the target folder and stay ASCII-only.** Plugin-local Kilo icons in `packages/kilo-jetbrains/frontend/src/main/resources/icons/` use kebab-case (e.g. `arrow-down-to-line.svg`, `book-open-check.svg`, `tool-window-chat.svg`), matching every existing icon there. Upstream IntelliJ Platform icons use camelCase; when copying one from `$INTELLIJ_REPO`, keep its original name.
8. **File names use kebab-case and stay ASCII-only** (e.g. `arrow-down-to-line.svg`, `book-open-check.svg`, `tool-window-chat.svg`), matching every existing icon in `packages/kilo-jetbrains/frontend/src/main/resources/icons/`.
## Icon roles
All roles land in this repo's `packages/kilo-jetbrains/frontend/src/main/resources/icons/` (with a `views/` subfolder for in-view icons — see [Where the SVGs live](#where-the-svgs-live)). The "Upstream reference dir" column below is where the same role lives in the IntelliJ Community sources (`$INTELLIJ_REPO`), useful when you need to look up an existing icon's conventions or geometry — it is not a path in this repo.
Pick the canvas size from the role, not the other way around. All icons land in this repo's `packages/kilo-jetbrains/frontend/src/main/resources/icons/` (with a `views/` subfolder for in-view icons — see [Where the SVGs live](#where-the-svgs-live)).
| Role | Canvas | Filename pattern | Upstream reference dir (`$INTELLIJ_REPO`) |
|---|---|---|---|
| Action icons (menus, popups, toolbars) | **16×16** | `name.svg` + `name_dark.svg` | `expui/actions/`, `expui/general/` |
| Tree node icons (PSI, structure view) | **16×16** | `name.svg` + `name_dark.svg` | `expui/nodes/` |
| Tool-window stripe icons (legacy/16) | **16×16** | `name.svg` + `name_dark.svg` | `expui/toolwindows/` |
| Tool-window stripe icons (New UI) | **20×20** | `name@20x20.svg` + `name@20x20_dark.svg` | `expui/toolwindows/` |
| Main toolbar (New UI) | **20×20** | `name@20x20.svg` + `name@20x20_dark.svg` | `expui/general/`, `expui/run/`, `expui/toolbar/` |
| Editor gutter icons | **14×14** (small marks **12×12**, **9×9**) | `name.svg` + `name_dark.svg` | `expui/gutter/` |
| Status bar / inline status | **16×16** | `name.svg` + `name_dark.svg` | `expui/status/`, `expui/inline/` |
| Breakpoint marks | **14×14** (12×12 for sub-marks) | `name.svg` + `name_dark.svg` | `expui/breakpoints/` |
| Run-config tags & disclosure chevrons | **16×16** (`chevron*.svg` may be 9×9 to 16×16) | `name.svg` + `name_dark.svg` | `expui/run/`, `expui/general/` |
| Welcome/onboarding & logos (system) | **16, 20, 28, 48** (per surface) | `name.svg` + `name_dark.svg` | `expui/ide/`, `expui/welcome/` |
| Role | Canvas | Filename pattern |
|---|---|---|
| Action icons (menus, popups, toolbars) | **16×16** | `name.svg` + `name_dark.svg` |
| Tree node icons (PSI, structure view) | **16×16** | `name.svg` + `name_dark.svg` |
| Tool-window stripe icons (compact/16) | **16×16** | `name.svg` + `name_dark.svg` |
| Tool-window stripe icons (New UI) | **20×20** | `name@20x20.svg` + `name@20x20_dark.svg` |
| Main toolbar (New UI) | **20×20** | `name@20x20.svg` + `name@20x20_dark.svg` |
| Editor gutter icons | **14×14** (small marks **12×12**, **9×9**) | `name.svg` + `name_dark.svg` |
| Status bar / inline status | **16×16** | `name.svg` + `name_dark.svg` |
| Breakpoint marks | **14×14** (12×12 for sub-marks) | `name.svg` + `name_dark.svg` |
| Run-config tags & disclosure chevrons | **16×16** (`chevron*.svg` may be 9×9 to 16×16) | `name.svg` + `name_dark.svg` |
| Welcome/onboarding & logos | **16, 20, 28, 48** (per surface) | `name.svg` + `name_dark.svg` |
When in doubt, find a sibling icon of the same role in this repo's flat icons folder (or, if none exists, the upstream reference dir via `$INTELLIJ_REPO`) and copy its `width` / `height` / `viewBox`.
When in doubt, find a sibling icon of the same role already in the icons folder and copy its `width` / `height` / `viewBox`.
## Where the SVGs live
**In this repo**, plugin icons go into `packages/kilo-jetbrains/frontend/src/main/resources/icons/` (action icons, tool-window icons) or the `icons/views/` subfolder (in-view icons used by the chat/session UI). There is no `expui/` subfolder structure and no `*IconMappings.json` file here; the plugin loads icons directly via `IconLoader`. Place both the light SVG and its `_dark` sibling there and reference them from the plugin's icon-holder class.
The rest of this section describes how the **upstream IntelliJ Community** sources (reachable via `$INTELLIJ_REPO` — see `packages/kilo-jetbrains/AGENTS.md`) organize the same icons. That structure is useful when consulting or copying conventions from an existing platform icon, but it is not a path that exists in this repo.
Upstream, two placements exist and both are valid — chosen based on whether a Classic-UI icon already exists:
- **Only New UI needed** → the SVG (and its `_dark` sibling) sits under an `expui/<role>/` resource root. Callers reference it directly through the plugin's generated `<Plugin>Icons` class.
- **Replacing an existing Classic-UI icon** → the Classic file stays at its old path, the New UI SVG sits under the matching `expui/<role>/` path, and a mapping entry is added to the nearest `*IconMappings.json`. At runtime, when `ExperimentalUI.isNewUI()` is true, the mapper substitutes the New UI file for the Classic one so no caller has to change its icon reference.
Upstream mapping-file shape (for reference only — no equivalent file exists in this repo):
```json
{
"expui": {
"actions": {
"addFile.svg": "actions/addFile.svg",
"checked.svg": ["actions/checked.svg", "actions/setDefault.svg"]
}
}
}
```
Key = path relative to the `expui/` root (the New UI file). Value = the Classic path(s) that should resolve to it. A single New UI icon may back multiple Classic paths.
Plugin icons go into `packages/kilo-jetbrains/frontend/src/main/resources/icons/` (action icons, tool-window icons) or the `icons/views/` subfolder (in-view icons used by the chat/session UI). Icons are loaded directly via `IconLoader` — there is no role-based subfolder structure and no mapping file. Place both the light SVG and its `_dark` sibling in the folder and reference them from the plugin's icon-holder class.
## SVG skeleton
@@ -81,15 +59,15 @@ The dark variant is the same file with light-palette colors swapped for their da
- **One semantic meaning per icon.** A status badge, an accent dot, or a "+" overlay is fine; two unrelated glyphs in one icon is not.
- **Optical centering, not geometric.** Plus/arrow/refresh glyphs sit slightly above center; round badges (class, method, status) are centered on `(cx=8, cy=8)` for 16×16 and `(cx=10, cy=10)` for 20×20.
- **Outer keep-out**: leave at least **1 px** of empty padding on each side of a 16×16 icon (so meaningful geometry lives within `1..15`). For 20×20 use **2 px** of padding. Stripe icons must stay visually balanced inside their 20×20 cell.
- **Stroke + fill pairing for node-style icons** (e.g. `nodes/class.svg`): a light-tinted fill at radius 6.5 with a stroke in the accent color, plus a glyph filled with the same accent.
- **Stroke + fill pairing for node-style icons** (class/method nodes): a light-tinted fill at radius 6.5 with a stroke in the accent color, plus a glyph filled with the same accent.
- Light: `<circle cx="8" cy="8" r="6.5" fill="<accent-bg-light>" stroke="<accent-light>"/>` then glyph `fill="<accent-light>"`.
- Dark: same circle with `fill="<accent-bg-dark>" stroke="<accent-dark>"` and glyph filled with `<accent-dark>`.
- **Stroke-only icons** (chevrons, refresh, edit pencil): a single-color path using the neutral stroke (`#6C707E` light / `#CED0D6` dark) at `stroke-width="1"`. Use `#818594` (light) / `#6F737A` (dark) for "secondary" stroke glyphs like dropdown chevrons.
- **Status badges** (error/warning/success/info) follow this template:
- Light: filled circle/triangle in the *accent* color, glyph painted in `white`.
- Dark: filled circle/triangle in the *dark accent* color, glyph painted in the matching *muted dark fill* (e.g. `#5E4D33` inside `#F2C55C` warning) — never plain white.
- **Two-tone action icons** (e.g. `actions/addFile.svg`): the base glyph uses the neutral gray, and the small "modifier" (`+`, ✕, ↻, gear) uses the primary blue accent. Light gray + blue accent → dark gray + blue accent in the dark variant.
- **Disabled / stroke-only variants** (e.g. `*_stroke.svg`): outline-only, same neutral stroke color, no fills.
- **Two-tone action icons** (a base glyph plus a small modifier): the base glyph uses the neutral gray, and the small modifier (`+`, ✕, ↻, gear) uses the primary blue accent. Light gray + blue accent → dark gray + blue accent in the dark variant.
- **Disabled / stroke-only variants** (e.g. `*-stroke.svg`): outline-only, same neutral stroke color, no fills.
## Palette quick reference
@@ -120,12 +98,12 @@ Do not use plain `#000000` or off-the-palette grays.
## Generation workflow
1. **Pick the role and canvas size** from the table above. For source lookup — including the upstream `expui/<role>/` reference dirs — follow the `$INTELLIJ_REPO` guidance in `packages/kilo-jetbrains/AGENTS.md`; use that AGENTS file alongside this skill when generating or reviewing plugin icons. Find at least two visually similar sibling icons (in this repo's flat icons folder, or upstream via `$INTELLIJ_REPO`) and mirror their stroke/fill mix.
1. **Pick the role and canvas size** from the table above. Find at least two visually similar sibling icons already in the icons folder and mirror their stroke/fill mix.
2. **Lay out geometry on the pixel grid** (whole-pixel fills, half-pixel stroke centers). Optical-center the glyph.
3. **Apply the canonical light palette** from [palette.md](./palette.md). Never invent colors.
4. **Save the light SVG** with `width`/`height`/`viewBox` matching the role and `fill="none"` on `<svg>`.
5. **Duplicate to the `_dark` filename** and swap each color for its dark-theme partner from the palette mapping. Keep paths byte-identical otherwise.
6. **Place and wire the file** → drop both files into `packages/kilo-jetbrains/frontend/src/main/resources/icons/` (action icons, tool-window icons) or `icons/views/` (in-view chat/session icons). There is no `expui/` or role subfolder — this repo has no such structure and no `*IconMappings.json`. Reference the icon from the plugin's icon-holder class. The upstream `expui/<role>/` placement and `*IconMappings.json` mapping described in [Where the SVGs live](#where-the-svgs-live) apply only to the IntelliJ Community sources under `$INTELLIJ_REPO`, not to this repo.
6. **Place and wire the file** → drop both files into `packages/kilo-jetbrains/frontend/src/main/resources/icons/` (action icons, tool-window icons) or `icons/views/` (in-view chat/session icons), then reference the icon from the plugin's icon-holder class.
7. **Verify visually** in both themes via the image preview in the IDE, or run the IDE and toggle *View ▸ Appearance ▸ New UI* to compare. Check selection states for stripe icons.
## Common mistakes
@@ -136,12 +114,10 @@ Do not use plain `#000000` or off-the-palette grays.
- Using `currentColor`, CSS, or `<style>` blocks. The IntelliJ icon loader requires explicit colors on every shape so it can do palette-based recoloring.
- Forgetting the `_dark` variant. The icon will look fine in Light theme then turn invisible in Dark.
- Pure-black (`#000`) or pure-white (`#FFF`) fills outside the status-badge glyph pattern. They break under accent recoloring.
- (Upstream IntelliJ Community sources only — not applicable in this repo) Adding a New UI SVG without a `*IconMappings.json` entry when replacing a Classic icon — the Classic icon will keep showing in New UI mode.
## References
- [palette.md](./palette.md) — full color palette with light↔dark mapping.
- [examples.md](./examples.md) — annotated SVG snippets for each icon role, copied from the live icon set.
- `packages/kilo-jetbrains/AGENTS.md` — repository-specific JetBrains plugin constraints, including how to set up `$INTELLIJ_REPO` for consulting upstream IntelliJ Community sources (used above for the `expui/<role>/` reference dirs); use it together with this skill for icon work.
- Repo-level `Icons guidelines.svg` is a design sheet (visual reference only; not machine-readable). When it disagrees with the folder, trust the folder.
- `packages/kilo-jetbrains/frontend/src/main/resources/icons/` — the actual flat resource folder in this repo where plugin icons live; this is the ground truth for placement, not any `expui/` path.
- [examples.md](./examples.md) — annotated SVG snippets for each icon role.
- `packages/kilo-jetbrains/AGENTS.md` — repository-specific JetBrains plugin constraints; use it together with this skill for icon work.
- `packages/kilo-jetbrains/frontend/src/main/resources/icons/` — the flat resource folder where plugin icons live; this is the ground truth for placement.
+15 -17
View File
@@ -1,14 +1,12 @@
# Annotated New UI Icon Examples
Each example below is paired with a dark variant and a short note on the pattern it represents. Use these as templates when generating new icons.
The `expui/<role>/...` names below are the upstream IntelliJ Community source paths the examples were copied from (consult via `$INTELLIJ_REPO`, see `packages/kilo-jetbrains/AGENTS.md`) — they are not paths in this repo. In this repo, place generated icons flat into `packages/kilo-jetbrains/frontend/src/main/resources/icons/`.
Each example below is paired with a dark variant and a short note on the pattern it represents. Use these as templates when generating new icons. Place generated icons flat into `packages/kilo-jetbrains/frontend/src/main/resources/icons/`.
---
## 1. Single-color action (16×16) — primary gray stroke + filled glyph
`expui/general/add.svg` — the most common action icon shape.
Plus / add action — the most common action icon shape.
**Light:**
```svg
@@ -25,7 +23,7 @@ Why it works: one filled path, primary gray. Drop in any glyph and you have a co
## 2. Two-tone action (16×16) — neutral base + blue accent modifier
`expui/actions/addFile.svg` — base glyph in neutral gray, "+" sticker in primary blue.
Add-file action — base glyph in neutral gray, "+" sticker in primary blue.
**Light:**
```svg
@@ -43,7 +41,7 @@ Pattern: stick the small modifier glyph in the bottom-right of the canvas (`(9..
## 3. Stroke-only action (16×16) — chevrons, refresh, edit
`expui/general/refresh.svg` — multiple stroked sub-paths sharing one color.
Refresh action — multiple stroked sub-paths sharing one color.
**Light:**
```svg
@@ -63,7 +61,7 @@ For secondary glyphs (chevrons in popups, dropdown arrows) use `#818594` light /
## 4. Node icon (16×16) — tinted circle with stroke + glyph
`expui/nodes/class.svg` and `expui/nodes/method.svg` — the canonical PSI-node shape.
Class / method nodes — the canonical PSI-node shape.
**Light (class — blue):**
```svg
@@ -90,7 +88,7 @@ Rules:
## 5. Status badge (16×16) — circle/triangle with white glyph
`expui/status/error.svg`:
Error badge:
```svg
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="8" cy="8" r="7" fill="#E55765"/>
@@ -99,15 +97,15 @@ Rules:
</svg>
```
`expui/status/warning.svg` is the same idea but with a rounded triangle filled `#FFAF0F` (light) / `#F2C55C` (dark). In the dark theme, the glyph inside the warning triangle uses `#5E4D33` instead of `white` (white on warm yellow is unreadable).
The warning badge is the same idea but with a rounded triangle filled `#FFAF0F` (light) / `#F2C55C` (dark). In the dark theme, the glyph inside the warning triangle uses `#5E4D33` instead of `white` (white on warm yellow is unreadable).
`expui/status/success.svg` uses a 2-px stroked checkmark inside a `#55A76A` (light) / `#57965C` (dark) circle — `stroke="white"` and `stroke-width="2"`.
The success badge uses a 2-px stroked checkmark inside a `#55A76A` (light) / `#57965C` (dark) circle — `stroke="white"` and `stroke-width="2"`.
---
## 6. Tool window stripe (20×20) — single-tone glyph on a larger canvas
`expui/toolwindows/build@20x20.svg` (excerpt):
Build tool-window stripe:
```svg
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.25 1C4.44891 1 4.63962 1.07907 4.78027 1.21973L5.56055 2H6.43945L7.21973 1.21973L7.33398 1.12598C7.45628 1.04445 7.60085 1 7.75 1H13.125C14.9473 1 16.4277 1.87993 17.4346 3.08105C18.4322 4.27143 19 5.81492 19 7.25C19 7.51735 18.8572 7.76425 18.626 7.89844C18.3948 8.03247 18.11 8.0339 17.8779 7.90137L15.4258 6.5H14.0605L13 7.56055V18.25C13 18.6642 12.6642 19 12.25 19H7.75C7.33579 19 7 18.6642 7 18.25V7.56055L6.43945 7H5.56055L4.78027 7.78027C4.63962 7.92093 4.44891 8 4.25 8H1.75C1.33579 8 1 7.66421 1 7.25V1.75C1 1.33579 1.33579 1 1.75 1H4.25ZM8.5 17.5H11.5V8H8.5V17.5ZM2.5 6.5H3.93945L4.71973 5.71973L4.83398 5.62598C4.95628 5.54445 5.10085 5.5 5.25 5.5H6.75C6.94891 5.5 7.13962 5.57907 7.28027 5.71973L8.06055 6.5H11.9395L13.2197 5.21973L13.334 5.12598C13.4563 5.04445 13.6008 5 13.75 5H15.625C15.7555 5 15.8838 5.03392 15.9971 5.09863L17.2637 5.82227C17.0602 5.19249 16.7287 4.57423 16.2842 4.04395C15.5098 3.12029 14.4276 2.5 13.125 2.5H8.06055L7.28027 3.28027C7.13962 3.42093 6.94891 3.5 6.75 3.5H5.25C5.05109 3.5 4.86038 3.42093 4.71973 3.28027L3.93945 2.5H2.5V6.5Z" fill="#6C707E"/>
@@ -117,13 +115,13 @@ Rules:
Notes:
- 20×20 canvas with **at least 2 px** breathing room on each edge (geometry lives in `2..18`).
- Re-balance vs. a 16×16 sibling — do not just paste a 16×16 path into a 20×20 viewBox.
- For the 16×16 stripe (legacy / compact mode), keep a sibling `build.svg`; New UI uses the `@20x20` variant by default.
- Keep a 16×16 sibling (`name.svg`) for compact mode; the `@20x20` variant is used by default.
---
## 7. Main toolbar action (20×20) — same template as tool window
`expui/general/add@20x20.svg`:
Add action (20×20):
```svg
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M9.75586 17.25C9.34165 17.25 9.00586 16.9142 9.00586 16.5V10.5059H3C2.58594 10.5059 2.25025 10.1699 2.25 9.75586C2.25 9.34165 2.58579 9.00586 3 9.00586H9.00586V3C9.00607 2.58597 9.34178 2.25 9.75586 2.25C10.1699 2.25 10.5056 2.58597 10.5059 3V9.00586H16.5C16.9142 9.00586 17.25 9.34165 17.25 9.75586C17.2498 10.1699 16.9141 10.5059 16.5 10.5059H10.5059V16.5C10.5059 16.9142 10.1701 17.25 9.75586 17.25Z" fill="#6C707E"/>
@@ -136,7 +134,7 @@ Main-toolbar icons reuse the 20×20 conventions of tool window stripes — the c
## 8. Gutter icon (14×14) — thin stroke, no padding
`expui/gutter/javadocEdit.svg`:
Edit-doc gutter mark:
```svg
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M10.5973 6.65471L12.6882 4.56049C13.1053 4.15406 13.1003 3.49602 12.6948 3.08627L11.0267 1.3136L11.0224 1.30932C10.6123 0.900035 9.94199 0.893268 9.53311 1.31079L7.3867 3.44406M10.5973 6.65471L7.3867 3.44406M10.5973 6.65471L4.74041 12.5H1.50036L1.5 9.32001L7.3867 3.44406" stroke="#6C707E" stroke-miterlimit="10"/>
@@ -152,7 +150,7 @@ Gutter icons:
## 9. Breakpoint mark (14×14) — solid filled circle
`expui/breakpoints/breakpoint.svg`:
Breakpoint mark:
```svg
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7 13C10.3137 13 13 10.3137 13 7C13 3.68629 10.3137 1 7 1C3.68629 1 1 3.68629 1 7C1 10.3137 3.68629 13 7 13Z" fill="#E55765"/>
@@ -165,7 +163,7 @@ Breakpoint marks read at a glance — a single solid fill in the status-error re
## 10. Dark-variant gotcha — white-on-warm vs. muted-dark
`expui/status/warning_dark.svg` shows the only common case where a literal find-and-replace from the light SVG would produce a broken icon:
The warning badge dark variant shows the only common case where a literal find-and-replace from the light SVG would produce a broken icon:
```svg
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -177,4 +175,4 @@ Breakpoint marks read at a glance — a single solid fill in the status-error re
`white``#5E4D33` inside the warning triangle. Apply the same swap whenever a light icon paints a glyph as `white` over a warm fill (`#FFAF0F`, `#E66D17`, etc.). For cool fills (`#3574F0`, `#208A3C`, `#DB3B4B`), the dark-theme glyph stays `white` because contrast is still sufficient.
Note: the exclamation glyph here (the vertical bar + dot paths) is shared verbatim with the error badge in section 5 — upstream `warning.svg` / `warning_dark.svg` reuse the same optically centered `!` glyph, so between the light and dark warning variants only the fill color changes. This is the one case where reusing a sibling glyph unchanged is correct; the general rule still stands that a glyph must be re-balanced when its container centers it differently.
Note: the exclamation glyph here (the vertical bar + dot paths) is the same optically centered `!` used by the error badge in section 5, so between the light and dark warning variants only the fill color changes. This is the one case where reusing a sibling glyph unchanged is correct; the general rule still stands that a glyph must be re-balanced when its container centers it differently.