From 18f2e3e9543c4f71d9ced3aad8deb85cee8a24b1 Mon Sep 17 00:00:00 2001 From: Ida Marie Andreassen <43541032+idamand@users.noreply.github.com> Date: Thu, 5 Dec 2024 09:30:13 +0100 Subject: [PATCH] V3 update header (#944) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add new header design with transistions * New header design works on desktop * Add mobile and tablet version of header * Working mobile and tablet version ✨ * Add aria-label to menu button * Fix: Remove breadcrumbs from PageHeader * Remove todos * Fix colors --- public/_assets/dot.svg | 5 + public/_assets/green-dot.svg | 5 + public/_assets/menu-close.svg | 8 +- public/_assets/menu.svg | 9 +- src/app/(main)/[locale]/[...path]/page.tsx | 9 +- src/app/(main)/[locale]/page.tsx | 2 - .../languageSwitcher/LanguageSwitcher.tsx | 15 ++- .../languageSwitcher.module.css | 40 ++++++- src/components/link/CustomLink.tsx | 98 +++++++++------- src/components/link/link.module.css | 50 +++++--- src/components/navigation/header/Header.tsx | 111 +++++++++--------- .../navigation/header/HeaderPreview.tsx | 6 - .../navigation/header/PageHeader.tsx | 8 -- .../navigation/header/header.module.css | 102 +++++++++++----- src/styles/global.css | 1 + src/utils/hooks/useScrollDirection.ts | 27 +++++ 16 files changed, 316 insertions(+), 180 deletions(-) create mode 100644 public/_assets/dot.svg create mode 100644 public/_assets/green-dot.svg create mode 100644 src/utils/hooks/useScrollDirection.ts diff --git a/public/_assets/dot.svg b/public/_assets/dot.svg new file mode 100644 index 000000000..7db8e7676 --- /dev/null +++ b/public/_assets/dot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/_assets/green-dot.svg b/public/_assets/green-dot.svg new file mode 100644 index 000000000..aff7b014b --- /dev/null +++ b/public/_assets/green-dot.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/_assets/menu-close.svg b/public/_assets/menu-close.svg index a96e8cd5c..55927f7f4 100644 --- a/public/_assets/menu-close.svg +++ b/public/_assets/menu-close.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/public/_assets/menu.svg b/public/_assets/menu.svg index f495871d4..6e9f2b1e0 100644 --- a/public/_assets/menu.svg +++ b/public/_assets/menu.svg @@ -1,3 +1,6 @@ - - - \ No newline at end of file + + + + + + diff --git a/src/app/(main)/[locale]/[...path]/page.tsx b/src/app/(main)/[locale]/[...path]/page.tsx index b3be90347..8caf1a233 100644 --- a/src/app/(main)/[locale]/[...path]/page.tsx +++ b/src/app/(main)/[locale]/[...path]/page.tsx @@ -86,16 +86,11 @@ async function Page({ params }: Props) { return Page404; } - const { queryResponse, docType, pathTitles, pathTranslations } = pageData; + const { queryResponse, docType, pathTranslations } = pageData; return ( <> - +
{(() => { switch (docType) { diff --git a/src/app/(main)/[locale]/page.tsx b/src/app/(main)/[locale]/page.tsx index 82b7a25d6..ddf7a4f64 100644 --- a/src/app/(main)/[locale]/page.tsx +++ b/src/app/(main)/[locale]/page.tsx @@ -74,8 +74,6 @@ const Home = async ({ params }: Props) => {
{initialLandingPage.data.sections.map((section, index) => ( diff --git a/src/components/languageSwitcher/LanguageSwitcher.tsx b/src/components/languageSwitcher/LanguageSwitcher.tsx index e61c0a096..587671b3d 100644 --- a/src/components/languageSwitcher/LanguageSwitcher.tsx +++ b/src/components/languageSwitcher/LanguageSwitcher.tsx @@ -21,23 +21,22 @@ export default function LanguageSwitcher({ if (pathTranslation._key === undefined) { return null; } - const linkText = ( - - {pathTranslation._key.toUpperCase()} - - ); return (
  • {pathTranslation._key !== currentLanguage ? ( - {linkText} + + {pathTranslation._key.toUpperCase()} + ) : ( - linkText + + {pathTranslation._key.toUpperCase()} + )}
  • {index < pathTranslations.length - 1 && ( diff --git a/src/components/languageSwitcher/languageSwitcher.module.css b/src/components/languageSwitcher/languageSwitcher.module.css index bb36ccc36..090a16be8 100644 --- a/src/components/languageSwitcher/languageSwitcher.module.css +++ b/src/components/languageSwitcher/languageSwitcher.module.css @@ -1,17 +1,47 @@ .list { display: flex; - gap: 1rem; + gap: 0.25rem; list-style: none; padding: 0; - margin: 0; + margin: auto 0; } .divider { - border-left: 1px solid var(--text-primary-light); - /* Todo: fix correct color */ + border-left: 1px solid var(--text-primary); flex-grow: 1; } .link { - color: var(--text-primary-light) !important; + color: var(--text-primary) !important; + letter-spacing: 0.1rem; + line-height: 120%; + font-weight: 300; + + &:hover { + text-decoration: underline; + } +} + +.selectedLink { + color: var(--text-primary) !important; + letter-spacing: 0.1rem; + line-height: 120%; + font-weight: 600; + + &:hover { + text-decoration: underline; + } + + &:active { + font-weight: 600; + text-decoration: none; + } +} + +.notSelected { + text-decoration: none; + + &:hover { + text-decoration: underline; + } } diff --git a/src/components/link/CustomLink.tsx b/src/components/link/CustomLink.tsx index 135757ec0..d30fb9dfb 100644 --- a/src/components/link/CustomLink.tsx +++ b/src/components/link/CustomLink.tsx @@ -27,47 +27,65 @@ const CustomLink = ({ const newTab = link.newTab; const target = newTab ? "_blank" : undefined; const rel = newTab ? "noopener noreferrer" : undefined; - const className = - type === "headerLink" - ? `${styles.headerLink} ${isSelected ? styles.selected : ""}` - : styles.footerLink; - return ( - link.linkTitle && - (type === "link" ? ( -
    - - {link.linkTitle} - -
    - ) : ( - - {link.linkTitle} - - )) - ); + switch (type) { + case "link": + return ( + link.linkTitle && ( +
    + + {link.linkTitle} + +
    + ) + ); + case "headerLink": + return ( + link.linkTitle && ( + + + {link.linkTitle} + + ) + ); + case "footerLink": + return ( + link.linkTitle && ( + + + {link.linkTitle} + + ) + ); + } }; export default CustomLink; diff --git a/src/components/link/link.module.css b/src/components/link/link.module.css index 112a51589..43eff49b1 100644 --- a/src/components/link/link.module.css +++ b/src/components/link/link.module.css @@ -140,26 +140,38 @@ } .headerLink { - color: var(--background-bg-light-primary); + color: var(--text-primary); cursor: pointer; text-decoration: none; font-family: var(--font-britti-sans); font-size: 1.125rem; font-weight: 500; + display: inline-flex; + padding: 0.75rem 1rem 0.75rem 0.75rem; + justify-content: center; + align-items: center; + gap: 0.375rem; + position: relative; +} - &:hover { - font-size: 1.125rem; - font-style: normal; - font-weight: 600; - line-height: normal; - } +.dot { + position: absolute; + top: 50%; + left: -8px; + transform: translateY(-50%); + width: 0.375rem; + height: 0.375rem; + background-image: url("/_assets/dot.svg"); + background-size: 0.375rem 0.375rem; + opacity: 0; + transition: + left 0.2s ease-in-out, + opacity 0.2s ease-in-out; +} - &:active { - font-size: 1.125rem; - font-style: normal; - font-weight: 700; - line-height: normal; - } +.headerLink:hover .dot { + left: 0; + opacity: 1; } .footerLink { @@ -180,7 +192,13 @@ } .selected { - color: var(--text-primary-light); - font-weight: 700; - line-height: normal; + font-weight: 600; +} + +.selected .dot { + left: 0; + opacity: 1; + transition: none; + background-image: url("/_assets/green-dot.svg"); + background-size: 0.375rem 0.375rem; } diff --git a/src/components/navigation/header/Header.tsx b/src/components/navigation/header/Header.tsx index 1247dfc51..64e6cf6b5 100644 --- a/src/components/navigation/header/Header.tsx +++ b/src/components/navigation/header/Header.tsx @@ -11,8 +11,8 @@ import Button from "src/components/buttons/Button"; import LanguageSwitcher from "src/components/languageSwitcher/LanguageSwitcher"; import CustomLink from "src/components/link/CustomLink"; import LinkButton from "src/components/linkButton/LinkButton"; -import { BreadCrumbMenu } from "src/components/navigation/breadCrumbMenu/BreadCrumbMenu"; import Text from "src/components/text/Text"; +import useScrollDirection from "src/utils/hooks/useScrollDirection"; import { getHref } from "src/utils/link"; import { Announcement } from "studio/lib/interfaces/announcement"; import { BrandAssets } from "studio/lib/interfaces/brandAssets"; @@ -28,9 +28,7 @@ export interface IHeader { assets: BrandAssets; announcement: Announcement | null; currentLanguage: string; - pathTitles: string[]; pathTranslations: InternationalizedString; - showBreadcrumbs: boolean; } const filterLinks = (data: ILink[], type: string) => @@ -40,14 +38,14 @@ export const Header = ({ navigation, announcement, currentLanguage, - pathTitles, pathTranslations, - showBreadcrumbs, }: IHeader) => { const pathname = usePathname(); const [isOpen, setIsOpen] = useState(false); const sidebarData = navigation.sidebar || navigation.main; + const scrollDirection = useScrollDirection(); + const links = filterLinks(navigation.main, linkID); const ctas = filterLinks(navigation.main, callToActionFieldID); @@ -92,51 +90,63 @@ export const Header = ({ className={`${styles.focusOn} ${isOpen && styles.isOpen}`} >
    +
    {showAnnouncement && (
    @@ -156,13 +166,6 @@ export const Header = ({ )}
    - {showBreadcrumbs && ( - - )} ); }; diff --git a/src/components/navigation/header/HeaderPreview.tsx b/src/components/navigation/header/HeaderPreview.tsx index 3fe13023d..bd2ce2201 100644 --- a/src/components/navigation/header/HeaderPreview.tsx +++ b/src/components/navigation/header/HeaderPreview.tsx @@ -18,17 +18,13 @@ export default function HeaderPreview({ initialBrandAssets, initialAnnouncement, currentLanguage, - pathTitles, pathTranslations, - showBreadcrumbs, }: { initialNav: QueryResponseInitial; initialBrandAssets: QueryResponseInitial; initialAnnouncement: QueryResponseInitial; currentLanguage: string; - pathTitles: string[]; pathTranslations: InternationalizedString; - showBreadcrumbs: boolean; }) { const { data: newNav } = useQuery( NAV_QUERY, @@ -54,9 +50,7 @@ export default function HeaderPreview({ assets={newBrandAssets} announcement={newAnnouncement} currentLanguage={currentLanguage} - pathTitles={pathTitles} pathTranslations={pathTranslations} - showBreadcrumbs={showBreadcrumbs} /> ) ); diff --git a/src/components/navigation/header/PageHeader.tsx b/src/components/navigation/header/PageHeader.tsx index e3008095e..e4d8eb76f 100644 --- a/src/components/navigation/header/PageHeader.tsx +++ b/src/components/navigation/header/PageHeader.tsx @@ -16,16 +16,12 @@ import HeaderPreview from "./HeaderPreview"; interface PageHeaderProps { language: string; - pathTitles: string[]; pathTranslations: InternationalizedString; - showBreadcrumbs: boolean; } export default async function PageHeader({ language, - pathTitles, pathTranslations, - showBreadcrumbs, }: PageHeaderProps) { const { perspective, isDraftMode } = getDraftModeInfo(); @@ -56,9 +52,7 @@ export default async function PageHeader({ initialBrandAssets={initialBrandAssets} initialAnnouncement={initialAnnouncement} currentLanguage={language} - pathTitles={pathTitles} pathTranslations={pathTranslations} - showBreadcrumbs={showBreadcrumbs} /> ) : (
    )) ); diff --git a/src/components/navigation/header/header.module.css b/src/components/navigation/header/header.module.css index 948c2f6f4..18fe8bfe9 100644 --- a/src/components/navigation/header/header.module.css +++ b/src/components/navigation/header/header.module.css @@ -1,52 +1,81 @@ .focusOn { - position: sticky; top: 0; overflow: hidden; z-index: 9999; - background-color: var(--background-bg-dark); } .isOpen { - height: 100vh; + height: 100%; display: flex; flex-direction: column; @media (max-width: 1024px) { - background: var(--background-bg-dark); + background: var(--background-bg-light-primary); & > header { - flex: 1; display: flex; flex-direction: column; - padding: 1rem 2.5rem; & > nav { flex: 1; display: flex; flex-direction: column; - justify-content: flex-end; } } } } +.spacer { + height: 8rem; + width: 100%; +} + .wrapper { display: flex; - justify-content: space-between; + flex-direction: column; + position: fixed; + top: 0rem; + margin: 1.5rem 0; + justify-content: center; align-items: center; - padding: 16px 24px; + min-height: 5rem; + padding: 0rem 3rem 0rem 3rem; + border-radius: 0.75rem; + background: var(--background-header-light-transparent); + box-shadow: 0px 0.5px 2px 0px #eaeaea; + backdrop-filter: blur(40px); + max-width: 960px; + width: calc(100vw - 2rem); + justify-self: center; + transition-property: top; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 500ms; + left: 50%; + transform: translate(-50%, 0); + z-index: 9998; + + @media (max-width: 375px) { + width: 100vw; + } } -.logo { - overflow: hidden; - width: auto; +.wrapper.hidden { + top: -6.5rem; +} + +.desktopWrapper { display: flex; - max-width: 100%; - height: 1.25rem; + justify-content: space-between; + align-self: stretch; + align-items: center; +} + +.wrapper:has(.mobileMenu) .desktopWrapper { + padding-top: 1.25rem; } .list { - width: fit-content; + width: 100%; list-style-type: none; margin: 0; padding: 0; @@ -77,13 +106,18 @@ } .desktopLinks { - composes: listDesktop; - gap: 1.5rem; + list-style-type: none; + width: 100%; + display: flex; + flex-direction: row; + gap: 0.25rem; + justify-items: center; justify-content: center; - align-self: stretch; + margin: 0; + padding: 0; - @media (min-width: 1200px) { - gap: 3rem; + @media (max-width: 834px) { + display: none; } } @@ -104,21 +138,35 @@ } } +.divider { + border: 0; + height: 1px; + background: var(--background-bg-light-secondary); + margin: 1rem 0; + width: 100%; +} + .mobileMenu { - flex: 1; display: flex; flex-direction: column; align-items: flex-start; - justify-content: flex-end; - gap: 5rem; - padding-top: 7.5rem; - padding-bottom: 7.5rem; + justify-content: flex-start; + width: 100%; + min-height: 5rem; + padding: 1.5rem 0; @media (min-width: 1024px) { display: none; } } +.mobileButtons { + display: flex; + flex-direction: row-reverse; + justify-content: space-between; + align-self: stretch; +} + /* MOBILE BUTTON */ .button { cursor: pointer; @@ -168,8 +216,8 @@ } .logo { - width: 130px; + width: 9rem; height: 37px; - background-color: var(--background-bg-light-primary); + background-color: var(--text-primary); -webkit-mask: url("/_assets/variant-logo.svg") no-repeat 50% 50%; } diff --git a/src/styles/global.css b/src/styles/global.css index 7aa42779c..e49b663c1 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -97,6 +97,7 @@ html { --background-bg-blue: var(--Blue-500); --background-bg-yellow: var(--Yellow-500); --background-disabled: var(--Light-700); + --background-header-light-transparent: #fafafabf; --stroke-primary: var(--Dark-700); --stroke-secondary: var(--Dark-400); diff --git a/src/utils/hooks/useScrollDirection.ts b/src/utils/hooks/useScrollDirection.ts new file mode 100644 index 000000000..b955d57f6 --- /dev/null +++ b/src/utils/hooks/useScrollDirection.ts @@ -0,0 +1,27 @@ +import { useEffect, useState } from "react"; + +export default function useScrollDirection() { + const [scrollDirection, setScrollDirection] = useState(null); + + useEffect(() => { + let lastScrollY = window.scrollY; + + const updateScrollDirection = () => { + const scrollY = window.scrollY; + const direction = scrollY > lastScrollY ? "down" : "up"; + if ( + direction !== scrollDirection && + (scrollY - lastScrollY > 10 || scrollY - lastScrollY < -10) + ) { + setScrollDirection(direction); + } + lastScrollY = scrollY > 0 ? scrollY : 0; + }; + window.addEventListener("scroll", updateScrollDirection); + return () => { + window.removeEventListener("scroll", updateScrollDirection); + }; + }, [scrollDirection]); + + return scrollDirection; +}