From 3cda9a51bfbbb59121bcda36e9af3f73986f4188 Mon Sep 17 00:00:00 2001 From: siruichen333 <146685394+siruichen333@users.noreply.github.com> Date: Sat, 13 Sep 2025 09:51:30 +0800 Subject: [PATCH 1/3] feat: add hover highlight --- app/components/MDXContent.tsx | 73 +++++++++++++++++-- app/docs/[...slug]/page.tsx | 37 ++++++---- app/globals.css | 131 ++++++++++++++++++++++++++-------- app/page.tsx | 39 +++++++--- 4 files changed, 223 insertions(+), 57 deletions(-) diff --git a/app/components/MDXContent.tsx b/app/components/MDXContent.tsx index 258033d..4fb463e 100644 --- a/app/components/MDXContent.tsx +++ b/app/components/MDXContent.tsx @@ -1,8 +1,73 @@ -"use client" +"use client"; -import { useMDXComponent } from 'next-contentlayer/hooks' +import { useMDXComponent } from "next-contentlayer/hooks"; export default function MDXContent({ code }: { code: string }) { - const MDX = useMDXComponent(code) - return + const MDX = useMDXComponent(code); + return ( +
+ + +
+ ); } diff --git a/app/docs/[...slug]/page.tsx b/app/docs/[...slug]/page.tsx index 07a428b..831d96a 100644 --- a/app/docs/[...slug]/page.tsx +++ b/app/docs/[...slug]/page.tsx @@ -1,22 +1,33 @@ -import { allDocs } from 'contentlayer/generated' -import { notFound } from 'next/navigation' -import MDXContent from '@/app/components/MDXContent' +import { allDocs } from "contentlayer/generated"; +import { notFound } from "next/navigation"; +import MDXContent from "@/app/components/MDXContent"; // Pre-generate all nested doc paths, split into segments for catch-all route export function generateStaticParams() { - return allDocs.map((d) => ({ slug: d.slug.split('/') })) + return allDocs.map((d) => ({ slug: d.slug.split("/") })); } -export default async function DocPage({ params }: { params: Promise<{ slug: string[] }> }) { - const { slug } = await params - const slugPath = slug.join('/') - const doc = allDocs.find((d) => d.slug === slugPath) - if (!doc) return notFound() +export default async function DocPage({ + params, +}: { + params: Promise<{ slug: string[] }>; +}) { + const { slug } = await params; + const slugPath = slug.join("/"); + const doc = allDocs.find((d) => d.slug === slugPath); + if (!doc) return notFound(); return ( -
-

{doc.title}

- +
+

+ {doc.title} +

