diff --git a/tildes/scss/_layout.scss b/tildes/scss/_layout.scss index 6c30906..c96950b 100644 --- a/tildes/scss/_layout.scss +++ b/tildes/scss/_layout.scss @@ -51,6 +51,19 @@ body { } } +#back-to-top-buffer { + // This is an invisible "buffer" for the "Back to top" button. It uses the + // Intersection Observer API to detect when the entire buffer has been + // scrolled off the screen, and will make the button visible at that point. + // So to have the button show up when the user has scrolled down at least two + // viewport heights, we set its height at 200vh. + height: 200vh; + position: absolute; + top: 0; + right: 0; + width: 1px; +} + #site-header { grid-area: header; diff --git a/tildes/scss/modules/_btn.scss b/tildes/scss/modules/_btn.scss index 29939ff..c62504f 100644 --- a/tildes/scss/modules/_btn.scss +++ b/tildes/scss/modules/_btn.scss @@ -68,3 +68,22 @@ border: 1px solid; } } + +.btn-back-to-top { + position: fixed; + bottom: 0.4rem; + right: 0.4rem; + + font-size: 0.5rem; + + opacity: 0; + visibility: hidden; + transition: all 0.5s ease; + + &.btn-back-to-top-visible { + @media (max-width: $size-md) { + opacity: 1; + visibility: visible; + } + } +} diff --git a/tildes/scss/themes/_theme_base.scss b/tildes/scss/themes/_theme_base.scss index 4be070e..fc3d77c 100644 --- a/tildes/scss/themes/_theme_base.scss +++ b/tildes/scss/themes/_theme_base.scss @@ -111,6 +111,14 @@ } } + .btn-back-to-top { + background-color: map-get($theme, "background-primary"); + + &:visited { + color: map-get($theme, "link"); + } + } + .btn-comment-collapse { color: map-get($theme, "foreground-secondary"); border-color: map-get($theme, "border"); diff --git a/tildes/static/js/behaviors/back-to-top-buffer.js b/tildes/static/js/behaviors/back-to-top-buffer.js new file mode 100644 index 0000000..747a517 --- /dev/null +++ b/tildes/static/js/behaviors/back-to-top-buffer.js @@ -0,0 +1,20 @@ +// Copyright (c) 2019 Tildes contributors +// SPDX-License-Identifier: AGPL-3.0-or-later + +$.onmount('[data-js-back-to-top-buffer]', function() { + // Uses the Intersection Observer API to observe when the div with ID + // back-to-top-buffer has been scrolled entirely out of the viewport, + // and adds the btn-back-to-top-visible class to the "Back to top" button + // at that point. If the buffer is scrolled back into the viewport, it + // removes the class, hiding the button again. + var callback = function(entries, observer) { + if (entries[0].isIntersecting) { + $(".btn-back-to-top").removeClass("btn-back-to-top-visible"); + } else { + $(".btn-back-to-top").addClass("btn-back-to-top-visible"); + } + }; + + this.intersectionObserver = new IntersectionObserver(callback); + this.intersectionObserver.observe(document.querySelector('#back-to-top-buffer')); +}); diff --git a/tildes/tildes/templates/base.jinja2 b/tildes/tildes/templates/base.jinja2 index 9c19f8b..ce7c6bd 100644 --- a/tildes/tildes/templates/base.jinja2 +++ b/tildes/tildes/templates/base.jinja2 @@ -126,6 +126,9 @@ +
+Back to top + {% assets "javascript-third-party" -%} {% endassets %}