Skip to content

Commit f0b1480

Browse files
authored
feat: landing page skills section
2 parents dbf22ac + 5a886d4 commit f0b1480

32 files changed

+4508
-689
lines changed

app/(articles)/ArticlePreview.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import { BiLinkExternal, BiSolidLock } from "react-icons/bi";
77
import { IoEyeOffOutline } from "react-icons/io5";
88

99
export type Article = {
10-
image: string;
1110
title: string;
1211
description: string;
1312
metadata: {
@@ -47,7 +46,7 @@ export default function ArticlePreview({
4746

4847
return (
4948
<div
50-
className={`article-preview ${hidden && "article-hidden"} transition-transform duration-300 hover:scale-105 hover:cursor-not-allowed`}
49+
className={`article-preview ${hidden && "article-hidden"} transition-transform duration-300 hover:scale-105 ${hidden ? "hover:cursor-not-allowed" : "hover:cursor-pointer"}`}
5150
onClick={(e) => {
5251
if (!hidden) {
5352
return;
@@ -65,11 +64,10 @@ export default function ArticlePreview({
6564
<MaybeLink href={metadata.href ?? ""}>
6665
<div
6766
style={{
68-
// backgroundImage: `url(${image})`,
6967
backgroundColor: "oklch(from var(--element) l c h / 0.2)",
7068
opacity: locking ? 1 : undefined,
7169
}}
72-
className={`article-preview-card relative flex aspect-[16/10] h-auto w-full flex-col items-end justify-between rounded-xl px-6 py-4 transition select-none ${hidden && "opacity-85 grayscale-32"}`}
70+
className={`article-preview-card relative flex aspect-16/10 h-auto w-full flex-col items-end justify-between rounded-xl px-6 py-4 transition select-none ${hidden && "opacity-85 grayscale-32"}`}
7371
title={hidden ? undefined : title}
7472
>
7573
<canvas
@@ -81,10 +79,10 @@ export default function ArticlePreview({
8179
className={`article-preview-card-lock absolute top-1/2 left-1/2 -translate-1/2 text-surface ${locking ? "opacity-100" : "opacity-0"} h-12 w-12 transition-opacity duration-300`}
8280
/>
8381
)}
84-
<span className={`text-xs ${hidden && "opacity-40"}`}>
82+
<span className={`font-theme-sans text-xs ${hidden && "opacity-40"}`}>
8583
[ {metadata.date} ]
8684
</span>
87-
<span className={`text-xs ${hidden && "opacity-40"}`}>
85+
<span className={`font-theme-sans text-xs ${hidden && "opacity-40"}`}>
8886
by [ {metadata.author} ]
8987
</span>
9088
</div>
@@ -93,7 +91,7 @@ export default function ArticlePreview({
9391
style={{ WebkitTextStrokeWidth: "0.01em" }}
9492
>
9593
<span
96-
className={`article-preview-title mx-3 ${classShaking}`}
94+
className={`article-preview-title mx-3 font-theme-sans font-medium ${classShaking}`}
9795
onAnimationEnd={() => hidden && !canHover && setShaking(false)}
9896
>
9997
{title}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
@reference "../../../globals.css";
2+
3+
.prose-theme {
4+
--tw-prose-body: var(--theme-raisin-700);
5+
--tw-prose-headings: var(--theme-raisin-900);
6+
--tw-prose-lead: var(--theme-raisin-600);
7+
--tw-prose-links: var(--theme-raisin-900);
8+
--tw-prose-bold: var(--theme-raisin-900);
9+
--tw-prose-counters: var(--theme-stone-600);
10+
--tw-prose-bullets: var(--theme-stone-500);
11+
--tw-prose-hr: var(--theme-stone-500);
12+
--tw-prose-quotes: var(--theme-raisin-500);
13+
--tw-prose-quote-borders: var(--theme-stone-400);
14+
--tw-prose-captions: var(--theme-raisin-500);
15+
--tw-prose-kbd: var(--theme-raisin-900);
16+
--tw-prose-kbd-shadows: rgb(from var(--theme-raisin-900) R G B);
17+
--tw-prose-code: var(--theme-raisin-900);
18+
--tw-prose-pre-code: var(--theme-raisin-700);
19+
--tw-prose-pre-bg: var(--theme-stone-300);
20+
--tw-prose-th-borders: var(--theme-raisin-300);
21+
--tw-prose-td-borders: var(--theme-raisin-200);
22+
23+
@variant dark {
24+
--tw-prose-body: var(--theme-stone-200);
25+
--tw-prose-headings: var(--theme-stone-100);
26+
--tw-prose-lead: var(--theme-stone-400);
27+
--tw-prose-links: var(--theme-stone-100);
28+
--tw-prose-bold: var(--theme-stone-100);
29+
--tw-prose-counters: var(--theme-raisin-400);
30+
--tw-prose-bullets: var(--theme-raisin-500);
31+
--tw-prose-hr: var(--theme-raisin-600);
32+
--tw-prose-quotes: var(--theme-stone-500);
33+
--tw-prose-quote-borders: var(--theme-raisin-700);
34+
--tw-prose-captions: var(--theme-stone-400);
35+
--tw-prose-kbd: var(--theme-stone-100);
36+
--tw-prose-kbd-shadows: rgb(from var(--theme-stone-100) R G B);
37+
--tw-prose-code: var(--theme-stone-100);
38+
--tw-prose-pre-code: var(--theme-stone-200);
39+
--tw-prose-pre-bg: var(--theme-raisin-800);
40+
--tw-prose-th-borders: var(--theme-stone-600);
41+
--tw-prose-td-borders: var(--theme-stone-700);
42+
}
43+
}

app/(articles)/ctf/[slug]/code.css

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
.hljs-subst {
2+
/* var(--highlight-color) */
3+
color: #ffffff;
4+
}
5+
6+
.hljs-comment {
7+
/* var(--highlight-comment) */
8+
color: #999999;
9+
}
10+
11+
.hljs-keyword,
12+
.hljs-selector-tag,
13+
.hljs-meta .hljs-keyword,
14+
.hljs-doctag,
15+
.hljs-section {
16+
/* var(--highlight-keyword) */
17+
color: #88aece;
18+
}
19+
20+
.hljs-attr {
21+
/* var(--highlight-attribute); */
22+
color: #88aece;
23+
}
24+
25+
.hljs-attribute {
26+
/* var(--highlight-symbol) */
27+
color: #c59bc1;
28+
}
29+
30+
.hljs-name,
31+
.hljs-type,
32+
.hljs-number,
33+
.hljs-selector-id,
34+
.hljs-quote,
35+
.hljs-template-tag {
36+
/* var(--highlight-namespace) */
37+
color: #ef5900;
38+
}
39+
40+
.hljs-selector-class {
41+
/* var(--highlight-keyword) */
42+
color: #88aece;
43+
}
44+
45+
.hljs-string,
46+
.hljs-regexp,
47+
.hljs-symbol,
48+
.hljs-variable,
49+
.hljs-template-variable,
50+
.hljs-link,
51+
.hljs-selector-attr {
52+
/* var(--highlight-variable) */
53+
color: #b5bd68;
54+
}
55+
56+
.hljs-meta,
57+
.hljs-selector-pseudo {
58+
/* var(--highlight-keyword) */
59+
color: #88aece;
60+
}
61+
62+
.hljs-built_in,
63+
.hljs-title,
64+
.hljs-literal {
65+
/* var(--highlight-literal) */
66+
color: #ef5900;
67+
}
68+
69+
.hljs-bullet,
70+
.hljs-code {
71+
/* var(--highlight-punctuation) */
72+
color: #cccccc;
73+
}
74+
75+
.hljs-meta .hljs-string {
76+
/* var(--highlight-variable) */
77+
color: #b5bd68;
78+
}
79+
80+
.hljs-deletion {
81+
/* var(--highlight-deletion) */
82+
color: #de7176;
83+
}
84+
85+
.hljs-addition {
86+
/* var(--highlight-addition) */
87+
color: #76c490;
88+
}
89+
90+
.hljs-emphasis {
91+
font-style: italic;
92+
}
93+
94+
.hljs-strong {
95+
font-weight: bold;
96+
}
97+
98+
.hljs-formula,
99+
.hljs-operator,
100+
.hljs-params,
101+
.hljs-property,
102+
.hljs-punctuation,
103+
.hljs-tag {
104+
/* purposely ignored */
105+
}

app/(articles)/ctf/[slug]/page.tsx

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import fs from "fs/promises";
2+
import path from "path";
3+
4+
import "@wooorm/starry-night/style/both";
5+
import "./article.css";
6+
7+
export async function generateStaticParams() {
8+
const dirs = await fs.readdir(path.join(process.cwd(), "articles/ctf"), {
9+
withFileTypes: true,
10+
});
11+
12+
return dirs
13+
.filter((dir) => dir.isDirectory())
14+
.map((dir) => ({
15+
slug: dir.name,
16+
}));
17+
}
18+
19+
type Params = {
20+
slug: string;
21+
};
22+
23+
export default async function ArticlePage({
24+
params,
25+
}: {
26+
params: Promise<Params>;
27+
}) {
28+
const { slug } = await params;
29+
const { default: Article } = await import(
30+
`@/articles/ctf/${slug}/README.mdx`
31+
);
32+
33+
const quote =
34+
"prose-blockquote:mx-2 prose-blockquote:px-6 prose-blockquote:not-italic prose-blockquote:bg-stone-300 prose-blockquote:dark:bg-raisin-800 prose-blockquote:font-normal prose-p:in-[blockquote]:first:pt-3 prose-p:in-[blockquote]:first:before:content-none prose-p:in-[blockquote]:last:after:content-none prose-p:in-[blockquote]:last:pb-3 prose-blockquote:rounded-tr-xs prose-blockquote:rounded-br-xs";
35+
const code =
36+
"prose-code:before:content-none prose-code:after:content-none prose-code:font-theme-mono prose-code:font-medium";
37+
const codeInline =
38+
"prose-code:not-in-[pre]:wrap-anywhere prose-code:not-in-[pre]:bg-stone-350 prose-code:not-in-[pre]:dark:bg-raisin-700 prose-code:not-in-[pre]:px-[0.4em] prose-code:not-in-[pre]:py-[0.2em] prose-code:not-in-[pre]:rounded-lg";
39+
const pre = "prose-pre:rounded-lg";
40+
const ol = "prose-ol:pl-10 prose-ol:leading-6";
41+
const ul = "prose-ul:pl-10 prose-ul:leading-6";
42+
const a =
43+
"prose-a:font-normal prose-a:text-theme prose-a:underline prose-a:wrap-anywhere";
44+
const strong = "prose-strong:font-medium";
45+
const img =
46+
"prose-img:rounded-lg prose-img:mx-auto prose-img:max-h-[80vh] prose-img:w-auto prose-img:mx-auto";
47+
const hr = "prose-hr:w-9/10 prose-hr:mx-auto";
48+
49+
return (
50+
<main
51+
className={[
52+
"prose-theme mx-auto prose prose-sm mt-4 w-full max-w-none px-2 text-base text-pretty md:prose-base md:w-4/5 md:px-0 md:text-base lg:max-w-[100ch]",
53+
quote,
54+
code,
55+
codeInline,
56+
pre,
57+
img,
58+
hr,
59+
ol,
60+
ul,
61+
a,
62+
strong,
63+
].join(" ")}
64+
>
65+
<Article />
66+
</main>
67+
);
68+
}

app/(articles)/articles.css renamed to app/(articles)/ctf/article-listing.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
@reference "../globals.css";
1+
@reference "../../globals.css";
22

33
.article-preview-title {
44
@apply text-element;

app/(articles)/ctf/layout.tsx

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,62 @@
1+
import Footer from "@/app/components/Footer";
2+
import Images from "@/app/components/Images";
3+
import Nav from "@/app/components/Nav";
4+
import ScrollDown from "@/app/components/ScrollDown";
15
import type { Metadata } from "next";
2-
import "../articles.css";
6+
import Link from "next/link";
7+
import React from "react";
38

49
export const metadata: Metadata = {
510
title: "Neplox | CTF Writeups",
611
};
712

813
export default function Layout({ children }: { children: React.ReactNode }) {
9-
return children;
14+
return (
15+
<React.Fragment>
16+
<header className="header-grid default-header sticky top-0 z-10 flex-none pt-4 before:absolute before:inset-0 before:-z-10 before:-mx-[4vw] before:-mb-4 before:bg-surface before:shadow-[0_7px_6px_-6px_rgba(0,0,0,0.25)] md:relative md:top-0 md:pt-8 md:before:hidden">
17+
{/* Branding sm */}
18+
<Link href="/" className="justify-self-start md:hidden">
19+
{/* Same width as "go to bottom" button */}
20+
<Images.Logo className="h-auto w-12 scale-125" />
21+
</Link>
22+
{/* Branding md+ */}
23+
<Link href="/" className="hidden flex-row gap-x-8 md:flex">
24+
<Images.Logo className="h-[min(10vh,6vw)] w-auto scale-125" />
25+
<h1 className="font-horizon leading-none text-theme md:text-[min(10vh,6vw)]">
26+
NEPLOX
27+
</h1>
28+
</Link>
29+
30+
<div className="sm:hidden">
31+
<nav className="mx-auto flex h-full max-w-lg justify-center gap-x-4">
32+
<Nav.Element
33+
key="ctf"
34+
path="ctf"
35+
blocked={false}
36+
className="default-nav"
37+
/>
38+
</nav>
39+
</div>
40+
{/* Nav sm+ */}
41+
<div className="hidden sm:block">
42+
<nav className="mx-auto flex h-full max-w-lg justify-between gap-x-4">
43+
{Nav.paths.map(({ path, blocked }) => (
44+
<Nav.Element
45+
key={path}
46+
path={path}
47+
blocked={blocked}
48+
className="default-nav"
49+
/>
50+
))}
51+
</nav>
52+
</div>
53+
54+
<ScrollDown className="w-12 justify-self-end md:hidden" />
55+
</header>
56+
57+
{children}
58+
59+
<Footer className="flex-none" />
60+
</React.Fragment>
61+
);
1062
}

0 commit comments

Comments
 (0)