diff --git a/landing/header.css b/landing/header.css index edd84b8cf..4073f6b97 100644 --- a/landing/header.css +++ b/landing/header.css @@ -1,153 +1,155 @@ nav.headerContainer { display: flex; justify-content: center; } div.headerNavContentContainer { display: flex; align-items: center; justify-content: space-between; padding: 18px 32px; width: 1850px; z-index: var(--header-z-index); background-color: transparent; transition: 650ms; transition-property: background-color; } div.headerContainerMobileNavActive { background-color: var(--comparison-cards); transition: 250ms; } .logo { display: flex; align-items: center; min-width: 180px; } .logoText { color: var(--white-100); margin-right: 8px; } .betaBadge { padding: 4px 16px; background-color: #ebebeb26; border-radius: 16px; color: var(--white-100); } .tab { color: var(--black-60); transition: 150ms; transition-property: color; } .tab:hover { color: var(--white-100); } .activeTab { color: var(--white-100); } .pageNav { display: flex; column-gap: 72px; } .socialIconsContainer { display: flex; justify-self: flex-end; align-items: center; } .icon { color: var(--white-100); } div.socialIconsContainer a { -webkit-tap-highlight-color: transparent; } div.twitterIcon, div.githubIcon, div.webappIcon { display: flex; justify-content: center; align-items: center; font-size: 24px; margin-inline: 4px; border-radius: 50px; width: 44px; height: 44px; transition: 300ms; } div.menuIcon { display: none; } div.twitterIcon svg, div.githubIcon svg, div.webappIcon svg { transition: 300ms; } @media screen and (hover: hover) { div.githubIcon:hover svg { color: #151013; transition: 300ms; } div.githubIcon:hover { background-color: #ffffff; transition: 300ms; } div.webappIcon:hover { background-color: var(--btn-bg); transition: 300ms; } div.twitterIcon:hover { background-color: #1d9bf0; transition: 300ms; } } +/* max-width should be kept in sync with HEADER_BREAKPOINT in header.react */ @media screen and (max-width: 848px) { .pageNav { display: none; } div.twitterIcon, div.githubIcon, div.webappIcon { display: none; } div.menuIcon { display: flex; justify-content: center; align-items: center; font-size: 24px; margin-inline: 4px; border-radius: 50px; width: 44px; height: 44px; transition: 300ms; } div.menuIcon svg { transition: 300ms; } } +/* max-width should be kept in sync with HEADER_BREAKPOINT in header.react */ @media screen and (max-width: 848px) and (hover: hover) { div.menuIcon:hover { cursor: pointer; background-color: var(--btn-bg); transition: 300ms; } } diff --git a/landing/header.react.js b/landing/header.react.js index 252e9ef68..6cbb08f27 100644 --- a/landing/header.react.js +++ b/landing/header.react.js @@ -1,118 +1,122 @@ // @flow import { faTwitter, faGithub } from '@fortawesome/free-brands-svg-icons'; import { faExternalLinkAlt, faBars, faTimes, } from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import classNames from 'classnames'; import * as React from 'react'; import { NavLink } from 'react-router-dom'; import type { SetState } from 'lib/types/hook-types.js'; import css from './header.css'; import typography from './typography.css'; +// This value comes from the breakpoint value in header.css. Please make sure +// that this value is in sync with header.css if ever changed +export const HEADER_BREAKPOINT = 848; // px + type Props = { +showMobileNav: boolean, +setShowMobileNav: SetState, }; function Header(props: Props): React.Node { const { showMobileNav, setShowMobileNav } = props; const headerContentContainerClassName = classNames({ [css.headerNavContentContainer]: true, [css.headerContainerMobileNavActive]: showMobileNav, }); const logoTextClassName = classNames([typography.heading2, css.logoText]); const badgeClassName = classNames([typography.paragraph2, css.betaBadge]); const navLinkClassName = classNames([typography.subheading2, css.tab]); const onClickLogo = React.useCallback(() => { setShowMobileNav(false); }, [setShowMobileNav]); const onClickMobileNavIcon = React.useCallback(() => { setShowMobileNav(!showMobileNav); }, [setShowMobileNav, showMobileNav]); return ( ); } export default Header; diff --git a/landing/landing.react.js b/landing/landing.react.js index 450634a9e..36847df91 100644 --- a/landing/landing.react.js +++ b/landing/landing.react.js @@ -1,121 +1,138 @@ // @flow import classNames from 'classnames'; import * as React from 'react'; import { useRouteMatch } from 'react-router-dom'; import { ModalProvider, useModalContext, } from 'lib/components/modal-provider.react.js'; import AppLanding from './app-landing.react.js'; import Footer from './footer.react.js'; -import Header from './header.react.js'; +import Header, { HEADER_BREAKPOINT } from './header.react.js'; import Investors from './investors.react.js'; import Keyservers from './keyservers.react.js'; import css from './landing.css'; import MobileNav from './mobile-nav.react.js'; import Privacy from './privacy.react.js'; import QR from './qr.react.js'; import SIWE from './siwe.react.js'; import Support from './support.react.js'; import Team from './team.react.js'; import Terms from './terms.react.js'; import useScrollToTopOnNavigate from './use-scroll-to-top-on-navigate.react.js'; import './reset.css'; import './global.css'; function Landing(): React.Node { const onSIWE = useRouteMatch({ path: '/siwe' }); if (onSIWE) { return ; } return ( ); } function LandingSite(): React.Node { const modalContext = useModalContext(); const modals = React.useMemo( () => modalContext.modals.map(([modal, key]) => ( {modal} )), [modalContext.modals], ); const [showMobileNav, setShowMobileNav] = React.useState(false); + const handleResize = React.useCallback(() => { + if (window.innerWidth > HEADER_BREAKPOINT) { + setShowMobileNav(false); + } + }, [setShowMobileNav]); + + React.useEffect(() => { + if (!showMobileNav) { + return undefined; + } + + window.addEventListener('resize', handleResize); + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [handleResize, showMobileNav]); + const innerContainerClassName = classNames({ [css.innerContainer]: true, [css.innerContainerMobileNav]: showMobileNav, }); useScrollToTopOnNavigate(); const onPrivacy = useRouteMatch({ path: '/privacy' }); const onTerms = useRouteMatch({ path: '/terms' }); const onSupport = useRouteMatch({ path: '/support' }); const onKeyservers = useRouteMatch({ path: '/keyservers' }); const onQR = useRouteMatch({ path: '/qr' }); const onTeam = useRouteMatch({ path: '/team' }); const onInvestors = useRouteMatch({ path: '/investors' }); const activePage = React.useMemo(() => { if (onPrivacy) { return ; } else if (onTerms) { return ; } else if (onSupport) { return ; } else if (onKeyservers) { return ; } else if (onQR) { return ; } else if (onTeam) { return ; } else if (onInvestors) { return ; } else { return ; } }, [onKeyservers, onPrivacy, onSupport, onTerms, onTeam, onInvestors, onQR]); let header; if (!onQR) { header = (
); } let footer; if (!onQR) { footer =