diff --git a/server-ce/test/learn-wiki.spec.ts b/server-ce/test/learn-wiki.spec.ts
index 802aeeac3e..c0cc8729fe 100644
--- a/server-ce/test/learn-wiki.spec.ts
+++ b/server-ce/test/learn-wiki.spec.ts
@@ -83,8 +83,7 @@ describe('LearnWiki', function () {
})
function checkDisabled() {
- // eslint-disable-next-line mocha/no-skipped-tests
- it.skip('should not add a documentation entry to the nav bar', () => {
+ it('should not add a documentation entry to the nav bar', () => {
login(REGULAR_USER)
cy.visit('/project')
cy.findByText('Documentation').should('not.exist')
diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js
index 975ca7d07c..e6ce12d74a 100644
--- a/services/web/app/src/infrastructure/ExpressLocals.js
+++ b/services/web/app/src/infrastructure/ExpressLocals.js
@@ -244,8 +244,8 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) {
}
// This function is used to add translations from the server for main
- // navigation items because it's tricky to get them in the front end
- // otherwise.
+ // navigation and footer items because it's tricky to get them in the front
+ // end otherwise.
res.locals.cloneAndTranslateText = obj => {
const clone = _.cloneDeep(obj)
addTranslatedTextDeep(clone)
diff --git a/services/web/app/views/layout-marketing.pug b/services/web/app/views/layout-marketing.pug
index ecdfb7cd8f..20126beda3 100644
--- a/services/web/app/views/layout-marketing.pug
+++ b/services/web/app/views/layout-marketing.pug
@@ -17,7 +17,10 @@ block body
if (typeof suppressFooter === "undefined")
if showThinFooter
- include layout/footer-marketing
+ if bootstrapVersion === 5
+ include layout/thin-footer-bootstrap-5
+ else
+ include layout/thin-footer
else
include layout/fat-footer
diff --git a/services/web/app/views/layout-react.pug b/services/web/app/views/layout-react.pug
index c64b768ab9..4fba24c226 100644
--- a/services/web/app/views/layout-react.pug
+++ b/services/web/app/views/layout-react.pug
@@ -40,8 +40,12 @@ block append meta
items: cloneAndTranslateText(nav.header_extras)
})
meta(name="ol-footer" data-type="json" content={
+ showThinFooter: showThinFooter,
+ showPoweredBy: !hasFeature('saas') && !settings.nav.hide_powered_by,
subdomainLang: settings.i18n.subdomainLang,
- translatedLanguages: settings.translatedLanguages
+ translatedLanguages: settings.translatedLanguages,
+ leftItems: cloneAndTranslateText(settings.nav.left_footer),
+ rightItems: settings.nav.right_footer
})
block body
@@ -55,7 +59,10 @@ block body
if (typeof suppressFooter === "undefined")
if showThinFooter
- include layout/footer-marketing
+ if bootstrapVersion === 5
+ include layout/thin-footer-bootstrap-5
+ else
+ include layout/thin-footer
else
if bootstrapVersion === 5
include layout/fat-footer-react-bootstrap-5
diff --git a/services/web/app/views/layout-website-redesign-bootstrap-5.pug b/services/web/app/views/layout-website-redesign-bootstrap-5.pug
index d1354a617d..092f5b765d 100644
--- a/services/web/app/views/layout-website-redesign-bootstrap-5.pug
+++ b/services/web/app/views/layout-website-redesign-bootstrap-5.pug
@@ -16,7 +16,7 @@ block body
if (typeof(suppressFooter) == "undefined")
if showThinFooter
- include layout/footer-marketing
+ include layout/thin-footer-bootstrap-5
else
include layout/fat-footer-website-redesign
diff --git a/services/web/app/views/layout-website-redesign.pug b/services/web/app/views/layout-website-redesign.pug
index e848600e15..37ba2f9f32 100644
--- a/services/web/app/views/layout-website-redesign.pug
+++ b/services/web/app/views/layout-website-redesign.pug
@@ -14,7 +14,7 @@ block body
if (typeof(suppressFooter) == "undefined")
if showThinFooter
- include layout/footer-marketing
+ include layout/thin-footer
else
include layout/fat-footer-website-redesign
diff --git a/services/web/app/views/layout/fat-footer-react-bootstrap-5.pug b/services/web/app/views/layout/fat-footer-react-bootstrap-5.pug
index 297afdf746..95b2605b86 100644
--- a/services/web/app/views/layout/fat-footer-react-bootstrap-5.pug
+++ b/services/web/app/views/layout/fat-footer-react-bootstrap-5.pug
@@ -1 +1 @@
-#fat-footer-container
+#footer-container
diff --git a/services/web/app/views/layout/language-picker-bootstrap-5.pug b/services/web/app/views/layout/language-picker-bootstrap-5.pug
index 2e20cadadc..47a9bc5d53 100644
--- a/services/web/app/views/layout/language-picker-bootstrap-5.pug
+++ b/services/web/app/views/layout/language-picker-bootstrap-5.pug
@@ -1,21 +1,19 @@
li.dropdown.dropup.subdued(dropdown).language-picker
- a#language-picker-toggle(
- href="#",
+ button#language-picker-toggle.btn.btn-link.btn-inline-link(
dropdown-toggle,
data-ol-lang-selector-tooltip,
data-bs-toggle="dropdown",
- role="button"
aria-haspopup="true",
aria-expanded="false",
aria-label="Select " + translate('language'),
tooltip=translate('language')
title=translate('language')
)
- i.fa.fa-fw.fa-language
- |
- | #{settings.translatedLanguages[currentLngCode]}
+ span.material-symbols(aria-hidden="true") translate
+ |
+ span.language-picker-text #{settings.translatedLanguages[currentLngCode]}
- ul.dropdown-menu(role="menu" aria-labelledby="language-picker-toggle")
+ ul.dropdown-menu.dropdown-menu-sm-width(role="menu" aria-labelledby="language-picker-toggle")
li.dropdown-header #{translate("language")}
each subdomainDetails, subdomain in settings.i18n.subdomainLang
if !subdomainDetails.hide
@@ -24,4 +22,4 @@ li.dropdown.dropup.subdued(dropdown).language-picker
a.menu-indent(href=subdomainDetails.url+currentUrlWithQueryParams, role="menuitem", class=isActive ? 'dropdown-item active' : 'dropdown-item', aria-selected=isActive ? 'true' : 'false')
| #{settings.translatedLanguages[subdomainDetails.lngCode]}
if subdomainDetails.lngCode === currentLngCode
- span.material-symbols.dropdown-item-trailing-icon.pull-right(aria-hidden="true") check
+ span.material-symbols.dropdown-item-trailing-icon(aria-hidden="true") check
diff --git a/services/web/app/views/layout/thin-footer-bootstrap-5.pug b/services/web/app/views/layout/thin-footer-bootstrap-5.pug
new file mode 100644
index 0000000000..0baef6a3fb
--- /dev/null
+++ b/services/web/app/views/layout/thin-footer-bootstrap-5.pug
@@ -0,0 +1,38 @@
+footer.site-footer
+ - var showLanguagePicker = Object.keys(settings.i18n.subdomainLang).length > 1
+ - var hasCustomLeftNav = nav.left_footer && nav.left_footer.length > 0
+ .site-footer-content.hidden-print
+ .row
+ ul.site-footer-items.col-lg-9
+ if !settings.nav.hide_powered_by
+ li
+ //- year of Server Pro release, static
+ | © 2024
+ |
+ a(href='https://www.overleaf.com/for/enterprises') Powered by Overleaf
+
+ if showLanguagePicker || hasCustomLeftNav
+ li
+ strong.text-muted |
+
+ if showLanguagePicker
+ include language-picker-bootstrap-5
+
+ if showLanguagePicker && hasCustomLeftNav
+ li
+ strong.text-muted |
+
+ each item in nav.left_footer
+ li
+ if item.url
+ a(href=item.url, class=item.class) !{translate(item.text)}
+ else
+ | !{item.text}
+
+ ul.site-footer-items.col-lg-3.text-end
+ each item in nav.right_footer
+ li
+ if item.url
+ a(href=item.url, class=item.class, aria-label=item.label) !{item.text}
+ else
+ | !{item.text}
diff --git a/services/web/app/views/layout/footer-marketing.pug b/services/web/app/views/layout/thin-footer.pug
similarity index 100%
rename from services/web/app/views/layout/footer-marketing.pug
rename to services/web/app/views/layout/thin-footer.pug
diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json
index 56f4f532eb..cfb29b91f5 100644
--- a/services/web/frontend/extracted-translations.json
+++ b/services/web/frontend/extracted-translations.json
@@ -790,6 +790,7 @@
"knowledge_base": "",
"labels_help_you_to_easily_reference_your_figures": "",
"labels_help_you_to_reference_your_tables": "",
+ "language": "",
"language_feedback": "",
"large_or_high-resolution_images_taking_too_long": "",
"last_active": "",
diff --git a/services/web/frontend/js/features/header-footer-react/index.tsx b/services/web/frontend/js/features/header-footer-react/index.tsx
index f3395fe316..2ac25a090f 100644
--- a/services/web/frontend/js/features/header-footer-react/index.tsx
+++ b/services/web/frontend/js/features/header-footer-react/index.tsx
@@ -1,7 +1,7 @@
import ReactDOM from 'react-dom'
import getMeta from '@/utils/meta'
import DefaultNavbar from '@/features/ui/components/bootstrap-5/navbar/default-navbar'
-import FatFooter from '@/features/ui/components/bootstrap-5/footer/fat-footer'
+import Footer from '@/features/ui/components/bootstrap-5/footer/footer'
const navbarElement = document.getElementById('navbar-container')
if (navbarElement) {
@@ -9,8 +9,8 @@ if (navbarElement) {
ReactDOM.render(, navbarElement)
}
-const footerElement = document.getElementById('fat-footer-container')
+const footerElement = document.getElementById('footer-container')
if (footerElement) {
const footerProps = getMeta('ol-footer')
- ReactDOM.render(, footerElement)
+ ReactDOM.render(, footerElement)
}
diff --git a/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx b/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
index 347edfb841..9787a7a371 100644
--- a/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
+++ b/services/web/frontend/js/features/project-list/components/project-list-ds-nav.tsx
@@ -22,7 +22,6 @@ import SidebarDsNav from '@/features/project-list/components/sidebar/sidebar-ds-
export function ProjectListDsNav() {
const navbarProps = getMeta('ol-navbar')
- const footerProps = getMeta('ol-footer')
const { t } = useTranslation()
const {
error,
@@ -120,7 +119,7 @@ export function ProjectListDsNav() {
-
+
diff --git a/services/web/frontend/js/features/project-list/components/project-list-root.tsx b/services/web/frontend/js/features/project-list/components/project-list-root.tsx
index 90b4191674..684aaa8025 100644
--- a/services/web/frontend/js/features/project-list/components/project-list-root.tsx
+++ b/services/web/frontend/js/features/project-list/components/project-list-root.tsx
@@ -17,7 +17,7 @@ import withErrorBoundary from '../../../infrastructure/error-boundary'
import { GenericErrorBoundaryFallback } from '@/shared/components/generic-error-boundary-fallback'
import getMeta from '@/utils/meta'
import DefaultNavbar from '@/features/ui/components/bootstrap-5/navbar/default-navbar'
-import FatFooter from '@/features/ui/components/bootstrap-5/footer/fat-footer'
+import Footer from '@/features/ui/components/bootstrap-5/footer/footer'
import WelcomePageContent from '@/features/project-list/components/welcome-page-content'
import ProjectListDefault from '@/features/project-list/components/project-list-default'
import { ProjectListDsNav } from '@/features/project-list/components/project-list-ds-nav'
@@ -57,7 +57,7 @@ function DefaultNavbarAndFooter({ children }: { children: ReactNode }) {
>
{children}
-
+
>
)
}
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer-base.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer-base.tsx
index 2e213cec96..e791060d97 100644
--- a/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer-base.tsx
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer-base.tsx
@@ -33,7 +33,7 @@ function FatFooterBase() {
-
+
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer.tsx
index 49913d40cf..361a3afbdb 100644
--- a/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer.tsx
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/fat-footer.tsx
@@ -1,7 +1,6 @@
import React from 'react'
import { useTranslation } from 'react-i18next'
import FatFooterBase from './fat-footer-base'
-import { FatFooterMetadata } from '../../types/fat-footer-metadata'
type FooterLinkProps = {
href: string
@@ -13,7 +12,7 @@ type FooterSectionProps = {
links: FooterLinkProps[]
}
-function FatFooter(props: FatFooterMetadata) {
+function FatFooter() {
const { t } = useTranslation()
const hideFatFooter = false
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/footer/footer.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/footer.tsx
new file mode 100644
index 0000000000..f3a0479f90
--- /dev/null
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/footer.tsx
@@ -0,0 +1,9 @@
+import { FooterMetadata } from '@/features/ui/components/types/footer-metadata'
+import ThinFooter from '@/features/ui/components/bootstrap-5/footer/thin-footer'
+import FatFooter from '@/features/ui/components/bootstrap-5/footer/fat-footer'
+
+function Footer(props: FooterMetadata) {
+ return props.showThinFooter ?
:
+}
+
+export default Footer
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/footer/thin-footer.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/thin-footer.tsx
new file mode 100644
index 0000000000..11c84aaf1a
--- /dev/null
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/footer/thin-footer.tsx
@@ -0,0 +1,90 @@
+import type {
+ FooterItem,
+ FooterMetadata,
+} from '@/features/ui/components/types/footer-metadata'
+import OLRow from '@/features/ui/components/ol/ol-row'
+import LanguagePicker from '@/features/ui/components/bootstrap-5/language-picker'
+import React from 'react'
+
+function FooterItemLi({
+ text,
+ translatedText,
+ url: href,
+ class: className,
+ label,
+}: FooterItem) {
+ const textToDisplay = translatedText || text
+
+ if (!href) {
+ return
{textToDisplay}
+ }
+
+ const linkProps = {
+ href,
+ className,
+ 'aria-label': label,
+ }
+
+ return (
+
+
+
+ )
+}
+
+function Separator() {
+ return (
+
+ |
+
+ )
+}
+
+function ThinFooter({
+ showPoweredBy,
+ subdomainLang,
+ leftItems,
+ rightItems,
+}: FooterMetadata) {
+ const showLanguagePicker = Boolean(
+ subdomainLang && Object.keys(subdomainLang).length > 1
+ )
+
+ const hasCustomLeftNav = Boolean(leftItems && leftItems.length > 0)
+
+ return (
+
+ )
+}
+
+export default ThinFooter
diff --git a/services/web/frontend/js/features/ui/components/bootstrap-5/language-picker.tsx b/services/web/frontend/js/features/ui/components/bootstrap-5/language-picker.tsx
index 9827648da3..0ae53c807c 100644
--- a/services/web/frontend/js/features/ui/components/bootstrap-5/language-picker.tsx
+++ b/services/web/frontend/js/features/ui/components/bootstrap-5/language-picker.tsx
@@ -4,12 +4,13 @@ import {
DropdownItem,
DropdownMenu,
DropdownToggle,
+ DropdownHeader,
} from './dropdown-menu'
import { useTranslation } from 'react-i18next'
import getMeta from '@/utils/meta'
-import Icon from '@/shared/components/icon'
+import MaterialIcon from '@/shared/components/material-icon'
-function LanguagePicker() {
+function LanguagePicker({ showHeader } = { showHeader: false }) {
const { t } = useTranslation()
const currentLangCode = getMeta('ol-i18n').currentLangCode
@@ -26,18 +27,18 @@ function LanguagePicker() {
className="btn-inline-link"
variant="link"
>
-
- {translatedLanguages?.[currentLangCode]}
+
+
+
+ {translatedLanguages?.[currentLangCode]}
+
+ {showHeader ? {t('language')} : null}
{subdomainLang &&
Object.entries(subdomainLang).map(([subdomain, subdomainDetails]) => {
if (
diff --git a/services/web/frontend/js/features/ui/components/types/fat-footer-metadata.ts b/services/web/frontend/js/features/ui/components/types/fat-footer-metadata.ts
deleted file mode 100644
index 1d695f91d2..0000000000
--- a/services/web/frontend/js/features/ui/components/types/fat-footer-metadata.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import type { SubdomainLang } from '@/features/ui/components/types/fat-footer'
-
-export type FatFooterMetadata = {
- subdomainLang?: SubdomainLang
- translatedLanguages: { [key: string]: string }
- currentLangCode: string
-}
diff --git a/services/web/frontend/js/features/ui/components/types/footer-metadata.ts b/services/web/frontend/js/features/ui/components/types/footer-metadata.ts
new file mode 100644
index 0000000000..6f0dbb40a1
--- /dev/null
+++ b/services/web/frontend/js/features/ui/components/types/footer-metadata.ts
@@ -0,0 +1,18 @@
+import type { SubdomainLang } from '@/features/ui/components/types/fat-footer'
+
+export type FooterItem = {
+ text: string
+ translatedText?: string
+ url?: string
+ class?: string
+ label?: string
+}
+
+export type FooterMetadata = {
+ showThinFooter: boolean
+ translatedLanguages: { [key: string]: string }
+ showPoweredBy?: boolean
+ subdomainLang?: SubdomainLang
+ leftItems?: FooterItem[]
+ rightItems?: FooterItem[]
+}
diff --git a/services/web/frontend/js/utils/meta.ts b/services/web/frontend/js/utils/meta.ts
index 3516969ece..09df91cdef 100644
--- a/services/web/frontend/js/utils/meta.ts
+++ b/services/web/frontend/js/utils/meta.ts
@@ -49,7 +49,7 @@ import { ThirdPartyIds } from '../../../types/third-party-ids'
import { Publisher } from '../../../types/subscription/dashboard/publisher'
import { SubscriptionChangePreview } from '../../../types/subscription/subscription-change-preview'
import { DefaultNavbarMetadata } from '@/features/ui/components/types/default-navbar-metadata'
-import { FatFooterMetadata } from '@/features/ui/components/types/fat-footer-metadata'
+import { FooterMetadata } from '@/features/ui/components/types/footer-metadata'
export interface Meta {
'ol-ExposedSettings': ExposedSettings
'ol-allInReconfirmNotificationPeriods': UserEmailData[]
@@ -89,7 +89,7 @@ export interface Meta {
'ol-error': { name: string } | undefined
'ol-expired': boolean
'ol-features': Features
- 'ol-footer': FatFooterMetadata
+ 'ol-footer': FooterMetadata
'ol-fromPlansPage': boolean
'ol-galleryTagName': string
'ol-gitBridgeEnabled': boolean
diff --git a/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss b/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss
index 8e24f4c800..109cf7cb6c 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss
@@ -1,10 +1,14 @@
-// Footer
-$footer-height: 50px;
-
// Header
$header-height: 68px;
$toolbar-height: 40px;
$toolbar-small-height: 32px;
+// Footer
+$thin-footer-height: 50px;
+$fat-footer-content-height: calc(100vh - $header-height);
+$thin-footer-content-height: calc(
+ 100vh - ($header-height + $thin-footer-height)
+);
+
// List of allowed themes
$themes: ('default', 'light');
diff --git a/services/web/frontend/stylesheets/bootstrap-5/base/layout.scss b/services/web/frontend/stylesheets/bootstrap-5/base/layout.scss
index 268cb8cff4..211f384d61 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/base/layout.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/base/layout.scss
@@ -1,7 +1,15 @@
+body {
+ position: relative;
+}
+
.content {
min-height: 100vh;
padding-top: $header-height + $spacing-08;
padding-bottom: $spacing-08;
+
+ .thin-footer & {
+ min-height: calc(100vh - #{$thin-footer-height});
+ }
}
.content-alt {
diff --git a/services/web/frontend/stylesheets/bootstrap-5/base/links.scss b/services/web/frontend/stylesheets/bootstrap-5/base/links.scss
index 7f5d390c0b..dc20dea1d3 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/base/links.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/base/links.scss
@@ -8,11 +8,13 @@
--link-color-dark: var(--link-ui-dark);
--link-hover-color-dark: var(--link-ui-hover-dark);
--link-visited-color-dark: var(--link-ui-visited-dark);
+ --link-text-decoration: underline;
--link-hover-text-decoration: none;
}
a {
color: var(--link-color);
+ text-decoration: var(--link-text-decoration);
&:visited {
color: var(--link-visited-color);
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/button.scss b/services/web/frontend/stylesheets/bootstrap-5/components/button.scss
index f1ab5477da..3b782ed1f8 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/button.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/button.scss
@@ -137,7 +137,7 @@
// Make a button look and behave like a link
.btn-link {
- color: var(--link-ui);
+ color: var(--link-color);
font-weight: normal;
cursor: pointer;
text-decoration: underline;
@@ -156,7 +156,7 @@
&:hover,
&:focus {
- color: var(--link-ui-hover);
+ color: var(--link-hover-color);
text-decoration: none;
background-color: transparent;
}
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss
index f312630bef..93491f0fbd 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/dropdown-menu.scss
@@ -1,3 +1,5 @@
+$dropdown-item-min-height: 36px;
+
.dropdown {
display: inline-flex;
@@ -20,6 +22,9 @@
.dropdown-header {
@include body-xs;
+ display: flex;
+ align-items: center;
+ min-height: $dropdown-item-min-height; // a minimum height of 36px to be accessible for touch screens
padding: var(--spacing-05) var(--spacing-06) var(--spacing-02)
var(--spacing-04);
}
@@ -55,7 +60,7 @@
display: grid;
grid-auto-flow: column;
place-content: center start;
- min-height: 36px; // a minimum height of 36px to be accessible for touch screens
+ min-height: $dropdown-item-min-height; // a minimum height of 36px to be accessible for touch screens
position: relative;
&:active {
diff --git a/services/web/frontend/stylesheets/bootstrap-5/components/footer.scss b/services/web/frontend/stylesheets/bootstrap-5/components/footer.scss
index 4c72cd283c..3f6b829a37 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/components/footer.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/components/footer.scss
@@ -56,56 +56,33 @@ $footer-width-delta: 320px;
}
footer.site-footer {
+ --link-color: var(--link-web);
+ --link-hover-color: var(--link-web-hover);
+ --link-visited-color: var(--link-web-visited);
+ --link-text-decoration: none;
+ --link-hover-text-decoration: underline;
+
background-color: var(--bg-light-primary);
border-top: 1px solid var(--border-primary-dark);
- position: absolute;
- bottom: 0;
- width: 100%;
@include body-sm;
- height: $footer-height;
+ min-height: $thin-footer-height;
+ line-height: $thin-footer-height - 1; // Hack — in Chrome, using the full $footer-height would generate vertical scrolling
- ul {
+ ul.site-footer-items {
list-style: none;
- margin: var(--spacing-00);
+ margin: 0;
- li {
+ > li {
display: inline-block;
- margin: var(--spacing-07) var(--spacing-04);
+ margin: 0 var(--spacing-04);
}
i {
font-size: var(--font-size-05);
}
}
-
- li.lng-option {
- text-align: left;
- display: list-item;
-
- img {
- vertical-align: text-bottom;
- }
-
- a:hover,
- a:focus {
- color: var(--content-primary-dark);
-
- &:visited {
- color: var(--content-primary-dark);
- }
- }
- }
-
- a {
- color: var(--content-secondary-dark);
-
- &:hover,
- &:focus {
- color: var(--content-secondary-dark);
- }
- }
}
.site-footer-content {
@@ -117,7 +94,44 @@ footer.site-footer {
vertical-align: middle;
}
+#language-picker-toggle {
+ text-decoration: none;
+ cursor: pointer;
+
+ &::after {
+ display: none;
+ }
+
+ &:hover {
+ .language-picker-text {
+ text-decoration: var(--link-hover-text-decoration);
+ }
+ }
+
+ .material-symbols {
+ vertical-align: -0.1875rem;
+ }
+}
+
+.language-picker .dropdown-menu {
+ .dropdown-item {
+ &.active {
+ color: var(--green-70);
+ }
+
+ &:hover {
+ text-decoration: none;
+ }
+ }
+}
+
.fat-footer {
+ --link-color: var(--content-secondary-dark);
+ --link-hover-color: var(--content-secondary-dark);
+ --link-visited-color: var(--content-secondary-dark);
+ --link-text-decoration: none;
+ --link-hover-text-decoration: underline;
+
container-name: fatfooter;
container-type: inline-size;
background: var(--bg-dark-primary);
@@ -133,20 +147,6 @@ footer.site-footer {
}
}
- a {
- color: var(--content-secondary-dark);
- text-decoration: none;
-
- &:hover {
- color: var(--content-secondary-dark);
- text-decoration: underline;
- }
-
- &:visited {
- color: var(--content-secondary-dark);
- }
- }
-
.footer-brand-container {
flex: 1;
}
@@ -196,16 +196,6 @@ footer.site-footer {
#language-picker-toggle {
color: var(--content-secondary-dark);
- cursor: pointer;
- text-decoration: none;
-
- &::after {
- display: none;
- }
-
- &:hover {
- text-decoration: underline;
- }
}
.fat-footer-base-meta a:not(.dropdown-toggle) {
@@ -216,16 +206,6 @@ footer.site-footer {
.dropdown-header {
display: none; /* hiding rather than removing as still needed in the thin footer */
}
-
- .dropdown-item {
- &.active {
- color: var(--green-70);
- }
-
- &:hover {
- text-decoration: none;
- }
- }
}
}
@@ -406,17 +386,13 @@ footer.site-footer {
}
&.fat-footer {
+ --link-color: var(--content-primary);
+ --link-hover-color: var(--content-primary);
+ --link-visited-color: var(--content-primary);
+
background: var(--content-primary-dark);
color: var(--content-primary);
- a {
- color: var(--content-primary);
-
- &:visited {
- color: var(--content-primary);
- }
- }
-
.footer-section-heading {
color: var(--content-primary);
}
diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/project-list-default.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/project-list-default.scss
index 92d0db7b62..caa4437773 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/pages/project-list-default.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/pages/project-list-default.scss
@@ -1,24 +1,30 @@
.project-list-react {
+ --project-list-content-height: #{$fat-footer-content-height};
+
#project-list-root > &.content {
padding-top: $header-height;
padding-bottom: 0;
- min-height: calc(100vh - #{$header-height});
+ min-height: var(--project-list-content-height);
display: flex;
flex-direction: column;
+
+ .thin-footer & {
+ --project-list-content-height: #{$thin-footer-content-height};
+ }
}
.project-list-wrapper {
display: flex;
align-items: stretch;
width: 100%;
- min-height: calc(100vh - #{$header-height});
+ min-height: var(--project-list-content-height);
}
.project-list-sidebar-wrapper-react {
position: relative;
background-color: var(--bg-dark-secondary);
flex: 0 0 15%;
- min-height: calc(100vh - #{$header-height});
+ min-height: var(--project-list-content-height);
max-width: 320px;
min-width: 200px;
@@ -121,7 +127,7 @@
flex-direction: column;
justify-content: center;
align-items: center;
- min-height: calc(100vh - #{$header-height});
+ min-height: var(--project-list-content-height);
.loading-screen-brand-container {
margin: 0 auto;
diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss
index 4ca5d50df2..a23189d688 100644
--- a/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss
+++ b/services/web/frontend/stylesheets/bootstrap-5/pages/project-list.scss
@@ -579,18 +579,6 @@
}
}
}
-
- .loading-container {
- display: flex;
- flex-direction: column;
- justify-content: center;
- align-items: center;
- min-height: calc(100vh - #{$header-height});
-
- .loading-screen-brand-container {
- margin: 0 auto;
- }
- }
}
.current-plan {
diff --git a/services/web/test/frontend/components/shared/language-picker.spec.tsx b/services/web/test/frontend/components/shared/language-picker.spec.tsx
index 867fc8e612..b681a1086c 100644
--- a/services/web/test/frontend/components/shared/language-picker.spec.tsx
+++ b/services/web/test/frontend/components/shared/language-picker.spec.tsx
@@ -10,6 +10,7 @@ describe('LanguagePicker', function () {
currentLangCode: 'en',
})
window.metaAttributesCache.set('ol-footer', {
+ showThinFooter: false,
translatedLanguages: {
en: 'English',
fr: 'Français',
@@ -26,12 +27,12 @@ describe('LanguagePicker', function () {
})
it('renders the language picker with the current language', function () {
- cy.mount()
+ cy.mount()
cy.get('#language-picker-toggle').should('contain', 'English')
})
it('opens the dropdown and lists available languages', function () {
- cy.mount()
+ cy.mount()
cy.get('#language-picker-toggle').click()
cy.get('.dropdown-menu').within(() => {
@@ -42,7 +43,7 @@ describe('LanguagePicker', function () {
})
it('changes the language and updates the URL when a language is selected', function () {
- cy.mount()
+ cy.mount()
cy.get('#language-picker-toggle').should('exist').click()
cy.contains('Français').click()
cy.url().should('include', 'fr.overleaf.com')
diff --git a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
index b01e86b426..2c5e3f6dc7 100644
--- a/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/project-list-root.test.tsx
@@ -55,6 +55,7 @@ describe('', function () {
window.metaAttributesCache.set('ol-user', {})
window.metaAttributesCache.set('ol-user_id', userId)
window.metaAttributesCache.set('ol-footer', {
+ showThinFooter: false,
translatedLanguages: { en: 'English' },
subdomainLang: { en: { lngCode: 'en', url: 'overleaf.com' } },
})
diff --git a/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools-rename.test.tsx b/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools-rename.test.tsx
index 44bc7e0fed..5a301a3200 100644
--- a/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools-rename.test.tsx
+++ b/services/web/test/frontend/features/project-list/components/table/project-tools/project-tools-rename.test.tsx
@@ -65,6 +65,7 @@ describe('', function () {
})
window.metaAttributesCache.set('ol-footer', {
+ showThinFooter: false,
translatedLanguages: { en: 'English' },
subdomainLang: { en: { lngCode: 'en', url: 'overleaf.com' } },
})