loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[PORT] gitea#29831: Prevent layout shift in `<overflow-menu>` items

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 `<overflow-menu>` 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
`<overflow-menu>`, 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 <wxiaoguang@gmail.com>

---

Conflict resolution: Moved an `:focus` selector to the new CSS rule.
Ref: https://codeberg.org/forgejo/forgejo/issues/2776
(cherry picked from commit 99d7ef50917e8d61798715e1b0b3dc1a99709f27)

authored by

silverwind
wxiaoguang
and committed by
Gusted
18d13000 a1111660

+41 -11
+21 -11
web_src/css/base.css
··· 1842 1842 border-color: var(--color-secondary); 1843 1843 } 1844 1844 1845 - .ui.tabular.menu .item { 1846 - padding: 11px 12px; 1847 - color: var(--color-text-light-2); 1848 - } 1849 - 1850 - .ui.tabular.menu .item:hover { 1851 - color: var(--color-text); 1852 - } 1853 - 1854 1845 .ui.tabular.menu .active.item, 1855 1846 .ui.tabular.menu .active.item:hover { 1856 1847 background: var(--color-body); ··· 1867 1858 border-color: var(--color-secondary); 1868 1859 } 1869 1860 1861 + .ui.tabular.menu .item, 1870 1862 .ui.secondary.pointing.menu .item { 1863 + padding: 11px 12px !important; 1871 1864 color: var(--color-text-light-2); 1865 + } 1866 + 1867 + .ui.tabular.menu .item:hover, 1868 + .ui.secondary.pointing.menu a.item:hover, .ui.secondary.pointing.menu a.item:focus { 1869 + color: var(--color-text); 1872 1870 } 1873 1871 1874 1872 .ui.secondary.pointing.menu .active.item, 1875 1873 .ui.secondary.pointing.menu .active.item:hover, .ui.secondary.pointing.menu .active.item:focus, 1876 - .ui.secondary.pointing.menu .dropdown.item:hover, .ui.secondary.pointing.menu .dropdown.item:focus, 1877 - .ui.secondary.pointing.menu a.item:hover, .ui.secondary.pointing.menu a.item:focus { 1874 + .ui.secondary.pointing.menu .dropdown.item:hover, .ui.secondary.pointing.menu .dropdown.item:focus { 1878 1875 color: var(--color-text-dark); 1876 + } 1877 + 1878 + .ui.tabular.menu .active.item, 1879 + .ui.secondary.pointing.menu .active.item, 1880 + .resize-for-semibold::before { 1881 + font-weight: var(--font-weight-semibold); 1882 + } 1883 + 1884 + .resize-for-semibold::before { 1885 + content: attr(data-text); 1886 + visibility: hidden; 1887 + display: block; 1888 + height: 0; 1879 1889 } 1880 1890 1881 1891 .ui.header {
+1
web_src/css/repo/header.css
··· 8 8 flex-flow: row wrap; 9 9 justify-content: space-between; 10 10 gap: 0.5rem; 11 + margin-bottom: 4px; 11 12 } 12 13 13 14 .repo-header .flex-item {
+19
web_src/js/webcomponents/overflow-menu.js
··· 127 127 }); 128 128 129 129 init() { 130 + // for horizontal menus where fomantic boldens active items, prevent this bold text from 131 + // enlarging the menu's active item replacing the text node with a div that renders a 132 + // invisible pseudo-element that enlarges the box. 133 + if (this.matches('.ui.secondary.pointing.menu, .ui.tabular.menu')) { 134 + for (const item of this.querySelectorAll('.item')) { 135 + for (const child of item.childNodes) { 136 + if (child.nodeType === Node.TEXT_NODE) { 137 + const text = child.textContent.trim(); // whitespace is insignificant inside flexbox 138 + if (!text) continue; 139 + const span = document.createElement('span'); 140 + span.classList.add('resize-for-semibold'); 141 + span.setAttribute('data-text', text); 142 + span.textContent = text; 143 + child.replaceWith(span); 144 + } 145 + } 146 + } 147 + } 148 + 130 149 // ResizeObserver triggers on initial render, so we don't manually call `updateItems` here which 131 150 // also avoids a full-page FOUC in Firefox that happens when `updateItems` is called too soon. 132 151 this.resizeObserver = new ResizeObserver((entries) => {