From 99d7ef50917e8d61798715e1b0b3dc1a99709f27 Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 20 Mar 2024 18:00:35 +0100 Subject: [PATCH] Prevent layout shift in `` items (#29831) There is a small layout shift in when active tab changes. Notice how the actions SVG is unstable: ![](https://github.com/go-gitea/gitea/assets/115237/a6928e89-5d47-4a91-8f36-1fa22fddbce7) This is because the active item with bold text is wider then the inactive one. I have applied [this trick](https://stackoverflow.com/a/32570813/808699) to prevent this layout shift. It's only active inside `` because I wanted to avoid changing HTML and doing it in regular JS would cause a flicker. I don't expect us to introduce other similar menus without ``, so that place is likely fine. ![after](https://github.com/go-gitea/gitea/assets/115237/d6089924-8de6-4ee0-8db4-15f16069a131) I also changed the weight from 500 to 600, slightly reduced horizontal padding, merged some tab-bar related CSS rules and a added a small margin below repo-header so it does not look so crammed against the buttons on top. --------- Co-authored-by: wxiaoguang --- web_src/css/base.css | 32 +++++++++++++++-------- web_src/css/repo/header.css | 1 + web_src/js/webcomponents/overflow-menu.js | 19 ++++++++++++++ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/web_src/css/base.css b/web_src/css/base.css index 018c7d7bc..fdfbea610 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1778,15 +1778,6 @@ table th[data-sortt-desc] .svg { border-color: var(--color-secondary); } -.ui.tabular.menu .item { - padding: 11px 12px; - color: var(--color-text-light-2); -} - -.ui.tabular.menu .item:hover { - color: var(--color-text); -} - .ui.tabular.menu .active.item, .ui.tabular.menu .active.item:hover { background: var(--color-body); @@ -1803,17 +1794,36 @@ table th[data-sortt-desc] .svg { border-color: var(--color-secondary); } +.ui.tabular.menu .item, .ui.secondary.pointing.menu .item { + padding: 11px 12px !important; color: var(--color-text-light-2); } +.ui.tabular.menu .item:hover, +.ui.secondary.pointing.menu a.item:hover { + color: var(--color-text); +} + .ui.secondary.pointing.menu .active.item, .ui.secondary.pointing.menu .active.item:hover, -.ui.secondary.pointing.menu .dropdown.item:hover, -.ui.secondary.pointing.menu a.item:hover { +.ui.secondary.pointing.menu .dropdown.item:hover { color: var(--color-text-dark); } +.ui.tabular.menu .active.item, +.ui.secondary.pointing.menu .active.item, +.resize-for-semibold::before { + font-weight: var(--font-weight-semibold); +} + +.resize-for-semibold::before { + content: attr(data-text); + visibility: hidden; + display: block; + height: 0; +} + .ui.header { color: var(--color-text); } diff --git a/web_src/css/repo/header.css b/web_src/css/repo/header.css index f66df4cd8..e998bb9a7 100644 --- a/web_src/css/repo/header.css +++ b/web_src/css/repo/header.css @@ -8,6 +8,7 @@ flex-flow: row wrap; justify-content: space-between; gap: 0.5rem; + margin-bottom: 4px; } .repo-header .flex-item { diff --git a/web_src/js/webcomponents/overflow-menu.js b/web_src/js/webcomponents/overflow-menu.js index 9fa458556..604fce7d4 100644 --- a/web_src/js/webcomponents/overflow-menu.js +++ b/web_src/js/webcomponents/overflow-menu.js @@ -127,6 +127,25 @@ window.customElements.define('overflow-menu', class extends HTMLElement { }); init() { + // for horizontal menus where fomantic boldens active items, prevent this bold text from + // enlarging the menu's active item replacing the text node with a div that renders a + // invisible pseudo-element that enlarges the box. + if (this.matches('.ui.secondary.pointing.menu, .ui.tabular.menu')) { + for (const item of this.querySelectorAll('.item')) { + for (const child of item.childNodes) { + if (child.nodeType === Node.TEXT_NODE) { + const text = child.textContent.trim(); // whitespace is insignificant inside flexbox + if (!text) continue; + const span = document.createElement('span'); + span.classList.add('resize-for-semibold'); + span.setAttribute('data-text', text); + span.textContent = text; + child.replaceWith(span); + } + } + } + } + // ResizeObserver triggers on initial render, so we don't manually call `updateItems` here which // also avoids a full-page FOUC in Firefox that happens when `updateItems` is called too soon. this.resizeObserver = new ResizeObserver((entries) => {