From 901d4e86fcce56fffa8aee55cd71f4c649832ffc Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 2 Nov 2026 13:32:12 +0100 Subject: [PATCH 1/6] fix(manager): align action bar button text and drop the save button's bottom band - The save button and its stay toggle carried an inset 2px darker band along the bottom edge; the button is now one flat colour like the others. - The forms.css padding left the text of the fixed-height action buttons 1px below the middle; it moves 1px up. The stay toggle keeps its padding, so its chevron stays where it was. Co-Authored-By: Claude Opus 5.5 --- .../Unit/Manager/ActionButtonsStyleTest.php | 20 +++++++++++++++++++ manager/media/style/default/css/custom.css | 6 +++--- manager/media/style/default/css/forms.css | 1 - 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 core/tests/Unit/Manager/ActionButtonsStyleTest.php diff --git a/core/tests/Unit/Manager/ActionButtonsStyleTest.php b/core/tests/Unit/Manager/ActionButtonsStyleTest.php new file mode 100644 index 0000000000..bd1852ba14 --- /dev/null +++ b/core/tests/Unit/Manager/ActionButtonsStyleTest.php @@ -0,0 +1,20 @@ +not->toMatch('/#actions[^{]*\.btn-success[^{]*\{[^}]*box-shadow/'); +}); + +test('action button text sits 1px higher than the forms.css padding puts it', function () { + $css = actionButtonsCss(); + + expect($css)->toContain('#actions .btn-group .btn { padding-top: calc(.46153846em - 1px) }') + // the chevron toggle keeps the full padding, so it does not move with the text + ->and($css)->toMatch('/#actions \.btn-group \.plus \{[^}]*padding: \.46153846em 0;/'); +}); diff --git a/manager/media/style/default/css/custom.css b/manager/media/style/default/css/custom.css index cacbbada49..277540fdc7 100644 --- a/manager/media/style/default/css/custom.css +++ b/manager/media/style/default/css/custom.css @@ -47,10 +47,10 @@ select[name=context] + input[type=submit] { float: none; margin: 0; } #actions .btn-group > .btn-group { display: block } #actions .btn-group .btn, #actions .btn-group .form-control { font-size: .6772rem; height: 2.4em; line-height: 1.4 } +/* the forms.css padding sets the text 1px below the middle of these fixed-height buttons */ +#actions .btn-group .btn { padding-top: calc(.46153846em - 1px) } #actions .btn-group .btn .fa, #_actions .btn-group .btn .fa { display: none; width: 1em; font-size: 1em; text-align: center } #actions .btn-group .btn-group #Button1 { min-width: 100%; text-align: left; float: right; } -#actions .btn-group .btn-success { box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .07); } -#actions .btn-group .btn-success:hover { box-shadow: none } /* the in-place save: the join box between "Save" and the stay option shows +, a spinner, then a check mark, at one width */ #actions .btn-group #Button1 .save-join { display: inline-block; position: relative; width: 1.4em; margin: 0 .25em; text-align: center } #actions .btn-group #Button1.saving { pointer-events: none; opacity: .75 } @@ -58,7 +58,7 @@ select[name=context] + input[type=submit] { float: none; margin: 0; } #actions .btn-group #Button1.saving .save-join::before { content: ''; position: absolute; left: 50%; top: 50%; width: .8em; height: .8em; margin: -.45em 0 0 -.45em; border: 2px solid rgba(255, 255, 255, .45); border-top-color: #fff; border-radius: 50%; animation: evo-save-spin .7s linear infinite } #actions .btn-group #Button1.saved .save-join::before { content: '\2713'; position: absolute; left: 0; right: 0; top: 0; color: #fff } @keyframes evo-save-spin { to { transform: rotate(360deg) } } -#actions .btn-group .plus { z-index: 2; float: right; margin: -2.4em 0 0 0; width: 2rem; padding-left: 0; padding-right: 0; text-align: center; cursor: pointer; } +#actions .btn-group .plus { z-index: 2; float: right; margin: -2.4em 0 0 0; width: 2rem; padding: .46153846em 0; text-align: center; cursor: pointer; } #actions .btn-group .plus::before { content: "\f107"; font-weight: 900; font-family: 'Font Awesome 5 Free' } #actions .btn-group .show .plus::before { content: "\f106"; } #actions .btn-group select#stay { display: block; position: relative; z-index: -1; clear: both; width: auto; height: 0; margin: -100% 0 -2rem 0; font-size: 0.675rem; opacity: 0; visibility: hidden; } diff --git a/manager/media/style/default/css/forms.css b/manager/media/style/default/css/forms.css index a1b5cbde82..ea8e6b2fa2 100644 --- a/manager/media/style/default/css/forms.css +++ b/manager/media/style/default/css/forms.css @@ -78,7 +78,6 @@ select:not([size]):not([multiple]) { text-indent: -2px } .darkness .btn-default { color: #bbb; background-color: #202329; border-color: #202329 } .darkness .btn-default:hover { color: #fff; background-color: #1a1c21; border-color: #1a1c21 } .darkness #actions { right: 0; background-color: #212328 } -.darkness #actions .btn-group .btn-success { box-shadow: none } .darkness #actions .dropdown-menu .btn { background-color: #343942; border-radius: 0; color: #fff; } .darkness #actions .dropdown-menu .btn:hover { background-color: #3c424e; color: #fff } .darkness optgroup { background-color: #202329 } From 0317d2976a99572e69487e08b15ce4f30425c675 Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 2 Nov 2026 13:41:33 +0100 Subject: [PATCH 2/6] fix(manager): centre the Clear cache icon beside its menu item Menu icons are inline SVGs, but the rule that drops their label gap in the icon-only button of an item group still matched only icon-font elements, so the recycle icon sat 2.75px left of centre in a wider block, and 0.75px low. Co-Authored-By: Claude Opus 5.5 --- core/tests/Unit/Manager/MenuItemGroupStyleTest.php | 9 +++++++++ manager/media/style/default/css/mainmenu.css | 7 ++++++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 core/tests/Unit/Manager/MenuItemGroupStyleTest.php diff --git a/core/tests/Unit/Manager/MenuItemGroupStyleTest.php b/core/tests/Unit/Manager/MenuItemGroupStyleTest.php new file mode 100644 index 0000000000..bdb5d1c6e0 --- /dev/null +++ b/core/tests/Unit/Manager/MenuItemGroupStyleTest.php @@ -0,0 +1,9 @@ +toMatch('/#mainMenu \.nav li\.item-group > \*:not\(:first-child\) svg \{\s*margin-right: 0;/') + ->and($css)->toMatch('/#mainMenu \.nav li\.item-group > \*:not\(:first-child\) \{\s*vertical-align: middle;/'); +}); diff --git a/manager/media/style/default/css/mainmenu.css b/manager/media/style/default/css/mainmenu.css index 57b3670db7..6517276ce7 100755 --- a/manager/media/style/default/css/mainmenu.css +++ b/manager/media/style/default/css/mainmenu.css @@ -434,7 +434,12 @@ body.sidebar-closed #bars .icon-collapse { display: none !important; } #mainMenu .nav li.item-group > *:first-child { width: 100%; } -#mainMenu .nav li.item-group > *:not(:first-child) .fa { +/* an icon-only button beside the item: centred, without the gap menu icons keep before their label */ +#mainMenu .nav li.item-group > *:not(:first-child) { + vertical-align: middle; +} +#mainMenu .nav li.item-group > *:not(:first-child) .fa, +#mainMenu .nav li.item-group > *:not(:first-child) svg { margin-right: 0; } /* statusbar */ From 2ed2797372fea7205b961116cdb0e8569d6536d0 Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 2 Nov 2026 13:41:41 +0100 Subject: [PATCH 3/6] fix(manager): draw the save stay toggle chevron 1px taller The SVG chevron that replaced the icon-font glyph fills only the middle of its 1em box and looked smaller than before; it grows to 1.25em, upward only, and the open state no longer resets it to 1em. Co-Authored-By: Claude Opus 5.5 --- core/tests/Unit/Manager/ActionButtonsStyleTest.php | 8 ++++++++ manager/media/style/default/css/custom.css | 5 +++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/tests/Unit/Manager/ActionButtonsStyleTest.php b/core/tests/Unit/Manager/ActionButtonsStyleTest.php index bd1852ba14..9a9efda372 100644 --- a/core/tests/Unit/Manager/ActionButtonsStyleTest.php +++ b/core/tests/Unit/Manager/ActionButtonsStyleTest.php @@ -18,3 +18,11 @@ function actionButtonsCss(): string // the chevron toggle keeps the full padding, so it does not move with the text ->and($css)->toMatch('/#actions \.btn-group \.plus \{[^}]*padding: \.46153846em 0;/'); }); + +test('the stay toggle chevron is drawn 1px taller and keeps that size while the menu is open', function () { + $css = actionButtonsCss(); + + expect($css)->toMatch('/#actions \.btn-group \.plus::before \{[^}]*width:1\.25em;height:1\.25em;margin-bottom:\.08em;/') + // the open state swaps only the icon, a size of its own would shrink it back + ->and($css)->toMatch('/#actions \.btn-group \.show \.plus::before \{ -webkit-mask-image:var\(--evo-icon-chevron-up\); mask-image:var\(--evo-icon-chevron-up\) \}/'); +}); diff --git a/manager/media/style/default/css/custom.css b/manager/media/style/default/css/custom.css index 6819676924..a9dbea7a22 100644 --- a/manager/media/style/default/css/custom.css +++ b/manager/media/style/default/css/custom.css @@ -58,8 +58,9 @@ select[name=context] + input[type=submit] { float: none; margin: 0; } #actions .btn-group #Button1.saved .save-join::before { content: '\2713'; position: absolute; left: 0; right: 0; top: 0; color: #fff } @keyframes evo-save-spin { to { transform: rotate(360deg) } } #actions .btn-group .plus { z-index: 2; float: right; margin: -2.4em 0 0 0; width: 2rem; padding: .46153846em 0; text-align: center; cursor: pointer; } -#actions .btn-group .plus::before { content:"";display:inline-block;width:1em;height:1em;vertical-align:middle;background-color:currentColor;-webkit-mask:var(--evo-icon-chevron-down) center/contain no-repeat;mask:var(--evo-icon-chevron-down) center/contain no-repeat; font-weight: 900; } -#actions .btn-group .show .plus::before { content:"";display:inline-block;width:1em;height:1em;vertical-align:middle;background-color:currentColor;-webkit-mask:var(--evo-icon-chevron-up) center/contain no-repeat;mask:var(--evo-icon-chevron-up) center/contain no-repeat; } +/* the chevron fills only the middle of its box: 1.25em draws it 1px taller, and the bottom margin keeps its lower edge in place */ +#actions .btn-group .plus::before { content:"";display:inline-block;width:1.25em;height:1.25em;margin-bottom:.08em;vertical-align:middle;background-color:currentColor;-webkit-mask:var(--evo-icon-chevron-down) center/contain no-repeat;mask:var(--evo-icon-chevron-down) center/contain no-repeat; } +#actions .btn-group .show .plus::before { -webkit-mask-image:var(--evo-icon-chevron-up); mask-image:var(--evo-icon-chevron-up) } #actions .btn-group select#stay { display: block; position: relative; z-index: -1; clear: both; width: auto; height: 0; margin: -100% 0 -2rem 0; font-size: 0.675rem; opacity: 0; visibility: hidden; } #actions .btn-group .dropdown-menu { display: none; position: absolute; z-index: -1; left: auto; right: 0; top: 100%; width: 100%; min-width: 2rem; margin: 0; padding: 0; text-align: left; font-size: .6772rem; border-top: none; border-radius: 0 0 0.1em 0.1em; overflow: hidden; } From ea070ec2e423c012616745a3b63121bbd80f5277 Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 2 Nov 2026 14:02:24 +0100 Subject: [PATCH 4/6] fix(manager): lift the save stay toggle chevron 1px Co-Authored-By: Claude Opus 5.5 --- core/tests/Unit/Manager/ActionButtonsStyleTest.php | 4 ++-- manager/media/style/default/css/custom.css | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/tests/Unit/Manager/ActionButtonsStyleTest.php b/core/tests/Unit/Manager/ActionButtonsStyleTest.php index 9a9efda372..22dcd87c1e 100644 --- a/core/tests/Unit/Manager/ActionButtonsStyleTest.php +++ b/core/tests/Unit/Manager/ActionButtonsStyleTest.php @@ -19,10 +19,10 @@ function actionButtonsCss(): string ->and($css)->toMatch('/#actions \.btn-group \.plus \{[^}]*padding: \.46153846em 0;/'); }); -test('the stay toggle chevron is drawn 1px taller and keeps that size while the menu is open', function () { +test('the stay toggle chevron is drawn 1px taller and 1px higher, and keeps that while the menu is open', function () { $css = actionButtonsCss(); - expect($css)->toMatch('/#actions \.btn-group \.plus::before \{[^}]*width:1\.25em;height:1\.25em;margin-bottom:\.08em;/') + expect($css)->toMatch('/#actions \.btn-group \.plus::before \{[^}]*width:1\.25em;height:1\.25em;margin-bottom:\.08em;position:relative;top:-1px;/') // the open state swaps only the icon, a size of its own would shrink it back ->and($css)->toMatch('/#actions \.btn-group \.show \.plus::before \{ -webkit-mask-image:var\(--evo-icon-chevron-up\); mask-image:var\(--evo-icon-chevron-up\) \}/'); }); diff --git a/manager/media/style/default/css/custom.css b/manager/media/style/default/css/custom.css index a9dbea7a22..b9b1697f01 100644 --- a/manager/media/style/default/css/custom.css +++ b/manager/media/style/default/css/custom.css @@ -58,8 +58,8 @@ select[name=context] + input[type=submit] { float: none; margin: 0; } #actions .btn-group #Button1.saved .save-join::before { content: '\2713'; position: absolute; left: 0; right: 0; top: 0; color: #fff } @keyframes evo-save-spin { to { transform: rotate(360deg) } } #actions .btn-group .plus { z-index: 2; float: right; margin: -2.4em 0 0 0; width: 2rem; padding: .46153846em 0; text-align: center; cursor: pointer; } -/* the chevron fills only the middle of its box: 1.25em draws it 1px taller, and the bottom margin keeps its lower edge in place */ -#actions .btn-group .plus::before { content:"";display:inline-block;width:1.25em;height:1.25em;margin-bottom:.08em;vertical-align:middle;background-color:currentColor;-webkit-mask:var(--evo-icon-chevron-down) center/contain no-repeat;mask:var(--evo-icon-chevron-down) center/contain no-repeat; } +/* the chevron fills only the middle of its box: 1.25em draws it 1px taller, the bottom margin keeps its lower edge in place, top lifts it 1px */ +#actions .btn-group .plus::before { content:"";display:inline-block;width:1.25em;height:1.25em;margin-bottom:.08em;position:relative;top:-1px;vertical-align:middle;background-color:currentColor;-webkit-mask:var(--evo-icon-chevron-down) center/contain no-repeat;mask:var(--evo-icon-chevron-down) center/contain no-repeat; } #actions .btn-group .show .plus::before { -webkit-mask-image:var(--evo-icon-chevron-up); mask-image:var(--evo-icon-chevron-up) } #actions .btn-group select#stay { display: block; position: relative; z-index: -1; clear: both; width: auto; height: 0; margin: -100% 0 -2rem 0; font-size: 0.675rem; opacity: 0; visibility: hidden; } #actions .btn-group .dropdown-menu { display: none; position: absolute; z-index: -1; left: auto; right: 0; top: 100%; width: 100%; min-width: 2rem; margin: 0; padding: 0; text-align: left; font-size: .6772rem; From 3a49f3c214eb468904702ffa6423d39f2155644e Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 2 Nov 2026 14:13:12 +0100 Subject: [PATCH 5/6] fix(manager): let the last top menu item take the dropdown's rounded corners The dropdowns kept 0.25rem of bottom padding to show their rounded corners, so a hovered last item stopped short of the edge above a thin white strip. Without it the last item reaches the edge, the list clips it to the corners, and every item keeps the same height. Co-Authored-By: Claude Opus 5.5 --- core/tests/Unit/Manager/MenuItemGroupStyleTest.php | 10 ++++++++++ manager/media/style/default/css/mainmenu.css | 3 ++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/core/tests/Unit/Manager/MenuItemGroupStyleTest.php b/core/tests/Unit/Manager/MenuItemGroupStyleTest.php index bdb5d1c6e0..6a68c7582b 100644 --- a/core/tests/Unit/Manager/MenuItemGroupStyleTest.php +++ b/core/tests/Unit/Manager/MenuItemGroupStyleTest.php @@ -7,3 +7,13 @@ expect($css)->toMatch('/#mainMenu \.nav li\.item-group > \*:not\(:first-child\) svg \{\s*margin-right: 0;/') ->and($css)->toMatch('/#mainMenu \.nav li\.item-group > \*:not\(:first-child\) \{\s*vertical-align: middle;/'); }); + +test('top menu dropdowns end at their last item, which takes the rounded corners', function () { + $css = file_get_contents(dirname(__DIR__, 4) . '/manager/media/style/default/css/mainmenu.css'); + preg_match('/#mainMenu \.nav > li > ul \{([^}]*)\}/', $css, $rule); + + // bottom padding under the last item showed as a white strip below its hover background + expect($rule[1] ?? '')->toMatch('/\bpadding: 0;/') + ->and($rule[1] ?? '')->toContain('border-radius: 0 0 0.25rem 0.25rem;') + ->and($rule[1] ?? '')->toContain('overflow: auto;'); +}); diff --git a/manager/media/style/default/css/mainmenu.css b/manager/media/style/default/css/mainmenu.css index 6517276ce7..f355e70aae 100755 --- a/manager/media/style/default/css/mainmenu.css +++ b/manager/media/style/default/css/mainmenu.css @@ -266,7 +266,8 @@ body.sidebar-closed #bars .icon-collapse { display: none !important; } top: 100%; left: 0; margin: 0; - padding: 0 0 0.25rem; + /* no bottom padding: the last item reaches the rounded edge (overflow clips it), so its hover leaves no strip */ + padding: 0; min-width: 15rem; max-height: -webkit-calc(100vh - var(--manager-menu-height, 2.2rem)); max-height: calc(100vh - var(--manager-menu-height, 2.2rem)); From 7e7cd610bb4857b3eff487e4392e0b7f6eb8e89c Mon Sep 17 00:00:00 2001 From: Artur Kyryliuk Date: Mon, 2 Nov 2026 14:24:16 +0100 Subject: [PATCH 6/6] fix(manager): make the last top menu item as tall as the others Items are 41px, a 40px link above a 1px separator, but the last one has no separator and was 40px. Its link takes one more pixel, which shows the menu background at rest and the hover colour when hovered. Co-Authored-By: Claude Opus 5.5 --- core/tests/Unit/Manager/MenuItemGroupStyleTest.php | 7 +++++++ manager/media/style/default/css/mainmenu.css | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/core/tests/Unit/Manager/MenuItemGroupStyleTest.php b/core/tests/Unit/Manager/MenuItemGroupStyleTest.php index 6a68c7582b..193d3aba81 100644 --- a/core/tests/Unit/Manager/MenuItemGroupStyleTest.php +++ b/core/tests/Unit/Manager/MenuItemGroupStyleTest.php @@ -17,3 +17,10 @@ ->and($rule[1] ?? '')->toContain('border-radius: 0 0 0.25rem 0.25rem;') ->and($rule[1] ?? '')->toContain('overflow: auto;'); }); + +test('the last top menu item is as tall as the others, which carry a 1px separator', function () { + $css = file_get_contents(dirname(__DIR__, 4) . '/manager/media/style/default/css/mainmenu.css'); + + expect($css)->toMatch('/#mainMenu \.nav > li > ul > li \{\s*border-bottom: 1px solid[^}]*margin-bottom: -1px;/') + ->and($css)->toMatch('/#mainMenu \.nav > li > ul > li:last-child > a:first-child,\s*#mainMenu \.nav > li > ul > li:last-child > span:first-child \{\s*padding-bottom: 1px;/'); +}); diff --git a/manager/media/style/default/css/mainmenu.css b/manager/media/style/default/css/mainmenu.css index f355e70aae..fc3f28bfaa 100755 --- a/manager/media/style/default/css/mainmenu.css +++ b/manager/media/style/default/css/mainmenu.css @@ -304,6 +304,11 @@ body.sidebar-closed #bars .icon-collapse { display: none !important; } border: none; margin-bottom: 0; } +/* no separator below the last item: one more pixel keeps it as tall as the others, in its own hover colour */ +#mainMenu .nav > li > ul > li:last-child > a:first-child, +#mainMenu .nav > li > ul > li:last-child > span:first-child { + padding-bottom: 1px; +} #mainMenu .nav > li > ul > li.item-input { padding: 0.375em 0.5em; }