Skip to content
Merged
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
50 changes: 50 additions & 0 deletions next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@ import remarkCustomContainer from "@echoja/remark-custom-container";
import bundleAnalyzer from "@next/bundle-analyzer";
import mdx from "@next/mdx";
import { remarkCodeHike } from "codehike/mdx";
import rehypeAutolinkHeadings from "rehype-autolink-headings";
import rehypeSlug from "rehype-slug";
import remarkGfm from "remark-gfm";
import remarkToc from "remark-toc";

/** @type {import('codehike/mdx').CodeHikeConfig} */
const chConfig = {
Expand Down Expand Up @@ -106,10 +109,57 @@ const customContainerOptions = {
const withMDX = mdx({
options: {
jsx: true,

remarkPlugins: [
[remarkCustomContainer, customContainerOptions],
remarkGfm,
[remarkCodeHike, chConfig],
[remarkToc, { heading: "목차" }],
],
rehypePlugins: [
rehypeSlug,
[
rehypeAutolinkHeadings,
{
properties: {
ariaHidden: true,
tabIndex: -1,
className: "heading-anchor",
},
content: {
type: "element",
tagName: "svg",
properties: {
className: "icon-link",
xmlns: "http://www.w3.org/2000/svg",
width: "24",
height: "24",
viewBox: "0 0 24 24",
fill: "none",
stroke: "currentColor",
"stroke-width": "2",
"stroke-linecap": "round",
"stroke-linejoin": "round",
},
children: [
{
type: "element",
tagName: "path",
properties: {
d: "M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71",
},
},
{
type: "element",
tagName: "path",
properties: {
d: "M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71",
},
},
],
},
},
],
],
},
});
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@
"react": "19.0.0",
"react-dom": "19.0.0",
"react-wrap-balancer": "^1.1.1",
"rehype-autolink-headings": "^7.1.0",
"rehype-slug": "^6.0.0",
"remark-gfm": "4.0.0",
"remark-toc": "^9.0.0",
"sharp": "^0.33.5",
"tailwind-merge": "^2.6.0",
"vite": "^6.0.6"
Expand Down
84 changes: 84 additions & 0 deletions pnpm-lock.yaml

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

4 changes: 4 additions & 0 deletions src/app/article/2024-12/functional/page.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export const metadata = getArticleMetadata(item);

<ArticleHeader {...getArticleHeaderProps(item)} />

## 목차

## 추상화하기 위한 요구조건

우리는 **모든** 상품 목록을 가져오고 싶습니다. 그리고 여기, 상품 목록을 가져오는 API가 있습니다. 이 API의 사용법은 다음과 같습니다.

- 한 번에 최대 100개까지만 가져올 수 있습니다. (`limit=100` 으로 고정이라 가정합니다)
Expand Down
20 changes: 20 additions & 0 deletions src/common/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,23 @@
.ch-scrollycoding-step-content {
@apply border-gray-200;
}

article .heading-anchor {
@apply absolute text-gray-300 -left-6 sm:-left-7 top-1 w-6 h-6 p-1;
}

h2:has(.heading-anchor),
h3:has(.heading-anchor),
h4:has(.heading-anchor),
h5:has(.heading-anchor),
h6:has(.heading-anchor) {
@apply relative ml-2 sm:ml-0;
}

article .heading-anchor:hover {
@apply text-inherit;
}

article .heading-anchor svg {
@apply w-4 h-4;
}
62 changes: 44 additions & 18 deletions src/modules/article/block-components.tsx
Original file line number Diff line number Diff line change
@@ -1,35 +1,61 @@
import type { ComponentProps } from "react";
import { twMerge } from "tailwind-merge";
import style from "./style.module.css";

export function Blockquote({ children }: { children?: React.ReactNode }) {
return <h1 className={style.blockquote}>{children}</h1>;
}

export function Heading2({ children }: { children?: React.ReactNode }) {
return <h2 className={style.heading2}>{children}</h2>;
export function Heading2({ children, ...props }: ComponentProps<"h2">) {
return (
<h2 {...props} className={twMerge(props.className, style.heading2)}>
{children}
</h2>
);
}

export function Heading3({ children }: { children?: React.ReactNode }) {
return <h3 className={style.heading3}>{children}</h3>;
export function Heading3({ children, ...props }: ComponentProps<"h3">) {
return (
<h3 {...props} className={twMerge(props.className, style.heading3)}>
{children}
</h3>
);
}

export function Heading4({ children }: { children?: React.ReactNode }) {
return <h4 className={style.heading4}>{children}</h4>;
export function Heading4({ children, ...props }: ComponentProps<"h4">) {
return (
<h4 {...props} className={twMerge(props.className, style.heading4)}>
{children}
</h4>
);
}

export function Heading5({ children }: { children?: React.ReactNode }) {
return <h5 className={style.heading5}>{children}</h5>;
export function Heading5({ children, ...props }: ComponentProps<"h5">) {
return (
<h5 {...props} className={twMerge(props.className, style.heading5)}>
{children}
</h5>
);
}

export function Heading6({ children }: { children?: React.ReactNode }) {
return <h6 className={style.heading6}>{children}</h6>;
export function Heading6({ children, ...props }: ComponentProps<"h6">) {
return (
<h6 {...props} className={twMerge(props.className, style.heading6)}>
{children}
</h6>
);
}

export function Paragraph({ children }: { children?: React.ReactNode }) {
return <p className={style.paragraph}>{children}</p>;
export function Paragraph({ children, ...props }: ComponentProps<"p">) {
return (
<p {...props} className={twMerge(props.className, style.paragraph)}>
{children}
</p>
);
}

export function Quote({ children }: { children?: React.ReactNode }) {
return <blockquote className={style.quote}>{children}</blockquote>;
export function Quote({ children, ...props }: ComponentProps<"blockquote">) {
return (
<blockquote {...props} className={twMerge(props.className, style.quote)}>
{children}
</blockquote>
);
}

export function Table({ children }: { children?: React.ReactNode }) {
Expand Down
Loading