Modified - [footer] [sticky-ui] Added a top lip show/hide toggle for the persistent footer.
release-nightly / nightly-binary (push) Has been cancelled
release-nightly / nightly-container (push) Has been cancelled

- 1 - I updated `templates/base/footer_content.tmpl`, `web_src/css/home.css`, and `web_src/js/features/common-page.ts` so pages with the persistent footer preference now render a top-edge toggle lip on desktop, remember the collapsed state in local user settings under `persistent-footer-collapsed`, measure the live footer height for smooth hide/show motion, and keep only the lip visible when the footer is collapsed.
- 2 - I added the new footer show/hide labels to `options/locale/locale_en-US.json` and `options/locale/locale_ro-RO.json`.
This commit is contained in:
2026-05-20 19:27:41 +03:00
parent 78f429361d
commit ced65dc5cd
7 changed files with 114 additions and 2 deletions
+4
View File
@@ -814,3 +814,7 @@ History search guidance:
163 - [2026-05-20 00:37:42] - v1.27.0-dev-174-gef319c5c07 - Type: Modified - [navbar] [sticky-ui] Refined the persistent navbar toggle lip to be shorter and vertically center the arrow icon.
- 1 - I updated `web_src/css/modules/navbar.css` so the bottom-right persistent-navbar toggle tab now uses a slimmer 8px lip, sits 8px below the navbar edge, and centers a smaller chevron within the lip instead of bottom-aligning it.
164 - [2026-05-20 00:48:53] - v1.27.0-dev-175-g78f429361d - Type: Modified - [footer] [sticky-ui] Added a top lip show/hide toggle for the persistent footer.
- 1 - I updated `templates/base/footer_content.tmpl`, `web_src/css/home.css`, and `web_src/js/features/common-page.ts` so pages with the persistent footer preference now render a top-edge toggle lip on desktop, remember the collapsed state in local user settings under `persistent-footer-collapsed`, measure the live footer height for smooth hide/show motion, and keep only the lip visible when the footer is collapsed.
- 2 - I added the new footer show/hide labels to `options/locale/locale_en-US.json` and `options/locale/locale_ro-RO.json`.
+2
View File
@@ -191,6 +191,8 @@
"aria.navbar": "Navigation Bar",
"navbar.show": "Show navigation bar",
"navbar.hide": "Hide navigation bar",
"footer.show": "Show footer",
"footer.hide": "Hide footer",
"aria.footer": "Footer",
"aria.footer.software": "About Software",
"aria.footer.links": "Links",
+2
View File
@@ -191,6 +191,8 @@
"aria.navbar": "Bara de navigare",
"navbar.show": "Arată bara de navigare",
"navbar.hide": "Ascunde bara de navigare",
"footer.show": "Arată footer-ul",
"footer.hide": "Ascunde footer-ul",
"aria.footer": "Subsol",
"aria.footer.software": "Despre software",
"aria.footer.links": "Link-uri",
+15
View File
@@ -1,4 +1,19 @@
<footer class="page-footer{{if .PageIsInstall}} page-footer-install{{end}}" role="group" aria-label="{{ctx.Locale.Tr "aria.footer"}}">
{{if .ShowPersistentFooter}}
<button
type="button"
id="persistent-footer-visibility-toggle"
data-show-text="{{ctx.Locale.Tr "footer.show"}}"
data-hide-text="{{ctx.Locale.Tr "footer.hide"}}"
data-tooltip-content="{{ctx.Locale.Tr "footer.hide"}}"
data-tooltip-placement="top"
aria-label="{{ctx.Locale.Tr "footer.hide"}}"
aria-pressed="false"
>
{{svg "octicon-chevron-up" 16 "persistent-footer-visibility-icon persistent-footer-visibility-icon-show tw-hidden"}}
{{svg "octicon-chevron-down" 16 "persistent-footer-visibility-icon persistent-footer-visibility-icon-hide"}}
</button>
{{end}}
<div class="left-links" role="contentinfo" aria-label="{{ctx.Locale.Tr "aria.footer.software"}}">
{{if ShowFooterPoweredBy}}
<a target="_blank" href="https://about.gitea.com">{{ctx.Locale.Tr "powered_by" "Gitea"}}</a>
+43
View File
@@ -44,11 +44,14 @@
}
.page-footer {
--persistent-footer-lip-height: 8px;
display: flex;
position: relative;
justify-content: space-between;
background-color: var(--color-footer);
border-top: 1px solid var(--color-secondary);
padding: 8px 20px;
transition: transform 0.2s ease, margin-top 0.2s ease;
}
body.show-persistent-footer .page-footer {
@@ -56,6 +59,10 @@ body.show-persistent-footer .page-footer {
flex-shrink: 0;
}
#persistent-footer-visibility-toggle {
display: none;
}
.page-footer .left-links {
display: flex;
flex-wrap: wrap;
@@ -109,3 +116,39 @@ Then the inner one needs to get padding and parent "item" padding needs to be re
gap: 0.5em;
}
}
@media (min-width: 768px) {
body.show-persistent-footer.persistent-footer-collapsed .page-footer {
transform: translateY(var(--persistent-footer-height, 0px));
margin-top: calc(-1 * var(--persistent-footer-height, 0px));
}
body.show-persistent-footer #persistent-footer-visibility-toggle {
position: absolute;
right: 24px;
top: calc(-1 * var(--persistent-footer-lip-height));
display: flex;
align-items: center;
justify-content: center;
width: 50px;
height: var(--persistent-footer-lip-height);
padding: 0;
color: var(--color-nav-text);
background: var(--color-footer);
border: 1px solid var(--color-secondary);
border-bottom: none;
border-radius: 10px 10px 0 0;
box-shadow: 0 -2px 6px var(--color-shadow);
cursor: pointer;
z-index: 2;
}
body.show-persistent-footer #persistent-footer-visibility-toggle:hover {
background: var(--color-nav-hover-bg);
}
body.show-persistent-footer #persistent-footer-visibility-toggle .svg {
width: 9px;
height: 9px;
}
}
+3 -2
View File
@@ -836,11 +836,12 @@ td .commit-summary {
justify-content: space-between;
align-items: center;
position: sticky;
top: var(--persistent-navbar-offset, 0px);
/* top: var(--persistent-navbar-offset, 0px); */
top: calc(var(--persistent-navbar-offset, 0px) + 1px);
z-index: 8;
padding: 7px 5px;
margin: 0 -5px; /* negative margin so it covers active file shadow */
height: 44px; /* this height should match sticky-2nd-row */
height: 55px; /* this height should match sticky-2nd-row */
background: var(--color-body);
}
+45
View File
@@ -9,6 +9,7 @@ import {initCompSearchRepoBox} from './comp/SearchRepoBox.ts';
const {appUrl, appSubUrl} = window.config;
const persistentNavbarCollapsedKey = 'persistent-navbar-collapsed';
const persistentFooterCollapsedKey = 'persistent-footer-collapsed';
function initHeadNavbarContentToggle() {
const navbar = document.querySelector('#navbar');
@@ -51,6 +52,49 @@ function initPersistentNavbarVisibilityToggle() {
});
}
function updatePersistentFooterHeight() {
if (!document.body.classList.contains('show-persistent-footer')) return;
const footer = document.querySelector<HTMLElement>('.page-footer');
if (!footer) return;
document.body.style.setProperty('--persistent-footer-height', `${footer.offsetHeight}px`);
}
function setPersistentFooterCollapsedState(collapsed: boolean) {
document.body.classList.toggle('persistent-footer-collapsed', collapsed);
updatePersistentFooterHeight();
const btn = document.querySelector<HTMLButtonElement>('#persistent-footer-visibility-toggle');
if (!btn) return;
const text = btn.getAttribute(collapsed ? 'data-show-text' : 'data-hide-text') ?? '';
btn.setAttribute('aria-pressed', String(collapsed));
btn.setAttribute('aria-label', text);
btn.setAttribute('data-tooltip-content', text);
btn.querySelector('.persistent-footer-visibility-icon-show')?.classList.toggle('tw-hidden', !collapsed);
btn.querySelector('.persistent-footer-visibility-icon-hide')?.classList.toggle('tw-hidden', collapsed);
}
function initPersistentFooterVisibilityToggle() {
if (!document.body.classList.contains('show-persistent-footer')) return;
const btn = document.querySelector<HTMLButtonElement>('#persistent-footer-visibility-toggle');
const footer = document.querySelector<HTMLElement>('.page-footer');
if (!btn || !footer) return;
const syncFooterHeight = () => updatePersistentFooterHeight();
syncFooterHeight();
setPersistentFooterCollapsedState(localUserSettings.getBoolean(persistentFooterCollapsedKey, false));
btn.addEventListener('click', () => {
const collapsed = !document.body.classList.contains('persistent-footer-collapsed');
localUserSettings.setBoolean(persistentFooterCollapsedKey, collapsed);
setPersistentFooterCollapsedState(collapsed);
});
window.addEventListener('resize', syncFooterHeight);
new ResizeObserver(syncFooterHeight).observe(footer);
}
function initFooterLanguageMenu() {
document.querySelector('.ui.dropdown .menu.language-menu')?.addEventListener('click', async (e) => {
const item = (e.target as HTMLElement).closest('.item');
@@ -144,6 +188,7 @@ export function initStickySideMenuIndicators(root: ParentNode = document) {
export function initCommmPageComponents() {
initHeadNavbarContentToggle();
initPersistentNavbarVisibilityToggle();
initPersistentFooterVisibilityToggle();
initFooterLanguageMenu();
initFooterThemeSelector();
initStickySideMenuIndicators();