Non-square icons are squashed instead of scaled to fit
- Dominant language
- TypeScript
- Stars
- 481
- Forks
- 108
- Avg merge
- 6d 8h
- Merged PRs (30d)
- 5
Description
A node icon that is not square is stretched into a square rather than scaled down to fit, so it renders distorted. Affects both the graph canvas and the DOM surfaces, consistently.
## Reproduce
1. Style a node type with a custom uploaded raster icon whose width and height differ — a wide logo shows it clearly.
2. Look at the node on the graph canvas, and at the same node type in a search result row.
The icon fills a square box, distorted. Expected: scaled down to fit the icon box, keeping its aspect ratio.
## Why it happens
**Canvas.** `components/Graph/styles/defaultNodeStyle.ts` sets:
```ts
backgroundFit: "none",
backgroundWidth: "60%",
backgroundHeight: "60%",
```
Per cytoscape 3.34.0 `drawInscribedImage`, `background-width`/`-height` **override** the image’s natural dimensions, and `background-fit` then scales whatever those left:
```js
var w = imgW, h = imgH;
if (background-width !== auto) w = pct * nodeTW; // 0.6 * 24 = 14.4
if (background-height !== auto) h = pct * nodeTH; // 0.6 * 24 = 14.4
if (fit === "contain") { var scale = Math.min(nodeTW / w, nodeTH / h); w *= scale; h *= scale; }
```
So both axes are forced to 14.4 and the image is stretched into a square.
**`background-fit: contain` does not fix this** — it runs *after* the override, when `w` and `h` are already equal, so it just scales the squashed square up to fill the node. Verified in headless Chrome against real cytoscape 3.34.0.
**DOM.** `components/VertexIcon.tsx` renders ``. Forcing both dimensions with the default `object-fit: fill` stretches the image the same way.
`VertexSymbol` is already correct — its `` carries `preserveAspectRatio="xMidYMid meet"`.
## Fix sketch
There is no configuration-only fix: cytoscape cannot fit an image to a box and preserve its ratio at the same time, so the intrinsic ratio has to be known before the style is generated.
1. Measure the raster’s natural size when it resolves (`core/icons/iconRegistry.ts`) and carry the aspect ratio on the resolved icon.
2. Compute `background-width`/`background-height` from it, shrinking the shorter axis so the icon fits the 60% box instead of stretching to it — e.g. a 4:1 image becomes `60%` / `15%`.
3. `VertexIcon`: add `object-contain` to the ``.
4. Generated SVG icons need nothing — they carry a viewBox and letterbox themselves.
Known costs, which is why this was split out rather than bundled into #2102:
- Raster resolution becomes asynchronous, where today the url needs no work.
- Adds an image-load failure mode, needing a fallback to square.
- No test environment loads images, so measuring never settles without an `Image` test double in `setupTests.ts` — which changes the environment for every test file.
- `useBackgroundImageMap` has to return dimensions alongside the image, so its return type and name both change, rippling into `useGraphStyles`.
## Notes
Pre-existing behavior, unchanged by the icon pipeline rework in #2102 — `defaultNodeStyle.ts` has carried these values since before that branch. Distinct from #2105, which is about *recoloring* custom SVG icons rather than sizing them.
---
> [!IMPORTANT]
> Internal only — this issue is maintained by the core team and is not accepting external contributions.
Contributor guide
Research direction
Start with components/Graph/styles/defaultNodeStyle.ts and components/VertexIcon.tsx, then trace icon resolution through core/icons/iconRegistry.ts, useBackgroundImageMap, and useGraphStyles. Review setupTests.ts for the image-loading test implications. Done means non-square raster icons preserve their aspect ratio on both the graph canvas and DOM search rows, with a fallback for failed image resolution and coverage for the changed behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 25/100