Skip to content

Commit 10e679b

Browse files
committed
wip
1 parent 9cc8b37 commit 10e679b

File tree

6 files changed

+217
-11
lines changed

6 files changed

+217
-11
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
"next-query-params": "^5.0.1",
4949
"next-sitemap": "^4.2.3",
5050
"next-with-less": "^3.0.1",
51-
"nextra": "^3",
52-
"nextra-theme-docs": "^3",
51+
"nextra": "3.3.1",
52+
"nextra-theme-docs": "3.3.1",
5353
"numbro": "2.5.0",
5454
"p-limit": "^4.0.0",
5555
"parser-front-matter": "1.6.4",

src/components/index-page/hero.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import logoBlurred from "./hero/logo-blurred.png"
77

88
export function Hero() {
99
return (
10-
<div className="relative bg-neu-0">
10+
<div className="relative bg-neu-50 dark:bg-neu-0">
1111
<div className="gql-conf-container flex flex-col-reverse lg:grid lg:grid-cols-2">
1212
<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">
1313
<h1 className="typography-h1 max-w-3xl text-neu-900">
@@ -44,7 +44,7 @@ function HeroStripes() {
4444
<div className="pointer-events-none relative overflow-hidden max-lg:h-[210px]">
4545
<ImageLoaded
4646
image={logoBlurred}
47-
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%]"
47+
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%]"
4848
style={{
4949
maskImage: `url(${logoBlurred.src})`,
5050
maskRepeat: "no-repeat",

src/components/navbar/navbar.tsx

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { MenuItem, Menu, MenuButton, MenuItems } from "@headlessui/react"
2+
import cn from "clsx"
3+
// eslint-disable-next-line no-restricted-imports -- since we don't need newWindow prop
4+
import NextLink from "next/link"
5+
import { Button } from "nextra/components"
6+
import { useFSRoute } from "nextra/hooks"
7+
import type * as normalizePages from "nextra/normalize-pages"
8+
import type { ReactElement, ReactNode } from "react"
9+
import { useMenu, useThemeConfig } from "nextra-theme-docs"
10+
import { Anchor } from "@/app/conf/_design-system/anchor"
11+
import { renderComponent } from "@/components/utils"
12+
13+
import MenuIcon from "@/app/conf/_design-system/pixelarticons/menu.svg?svgr"
14+
import CloseIcon from "@/app/conf/_design-system/pixelarticons/close.svg?svgr"
15+
16+
type Item = normalizePages.PageItem | normalizePages.MenuItem
17+
export interface NavBarProps {
18+
items: Item[]
19+
}
20+
21+
const classes = {
22+
link: cn(
23+
"_text-sm contrast-more:_text-gray-700 contrast-more:dark:_text-gray-100",
24+
),
25+
active: cn("_font-medium _subpixel-antialiased"),
26+
inactive: cn(
27+
"_text-gray-600 hover:_text-gray-800 dark:_text-gray-400 dark:hover:_text-gray-200",
28+
),
29+
}
30+
31+
function NavbarMenu({
32+
menu,
33+
children,
34+
}: {
35+
menu: normalizePages.MenuItem
36+
children: ReactNode
37+
}): ReactElement {
38+
const routes = Object.fromEntries(
39+
(menu.children || []).map(route => [route.name, route]),
40+
)
41+
return (
42+
<Menu>
43+
<MenuButton
44+
className={({ focus }) =>
45+
cn(
46+
classes.link,
47+
classes.inactive,
48+
"max-md:_hidden _items-center _whitespace-nowrap _flex _gap-1.5 _ring-inset",
49+
focus && "nextra-focusable",
50+
)
51+
}
52+
>
53+
{children}
54+
{"->"}
55+
</MenuButton>
56+
<MenuItems
57+
transition
58+
className={({ open }) =>
59+
cn(
60+
"motion-reduce:_transition-none",
61+
"nextra-focus",
62+
open ? "_opacity-100" : "_opacity-0",
63+
"nextra-scrollbar _transition-opacity",
64+
"_border _border-black/5 dark:_border-white/20",
65+
"_backdrop-blur-lg bg-[rgb(var(--nextra-bg),.8)]", // todo: full screen overlay
66+
"_z-20 _rounded-md _py-1 _text-sm",
67+
// headlessui adds max-height as style, use !important to override
68+
"!_max-h-[min(calc(100vh-5rem),256px)]",
69+
)
70+
}
71+
anchor={{ to: "top end", gap: 10, padding: 16 }}
72+
>
73+
{Object.entries(menu.items || {}).map(([key, item]) => (
74+
<MenuItem
75+
key={key}
76+
as={Anchor}
77+
href={item.href || routes[key]?.route}
78+
>
79+
<Anchor
80+
href={item.href || routes[key]?.route}
81+
className="data-focus:text-gray-900 block py-1.5 pl-3 pr-9 transition-colors rtl:pl-9 rtl:pr-3"
82+
target={item.newWindow ? "_blank" : undefined}
83+
>
84+
{item.title}
85+
</Anchor>
86+
</MenuItem>
87+
))}
88+
</MenuItems>
89+
</Menu>
90+
)
91+
}
92+
93+
export function Navbar({ items }: NavBarProps): ReactElement {
94+
const themeConfig = useThemeConfig()
95+
96+
const activeRoute = useFSRoute()
97+
const { menu, setMenu } = useMenu()
98+
99+
return (
100+
<div className="nextra-nav-container _top-0 _z-20 _w-full _bg-transparent print:_hidden fixed">
101+
<div className="nextra-nav-container-blur" />
102+
<nav className="mx-auto flex h-[var(--nextra-navbar-height)] max-w-[90rem] items-center justify-end gap-4 pl-[max(env(safe-area-inset-left),1.5rem)] pr-[max(env(safe-area-inset-right),1.5rem)]">
103+
{themeConfig.logoLink ? (
104+
<NextLink
105+
href={
106+
typeof themeConfig.logoLink === "string"
107+
? themeConfig.logoLink
108+
: "/"
109+
}
110+
className="nextra-focus flex items-center hover:opacity-75 ltr:mr-auto rtl:ml-auto"
111+
>
112+
{renderComponent(themeConfig.logo)}
113+
</NextLink>
114+
) : (
115+
<div className="flex items-center ltr:mr-auto rtl:ml-auto">
116+
{renderComponent(themeConfig.logo)}
117+
</div>
118+
)}
119+
<div className="nextra-scrollbar flex gap-4 overflow-x-auto py-1.5">
120+
{items.map(pageOrMenu => {
121+
if (pageOrMenu.display === "hidden") return null
122+
123+
if (pageOrMenu.type === "menu") {
124+
const menu = pageOrMenu as normalizePages.MenuItem
125+
return (
126+
<NavbarMenu key={menu.title} menu={menu}>
127+
{menu.title}
128+
</NavbarMenu>
129+
)
130+
}
131+
const page = pageOrMenu as normalizePages.PageItem
132+
let href = page.href || page.route || "#"
133+
134+
// If it's a directory
135+
if (page.children) {
136+
href =
137+
(page.withIndexPage ? page.route : page.firstChildRoute) || href
138+
}
139+
140+
const isActive =
141+
page.route === activeRoute ||
142+
activeRoute.startsWith(page.route + "/")
143+
144+
return (
145+
<Anchor
146+
href={href}
147+
key={href}
148+
className={cn(
149+
classes.link,
150+
"max-md:_hidden _whitespace-nowrap _ring-inset",
151+
!isActive || page.newWindow
152+
? classes.inactive
153+
: classes.active,
154+
)}
155+
target={page.newWindow ? "_blank" : undefined}
156+
aria-current={!page.newWindow && isActive}
157+
>
158+
{page.title}
159+
</Anchor>
160+
)
161+
})}
162+
</div>
163+
164+
{process.env.NEXTRA_SEARCH &&
165+
renderComponent(themeConfig.search.component, {
166+
className: "max-md:_hidden",
167+
})}
168+
169+
{themeConfig.project.link ? (
170+
<Anchor href={themeConfig.project.link}>
171+
{renderComponent(themeConfig.project.icon)}
172+
</Anchor>
173+
) : null}
174+
175+
{themeConfig.chat.link ? (
176+
<Anchor href={themeConfig.chat.link}>
177+
{renderComponent(themeConfig.chat.icon)}
178+
</Anchor>
179+
) : null}
180+
181+
{renderComponent(themeConfig.navbar.extraContent)}
182+
183+
<Button
184+
aria-label="Menu"
185+
className={({ active }) =>
186+
cn(
187+
"nextra-hamburger _rounded md:_hidden",
188+
active && "_bg-gray-400/20",
189+
)
190+
}
191+
onClick={() => setMenu(!menu)}
192+
>
193+
{menu ? <CloseIcon /> : <MenuIcon />}
194+
</Button>
195+
</nav>
196+
</div>
197+
)
198+
}

src/components/utils.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export function renderComponent<T>(
2+
ComponentOrNode: React.FC<T> | React.ReactNode,
3+
props?: T,
4+
) {
5+
if (!ComponentOrNode) return null
6+
if (typeof ComponentOrNode !== "function") return ComponentOrNode
7+
// @ts-expect-error TS2322: Type '{}' is not assignable to type 'T'
8+
return <ComponentOrNode {...props} />
9+
}

src/globals.css

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33
@import "tailwindcss/utilities";
44
@import "tailwindcss/components";
55

6-
/* #region nextra tweaks (preferably removed later, replaced with components) */
7-
.nextra-nav-container > nav > div:nth-child(2) {
8-
margin-right: auto;
9-
}
10-
/* #endregion nextra tweaks */
11-
126
:root {
137
--foreground-rgb: 0, 0, 0;
148
--background-start-rgb: 214, 219, 220;

theme.config.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { DocsThemeConfig, ThemeSwitch, useConfig } from "nextra-theme-docs"
22
import NextLink from "next/link"
3+
import { useRouter } from "next/router"
4+
5+
import { Navbar } from "@/components/navbar/navbar"
6+
37
import {
48
GraphQLWordmarkLogo,
59
StackOverflowIcon,
610
GitHubIcon,
711
DiscordIcon,
812
TwitterIcon,
913
} from "./src/icons"
10-
import { useRouter } from "next/router"
14+
1115
// import { createElement } from "react"
1216
// import NextImage from "next-image-export-optimizer"
1317

@@ -229,6 +233,7 @@ export default {
229233
content: Footer,
230234
},
231235
navbar: {
236+
component: Navbar,
232237
extraContent: <ThemeSwitch lite className="[&_span]:hidden" />,
233238
},
234239
toc: {

0 commit comments

Comments
 (0)