Repository navigation
fix(editor): Blueprint icons paint under page zoom in Chrome 153 - #41
Closed
pythonlearner1025 wants to merge 1 commit into
Closed
pythonlearner1025 wants to merge 1 commit into
pythonlearner1025 wants to merge 1 commit into
Conversation
Chrome 153 painted nothing inside a Blueprint icon below 80% page zoom. The icon stayed in the dom, laid out and clickable. Its pixels never appeared. The owner runs the editor at 75%. There the navbar icons, both viewport toolbars, the panel flaps, the tab kind icon and the hierarchy icons were gone. Page zoom is a css zoom on the root element. The user agent stylesheet clips an inline svg with overflow: hidden. Under that zoom Chrome computes the clip as empty for these small icons. One rule lifts the clip. Blueprint draws inside the viewBox, so the rule changes nothing at 100%. I measured all 1360 icon paths the package ships. The largest excursion past the viewBox is 0.08 of a 16 unit box. That is a fraction of one device pixel. Verified headless in Google Chrome 153.0.8010.48 against the terminator project. I built this branch and origin/main, and measured both: - Icons painted at zoom 0.75: 38 of 69 before, 69 of 69 after. - Icons painted at zoom 0.9: 64 of 66 before, 66 of 66 after. - Navbar folder-close, a toolbar icon and a hierarchy icon at zoom 0.75: 0, 0 and 0 lit pixels before; 315, 237 and 180 after. - At zoom 1 all 63 icons keep the same pixel count in both builds. The navbar strip of the two screenshots is byte for byte identical. - Chrome zoom steps 25%, 33%, 50%, 67% and 75% lose the icons without the rule. They keep them with it, at device pixel ratio 1 and 2. npm run typecheck, npm run lint, npm test -w packages/kite3d (6 tests) and npm run test:scripts (2 tests) all pass. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What part this touches
The editor is a React app. Blueprint is its interface library. Blueprint draws each icon as an
inline
svginside aspan.bp5-icon.The browser's own default stylesheet is called the user agent stylesheet. It puts
overflow: hiddenon every inlinesvg. That clip is what this change lifts, for Blueprint iconsonly.
Page zoom in Chrome 153 is a css
zoomon the root element. It is not a repaint at a differentscale. Layout runs in zoomed units, and the clip is computed there too.
This change touches one file,
packages/editor/src/renderer.scss, the editor stylesheet. It addsone rule. There is no JavaScript and no zoom detection.
The bug
The owner runs the editor in Google Chrome 153 at 75% page zoom, on a Retina display. Every
Blueprint icon was gone. That is the navbar folder, scene and save icons. It is also both floating
viewport toolbars, the panel flaps and the tab kind icon. The hierarchy icons went too. The two
active toolbar buttons showed as blank green and blue squares.
The icon was never missing from the page. At 75% zoom I read each one back:
svgand itspathare in the dom.svghas a real rect,10.5 by 10.5css pixels for a 14 pixel navbar icon.document.elementFromPointon the middle of that rect returns the icon. It is still clickable.The count of lit pixels in a crop around the navbar
folder-closeicon, in the owner's Chrome:It is not only 75%, and it is not only Retina. I swept every zoom step in Chrome's own menu. I ran
the sweep at device pixel ratio 2 and 1. Every step from 25% to 75% loses the icons. From 80% up
they paint, though at some steps only part of an icon paints.
The cause is the clip. These icons are small inline
svgelements. Under css zoom, Chrome 153computes their user agent
overflow: hiddenas an empty clip. Nothing inside them reaches thescreen.
Blueprint's own stylesheet already carries a matching workaround for one case,
.bp5-non-ideal-state-visual .bp5-icon svg { overflow: visible }.Two other clues fit the same cause. The tree carets keep painting at 75%, which is why the
hierarchy looked half broken rather than empty. The 32 pixel folder icons in the Files panel keep
painting too. They are large enough that the clip does not collapse.
The fix
Lift the clip on Blueprint icon svgs, once, in
packages/editor/src/renderer.scss:The comment above the rule names the browser, the mechanism and why it is free at 100%.
No JavaScript. No zoom detection. No per component override. No
!important, because an authorrule already beats the user agent rule.
The risk trade
Lifting a clip can only cost one thing. An icon that draws outside its own
viewBoxwould nowpaint over its neighbour, instead of being cut off.
I measured that. I read all 1360 icon paths the installed
@blueprintjs/iconsships. That is 680icons, in both the 16 and the 20 pixel size. I measured each drawn bounding box against its
viewBoxin Chrome. 230 of them leave the box. The largest excursion is0.08user units out ofa 16 unit box. On a 14 pixel icon at device pixel ratio 2 that is 0.14 of one device pixel.
Nothing can visibly bleed.
The measurement is in
bbox.jsonin the evidence folder, andbbox.mjsreproduces it.I rejected the other candidate that worked,
.bp5-icon > svg { width: 100%; height: 100% }. Itlit the icon again, but it changes layout. The shipped stylesheet already sets a size on that
selector in five places. A size rule would have to win against all of them. It would also be a
real visual change at 100%.
overflowis not a layout property, so it changes nothing that isalready correct.
I rejected a zoom listener that swaps styles. There is nothing to detect. The rule is correct at
every zoom.
Layer promotion,
will-change,contain,isolation,displaychanges,vector-effect,image-renderingand a tiny transform were all tried first. None of them makes the icon paint.Tests
No test was added. The editor package has no test runner, by decision. A test that guards this
needs a browser at a zoom, a dev server and a pixel read. The repo rule is to keep the suites
small. So the proof is the headless run below, and its scripts stay in the evidence folder.
All runs are headless Google Chrome 153.0.8010.48, the owner's own binary. The viewport was 1512
by 982 at device pixel ratio 2. The project was
/Users/minjunes/games/terminator-next. The devserver ran with
CI=1and--no-open. Nothing took focus. I confirmed by sha256 that the editorbundles the server sent were the ones this branch built.
I measured twice, once on a build of
origin/mainand once on a build of this branch.measure.mjscounts the lit pixels of three named icons. It then counts every icon on the page,from one full screenshot.
Icons painted, out of the icons visible in their panel:
The three named icons, lit pixels, before to after:
folder-closeWhat fails without the fix is the 0.75 row. Three of three named icons sit at zero. 31 of 69 icons
on the page are blank.
The rule is invisible at 100%. Across all 63 icons at zoom 1, the largest change in pixel count
between the two builds is 0. The navbar strip of the two screenshots is byte for byte identical,
0 differing subpixels out of 907,200.
Screenshots, all looked at before writing this:
before-full-zoom0_75.png, the owner's report reproduced. Text with no icons.after-full-zoom0_75.png, the same page on this branch. Every icon draws, in line with itslabel, with no overlap and no change of spacing.
navbar-before-zoom075.pngandnavbar-after-zoom075.png, the navbar alone, side by side.Suites, from the worktree, all pass:
npm run typecheck, engine, editor and kite3d.npm run lint, engine, editor and kite3d.npm test -w packages/kite3d, 3 files, 6 tests.npm run test:scripts, 2 tests.Edges I tested, and what happens off the happy path:
25%, 33%, 50%, 67% and 75% lose all three icons at both ratios. With the rule, all three paint
at every step.
175% went from 1556 to 3032 lit pixels. The rule completes those too.
than measured. The other two keep painting.
changed nothing else. The icons go blank and come back.
outside their own panel's scroll box, which is not this bug. The census does not count them.
Deploy
The artifact is the editor stylesheet, inside the editor bundle in
@kite3d/editor.kite3d devserves that bundle, and the next
kite3dnpm release ships it. There is no server change and noCLI change.
Roll back by reverting this commit and rebuilding, or by installing the previous version: