Skip to content

Commit 695f06e

Browse files
authored
Learn tweaks (#2253)
## Description Following the updates in Figma, things I postponed (the card) or didn't have a chance to get to yet (restyling arrow nav links where we can't use the card). I also unified the paddings a little bit so sidebar aligns with logo better. https://github.com/user-attachments/assets/325cd754-e878-420b-8d3f-1f69c10700c2
1 parent 3499ab2 commit 695f06e

File tree

20 files changed

+339
-82
lines changed

20 files changed

+339
-82
lines changed

src/app/colors.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
:root,
22
.light {
3+
--color-pri-lightest: 319 100% 96%;
34
--color-pri-lighter: 319 100% 90%;
45
--color-pri-light: 318 100% 80%;
56
--color-pri-base: 319 100% 44.1%;

src/components/cards.tsx

Lines changed: 70 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,88 @@
1-
import { ReactElement } from "react"
2-
import { Card } from "./card"
31
import { clsx } from "clsx"
4-
import NextLink from "next/link"
2+
import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr"
3+
import { learnPages } from "./learn-aggregator/learn-pages"
54

65
export function Cards({
76
items,
7+
numbered,
88
}: {
99
items: {
10-
icon: ReactElement
10+
icon?:
11+
| (({ className }: { className?: string }) => React.ReactNode)
12+
| React.ReactNode
1113
title: string
1214
description?: string
1315
link: string
1416
}[]
17+
numbered?: string
1518
}) {
1619
return (
17-
<div className="mt-6 grid grid-cols-2 gap-4">
18-
{items.map(({ icon: Icon, title, link, description }) => {
19-
const isExternal = link.startsWith("https://")
20+
<ul className="grid grid-cols-1 justify-stretch gap-2 pt-6 sm:grid-cols-2 lg:gap-4">
21+
{items.map((item, index) => {
22+
// Try to get section from learn-pages
23+
let section: "getting-started" | "best-practices" | undefined
24+
25+
const path = item.link.replace(/^\/learn\//, "").replace(/\/$/, "")
26+
const learnPage = learnPages[path as keyof typeof learnPages]
27+
if (learnPage) {
28+
section = learnPage.section
29+
}
30+
2031
return (
21-
<Card
22-
key={title}
23-
as={isExternal ? "a" : NextLink}
24-
// @ts-expect-error
25-
href={link}
26-
className={clsx(
27-
"flex flex-col items-center",
28-
isExternal &&
29-
"relative after:absolute after:right-4 after:top-4 after:font-sans after:content-['_↗']",
30-
)}
31-
>
32-
{/* @ts-expect-error */}
33-
{typeof Icon === "function" ? <Icon className="h-12" /> : Icon}
34-
<b className="mb-2 mt-4 text-center text-lg">{title}</b>
35-
<span
36-
className={`text-xs md:text-sm text-center${description ? "" : "break-all"}`}
32+
<li key={item.title} className="flex text-neu-900">
33+
<a
34+
href={item.link}
35+
className={clsx(
36+
"gql-focus-visible grid w-full border border-neu-200 bg-neu-0 transition-colors [grid-template-areas:'header''desc'] [grid-template-rows:auto_1fr] hover:ring hover:ring-neu-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 dark:border-neu-100 dark:hover:ring-neu-50 lg:[grid-template-areas:'header_header''desc_arrow'] lg:[grid-template-columns:1fr_64px]",
37+
section === "getting-started" &&
38+
"bg-pri-lighter/10 dark:bg-pri-lighter/5",
39+
section === "best-practices" &&
40+
"bg-sec-lighter/10 dark:bg-sec-lighter/5",
41+
)}
3742
>
38-
{description ? description : link.replace(/^https?:\/\//, "")}
39-
</span>
40-
</Card>
43+
<span className="flex flex-col gap-1 [grid-area:header]">
44+
{numbered && (
45+
<span className="typography-body-sm px-2 pt-2 text-neu-700 max-lg:typography-body-md lg:px-4 lg:pt-4">
46+
{numbered} {index + 1}
47+
</span>
48+
)}
49+
<span
50+
className={clsx(
51+
"typography-h3 flex items-center gap-2 border-neu-200 text-neu-900 dark:border-neu-100",
52+
item.icon ? "border-b" : "pl-2 lg:pl-4",
53+
)}
54+
>
55+
{item.icon && (
56+
<span className="flex items-center justify-center border-r border-neu-200 p-2 lg:p-4">
57+
{typeof item.icon === "function" ? (
58+
<item.icon className="size-8 shrink-0" />
59+
) : (
60+
item.icon
61+
)}
62+
</span>
63+
)}
64+
{item.title}
65+
</span>
66+
</span>
67+
68+
<p className="typography-body-sm text-pretty p-4 text-neu-900 [grid-area:desc] max-lg:typography-body-md max-lg:border-t max-lg:border-neu-200 dark:max-lg:border-neu-100">
69+
{item.description
70+
? item.description
71+
: item.link.replace(/^https?:\/\//, "")}
72+
</p>
73+
74+
<span
75+
className={clsx(
76+
"hidden items-center justify-center place-self-end border-l border-neu-200 p-4 [grid-area:arrow] dark:border-neu-100 lg:flex",
77+
item.icon ? "h-full" : "border-t",
78+
)}
79+
>
80+
<ArrowDownIcon className="size-8 shrink-0 -rotate-90" />
81+
</span>
82+
</a>
83+
</li>
4184
)
4285
})}
43-
</div>
86+
</ul>
4487
)
4588
}

src/components/footer/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ export function Footer() {
115115
<footer className="relative isolate !bg-neu-100 text-neu-900 dark:!bg-neu-0 max-md:px-0 max-md:pt-0">
116116
<Stripes />
117117

118-
<div className="mx-auto max-w-[120rem] border-neu-400 dark:border-neu-100 3xl:border-x 3xl:dark:border-t">
118+
<div className="mx-auto max-w-[120rem] border-neu-400 dark:border-neu-100 xl:border-t 3xl:border-x">
119119
<div className="flex flex-wrap justify-between gap-4 p-4 max-md:w-full md:p-6 2xl:px-10">
120120
<NextLink href="/" className="nextra-logo flex items-center">
121121
<GraphQLWordmarkLogo className="h-6" title="GraphQL" />
Lines changed: 16 additions & 0 deletions
Loading
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration"
2+
3+
import blurBean from "./learn-blur-bean.webp"
4+
5+
export function LearnHeroStripes() {
6+
return (
7+
<div
8+
role="presentation"
9+
// eslint-disable-next-line tailwindcss/no-contradicting-classname
10+
className="pointer-events-none absolute inset-0 bg-neu-50 [--end-1:#FFF] [--end-2:rgb(255_204_239/.2)] [--start-1:#FFEAF8] [--start-2:hsl(var(--color-sec-lighter))] dark:[--end-1:hsl(75,15%,5%)] dark:[--end-2:hsl(319,100%,10%)] dark:[--start-1:hsl(319,100%,14%)] dark:[--start-2:hsl(319,100%,30%)] sm:h-[360px] lg:h-[calc(100%-163px)]"
11+
style={{
12+
maskImage: `url(${blurBean.src})`,
13+
WebkitMaskImage: `url(${blurBean.src})`,
14+
maskSize: "2200px",
15+
WebkitMaskSize: "2200px",
16+
maskPosition: "50% 60%",
17+
WebkitMaskPosition: "50% 60%",
18+
maskRepeat: "no-repeat",
19+
WebkitMaskRepeat: "no-repeat",
20+
}}
21+
>
22+
<StripesDecoration
23+
evenClassName="bg-[linear-gradient(180deg,var(--start-1)_-80%,var(--end-1)_102%)]"
24+
oddClassName="bg-[linear-gradient(180deg,var(--start-2)_-80%,var(--end-2)_102%)]"
25+
/>
26+
</div>
27+
)
28+
}

src/components/learn-aggregator/learn-pages.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ const _items: Record<
6969
section: "getting-started",
7070
},
7171
// ---
72-
"best-practices": null,
72+
"best-practices": {
73+
description:
74+
"Understand the context behind the GraphQL Best Practices lessons.",
75+
icon: new URL("./assets/keyboard.svg", import.meta.url).href,
76+
section: "best-practices",
77+
},
7378
"thinking-in-graphs": {
7479
description:
7580
"Learn how to shift your mindset from RESTful endpoints to graph-based thinking, aligning your schema with business logic and legacy systems.",

src/components/learn-aggregator/index.tsx renamed to src/components/learn-aggregator/teaser-section.tsx

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import { ReactNode, useState } from "react"
22
import { clsx } from "clsx"
33

4-
import { StripesDecoration } from "@/app/conf/_design-system/stripes-decoration"
54
import ArrowDownIcon from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr"
65

76
import { Eyebrow } from "@/_design-system/eyebrow"
87

9-
import blurBean from "./learn-blur-bean.webp"
108
import { Button } from "@/app/conf/_design-system/button"
119

1210
export interface TeaserSectionProps
@@ -121,7 +119,7 @@ function TeaserSectionListItem({
121119
<li className={clsx("flex text-neu-900", className)} {...rest}>
122120
<a
123121
href={href}
124-
className="gql-focus-visible grid border border-neu-200 bg-neu-0 transition-colors [grid-template-areas:'icon_header''desc_desc'] [grid-template-columns:72px_1fr] [grid-template-rows:auto_1fr] hover:ring hover:ring-neu-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 dark:border-neu-100 dark:hover:ring-neu-50 lg:[grid-template-areas:'icon_header_header''icon_desc_arrow'] lg:[grid-template-columns:190px_1fr_64px]"
122+
className="gql-focus-visible grid w-full border border-neu-200 bg-neu-0 transition-colors [grid-template-areas:'icon_header''desc_desc'] [grid-template-columns:72px_1fr] [grid-template-rows:auto_1fr] hover:ring hover:ring-neu-100 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 dark:border-neu-100 dark:hover:ring-neu-50 lg:[grid-template-areas:'icon_header_header''icon_desc_arrow'] lg:[grid-template-columns:190px_1fr_64px]"
125123
>
126124
<span
127125
className={clsx(
@@ -156,28 +154,3 @@ function TeaserSectionListItem({
156154
</li>
157155
)
158156
}
159-
160-
export function LearnHeroStripes() {
161-
return (
162-
<div
163-
role="presentation"
164-
// eslint-disable-next-line tailwindcss/no-contradicting-classname
165-
className="pointer-events-none absolute inset-0 h-[300px] bg-neu-50 [--end-1:#FFF] [--end-2:rgb(255_204_239/.2)] [--start-1:#FFEAF8] [--start-2:hsl(var(--color-sec-lighter))] dark:[--end-1:hsl(var(--color-neu-0))] dark:[--end-2:hsl(var(--color-pri-base)/.1)] dark:[--start-1:hsl(var(--color-neu-100)/.2)] dark:[--start-2:hsl(var(--color-sec-light)/.1)] sm:h-[360px] lg:h-[480px]"
166-
style={{
167-
maskImage: `url(${blurBean.src})`,
168-
WebkitMaskImage: `url(${blurBean.src})`,
169-
maskSize: "2200px",
170-
WebkitMaskSize: "2200px",
171-
maskPosition: "50% 60%",
172-
WebkitMaskPosition: "50% 60%",
173-
maskRepeat: "no-repeat",
174-
WebkitMaskRepeat: "no-repeat",
175-
}}
176-
>
177-
<StripesDecoration
178-
evenClassName="bg-[linear-gradient(180deg,var(--start-1)_-80%,var(--end-1)_102%)]"
179-
oddClassName="bg-[linear-gradient(180deg,var(--start-2)_-80%,var(--end-2)_102%)]"
180-
/>
181-
</div>
182-
)
183-
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// eslint-disable-next-line no-restricted-imports -- since we don't need newWindow prop
2+
import NextLink from "next/link"
3+
import type { Item } from "nextra/normalize-pages"
4+
import type { ReactElement } from "react"
5+
import { useThemeConfig } from "nextra-theme-docs"
6+
import type { DocsThemeConfig } from "nextra-theme-docs"
7+
8+
import ArrowDown from "@/app/conf/_design-system/pixelarticons/arrow-down.svg?svgr"
9+
10+
interface NavLinkProps {
11+
currentIndex: number
12+
flatDocsDirectories: Item[]
13+
}
14+
15+
export function ArrowNavLinks({
16+
flatDocsDirectories,
17+
currentIndex,
18+
}: NavLinkProps): ReactElement | null {
19+
const themeConfig = useThemeConfig()
20+
const nav = themeConfig.navigation
21+
const navigation: Exclude<DocsThemeConfig["navigation"], boolean> =
22+
typeof nav === "boolean" ? { prev: nav, next: nav } : nav
23+
let prev = navigation.prev && flatDocsDirectories[currentIndex - 1]
24+
let next = navigation.next && flatDocsDirectories[currentIndex + 1]
25+
26+
if (prev && !prev.isUnderCurrentDocsTree) prev = false
27+
if (next && !next.isUnderCurrentDocsTree) next = false
28+
29+
if (!prev && !next) return null
30+
31+
return (
32+
<div className="mb-8 flex items-center gap-4 border-t border-neu-200 pt-8 print:hidden">
33+
{prev && (
34+
<NextLink
35+
href={prev.route}
36+
title={prev.title}
37+
className="gql-focus-visible typography-link flex max-w-[50%] items-center gap-2 border border-neu-200 pr-2 text-left text-base no-underline hover:bg-neu-50 hover:ring hover:ring-neu-100 dark:border-neu-100 dark:hover:bg-neu-50/50 dark:hover:ring-neu-50"
38+
>
39+
<span className="border-r p-2">
40+
<ArrowDown className="size-8 shrink-0 rotate-90" />
41+
</span>
42+
<span className="[word-break:break-word]">{prev.title}</span>
43+
</NextLink>
44+
)}
45+
{next && (
46+
<NextLink
47+
href={next.route}
48+
title={next.title}
49+
className="gql-focus-visible typography-link ml-auto flex max-w-[50%] items-center gap-2 border border-neu-200 pl-2 text-left text-base no-underline hover:bg-neu-50 hover:ring hover:ring-neu-100 dark:border-neu-100 dark:hover:bg-neu-50/50 dark:hover:ring-neu-50"
50+
>
51+
<span className="[word-break:break-word]">{next.title}</span>
52+
<span className="border-l border-neu-200 p-2 dark:border-neu-100">
53+
<ArrowDown className="size-8 shrink-0 -rotate-90" />
54+
</span>
55+
</NextLink>
56+
)}
57+
</div>
58+
)
59+
}
3.17 KB
Loading

src/components/nav-links/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { LearnNavLinkCard } from "./learn-nav-link-card"
2+
export { ArrowNavLinks } from "./arrow-nav-links"

0 commit comments

Comments
 (0)