From a8acb74a129694ebfb42d8b2663f96f867d3fae0 Mon Sep 17 00:00:00 2001 From: cxf213 Date: Mon, 7 Apr 2025 11:45:48 +0800 Subject: [PATCH 1/2] feat: add sticky TOC to post layout Add table of contents to the post sidebar with: - Sticky positioning that keeps TOC visible while scrolling - Consistent link styling with the rest of the site - Max height with overflow scrolling for longer TOCs Improves navigation experience for longer articles. --- app/blog/[...slug]/page.tsx | 10 +- css/tailwind.css | 4 + ...e-of-tailwind-nextjs-starter-blog-v2.0.mdx | 3 +- layouts/PostwithTocLayout.tsx | 188 ++++++++++++++++++ package.json | 1 + 5 files changed, 203 insertions(+), 3 deletions(-) create mode 100644 layouts/PostwithTocLayout.tsx diff --git a/app/blog/[...slug]/page.tsx b/app/blog/[...slug]/page.tsx index af96a2b59b..6e62036a5c 100644 --- a/app/blog/[...slug]/page.tsx +++ b/app/blog/[...slug]/page.tsx @@ -10,6 +10,7 @@ import type { Authors, Blog } from 'contentlayer/generated' import PostSimple from '@/layouts/PostSimple' import PostLayout from '@/layouts/PostLayout' import PostBanner from '@/layouts/PostBanner' +import PostwithTocLayout from '@/layouts/PostwithTocLayout' import { Metadata } from 'next' import siteMetadata from '@/data/siteMetadata' import { notFound } from 'next/navigation' @@ -19,6 +20,7 @@ const layouts = { PostSimple, PostLayout, PostBanner, + PostwithTocLayout, } export async function generateMetadata(props: { @@ -112,7 +114,13 @@ export default async function Page(props: { params: Promise<{ slug: string[] }> type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} /> - + diff --git a/css/tailwind.css b/css/tailwind.css index 5a8a4c2f15..9af79ef48e 100644 --- a/css/tailwind.css +++ b/css/tailwind.css @@ -173,3 +173,7 @@ input:-webkit-autofill:focus { display: inline-block; vertical-align: middle; } + +.toc-container a { + @apply text-primary-500 hover:text-primary-600 dark:hover:text-primary-400; +} diff --git a/data/blog/release-of-tailwind-nextjs-starter-blog-v2.0.mdx b/data/blog/release-of-tailwind-nextjs-starter-blog-v2.0.mdx index 37afcbbf9b..3a2037cc9a 100644 --- a/data/blog/release-of-tailwind-nextjs-starter-blog-v2.0.mdx +++ b/data/blog/release-of-tailwind-nextjs-starter-blog-v2.0.mdx @@ -6,14 +6,13 @@ tags: ['next-js', 'tailwind', 'guide', 'feature'] draft: false summary: 'Release of Tailwind Nextjs Starter Blog template v2.0, refactored with Nextjs App directory and React Server Components setup.Discover the new features and how to migrate from V1.' images: ['/static/images/twitter-card.png'] +layout: PostwithTocLayout --- ## Introduction Welcome to the release of Tailwind Nextjs Starter Blog template v2.0. This release is a major refactor of the codebase to support Nextjs App directory and React Server Components. Read on to discover the new features and how to migrate from V1. - - ## V1 to V2 ![Github Traffic](/static/images/github-traffic.png) diff --git a/layouts/PostwithTocLayout.tsx b/layouts/PostwithTocLayout.tsx new file mode 100644 index 0000000000..ee21a98d04 --- /dev/null +++ b/layouts/PostwithTocLayout.tsx @@ -0,0 +1,188 @@ +import { ReactNode } from 'react' +import { CoreContent } from 'pliny/utils/contentlayer' +import type { Blog, Authors } from 'contentlayer/generated' +import Comments from '@/components/Comments' +import Link from '@/components/Link' +import PageTitle from '@/components/PageTitle' +import SectionContainer from '@/components/SectionContainer' +import Image from '@/components/Image' +import Tag from '@/components/Tag' +import siteMetadata from '@/data/siteMetadata' +import ScrollTopAndComment from '@/components/ScrollTopAndComment' +import TOCInline from 'pliny/ui/TOCInline' + +const editUrl = (path) => `${siteMetadata.siteRepo}/blob/main/data/${path}` +const discussUrl = (path) => + `https://mobile.twitter.com/search?q=${encodeURIComponent(`${siteMetadata.siteUrl}/${path}`)}` + +const postDateTemplate: Intl.DateTimeFormatOptions = { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', +} + +interface LayoutProps { + content: CoreContent + authorDetails: CoreContent[] + next?: { path: string; title: string } + prev?: { path: string; title: string } + children: ReactNode + // eslint-disable-next-line @typescript-eslint/no-explicit-any + toc: any +} + +export default function PostwithTocLayout({ + content, + authorDetails, + next, + prev, + children, + toc, +}: LayoutProps) { + const { filePath, path, slug, date, title, tags } = content + const basePath = path.split('/')[0] + + return ( + + +
+
+
+
+
+
+
Published on
+
+ +
+
+
+
+ {title} +
+
+
+
+
+
Authors
+
+
    + {authorDetails.map((author) => ( +
  • + {author.avatar && ( + avatar + )} +
    +
    Name
    +
    {author.name}
    +
    Twitter
    +
    + {author.twitter && ( + + {author.twitter + .replace('https://twitter.com/', '@') + .replace('https://x.com/', '@')} + + )} +
    +
    +
  • + ))} +
+
+
+
+
{children}
+
+ + Discuss on Twitter + + {` • `} + View on GitHub +
+ {siteMetadata.comments && ( +
+ +
+ )} +
+
+
+ {tags && ( +
+

+ Tags +

+
+ {tags.map((tag) => ( + + ))} +
+
+ )} + {(next || prev) && ( +
+ {prev && prev.path && ( +
+

+ Previous Article +

+
+ {prev.title} +
+
+ )} + {next && next.path && ( +
+

+ Next Article +

+
+ {next.title} +
+
+ )} +
+ )} +
+ {/* Sidebar TOC */} +
+

+ MENU +

+
+ +
+
+ {/* Back to blog link */} +
+ + ← Back to the blog + +
+
+
+
+
+
+ ) +} diff --git a/package.json b/package.json index f67158d372..180143d352 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "scripts": { "start": "next dev", + "format": "prettier --write .", "dev": "cross-env INIT_CWD=$PWD next dev", "build": "cross-env INIT_CWD=$PWD next build && cross-env NODE_OPTIONS='--experimental-json-modules' node ./scripts/postbuild.mjs", "serve": "next start", From 44a8e8f7eadfd1a9a0f07418d18490c0a681daae Mon Sep 17 00:00:00 2001 From: cxf213 Date: Mon, 7 Apr 2025 12:23:58 +0800 Subject: [PATCH 2/2] using TABLE OF CONTENTS instead of MENU ; removed 'any'; correct position of toc component --- layouts/PostwithTocLayout.tsx | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/layouts/PostwithTocLayout.tsx b/layouts/PostwithTocLayout.tsx index ee21a98d04..827ffc4013 100644 --- a/layouts/PostwithTocLayout.tsx +++ b/layouts/PostwithTocLayout.tsx @@ -22,14 +22,19 @@ const postDateTemplate: Intl.DateTimeFormatOptions = { day: 'numeric', } +interface TOCItem { + value: string + url: string + depth: number +} + interface LayoutProps { content: CoreContent authorDetails: CoreContent[] next?: { path: string; title: string } prev?: { path: string; title: string } children: ReactNode - // eslint-disable-next-line @typescript-eslint/no-explicit-any - toc: any + toc: TOCItem[] } export default function PostwithTocLayout({ @@ -42,7 +47,6 @@ export default function PostwithTocLayout({ }: LayoutProps) { const { filePath, path, slug, date, title, tags } = content const basePath = path.split('/')[0] - return ( @@ -159,17 +163,15 @@ export default function PostwithTocLayout({ )} )} - - {/* Sidebar TOC */} -
-

- MENU -

-
- +
+

+ TABLE OF CONTENTS +

+
+ +
- {/* Back to blog link */}