Summary
Toolbar icons render fine here, which I assume is the result of the #199 fix. Menu
icons do not: every dropdown menu item that declares an icon shows an empty 20×20
box where the icon should be. Captions, shortcuts and behaviour are all correct.
Common.UI.Button was migrated to the SVG symbol sprite, but Common.UI.MenuItem
was not. It still emits the old CSS-sprite <span>, and the PNG sprites that
mechanism needs (iconssmall*.png, iconsbig*.png, iconshuge*.png) are no longer
generated or shipped, so nothing paints.
This is unchanged in v9.3.3: apps/common/main/lib/component/Menu.js is identical
between the web-apps submodule of v9.3.1 (7c69898) and of v9.3.3 (91f0f97).
Where
apps/common/main/lib/component/Menu.js:846-848, in MenuItem.itemTemplate:
'<% if (typeof iconCls !== "undefined") { %>',
'<span class="menu-item-icon <%= iconCls %>"></span>',
'<% } %>',
apps/common/main/lib/component/Button.js:176-187, which was migrated:
// SVG sprite approach - uses <svg><use href="#id"> for dark mode support
// Sprite is injected into DOM via svg-injector, so we use fragment-only references
var templateBtnIcon =
'<% if ( iconImg ) { %>' +
'<img src="<%= iconImg %>">' +
'<% } else { %>' +
'<% var iconMatch = /btn-[^\\s]+/.exec(iconCls); ' +
'if (iconMatch) {' +
'print(\'<svg class=\"icon uni-scale\"><use href=\"#\' + iconMatch[0] + \'\"></use></svg>\');' +
'} else ' +
'print(\'<i class=\"icon \' + iconCls + \'\"> </i>\'); %>' +
'<% } %>';
The only rule matching the markup MenuItem emits is geometry — there is no image
anywhere in the chain:
.dropdown-menu li .menu-item-icon {
display: inline-block; float: left;
width: var(--x-small-btn-icon-size, 20px);
height: var(--x-small-btn-icon-size, 20px);
margin: calc((16px - var(--x-small-btn-icon-size, 20px)) / 2) 4px 0 -16px;
background-repeat: no-repeat;
opacity: var(--component-normal-icon-opacity, 0.8);
}
The background-image / background-position for each icon class used to come from
apps/<editor>/main/resources/less/sprites/*.less. Those files no longer exist and
their @imports are commented out in every app.less. Grepping the built app.css
for .btn-* selectors returns 55 hits, all of them component classes
(.btn-default, .btn-danger, .btn-toolbar, …) — no icon classes at all.
Impact
Menu items declaring an icon, counted on v9.3.3 sources:
| editor |
items |
| documenteditor |
315 |
| presentationeditor |
298 |
| pdfeditor |
266 |
| spreadsheeteditor |
194 |
| visioeditor |
19 |
| total |
1092 |
Where it shows most, in the document editor:
app/view/DocumentHolderExt.js — 273 (right-click context menu and its submenus)
app/view/FileMenu.js — 21 (File menu)
app/view/Toolbar.js — 13 (toolbar dropdowns)
The sprite already contains what is needed
The 91 distinct icons requested by the document editor's menus are all present as
<symbol id="btn-…"> in the shipped
apps/documenteditor/main/resources/img/toolbar/icons.svg. So no artwork is missing
— only the template needs to emit <use>.
Aligning MenuItem.itemTemplate with the button approach should be enough:
'<% if (typeof iconCls !== "undefined") { %>',
'<% var iconMatch = /btn-[^\\s]+/.exec(iconCls); %>',
'<% if (iconMatch) { %>',
'<svg class="menu-item-icon icon"><use href="#<%= iconMatch[0] %>"></use></svg>',
'<% } else { %>',
'<span class="menu-item-icon <%= iconCls %>"></span>',
'<% } %>',
'<% } %>',
.dropdown-menu li .menu-item-icon then needs fill: currentColor so the symbols
follow the theme colour, the way .svg-icon does in icons-svg.less.
One related spot to check while fixing: Menu.js:1054, in setChecked(), still
clears the icon with itemEl.css('background-image', 'none'). That assumes the icon
is a CSS background, so it will need the same treatment once menu icons become
<svg><use>.
Smaller findings in the same area
-
apps/common/main/lib/util/themeinit.js:107 requests three sprite files the build
never produces — iconssmall@2.5x.svg, iconsbig@2.5x.svg, iconshuge@2.5x.svg.
build/scripts/deploy-sprites.js only emits toolbar/icons.svg per editor plus
doc-formats/formats@2.5x.svg. That is three 404s on every editor load, and any
path relying on apply_icons_from_url() to re-inject icons (theme switch, custom
themes with their own icons.basepath) gets nothing back.
-
apps/common/main/resources/less/colors-table.less:205-224 still defines 15
--sprite-button-{small,big,huge}-{100,125,150,175,200} variables pointing at
iconssmall*.png / iconsbig*.png / iconshuge*.png, none of which exist any
more. Nothing consumes them in the built CSS, so they are harmless, but they are
what sends you chasing missing PNGs when tracing this.
-
btn-find-redacted is referenced by the document editor but has no symbol in
icons.svg.
-
apps/common/main/resources/img/plugin/icon_add_on_default@1.5x.png → 404, while
the 1x and 2x variants ship. Default plugin icon is blank on 1.5x displays.
-
apps/common/main/resources/img/doc-formats/large/hwp.svg and hwpx.svg → 404,
both referenced by the format-list CSS.
Environment
ghcr.io/euro-office/documentserver, amd64, served resources report 9.3.3
- Behind Traefik, TLS terminated at the proxy
- Integrated through a small self-hosted file manager (JWT + callback), not Nextcloud
- All 404s above verified over HTTP against the running instance; code references
checked against the v9.3.3 sources (web-apps 91f0f97)

Summary
Toolbar icons render fine here, which I assume is the result of the #199 fix. Menu
icons do not: every dropdown menu item that declares an icon shows an empty 20×20
box where the icon should be. Captions, shortcuts and behaviour are all correct.
Common.UI.Buttonwas migrated to the SVG symbol sprite, butCommon.UI.MenuItemwas not. It still emits the old CSS-sprite
<span>, and the PNG sprites thatmechanism needs (
iconssmall*.png,iconsbig*.png,iconshuge*.png) are no longergenerated or shipped, so nothing paints.
This is unchanged in v9.3.3:
apps/common/main/lib/component/Menu.jsis identicalbetween the web-apps submodule of v9.3.1 (
7c69898) and of v9.3.3 (91f0f97).Where
apps/common/main/lib/component/Menu.js:846-848, inMenuItem.itemTemplate:apps/common/main/lib/component/Button.js:176-187, which was migrated:The only rule matching the markup
MenuItememits is geometry — there is no imageanywhere in the chain:
The
background-image/background-positionfor each icon class used to come fromapps/<editor>/main/resources/less/sprites/*.less. Those files no longer exist andtheir
@imports are commented out in everyapp.less. Grepping the builtapp.cssfor
.btn-*selectors returns 55 hits, all of them component classes(
.btn-default,.btn-danger,.btn-toolbar, …) — no icon classes at all.Impact
Menu items declaring an icon, counted on v9.3.3 sources:
Where it shows most, in the document editor:
app/view/DocumentHolderExt.js— 273 (right-click context menu and its submenus)app/view/FileMenu.js— 21 (File menu)app/view/Toolbar.js— 13 (toolbar dropdowns)The sprite already contains what is needed
The 91 distinct icons requested by the document editor's menus are all present as
<symbol id="btn-…">in the shippedapps/documenteditor/main/resources/img/toolbar/icons.svg. So no artwork is missing— only the template needs to emit
<use>.Aligning
MenuItem.itemTemplatewith the button approach should be enough:.dropdown-menu li .menu-item-iconthen needsfill: currentColorso the symbolsfollow the theme colour, the way
.svg-icondoes inicons-svg.less.One related spot to check while fixing:
Menu.js:1054, insetChecked(), stillclears the icon with
itemEl.css('background-image', 'none'). That assumes the iconis a CSS background, so it will need the same treatment once menu icons become
<svg><use>.Smaller findings in the same area
apps/common/main/lib/util/themeinit.js:107requests three sprite files the buildnever produces —
iconssmall@2.5x.svg,iconsbig@2.5x.svg,iconshuge@2.5x.svg.build/scripts/deploy-sprites.jsonly emitstoolbar/icons.svgper editor plusdoc-formats/formats@2.5x.svg. That is three 404s on every editor load, and anypath relying on
apply_icons_from_url()to re-inject icons (theme switch, customthemes with their own
icons.basepath) gets nothing back.apps/common/main/resources/less/colors-table.less:205-224still defines 15--sprite-button-{small,big,huge}-{100,125,150,175,200}variables pointing aticonssmall*.png/iconsbig*.png/iconshuge*.png, none of which exist anymore. Nothing consumes them in the built CSS, so they are harmless, but they are
what sends you chasing missing PNGs when tracing this.
btn-find-redactedis referenced by the document editor but has no symbol inicons.svg.apps/common/main/resources/img/plugin/icon_add_on_default@1.5x.png→ 404, whilethe 1x and 2x variants ship. Default plugin icon is blank on 1.5x displays.
apps/common/main/resources/img/doc-formats/large/hwp.svgandhwpx.svg→ 404,both referenced by the format-list CSS.
Environment
ghcr.io/euro-office/documentserver, amd64, served resources report 9.3.3checked against the v9.3.3 sources (web-apps
91f0f97)