+
+ +
- ) + ); } diff --git a/app/globals.css b/app/globals.css index 0204599..129ffac 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,4 +1,4 @@ -@import 'tailwindcss'; +@import "tailwindcss"; @import "tw-animate-css"; /* ---break--- @@ -130,18 +130,18 @@ :root { --radius: 1.25rem; - --background: oklch(0.9670 0.0029 264.5419); - --foreground: oklch(0.2795 0.0368 260.0310); - --card: oklch(1.0000 0 0); - --card-foreground: oklch(0.2795 0.0368 260.0310); - --popover: oklch(1.0000 0 0); - --popover-foreground: oklch(0.2795 0.0368 260.0310); + --background: oklch(0.967 0.0029 264.5419); + --foreground: oklch(0.2795 0.0368 260.031); + --card: oklch(1 0 0); + --card-foreground: oklch(0.2795 0.0368 260.031); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.2795 0.0368 260.031); --primary: oklch(0.5854 0.2041 277.1173); - --primary-foreground: oklch(1.0000 0 0); + --primary-foreground: oklch(1 0 0); --secondary: oklch(0.9276 0.0058 264.5313); --secondary-foreground: oklch(0.3729 0.0306 259.7328); - --muted: oklch(0.9670 0.0029 264.5419); - --muted-foreground: oklch(0.5510 0.0234 264.3637); + --muted: oklch(0.967 0.0029 264.5419); + --muted-foreground: oklch(0.551 0.0234 264.3637); --accent: oklch(0.9299 0.0334 272.7879); --accent-foreground: oklch(0.3729 0.0306 259.7328); --destructive: oklch(0.6368 0.2078 25.3313); @@ -153,15 +153,15 @@ --chart-3: oklch(0.4568 0.2146 277.0229); --chart-4: oklch(0.3984 0.1773 277.3662); --chart-5: oklch(0.3588 0.1354 278.6973); - --sidebar: oklch(0.9670 0.0029 264.5419); - --sidebar-foreground: oklch(0.2795 0.0368 260.0310); + --sidebar: oklch(0.967 0.0029 264.5419); + --sidebar-foreground: oklch(0.2795 0.0368 260.031); --sidebar-primary: oklch(0.5854 0.2041 277.1173); - --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-primary-foreground: oklch(1 0 0); --sidebar-accent: oklch(0.9299 0.0334 272.7879); --sidebar-accent-foreground: oklch(0.3729 0.0306 259.7328); --sidebar-border: oklch(0.8717 0.0093 258.3382); --sidebar-ring: oklch(0.5854 0.2041 277.1173); - --destructive-foreground: oklch(1.0000 0 0); + --destructive-foreground: oklch(1 0 0); --font-sans: Inter, ui-sans-serif, sans-serif, system-ui; --font-serif: Merriweather, serif; --font-mono: JetBrains Mono, monospace; @@ -175,11 +175,16 @@ --spacing: 0.25rem; --shadow-2xs: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.05); --shadow-xs: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.05); - --shadow-sm: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.10); - --shadow: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.10); - --shadow-md: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 2px 4px -2px hsl(0 0% 10.1961% / 0.10); - --shadow-lg: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 4px 6px -2px hsl(0 0% 10.1961% / 0.10); - --shadow-xl: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 8px 10px -2px hsl(0 0% 10.1961% / 0.10); + --shadow-sm: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.1); + --shadow: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.1); + --shadow-md: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 2px 4px -2px hsl(0 0% 10.1961% / 0.1); + --shadow-lg: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 4px 6px -2px hsl(0 0% 10.1961% / 0.1); + --shadow-xl: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 8px 10px -2px hsl(0 0% 10.1961% / 0.1); --shadow-2xl: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.25); --tracking-normal: 0em; } @@ -191,15 +196,15 @@ .dark { --background: oklch(0.2077 0.0398 265.7549); --foreground: oklch(0.9288 0.0126 255.5078); - --card: oklch(0.2795 0.0368 260.0310); + --card: oklch(0.2795 0.0368 260.031); --card-foreground: oklch(0.9288 0.0126 255.5078); - --popover: oklch(0.2795 0.0368 260.0310); + --popover: oklch(0.2795 0.0368 260.031); --popover-foreground: oklch(0.9288 0.0126 255.5078); --primary: oklch(0.6801 0.1583 276.9349); --primary-foreground: oklch(0.2077 0.0398 265.7549); - --secondary: oklch(0.3351 0.0331 260.9120); + --secondary: oklch(0.3351 0.0331 260.912); --secondary-foreground: oklch(0.8717 0.0093 258.3382); - --muted: oklch(0.2795 0.0368 260.0310); + --muted: oklch(0.2795 0.0368 260.031); --muted-foreground: oklch(0.7137 0.0192 261.3246); --accent: oklch(0.3729 0.0306 259.7328); --accent-foreground: oklch(0.8717 0.0093 258.3382); @@ -212,7 +217,7 @@ --chart-3: oklch(0.5106 0.2301 276.9656); --chart-4: oklch(0.4568 0.2146 277.0229); --chart-5: oklch(0.3984 0.1773 277.3662); - --sidebar: oklch(0.2795 0.0368 260.0310); + --sidebar: oklch(0.2795 0.0368 260.031); --sidebar-foreground: oklch(0.9288 0.0126 255.5078); --sidebar-primary: oklch(0.6801 0.1583 276.9349); --sidebar-primary-foreground: oklch(0.2077 0.0398 265.7549); @@ -235,11 +240,16 @@ --spacing: 0.25rem; --shadow-2xs: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.05); --shadow-xs: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.05); - --shadow-sm: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.10); - --shadow: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.10); - --shadow-md: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 2px 4px -2px hsl(0 0% 10.1961% / 0.10); - --shadow-lg: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 4px 6px -2px hsl(0 0% 10.1961% / 0.10); - --shadow-xl: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.10), 0px 8px 10px -2px hsl(0 0% 10.1961% / 0.10); + --shadow-sm: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.1); + --shadow: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 1px 2px -2px hsl(0 0% 10.1961% / 0.1); + --shadow-md: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 2px 4px -2px hsl(0 0% 10.1961% / 0.1); + --shadow-lg: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 4px 6px -2px hsl(0 0% 10.1961% / 0.1); + --shadow-xl: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.1), + 0px 8px 10px -2px hsl(0 0% 10.1961% / 0.1); --shadow-2xl: 0px 0px 15px -1px hsl(0 0% 10.1961% / 0.25); } @@ -254,4 +264,65 @@ body { @apply bg-background text-foreground; } -} \ No newline at end of file +} + +@layer utilities { + /* 通用 hover 变深效果 */ + .hover-darken { + transition: filter 0.2s ease-in-out; + } + + .hover-darken:hover { + filter: brightness(0.8); + } + + /* 针对不同颜色类型的 hover 变深效果 */ + .hover-darken-bg { + transition: all 0.2s ease-in-out; + } + + .hover-darken-bg:hover { + background-color: #f0f0f0 !important; + transform: scale(1.02); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + } + + .hover-darken-text { + transition: filter 0.2s ease-in-out; + } + + .hover-darken-text:hover { + filter: brightness(0.8); + } + + /* 使用 CSS 变量实现更精确的颜色控制 */ + .hover-darken-custom { + transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out; + } + + .hover-darken-custom:hover { + background-color: color-mix(in srgb, currentColor 15%, transparent); + } + + /* 针对链接的特殊 hover 效果 */ + .hover-link { + transition: all 0.2s ease-in-out; + position: relative; + } + + .hover-link:hover { + filter: brightness(0.8); + transform: translateY(-1px); + } + + /* 针对按钮的 hover 效果 */ + .hover-button { + transition: all 0.2s ease-in-out; + } + + .hover-button:hover { + filter: brightness(0.9); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + } +} diff --git a/app/page.tsx b/app/page.tsx index 95b21d3..d98af98 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,24 +1,43 @@ -import Link from 'next/link' -import { allDocs } from 'contentlayer/generated' +import Link from "next/link"; +import { allDocs } from "contentlayer/generated"; export default function DocsIndex() { const docs = [...allDocs].sort((a, b) => - (b.date ?? '').localeCompare(a.date ?? '') - ) + (b.date ?? "").localeCompare(a.date ?? "") + ); return ( -
+

Docs

-
    +
      {docs.map((d) => ( -
    • - +
    • + {d.title} + {d.description && ( +

      {d.description}

      + )} - {d.description &&

      {d.description}

      }
    • ))}
- ) + ); } From c7e7a5a3019c1bda32ac2e1a7a0a2145ec897b5f Mon Sep 17 00:00:00 2001 From: Siz Long Date: Sat, 13 Sep 2025 20:04:28 +0800 Subject: [PATCH 2/3] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- app/globals.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/globals.css b/app/globals.css index e471765..d1dea94 100644 --- a/app/globals.css +++ b/app/globals.css @@ -291,10 +291,11 @@ /* 针对不同颜色类型的 hover 变深效果 */ .hover-darken-bg { transition: all 0.2s ease-in-out; + --hover-darken-bg-color: #f0f0f0; } .hover-darken-bg:hover { - background-color: #f0f0f0 !important; + background-color: var(--hover-darken-bg-color); transform: scale(1.02); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } From 211b8e79763202355778d4a893fa4df810fe6293 Mon Sep 17 00:00:00 2001 From: siruichen333 <146685394+siruichen333@users.noreply.github.com> Date: Sat, 13 Sep 2025 20:53:22 +0800 Subject: [PATCH 3/3] fix: code block style conflict --- app/components/HoverCard.tsx | 30 +++++++++++++++++ app/docs/[...slug]/page.tsx | 6 ++-- app/globals.css | 63 ------------------------------------ app/page.tsx | 47 +++++++++++++-------------- mdx-components.tsx | 50 ---------------------------- 5 files changed, 54 insertions(+), 142 deletions(-) create mode 100644 app/components/HoverCard.tsx diff --git a/app/components/HoverCard.tsx b/app/components/HoverCard.tsx new file mode 100644 index 0000000..dd6d728 --- /dev/null +++ b/app/components/HoverCard.tsx @@ -0,0 +1,30 @@ +import React from "react"; + +interface HoverCardProps { + children: React.ReactNode; + className?: string; + hoverType?: "darken" | "scale" | "lift" | "glow"; +} + +export default function HoverCard({ + children, + className = "", + hoverType = "scale", +}: HoverCardProps) { + const getHoverStyles = () => { + switch (hoverType) { + case "darken": + return "hover:brightness-90 transition-all duration-200 ease-in-out"; + case "scale": + return "hover:scale-105 hover:shadow-lg transition-all duration-200 ease-in-out"; + case "lift": + return "hover:-translate-y-1 hover:shadow-lg transition-all duration-200 ease-in-out"; + case "glow": + return "hover:shadow-xl hover:shadow-primary/20 transition-all duration-200 ease-in-out"; + default: + return "hover:scale-105 hover:shadow-lg transition-all duration-200 ease-in-out"; + } + }; + + return
{children}
; +} diff --git a/app/docs/[...slug]/page.tsx b/app/docs/[...slug]/page.tsx index 87a36a4..332e8fa 100644 --- a/app/docs/[...slug]/page.tsx +++ b/app/docs/[...slug]/page.tsx @@ -23,10 +23,8 @@ export default async function DocPage({ params }: Param) { return ( -

{page.data.title}

-
- -
+

{page.data.title}

+
); diff --git a/app/globals.css b/app/globals.css index e471765..c51e403 100644 --- a/app/globals.css +++ b/app/globals.css @@ -277,66 +277,3 @@ @apply bg-background text-foreground; } } - -@layer utilities { - /* 通用 hover 变深效果 */ - .hover-darken { - transition: filter 0.2s ease-in-out; - } - - .hover-darken:hover { - filter: brightness(0.8); - } - - /* 针对不同颜色类型的 hover 变深效果 */ - .hover-darken-bg { - transition: all 0.2s ease-in-out; - } - - .hover-darken-bg:hover { - background-color: #f0f0f0 !important; - transform: scale(1.02); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - } - - .hover-darken-text { - transition: filter 0.2s ease-in-out; - } - - .hover-darken-text:hover { - filter: brightness(0.8); - } - - /* 使用 CSS 变量实现更精确的颜色控制 */ - .hover-darken-custom { - transition: - background-color 0.2s ease-in-out, - color 0.2s ease-in-out; - } - - .hover-darken-custom:hover { - background-color: color-mix(in srgb, currentColor 15%, transparent); - } - - /* 针对链接的特殊 hover 效果 */ - .hover-link { - transition: all 0.2s ease-in-out; - position: relative; - } - - .hover-link:hover { - filter: brightness(0.8); - transform: translateY(-1px); - } - - /* 针对按钮的 hover 效果 */ - .hover-button { - transition: all 0.2s ease-in-out; - } - - .hover-button:hover { - filter: brightness(0.9); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); - } -} diff --git a/app/page.tsx b/app/page.tsx index fa10b8f..4565547 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { source } from "@/lib/source"; +import HoverCard from "@/app/components/HoverCard"; export default function DocsIndex() { const pages = source @@ -11,32 +12,28 @@ export default function DocsIndex() {

Docs

    {pages.map((d) => ( -
  • - + - {d.data.title} - {d.data.description && ( -

    - {d.data.description} -

    - )} - + + {d.data.title} + {d.data.description && ( +

    + {d.data.description} +

    + )} + +
  • ))}
diff --git a/mdx-components.tsx b/mdx-components.tsx index f061d95..b21a161 100644 --- a/mdx-components.tsx +++ b/mdx-components.tsx @@ -4,56 +4,6 @@ import type { MDXComponents } from "mdx/types"; export function getMDXComponents(components?: MDXComponents): MDXComponents { return { ...defaultMdxComponents, - a: ({ children, ...props }) => ( - - {children} - - ), - code: ({ children, ...props }) => ( - - {children} - - ), - pre: ({ children, ...props }) => ( -
-        {children}
-      
- ), - blockquote: ({ children, ...props }) => ( -
- {children} -
- ), - h1: ({ children, ...props }) => ( -

- {children} -

- ), - h2: ({ children, ...props }) => ( -

- {children} -

- ), - h3: ({ children, ...props }) => ( -

- {children} -

- ), - h4: ({ children, ...props }) => ( -

- {children} -

- ), - h5: ({ children, ...props }) => ( -
- {children} -
- ), - h6: ({ children, ...props }) => ( -
- {children} -
- ), ...components, }; }