diff --git a/services/web/app/src/infrastructure/ExpressLocals.js b/services/web/app/src/infrastructure/ExpressLocals.js index 17fdbab086..936453fe93 100644 --- a/services/web/app/src/infrastructure/ExpressLocals.js +++ b/services/web/app/src/infrastructure/ExpressLocals.js @@ -210,9 +210,10 @@ module.exports = function (webRouter, privateApiRouter, publicApiRouter) { ) { // Pick which main stylesheet to use based on Bootstrap version const bootstrap5Modifier = bootstrapVersion === 5 ? '-bootstrap-5' : '' + const computedThemeModifier = bootstrapVersion === 5 ? '' : themeModifier return res.locals.buildStylesheetPath( - `main-${themeModifier}style${bootstrap5Modifier}.css` + `main-${computedThemeModifier}style${bootstrap5Modifier}.css` ) } diff --git a/services/web/app/views/layout-base.pug b/services/web/app/views/layout-base.pug index 944435317e..53e88d8131 100644 --- a/services/web/app/views/layout-base.pug +++ b/services/web/app/views/layout-base.pug @@ -73,7 +73,7 @@ html( block head-scripts - body(class=(showThinFooter ? 'thin-footer' : undefined)) + body(class=(showThinFooter ? 'thin-footer' : undefined), data-theme="default") if(settings.recaptcha && settings.recaptcha.siteKeyV3) script(type="text/javascript", nonce=scriptNonce, src="https://www.recaptcha.net/recaptcha/api.js?render=" + settings.recaptcha.siteKeyV3, defer=deferScripts) diff --git a/services/web/frontend/js/features/editor-left-menu/hooks/use-set-overall-theme.tsx b/services/web/frontend/js/features/editor-left-menu/hooks/use-set-overall-theme.tsx index 00634ea8d5..947bb9bc8d 100644 --- a/services/web/frontend/js/features/editor-left-menu/hooks/use-set-overall-theme.tsx +++ b/services/web/frontend/js/features/editor-left-menu/hooks/use-set-overall-theme.tsx @@ -6,6 +6,7 @@ import { saveUserSettings } from '../utils/api' import { UserSettings } from '../../../../../types/user-settings' import { useUserSettingsContext } from '@/shared/context/user-settings-context' import getMeta from '@/utils/meta' +import { isBootstrap5 } from '@/features/utils/bootstrap-5' export default function useSetOverallTheme() { const [chosenTheme, setChosenTheme] = useState(null) @@ -23,7 +24,19 @@ export default function useSetOverallTheme() { [setUserSettings] ) + const skipLoadingStyleSheet = isBootstrap5() + useEffect(() => { + // Sets `data-theme` attribute to the body element, needed for Bootstrap 5 theming + const theme = overallTheme === 'light-' ? 'light' : 'default' + document.body.dataset.theme = theme + }, [overallTheme]) + + useEffect(() => { + if (skipLoadingStyleSheet) { + return + } + const docHeadEl = document.querySelector('head') const oldStyleSheetEl = document.getElementById('main-stylesheet') @@ -51,7 +64,12 @@ export default function useSetOverallTheme() { return () => { newStyleSheetEl.removeEventListener('load', loadEventCallback) } - }, [loadingStyleSheet, setLoadingStyleSheet, chosenTheme?.path]) + }, [ + loadingStyleSheet, + setLoadingStyleSheet, + skipLoadingStyleSheet, + chosenTheme?.path, + ]) return useCallback( (newOverallTheme: UserSettings['overallTheme']) => { @@ -62,13 +80,15 @@ export default function useSetOverallTheme() { ) if (chosenTheme) { - setLoadingStyleSheet(true) + if (!skipLoadingStyleSheet) { + setLoadingStyleSheet(true) + } setChosenTheme(chosenTheme) setOverallTheme(newOverallTheme) saveUserSettings('overallTheme', newOverallTheme) } } }, - [overallTheme, setLoadingStyleSheet, setOverallTheme] + [overallTheme, setLoadingStyleSheet, skipLoadingStyleSheet, setOverallTheme] ) } diff --git a/services/web/frontend/js/features/editor-navigation-toolbar/components/chat-toggle-button.jsx b/services/web/frontend/js/features/editor-navigation-toolbar/components/chat-toggle-button.jsx index ee1926e1bf..1ebcdbe496 100644 --- a/services/web/frontend/js/features/editor-navigation-toolbar/components/chat-toggle-button.jsx +++ b/services/web/frontend/js/features/editor-navigation-toolbar/components/chat-toggle-button.jsx @@ -8,12 +8,7 @@ import OLBadge from '@/features/ui/components/ol/ol-badge' function ChatToggleButton({ chatIsOpen, unreadMessageCount, onClick }) { const { t } = useTranslation() - const classes = classNames( - 'btn', - 'btn-full-height', - 'btn-full-height-no-border', - { active: chatIsOpen } - ) + const classes = classNames('btn', 'btn-full-height', { active: chatIsOpen }) const hasUnreadMessages = unreadMessageCount > 0 diff --git a/services/web/frontend/stylesheets/app/editor/toolbar.less b/services/web/frontend/stylesheets/app/editor/toolbar.less index a41f2ee69d..d215966aba 100644 --- a/services/web/frontend/stylesheets/app/editor/toolbar.less +++ b/services/web/frontend/stylesheets/app/editor/toolbar.less @@ -118,10 +118,6 @@ padding: 8px 10px; } } - .btn-full-height-no-border { - border-right: 0; - border-left: 0; - } .toolbar-left { display: flex; diff --git a/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss b/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss index 54cf94afcd..b2be7994eb 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/abstracts/mixins.scss @@ -86,3 +86,13 @@ @mixin file-tree-bg { background-color: var(--bg-dark-tertiary); } + +@mixin theme($name) { + @if index($themes, $name) { + [data-theme='#{$name}'] { + @content; + } + } @else { + @error 'Theme "#{$name}" is not supported. Supported themes are: #{$themes}.'; + } +} diff --git a/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss b/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss index 62f20c9be8..ad92e08c3c 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/abstracts/variables.scss @@ -6,3 +6,6 @@ $header-height: 68px; // Forms $form-group-margin-bottom: $spacing-06; + +// List of allowed themes +$themes: ('default', 'light'); diff --git a/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss b/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss index 97fb2f79d1..b486476f67 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/base/bootstrap.scss @@ -56,5 +56,8 @@ // Custom helpers @import '../helpers/all'; +// Modals +@import '../modals/all'; + // Pages custom style @import '../pages/all'; diff --git a/services/web/frontend/stylesheets/bootstrap-5/main-ieee-style.scss b/services/web/frontend/stylesheets/bootstrap-5/main-ieee-style.scss deleted file mode 100644 index 0c2708007b..0000000000 --- a/services/web/frontend/stylesheets/bootstrap-5/main-ieee-style.scss +++ /dev/null @@ -1,4 +0,0 @@ -@import 'main-style'; - -// IEEE-specific rules -$ieee-wedge: 30px; diff --git a/services/web/frontend/stylesheets/bootstrap-5/main-light-style.scss b/services/web/frontend/stylesheets/bootstrap-5/main-light-style.scss deleted file mode 100644 index 2463ed9611..0000000000 --- a/services/web/frontend/stylesheets/bootstrap-5/main-light-style.scss +++ /dev/null @@ -1,3 +0,0 @@ -@import 'main-style'; - -$is-overleaf-light: true; diff --git a/services/web/frontend/stylesheets/bootstrap-5/main-style.scss b/services/web/frontend/stylesheets/bootstrap-5/main-style.scss index 5f4635ab9c..0060b630bb 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/main-style.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/main-style.scss @@ -8,8 +8,6 @@ @import '../../fonts/material-symbols'; @import '../../fonts/font-awesome'; -$is-overleaf-light: false; - // Vendor CSS // TODO Bootstrap 5: Check whether this works with Bootstrap 5, and whether we can replace it @import '../vendor/select/select'; @@ -32,9 +30,3 @@ $is-overleaf-light: false; // Page layout that isn't related to a particular component or page @import 'base/layout'; - -// Modals -@import 'modals/all'; - -// Pages custom style -@import 'pages/all'; diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/online-users.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/online-users.scss index c67e6da3b4..52265c6e97 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/online-users.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/online-users.scss @@ -2,6 +2,10 @@ --toolbar-btn-color: var(--white); } +@include theme('light') { + --toolbar-btn-color: var(--neutral-70); +} + .online-users { display: flex; align-items: center; diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel.scss index d7ff2e93c2..0aee1bdf17 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/review-panel.scss @@ -2,6 +2,19 @@ --review-icon: url('../../../../../public/img/ol-icons/review-icon-dark-theme.svg'); } +@include theme('light') { + --review-icon: url('../../../../../public/img/ol-icons/review-icon-light-theme.svg'); + + button { + &.active, + &:active { + .review-icon { + --review-icon: url('../../../../../public/img/ol-icons/review-icon-dark-theme.svg'); + } + } + } +} + .review-icon { display: inline-block; background-image: var(--review-icon); diff --git a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss index 3b52ed15b7..1500e978f5 100644 --- a/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss +++ b/services/web/frontend/stylesheets/bootstrap-5/pages/editor/toolbar.scss @@ -11,6 +11,19 @@ center / contain no-repeat; } +@include theme('light') { + --toolbar-border-color: var(--neutral-20); + --toolbar-header-bg-color: var(--white); + --toolbar-header-btn-border-color: var(--neutral-20); + --toolbar-btn-color: var(--neutral-70); + --toolbar-btn-hover-bg-color: var(--neutral-10); + --project-name-color: var(--neutral-70); + --project-rename-link-color: var(--neutral-70); + --project-rename-link-color-hover: var(--neutral-70); + --editor-header-logo-background: url(../../../../../public/img/ol-brand/overleaf-o.svg) + center / contain no-repeat; +} + .toolbar { --toolbar-height: 40px; @@ -37,7 +50,7 @@ .toolbar-right, .toolbar-left { - button { + button:not(.back-to-editor-btn) { background: transparent; box-shadow: none; } @@ -51,7 +64,7 @@ button.btn-full-height { border: none; border-radius: 0; - border-right: 1px solid var(--toolbar-header-btn-border-color) !important; + border-right: 1px solid var(--toolbar-header-btn-border-color); color: var(--toolbar-btn-color); padding: var(--spacing-02) var(--spacing-05) var(--spacing-03); font-size: var(--font-size-05); @@ -95,11 +108,6 @@ } } - .btn-full-height-no-border { - border-right: 0; - border-left: 0; - } - .toolbar-left { display: flex; align-items: stretch; @@ -113,7 +121,8 @@ flex-grow: 1; justify-content: flex-end; - .btn-full-height { + a.btn-full-height, + button.btn-full-height { border-right: 0; border-left: 1px solid var(--toolbar-header-btn-border-color); } diff --git a/services/web/webpack.config.js b/services/web/webpack.config.js index 52e28f8d2f..002cfda877 100644 --- a/services/web/webpack.config.js +++ b/services/web/webpack.config.js @@ -27,10 +27,6 @@ const entryPoints = { 'main-light-style': './frontend/stylesheets/main-light-style.less', 'main-style-bootstrap-5': './frontend/stylesheets/bootstrap-5/main-style.scss', - 'main-ieee-style-bootstrap-5': - './frontend/stylesheets/bootstrap-5/main-ieee-style.scss', - 'main-light-style-bootstrap-5': - './frontend/stylesheets/bootstrap-5/main-light-style.scss', } // Add entrypoints for each "page"