diff options
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/Footer/Footer.module.scss | 73 | ||||
| -rw-r--r-- | src/components/Footer/Footer.tsx | 33 | ||||
| -rw-r--r-- | src/components/Header/Header.tsx | 2 |
3 files changed, 107 insertions, 1 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> ); }; diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 9f2c454..0b773e9 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -4,7 +4,7 @@ import styles from './Header.module.scss'; const Header = ({ isHome }: { isHome: boolean }) => { return ( - <header className={styles.wrapper}> + <header id="top" className={styles.wrapper}> <div className={styles.body}> <Branding isHome={isHome} /> <Toolbar /> |
