diff --git a/theme/boost/amd/build/loader.min.js b/theme/boost/amd/build/loader.min.js
index 27cf1fa6872dd..91e6786413051 100644
--- a/theme/boost/amd/build/loader.min.js
+++ b/theme/boost/amd/build/loader.min.js
@@ -1,2 +1,2 @@
-define ("theme_boost/loader",["jquery","./tether","core/event","core/custom_interaction_events"],function(a,b,c,d){window.jQuery=a;window.Tether=b;M.util.js_pending("theme_boost/loader:children");require(["theme_boost/aria","theme_boost/pending","theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(b){a("body").popover({trigger:"focus",selector:"[data-toggle=popover][data-trigger!=hover]",placement:"auto"});d.define(a("body"),[d.events.escape]);a("body").on(d.events.escape,"[data-toggle=popover]",function(){a(this).trigger("blur")});a("html").popover({container:"body",selector:"[data-toggle=popover][data-trigger=hover]",trigger:"hover",delay:{hide:500}});a("html").tooltip({selector:"[data-toggle=\"tooltip\"]"});a.fn.dropdown.Constructor.Default.flip=!1;a("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(b){var c=a(b.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var e=window.location.hash;if(e){a(".nav-link[href=\""+e+"\"]").tab("show")}c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:"[data-toggle=\"popover\"]",trigger:"focus"})})});b.init();M.util.js_complete("theme_boost/loader:children")});return{}});
+define ("theme_boost/loader",["jquery","./tether","core/event","core/custom_interaction_events"],function(a,b,c,d){window.jQuery=a;window.Tether=b;M.util.js_pending("theme_boost/loader:children");require(["theme_boost/aria","theme_boost/scroll","theme_boost/pending","theme_boost/util","theme_boost/alert","theme_boost/button","theme_boost/carousel","theme_boost/collapse","theme_boost/dropdown","theme_boost/modal","theme_boost/scrollspy","theme_boost/tab","theme_boost/tooltip","theme_boost/popover"],function(b,e){a("body").popover({trigger:"focus",selector:"[data-toggle=popover][data-trigger!=hover]",placement:"auto"});d.define(a("body"),[d.events.escape]);a("body").on(d.events.escape,"[data-toggle=popover]",function(){a(this).trigger("blur")});a("html").popover({container:"body",selector:"[data-toggle=popover][data-trigger=hover]",trigger:"hover",delay:{hide:500}});a("html").tooltip({selector:"[data-toggle=\"tooltip\"]"});a.fn.dropdown.Constructor.Default.flip=!1;a("a[data-toggle=\"tab\"]").on("shown.bs.tab",function(b){var c=a(b.target).attr("href");if(history.replaceState){history.replaceState(null,null,c)}else{location.hash=c}});var f=window.location.hash;if(f){a(".nav-link[href=\""+f+"\"]").tab("show")}c.getLegacyEvents().done(function(b){a(document).on(b.FILTER_CONTENT_UPDATED,function(){a("body").popover({selector:"[data-toggle=\"popover\"]",trigger:"focus"})})});b.init();this.scroll=new e;this.scroll.init();M.util.js_complete("theme_boost/loader:children")});return{}});
//# sourceMappingURL=loader.min.js.map
diff --git a/theme/boost/amd/build/loader.min.js.map b/theme/boost/amd/build/loader.min.js.map
index 6ac8fe5c7f8f0..eb807560cc4cd 100644
--- a/theme/boost/amd/build/loader.min.js.map
+++ b/theme/boost/amd/build/loader.min.js.map
@@ -1 +1 @@
-{"version":3,"sources":["../src/loader.js"],"names":["define","jQuery","Tether","Event","customEvents","window","M","util","js_pending","require","Aria","popover","trigger","selector","placement","events","escape","on","container","delay","hide","tooltip","fn","dropdown","Constructor","Default","flip","e","hash","target","attr","history","replaceState","location","tab","getLegacyEvents","done","document","FILTER_CONTENT_UPDATED","init","js_complete"],"mappings":"AAyBAA,OAAM,sBAAC,CAAC,QAAD,CAAW,UAAX,CAAuB,YAAvB,CAAqC,gCAArC,CAAD,CAAyE,SAASC,CAAT,CAAiBC,CAAjB,CAAyBC,CAAzB,CAAgCC,CAAhC,CAA8C,CAEzHC,MAAM,CAACJ,MAAP,CAAgBA,CAAhB,CACAI,MAAM,CAACH,MAAP,CAAgBA,CAAhB,CACAI,CAAC,CAACC,IAAF,CAAOC,UAAP,CAAkB,6BAAlB,EAEAC,OAAO,CAAC,CAAC,kBAAD,CACA,qBADA,CAEA,kBAFA,CAGA,mBAHA,CAIA,oBAJA,CAKA,sBALA,CAMA,sBANA,CAOA,sBAPA,CAQA,mBARA,CASA,uBATA,CAUA,iBAVA,CAWA,qBAXA,CAYA,qBAZA,CAAD,CAaC,SAASC,CAAT,CAAe,CAGnBT,CAAM,CAAC,MAAD,CAAN,CAAeU,OAAf,CAAuB,CACnBC,OAAO,CAAE,OADU,CAEnBC,QAAQ,CAAE,4CAFS,CAGnBC,SAAS,CAAE,MAHQ,CAAvB,EAOAV,CAAY,CAACJ,MAAb,CAAoBC,CAAM,CAAC,MAAD,CAA1B,CAAoC,CAChCG,CAAY,CAACW,MAAb,CAAoBC,MADY,CAApC,EAGAf,CAAM,CAAC,MAAD,CAAN,CAAegB,EAAf,CAAkBb,CAAY,CAACW,MAAb,CAAoBC,MAAtC,CAA8C,uBAA9C,CAAuE,UAAW,CAE9Ef,CAAM,CAAC,IAAD,CAAN,CAAaW,OAAb,CAAqB,MAArB,CACH,CAHD,EAKAX,CAAM,CAAC,MAAD,CAAN,CAAeU,OAAf,CAAuB,CACnBO,SAAS,CAAE,MADQ,CAEnBL,QAAQ,CAAE,2CAFS,CAGnBD,OAAO,CAAE,OAHU,CAInBO,KAAK,CAAE,CACHC,IAAI,CAAE,GADH,CAJY,CAAvB,EASAnB,CAAM,CAAC,MAAD,CAAN,CAAeoB,OAAf,CAAuB,CACnBR,QAAQ,CAAE,2BADS,CAAvB,EAKAZ,CAAM,CAACqB,EAAP,CAAUC,QAAV,CAAmBC,WAAnB,CAA+BC,OAA/B,CAAuCC,IAAvC,IAEAzB,CAAM,CAAC,wBAAD,CAAN,CAA+BgB,EAA/B,CAAkC,cAAlC,CAAkD,SAASU,CAAT,CAAY,CAC1D,GAAIC,CAAAA,CAAI,CAAG3B,CAAM,CAAC0B,CAAC,CAACE,MAAH,CAAN,CAAiBC,IAAjB,CAAsB,MAAtB,CAAX,CACA,GAAIC,OAAO,CAACC,YAAZ,CAA0B,CACtBD,OAAO,CAACC,YAAR,CAAqB,IAArB,CAA2B,IAA3B,CAAiCJ,CAAjC,CACH,CAFD,IAEO,CACHK,QAAQ,CAACL,IAAT,CAAgBA,CACnB,CACJ,CAPD,EASA,GAAIA,CAAAA,CAAI,CAAGvB,MAAM,CAAC4B,QAAP,CAAgBL,IAA3B,CACA,GAAIA,CAAJ,CAAU,CACP3B,CAAM,CAAC,oBAAqB2B,CAArB,CAA4B,KAA7B,CAAN,CAAyCM,GAAzC,CAA6C,MAA7C,CACF,CAGD/B,CAAK,CAACgC,eAAN,GAAwBC,IAAxB,CAA6B,SAASrB,CAAT,CAAiB,CAC1Cd,CAAM,CAACoC,QAAD,CAAN,CAAiBpB,EAAjB,CAAoBF,CAAM,CAACuB,sBAA3B,CAAmD,UAAW,CAC1DrC,CAAM,CAAC,MAAD,CAAN,CAAeU,OAAf,CAAuB,CACnBE,QAAQ,CAAE,2BADS,CAEnBD,OAAO,CAAE,OAFU,CAAvB,CAKH,CAND,CAOH,CARD,EAUAF,CAAI,CAAC6B,IAAL,GACAjC,CAAC,CAACC,IAAF,CAAOiC,WAAP,CAAmB,6BAAnB,CACH,CA1EM,CAAP,CA6EA,MAAO,EACV,CApFK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module core/templates\n * @package core\n * @class templates\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\ndefine(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], function(jQuery, Tether, Event, customEvents) {\n\n window.jQuery = jQuery;\n window.Tether = Tether;\n M.util.js_pending('theme_boost/loader:children');\n\n require(['theme_boost/aria',\n 'theme_boost/pending',\n 'theme_boost/util',\n 'theme_boost/alert',\n 'theme_boost/button',\n 'theme_boost/carousel',\n 'theme_boost/collapse',\n 'theme_boost/dropdown',\n 'theme_boost/modal',\n 'theme_boost/scrollspy',\n 'theme_boost/tab',\n 'theme_boost/tooltip',\n 'theme_boost/popover'],\n function(Aria) {\n\n // We do twice because: https://github.com/twbs/bootstrap/issues/10547\n jQuery('body').popover({\n trigger: 'focus',\n selector: \"[data-toggle=popover][data-trigger!=hover]\",\n placement: 'auto'\n });\n\n // Popovers must close on Escape for accessibility reasons.\n customEvents.define(jQuery('body'), [\n customEvents.events.escape,\n ]);\n jQuery('body').on(customEvents.events.escape, '[data-toggle=popover]', function() {\n // Use \"blur\" instead of \"popover('hide')\" to prevent issue that the same tooltip can't be opened again.\n jQuery(this).trigger('blur');\n });\n\n jQuery(\"html\").popover({\n container: \"body\",\n selector: \"[data-toggle=popover][data-trigger=hover]\",\n trigger: \"hover\",\n delay: {\n hide: 500\n }\n });\n\n jQuery(\"html\").tooltip({\n selector: '[data-toggle=\"tooltip\"]'\n });\n\n // Disables flipping the dropdowns up and getting hidden behind the navbar.\n jQuery.fn.dropdown.Constructor.Default.flip = false;\n\n jQuery('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = jQuery(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n\n var hash = window.location.hash;\n if (hash) {\n jQuery('.nav-link[href=\"' + hash + '\"]').tab('show');\n }\n\n // We need to call popover automatically if nodes are added to the page later.\n Event.getLegacyEvents().done(function(events) {\n jQuery(document).on(events.FILTER_CONTENT_UPDATED, function() {\n jQuery('body').popover({\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus'\n });\n\n });\n });\n\n Aria.init();\n M.util.js_complete('theme_boost/loader:children');\n });\n\n\n return {};\n});\n"],"file":"loader.min.js"}
\ No newline at end of file
+{"version":3,"sources":["../src/loader.js"],"names":["define","jQuery","Tether","Event","customEvents","window","M","util","js_pending","require","Aria","MoodleScroll","popover","trigger","selector","placement","events","escape","on","container","delay","hide","tooltip","fn","dropdown","Constructor","Default","flip","e","hash","target","attr","history","replaceState","location","tab","getLegacyEvents","done","document","FILTER_CONTENT_UPDATED","init","scroll","js_complete"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Template renderer for Moodle. Load and render Moodle templates with Mustache.\n *\n * @module core/templates\n * @package core\n * @class templates\n * @copyright 2015 Damyon Wiese \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 2.9\n */\ndefine(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], function(jQuery, Tether, Event, customEvents) {\n\n window.jQuery = jQuery;\n window.Tether = Tether;\n M.util.js_pending('theme_boost/loader:children');\n\n require(['theme_boost/aria',\n 'theme_boost/scroll',\n 'theme_boost/pending',\n 'theme_boost/util',\n 'theme_boost/alert',\n 'theme_boost/button',\n 'theme_boost/carousel',\n 'theme_boost/collapse',\n 'theme_boost/dropdown',\n 'theme_boost/modal',\n 'theme_boost/scrollspy',\n 'theme_boost/tab',\n 'theme_boost/tooltip',\n 'theme_boost/popover'],\n function(Aria, MoodleScroll) {\n\n // We do twice because: https://github.com/twbs/bootstrap/issues/10547\n jQuery('body').popover({\n trigger: 'focus',\n selector: \"[data-toggle=popover][data-trigger!=hover]\",\n placement: 'auto'\n });\n\n // Popovers must close on Escape for accessibility reasons.\n customEvents.define(jQuery('body'), [\n customEvents.events.escape,\n ]);\n jQuery('body').on(customEvents.events.escape, '[data-toggle=popover]', function() {\n // Use \"blur\" instead of \"popover('hide')\" to prevent issue that the same tooltip can't be opened again.\n jQuery(this).trigger('blur');\n });\n\n jQuery(\"html\").popover({\n container: \"body\",\n selector: \"[data-toggle=popover][data-trigger=hover]\",\n trigger: \"hover\",\n delay: {\n hide: 500\n }\n });\n\n jQuery(\"html\").tooltip({\n selector: '[data-toggle=\"tooltip\"]'\n });\n\n // Disables flipping the dropdowns up and getting hidden behind the navbar.\n jQuery.fn.dropdown.Constructor.Default.flip = false;\n\n jQuery('a[data-toggle=\"tab\"]').on('shown.bs.tab', function(e) {\n var hash = jQuery(e.target).attr('href');\n if (history.replaceState) {\n history.replaceState(null, null, hash);\n } else {\n location.hash = hash;\n }\n });\n\n var hash = window.location.hash;\n if (hash) {\n jQuery('.nav-link[href=\"' + hash + '\"]').tab('show');\n }\n\n // We need to call popover automatically if nodes are added to the page later.\n Event.getLegacyEvents().done(function(events) {\n jQuery(document).on(events.FILTER_CONTENT_UPDATED, function() {\n jQuery('body').popover({\n selector: '[data-toggle=\"popover\"]',\n trigger: 'focus'\n });\n\n });\n });\n\n Aria.init();\n this.scroll = new MoodleScroll();\n this.scroll.init();\n M.util.js_complete('theme_boost/loader:children');\n });\n\n\n return {};\n});\n"],"file":"loader.min.js"}
\ No newline at end of file
diff --git a/theme/boost/amd/build/scroll.min.js b/theme/boost/amd/build/scroll.min.js
new file mode 100644
index 0000000000000..807807d9c469a
--- /dev/null
+++ b/theme/boost/amd/build/scroll.min.js
@@ -0,0 +1,2 @@
+define ("theme_boost/scroll",["exports"],function(a){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;function b(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function c(a,b){for(var c=0,d;c=window.innerHeight){a.classList.add("scrolled")}else{a.classList.remove("scrolled")}}}]);return a}();a.default=e;return a.default});
+//# sourceMappingURL=scroll.min.js.map
diff --git a/theme/boost/amd/build/scroll.min.js.map b/theme/boost/amd/build/scroll.min.js.map
new file mode 100644
index 0000000000000..5bafaafd94077
--- /dev/null
+++ b/theme/boost/amd/build/scroll.min.js.map
@@ -0,0 +1 @@
+{"version":3,"sources":["../src/scroll.js"],"names":["MoodleScroll","scrollY","window","addEventListener","scrollHandler","bind","pageYOffset","document","documentElement","scrollTop","body","querySelector","getScrollPosition","innerHeight","classList","add","remove"],"mappings":"wcA6BqBA,CAAAA,C,yEASV,CACH,KAAKC,OAAL,CAAe,CAAf,CACAC,MAAM,CAACC,gBAAP,CAAwB,QAAxB,CAAkC,KAAKC,aAAL,CAAmBC,IAAnB,CAAwB,IAAxB,CAAlC,EACA,MAAO,KACV,C,6DASmB,CAChB,MAAOH,CAAAA,MAAM,CAACI,WAAP,EAAsBC,QAAQ,CAACC,eAAT,CAAyBC,SACzD,C,qDAQe,IACNC,CAAAA,CAAI,CAAGH,QAAQ,CAACI,aAAT,CAAuB,MAAvB,CADD,CAENV,CAAO,CAAG,KAAKW,iBAAL,EAFJ,CAGZ,GAAIX,CAAO,EAAIC,MAAM,CAACW,WAAtB,CAAmC,CAC/BH,CAAI,CAACI,SAAL,CAAeC,GAAf,CAAmB,UAAnB,CACH,CAFD,IAEO,CACHL,CAAI,CAACI,SAAL,CAAeE,MAAf,CAAsB,UAAtB,CACH,CACJ,C","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Manage user scroll in Moodle for future floating elements.\n *\n * @copyright 2020 Ferran Recio \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\n/**\n * Moodle scroll handling. For now it just handle a \"scrolled\" class\n * on the body tag but in the near future could handle more floating\n * elements like option bars, docked elements or other active elements.\n *\n * @class MoodleScroll\n */\nexport default class MoodleScroll {\n\n /**\n * Initialise the scroll monitoring.\n *\n * @method init\n * @chainable\n * @return {Object} this.\n */\n init() {\n this.scrollY = 0;\n window.addEventListener(\"scroll\", this.scrollHandler.bind(this));\n return this;\n }\n\n /**\n * Add special classes to body depending on scroll position.\n *\n * @method update\n * @chainable\n * @return {Integer} current scroll position.\n */\n getScrollPosition() {\n return window.pageYOffset || document.documentElement.scrollTop;\n }\n\n /**\n * Add special classes to body depending on scroll position.\n *\n * @method update\n * @chainable\n */\n scrollHandler() {\n const body = document.querySelector('body');\n const scrollY = this.getScrollPosition();\n if (scrollY >= window.innerHeight) {\n body.classList.add('scrolled');\n } else {\n body.classList.remove('scrolled');\n }\n }\n}\n"],"file":"scroll.min.js"}
\ No newline at end of file
diff --git a/theme/boost/amd/src/loader.js b/theme/boost/amd/src/loader.js
index 03fef8e7658f9..65193d7fe3337 100644
--- a/theme/boost/amd/src/loader.js
+++ b/theme/boost/amd/src/loader.js
@@ -30,6 +30,7 @@ define(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], f
M.util.js_pending('theme_boost/loader:children');
require(['theme_boost/aria',
+ 'theme_boost/scroll',
'theme_boost/pending',
'theme_boost/util',
'theme_boost/alert',
@@ -42,7 +43,7 @@ define(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], f
'theme_boost/tab',
'theme_boost/tooltip',
'theme_boost/popover'],
- function(Aria) {
+ function(Aria, MoodleScroll) {
// We do twice because: https://github.com/twbs/bootstrap/issues/10547
jQuery('body').popover({
@@ -102,6 +103,8 @@ define(['jquery', './tether', 'core/event', 'core/custom_interaction_events'], f
});
Aria.init();
+ this.scroll = new MoodleScroll();
+ this.scroll.init();
M.util.js_complete('theme_boost/loader:children');
});
diff --git a/theme/boost/amd/src/scroll.js b/theme/boost/amd/src/scroll.js
new file mode 100644
index 0000000000000..a0ded31349f1b
--- /dev/null
+++ b/theme/boost/amd/src/scroll.js
@@ -0,0 +1,71 @@
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle. If not, see .
+
+/**
+ * Manage user scroll in Moodle for future floating elements.
+ *
+ * @copyright 2020 Ferran Recio
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Moodle scroll handling. For now it just handle a "scrolled" class
+ * on the body tag but in the near future could handle more floating
+ * elements like option bars, docked elements or other active elements.
+ *
+ * @class MoodleScroll
+ */
+export default class MoodleScroll {
+
+ /**
+ * Initialise the scroll monitoring.
+ *
+ * @method init
+ * @chainable
+ * @return {Object} this.
+ */
+ init() {
+ this.scrollY = 0;
+ window.addEventListener("scroll", this.scrollHandler.bind(this));
+ return this;
+ }
+
+ /**
+ * Add special classes to body depending on scroll position.
+ *
+ * @method update
+ * @chainable
+ * @return {Integer} current scroll position.
+ */
+ getScrollPosition() {
+ return window.pageYOffset || document.documentElement.scrollTop;
+ }
+
+ /**
+ * Add special classes to body depending on scroll position.
+ *
+ * @method update
+ * @chainable
+ */
+ scrollHandler() {
+ const body = document.querySelector('body');
+ const scrollY = this.getScrollPosition();
+ if (scrollY >= window.innerHeight) {
+ body.classList.add('scrolled');
+ } else {
+ body.classList.remove('scrolled');
+ }
+ }
+}
diff --git a/theme/boost/lang/en/theme_boost.php b/theme/boost/lang/en/theme_boost.php
index 1133f7bf9968f..97e698385bd91 100644
--- a/theme/boost/lang/en/theme_boost.php
+++ b/theme/boost/lang/en/theme_boost.php
@@ -52,3 +52,4 @@
$string['privacy:metadata:preference:draweropennav'] = 'The user\'s preference for hiding or showing the drawer menu navigation.';
$string['privacy:drawernavclosed'] = 'The current preference for the navigation drawer is closed.';
$string['privacy:drawernavopen'] = 'The current preference for the navigation drawer is open.';
+$string['totop'] = 'Go to top';
diff --git a/theme/boost/scss/moodle/core.scss b/theme/boost/scss/moodle/core.scss
index 626f558983f95..2c3ad22db47a3 100644
--- a/theme/boost/scss/moodle/core.scss
+++ b/theme/boost/scss/moodle/core.scss
@@ -26,6 +26,35 @@ $bg-inverse-link-color: #fff !default;
margin-top: 4px;
}
+$gototop-bottom-position: 50px !default;
+
+#goto-top-link {
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity .7s ease 0s, visibility .1s ease .8s;
+ display: block;
+ position: fixed; /* IE compatibility hack */
+ @supports (position: sticky) {
+ position: sticky;
+ }
+ @supports (-ms-ime-align:auto) {
+ position: fixed; /* Edge compatibility hack */
+ }
+ bottom: $gototop-bottom-position;
+ right: 0;
+ a {
+ position: absolute;
+ right: 0;
+ transform: translateY(-100%);
+ }
+}
+
+body.scrolled #goto-top-link {
+ opacity: 1;
+ visibility: visible;
+ transition: visibility 0s ease 0s, opacity .7s ease .1s;
+}
+
.context-header-settings-menu .dropdown-toggle > .icon,
#region-main-settings-menu .dropdown-toggle > .icon {
height: 24px;
diff --git a/theme/boost/scss/preset/default.scss b/theme/boost/scss/preset/default.scss
index c44d3ac71d6a6..60783844bb9ab 100644
--- a/theme/boost/scss/preset/default.scss
+++ b/theme/boost/scss/preset/default.scss
@@ -55,6 +55,9 @@ $breadcrumb-bg: transparent !default;
$breadcrumb-divider: "/" !default;
$breadcrumb-divider-rtl: "/" !default;
+// Floating elements positions
+$gototop-bottom-position: 50px !default;
+
// Alerts
$alert-border-width: 0 !default;
diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css
index 3f0c2df9ba2be..bc138fa075cb2 100644
--- a/theme/boost/style/moodle.css
+++ b/theme/boost/style/moodle.css
@@ -9413,6 +9413,32 @@ input[disabled] {
display: block;
margin-top: 4px; }
+#goto-top-link {
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity .7s ease 0s, visibility .1s ease .8s;
+ display: block;
+ position: fixed;
+ /* IE compatibility hack */
+ bottom: 50px;
+ right: 0; }
+ @supports (position: sticky) {
+ #goto-top-link {
+ position: sticky; } }
+ @supports (-ms-ime-align: auto) {
+ #goto-top-link {
+ position: fixed;
+ /* Edge compatibility hack */ } }
+ #goto-top-link a {
+ position: absolute;
+ right: 0;
+ transform: translateY(-100%); }
+
+body.scrolled #goto-top-link {
+ opacity: 1;
+ visibility: visible;
+ transition: visibility 0s ease 0s, opacity .7s ease .1s; }
+
.context-header-settings-menu .dropdown-toggle > .icon,
#region-main-settings-menu .dropdown-toggle > .icon {
height: 24px;
diff --git a/theme/boost/templates/footer.mustache b/theme/boost/templates/footer.mustache
index 9827bbd5c60b5..2655fd4635c8c 100644
--- a/theme/boost/templates/footer.mustache
+++ b/theme/boost/templates/footer.mustache
@@ -17,6 +17,12 @@
{{!
Page footer.
}}
+