Skip to content

new landing — navbar #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: new-landing--hero
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@
"next-query-params": "^5.0.1",
"next-sitemap": "^4.2.3",
"next-with-less": "^3.0.1",
"nextra": "^3",
"nextra-theme-docs": "^3",
"nextra": "3.3.1",
"nextra-theme-docs": "3.3.1",
"numbro": "2.5.0",
"p-limit": "^4.0.0",
"parser-front-matter": "1.6.4",
Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/components/index-page/hero.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import logoBlurred from "./hero/logo-blurred.png"

export function Hero() {
return (
<div className="relative bg-neu-0">
<div className="relative bg-neu-50 dark:bg-neu-0">
<div className="gql-conf-container flex flex-col-reverse lg:grid lg:grid-cols-2">
<div className="flex max-w-4xl flex-col justify-center gap-4 p-4 lg:min-h-[800px] xl:gap-8 xl:py-24 xl:pl-24 xl:pr-10">
<h1 className="typography-h1 max-w-3xl text-neu-900">
Expand Down Expand Up @@ -44,7 +44,7 @@ function HeroStripes() {
<div className="pointer-events-none relative overflow-hidden max-lg:h-[210px]">
<ImageLoaded
image={logoBlurred}
className="relative h-full bg-gradient-to-b from-pri-base to-pri-lighter opacity-0 transition-opacity duration-[1.5s] [mask-position:center_12%] [mask-size:110%] data-[loaded=true]:opacity-100 dark:to-pri-base lg:[mask-position:7%_7%] lg:[mask-size:200%]"
className="relative h-full bg-gradient-to-b from-pri-base to-pri-lighter opacity-0 transition-opacity duration-[1.5s] [mask-position:center_12%] [mask-size:110%] data-[loaded=true]:opacity-100 dark:to-pri-base sm:[mask-size:auto_300%] lg:[mask-position:7%_7%] lg:[mask-size:200%]"
style={{
maskImage: `url(${logoBlurred.src})`,
maskRepeat: "no-repeat",
Expand Down
277 changes: 277 additions & 0 deletions src/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
import { MenuItem, Menu, MenuButton, MenuItems } from "@headlessui/react"
import clsx from "clsx"
// eslint-disable-next-line no-restricted-imports -- since we don't need newWindow prop
import NextLink from "next/link"
import { Button } from "nextra/components"
import { useFSRoute } from "nextra/hooks"
import type * as normalizePages from "nextra/normalize-pages"
import { Fragment, useState, type ReactElement, type ReactNode } from "react"
import { useMenu, useThemeConfig } from "nextra-theme-docs"
import { Anchor } from "@/app/conf/_design-system/anchor"
import { renderComponent } from "@/components/utils"

import MenuIcon from "@/app/conf/_design-system/pixelarticons/menu.svg?svgr"
import CloseIcon from "@/app/conf/_design-system/pixelarticons/close.svg?svgr"

type Item = normalizePages.PageItem | normalizePages.MenuItem
export interface NavBarProps {
items: Item[]
}

const classes = {
link: "typography-menu flex items-center text-neu-900 px-3 py-1 nextra-focus [text-box:trim-both_cap_alphabetic] leading-none hover:underline underline-offset-2",
}

function NavbarMenu({
menu,
children,
onSubmenuOpen,
}: {
menu: normalizePages.MenuItem
children: ReactNode
onSubmenuOpen: (open: boolean) => void
}): ReactElement {
const routes = Object.fromEntries(
(menu.children || []).map(route => [route.name, route]),
)
return (
<Menu>
<MenuButton as={Fragment}>
{({ focus, open }) => {
// I'm sorry, I know this is so cursed.
// I need to migrate out of HeadlessUI to something with change handlers.
onSubmenuOpen(open)

return (
<button
onClick={() => onSubmenuOpen(open)}
className={clsx(
classes.link,
"flex items-center gap-1.5 whitespace-nowrap max-md:hidden",
focus && "nextra-focusable",
)}
>
{children}
</button>
)
}}
</MenuButton>
<MenuItems
transition
modal={false}
className={({ open }) =>
// eslint-disable-next-line tailwindcss/no-custom-classname
clsx(
"gql-navbar-menu-items",
"motion-reduce:transition-none",
"focus-visible:outline-none",
open ? "opacity-100" : "opacity-0",
"nextra-scrollbar overflow-visible transition-opacity",
"z-20 rounded-md py-1 text-sm",
// headlessui adds max-height as style, use !important to override
"!max-h-[min(calc(100vh-5rem),256px)]",
)
}
anchor={{ to: "top start", gap: 21, padding: 16, offset: -8 }}
>
{Object.entries(menu.items || {}).map(([key, item]) => (
<MenuItem key={key}>
<Anchor
href={item.href || routes[key]?.route}
className="block py-1.5 pl-2 pr-9"
target={item.newWindow ? "_blank" : undefined}
>
<span className="typography-menu px-3 py-1 underline-offset-2 [[data-active]>&]:underline">
{item.title}
</span>
</Anchor>
</MenuItem>
))}
</MenuItems>
</Menu>
)
}

export function Navbar({ items }: NavBarProps): ReactElement {
const themeConfig = useThemeConfig()

const activeRoute = useFSRoute()
const { menu, setMenu } = useMenu()
const [submenuOpen, setSubmenuOpen] = useState(false)

return (
<div
className={clsx(
"nextra-nav-container top-0 z-20 w-full bg-transparent print:hidden",
activeRoute === "/" ? "fixed" : "sticky",
)}
>
<BackdropBlur />
<nav className="mx-auto flex h-[var(--nextra-navbar-height)] max-w-[90rem] items-center justify-end pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)]">
{themeConfig.logoLink ? (
<NextLink
href={
typeof themeConfig.logoLink === "string"
? themeConfig.logoLink
: "/"
}
className="nextra-focus flex items-center hover:opacity-75"
>
{renderComponent(themeConfig.logo)}
</NextLink>
) : (
<div className="flex items-center">
{renderComponent(themeConfig.logo)}
</div>
)}
<div className="flex-1" />
<div className="-mx-2 flex overflow-x-auto px-2 py-1.5 lg:gap-2 xl:absolute xl:left-1/2 xl:-translate-x-1/2">
{items.map(pageOrMenu => {
if (pageOrMenu.display === "hidden") return null

if (pageOrMenu.type === "menu") {
const menu = pageOrMenu as normalizePages.MenuItem
return (
<NavbarMenu
key={menu.title}
menu={menu}
onSubmenuOpen={open => {
if (typeof window !== "undefined") {
if (open) {
document.body.style.overflow = "hidden"
} else {
document.body.style.overflow = "auto"
}
}
setSubmenuOpen(open)
}}
>
{menu.title}
</NavbarMenu>
)
}
const page = pageOrMenu as normalizePages.PageItem
let href = page.href || page.route || "#"

// If it's a directory
if (page.children) {
href =
(page.withIndexPage ? page.route : page.firstChildRoute) || href
}

const isActive =
page.route === activeRoute ||
activeRoute.startsWith(page.route + "/")

return (
<Anchor
href={href}
key={href}
className={clsx(
classes.link,
"whitespace-nowrap max-md:hidden",
isActive && !page.newWindow && "underline",
)}
target={page.newWindow ? "_blank" : undefined}
aria-current={!page.newWindow && isActive}
>
{page.title}
</Anchor>
)
})}
</div>

{process.env.NEXTRA_SEARCH &&
renderComponent(themeConfig.search.component, {
className: "max-md:_hidden",
})}

{themeConfig.project.link ? (
<Anchor href={themeConfig.project.link}>
{renderComponent(themeConfig.project.icon)}
</Anchor>
) : null}

{themeConfig.chat.link ? (
<Anchor href={themeConfig.chat.link}>
{renderComponent(themeConfig.chat.icon)}
</Anchor>
) : null}

{renderComponent(themeConfig.navbar.extraContent)}

<Button
aria-label="Menu"
className={({ active }) =>
clsx(
"nextra-hamburger p-2 text-pri-base md:hidden",
active && "bg-neu-400/20",
)
}
onClick={() => setMenu(!menu)}
>
{menu ? (
<CloseIcon className="size-5" />
) : (
<MenuIcon className="size-5" />
)}
</Button>
</nav>
<SubmenuBackdrop className={submenuOpen ? "opacity-100" : "opacity-0"} />
</div>
)
}

function BackdropBlur() {
const mask = "linear-gradient(to bottom, #000 0% 50%, transparent 50% 100%)"
const thickness = "1px"
return (
<>
<div
// note: we can't use the background trick to reduce flickering, because we have many section
// background colors and big images, so we'd have to change the --bg var with javascript
className="pointer-events-none absolute inset-0 -z-10 h-[200%] backdrop-blur-[12.8px]"
style={{
maskImage: mask,
WebkitMaskImage: mask,
background:
"linear-gradient(to bottom,rgb(var(--nextra-bg),.97) 0%, rgb(var(--nextra-bg),.5) 50% 100%)",
}}
/>
<div
className="pointer-events-none absolute inset-0 h-full translate-y-full bg-white/10"
style={{
backdropFilter: "blur(8px) brightness(120%) saturate(113%)",
maskImage: `linear-gradient(to bottom, black 0, black ${thickness}, transparent ${thickness})`,
}}
/>
</>
)
}

export function NavbarPlaceholder({
className,
...rest
}: React.HTMLAttributes<HTMLDivElement>) {
return (
<div
// placeholder: the colors here on `before` must match the ones on Hero `before` strip
className={clsx(
"absolute h-[calc(var(--nextra-navbar-height)+1px)] w-full before:absolute before:top-0 before:h-[calc(var(--nextra-navbar-height)+1px)] before:w-full",
className,
)}
{...rest}
/>
)
}

function SubmenuBackdrop({ className }: { className: string }) {
return (
<div
className={clsx(
"fixed inset-0 top-[calc(var(--nextra-navbar-height)+1px)] bg-[rgb(var(--nextra-bg),.4)] backdrop-blur-[6.4px] transition-opacity",
className,
)}
/>
)
}
8 changes: 8 additions & 0 deletions src/components/utils.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export function renderComponent<T>(
ComponentOrNode: React.FC<T> | React.ReactNode,
props?: T,
) {
if (!ComponentOrNode) return null
if (typeof ComponentOrNode !== "function") return ComponentOrNode
return <ComponentOrNode {...props!} />
}
11 changes: 5 additions & 6 deletions src/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,6 @@
@import "tailwindcss/utilities";
@import "tailwindcss/components";

/* #region nextra tweaks (preferably removed later, replaced with components) */
.nextra-nav-container > nav > div:nth-child(2) {
margin-right: auto;
}
/* #endregion nextra tweaks */

:root {
--foreground-rgb: 0, 0, 0;
--background-start-rgb: 214, 219, 220;
Expand Down Expand Up @@ -517,10 +511,15 @@ div[id^="headlessui-menu-items"] {
@apply px-4 py-8 lg:px-12 xl:gap-x-24 xl:px-24 3xl:px-[240px];
}

.gql-navbar-strip,
.gql-conf-navbar-strip {
@apply relative [contain:paint] before:sticky before:top-0 before:z-[9] before:-mt-[var(--navbar-h)] before:block before:h-[var(--navbar-h)] before:w-full before:content-[''];
}

.gql-navbar-strip {
--navbar-h: var(--nextra-navbar-height);
}

:root {
--navbar-h: 70px;
}
Expand Down
Loading