aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/Footer
diff options
context:
space:
mode:
authorArmand Philippot <git@armandphilippot.com>2022-01-04 12:50:31 +0100
committerArmand Philippot <git@armandphilippot.com>2022-01-04 12:52:57 +0100
commit223e164ce8639ac2079d39bb04d7d03f9634ffed (patch)
treeca78c53b17beaf5cad9e7b727b20c3e03c2391f5 /src/components/Footer
parente145ef983e64ba1ab0ffacf0e3e9eae94db1d8e4 (diff)
chore: add a back to top link
Diffstat (limited to 'src/components/Footer')
-rw-r--r--src/components/Footer/Footer.module.scss73
-rw-r--r--src/components/Footer/Footer.tsx33
2 files changed, 106 insertions, 0 deletions
diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss
index b5d098d..debddfb 100644
--- a/src/components/Footer/Footer.module.scss
+++ b/src/components/Footer/Footer.module.scss
@@ -1,3 +1,5 @@
+@use "@styles/abstracts/functions" as fun;
+
.wrapper {
display: flex;
flex-flow: row wrap;
@@ -6,3 +8,74 @@
gap: var(--spacing-xs);
padding: var(--spacing-md) 0 calc(var(--toolbar-size) + var(--spacing-md));
}
+
+.back-to-top {
+ --button-size: #{fun.convert-px(55)};
+
+ position: fixed;
+ bottom: calc(var(--toolbar-size) + var(--spacing-md));
+ right: var(--spacing-md);
+ transition: all 0.4s ease-in 0s;
+
+ &--hidden {
+ opacity: 0;
+ transform: translateY(calc(var(--button-size) + var(--spacing-md)));
+ }
+
+ &--visible {
+ opacity: 1;
+ transform: translateY(0);
+ }
+
+ a {
+ display: flex;
+ place-content: center;
+ padding: 0;
+ width: var(--button-size);
+ height: var(--button-size);
+
+ svg {
+ width: 100%;
+ padding: var(--spacing-2xs);
+ }
+
+ :global {
+ .arrow-head {
+ transform: translateY(12px) translateX(-5px) scale(1.5);
+ transition: all 0.6s ease-in-out 0s;
+ }
+
+ .arrow-bar {
+ opacity: 0;
+ transform: translateY(12px) translateX(5px) scale(0.5);
+ transition: transform 0.6s ease-in-out 0s, opacity 0.7s ease-in-out 0s;
+ }
+ }
+
+ &:hover,
+ &:focus {
+ :global {
+ .arrow-head {
+ transform: translateY(0) translateX(0) scale(1);
+ }
+
+ .arrow-bar {
+ opacity: 1;
+ transform: translateY(0) translateX(0) scale(1);
+ }
+ }
+
+ svg {
+ :global {
+ animation: pulse 1.2s ease-in-out 0.6s infinite;
+ }
+ }
+ }
+
+ &:active {
+ svg {
+ animation-play-state: paused;
+ }
+ }
+ }
+}
diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx
index 12d86ce..15a4660 100644
--- a/src/components/Footer/Footer.tsx
+++ b/src/components/Footer/Footer.tsx
@@ -1,12 +1,45 @@
+import { ButtonLink } from '@components/Buttons';
import Copyright from '@components/Copyright/Copyright';
import FooterNav from '@components/FooterNav/FooterNav';
+import { ArrowIcon } from '@components/Icons';
+import { t } from '@lingui/macro';
+import { useEffect, useState } from 'react';
import styles from './Footer.module.scss';
const Footer = () => {
+ const [backToTopClasses, setBackToTopClasses] = useState(
+ `${styles['back-to-top']} ${styles['back-to-top--hidden']}`
+ );
+
+ const handleScroll = () => {
+ const currentScrollY = window.scrollY;
+
+ if (currentScrollY > 300) {
+ setBackToTopClasses(
+ `${styles['back-to-top']} ${styles['back-to-top--visible']}`
+ );
+ } else {
+ setBackToTopClasses(
+ `${styles['back-to-top']} ${styles['back-to-top--hidden']}`
+ );
+ }
+ };
+
+ useEffect(() => {
+ window.addEventListener('scroll', handleScroll);
+ return () => window.removeEventListener('scroll', handleScroll);
+ }, []);
+
return (
<footer className={styles.wrapper}>
<Copyright />
<FooterNav />
+ <div className={backToTopClasses}>
+ <ButtonLink target="#top" position="center">
+ <span className="screen-reader-text">{t`Back to top`}</span>
+ <ArrowIcon direction="top" />
+ </ButtonLink>
+ </div>
</footer>
);
};