diff options
| author | Armand Philippot <git@armandphilippot.com> | 2022-01-04 12:50:31 +0100 | 
|---|---|---|
| committer | Armand Philippot <git@armandphilippot.com> | 2022-01-04 12:52:57 +0100 | 
| commit | 223e164ce8639ac2079d39bb04d7d03f9634ffed (patch) | |
| tree | ca78c53b17beaf5cad9e7b727b20c3e03c2391f5 /src | |
| parent | e145ef983e64ba1ab0ffacf0e3e9eae94db1d8e4 (diff) | |
chore: add a back to top link
Diffstat (limited to 'src')
| -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 | ||||
| -rw-r--r-- | src/styles/base/_animations.scss | 13 | ||||
| -rw-r--r-- | src/styles/globals.scss | 1 | 
5 files changed, 121 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 /> diff --git a/src/styles/base/_animations.scss b/src/styles/base/_animations.scss new file mode 100644 index 0000000..903645f --- /dev/null +++ b/src/styles/base/_animations.scss @@ -0,0 +1,13 @@ +@keyframes pulse { +  from { +    transform: scale(1); +  } + +  50% { +    transform: scale(0.8); +  } + +  to { +    transform: scale(1); +  } +} diff --git a/src/styles/globals.scss b/src/styles/globals.scss index 86e3ea8..0a7a618 100644 --- a/src/styles/globals.scss +++ b/src/styles/globals.scss @@ -14,6 +14,7 @@   * Define some standard styles and CSS variables (colors, fonts...).   */  @use "base/base"; +@use "base/animations";  @use "base/colors";  @use "base/fonts";  @use "base/helpers"; | 
