Remove next-view-transitions and use native View Transition API
- Remove external next-view-transitions dependency - Use Next.js 16 native navigation and Safari 18+ native View Transition API - Add ViewTransitionProvider for minimal wrapping with Safari 18+ detection - Updated all Link imports from external package to next/link - Removed link-wrapper.tsx and view-transitions-wrapper.tsx This resolves Safari compatibility issues while maintaining transitions on modern browsers.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Link } from 'next-view-transitions';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { notFound } from 'next/navigation';
|
||||
import type { Metadata } from 'next';
|
||||
@@ -222,7 +222,7 @@ export default async function BlogPostPage({ params }: Props) {
|
||||
href={`/tags/${encodeURIComponent(
|
||||
t.toLowerCase().replace(/\s+/g, '-')
|
||||
)}`}
|
||||
className="tag-chip rounded-full bg-accent-soft px-3 py-1 text-sm text-accent-textLight dark:bg-slate-800 dark:text-slate-100"
|
||||
className="tag-chip rounded-full bg-accent-soft px-3 py-1 text-sm text-accent-textLight dark:bg-slate-800 dark:text-slate-100 dark:hover:bg-slate-700 dark:hover:text-white"
|
||||
>
|
||||
#{t}
|
||||
</Link>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from 'next-view-transitions';
|
||||
import Link from 'next/link';
|
||||
import { getAllPostsSorted } from '@/lib/posts';
|
||||
import { PostListWithControls } from '@/components/post-list-with-controls';
|
||||
import { TimelineWrapper } from '@/components/timeline-wrapper';
|
||||
|
||||
@@ -7,7 +7,7 @@ import { ThemeProvider } from 'next-themes';
|
||||
import { Playfair_Display, LXGW_WenKai_TC } from 'next/font/google';
|
||||
import { JsonLd } from '@/components/json-ld';
|
||||
import { WebVitals } from '@/components/web-vitals';
|
||||
import { ViewTransitions } from 'next-view-transitions';
|
||||
import { ViewTransitionProvider } from '@/components/view-transition-provider';
|
||||
import NextTopLoader from 'nextjs-toploader';
|
||||
|
||||
const playfair = Playfair_Display({
|
||||
@@ -17,12 +17,12 @@ const playfair = Playfair_Display({
|
||||
});
|
||||
|
||||
const lxgwWenKai = LXGW_WenKai_TC({
|
||||
weight: ['400', '700'], // 只加载 Regular 和 Bold
|
||||
weight: ['400', '700'],
|
||||
subsets: ['latin'],
|
||||
variable: '--font-serif-cn',
|
||||
display: 'swap',
|
||||
preload: true,
|
||||
adjustFontFallback: false, // 中文字体不需要 fallback 调整,使用系统字体作为 fallback
|
||||
adjustFontFallback: false,
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
@@ -88,7 +88,6 @@ export default async function RootLayout({
|
||||
.slice(0, 5)
|
||||
.map((p) => ({ title: p.title, url: p.url }));
|
||||
|
||||
// WebSite Schema
|
||||
const websiteSchema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
@@ -111,7 +110,6 @@ export default async function RootLayout({
|
||||
},
|
||||
};
|
||||
|
||||
// Organization Schema
|
||||
const organizationSchema = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
@@ -125,28 +123,27 @@ export default async function RootLayout({
|
||||
].filter(Boolean),
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<ViewTransitions>
|
||||
<html lang={siteConfig.defaultLocale} suppressHydrationWarning className={`${playfair.variable} ${lxgwWenKai.variable}`}>
|
||||
<head>
|
||||
{/* Preconnect to Google Fonts for faster font loading */}
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
</head>
|
||||
<body>
|
||||
<NextTopLoader
|
||||
color={theme.accent}
|
||||
height={3}
|
||||
showSpinner={false}
|
||||
speed={200}
|
||||
shadow={`0 0 10px ${theme.accent}, 0 0 5px ${theme.accent}`}
|
||||
/>
|
||||
<JsonLd data={websiteSchema} />
|
||||
<JsonLd data={organizationSchema} />
|
||||
<style
|
||||
// Set CSS variables for accent colors (light + dark variants)
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
<html lang={siteConfig.defaultLocale} suppressHydrationWarning className={`${playfair.variable} ${lxgwWenKai.variable}`}>
|
||||
<head>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossOrigin="anonymous" />
|
||||
</head>
|
||||
<body>
|
||||
<NextTopLoader
|
||||
color={theme.accent}
|
||||
height={3}
|
||||
showSpinner={false}
|
||||
speed={200}
|
||||
shadow={`0 0 10px ${theme.accent}, 0 0 5px ${theme.accent}`}
|
||||
/>
|
||||
<JsonLd data={websiteSchema} />
|
||||
<JsonLd data={organizationSchema} />
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
:root {
|
||||
--color-accent: ${theme.accent};
|
||||
--color-accent-soft: ${theme.accentSoft};
|
||||
@@ -155,13 +152,14 @@ export default async function RootLayout({
|
||||
}
|
||||
`
|
||||
}}
|
||||
/>
|
||||
/>
|
||||
<ThemeProvider attribute="class" defaultTheme="system" enableSystem>
|
||||
<LayoutShell recentPosts={recentPosts}>{children}</LayoutShell>
|
||||
<ViewTransitionProvider>
|
||||
<LayoutShell recentPosts={recentPosts}>{children}</LayoutShell>
|
||||
</ViewTransitionProvider>
|
||||
</ThemeProvider>
|
||||
<WebVitals />
|
||||
</body>
|
||||
</html>
|
||||
</ViewTransitions>
|
||||
<WebVitals />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from 'next-view-transitions';
|
||||
import Link from 'next/link';
|
||||
import { getAllPostsSorted } from '@/lib/posts';
|
||||
import { siteConfig } from '@/lib/config';
|
||||
import { PostListItem } from '@/components/post-list-item';
|
||||
@@ -51,7 +51,7 @@ export default function HomePage() {
|
||||
<Link
|
||||
href="/blog"
|
||||
prefetch={true}
|
||||
className="text-xs text-blue-600 hover:underline dark:text-blue-400"
|
||||
className="text-xs text-accent hover:underline"
|
||||
>
|
||||
所有文章 →
|
||||
</Link>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from 'next-view-transitions';
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { notFound } from 'next/navigation';
|
||||
import type { Metadata } from 'next';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Link } from 'next-view-transitions';
|
||||
import Link from 'next/link';
|
||||
import type { Metadata } from 'next';
|
||||
import { FiTag, FiTrendingUp } from 'react-icons/fi';
|
||||
import { getAllTagsWithCount } from '@/lib/posts';
|
||||
@@ -87,7 +87,7 @@ export default function TagIndexPage() {
|
||||
>
|
||||
<span className={`mb-3 block h-1.5 w-16 rounded-full bg-gradient-to-r ${color}`} aria-hidden="true" />
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="type-subtitle font-semibold text-slate-900 group-hover:text-blue-600 dark:text-slate-50 dark:group-hover:text-blue-400">
|
||||
<h2 className="type-subtitle font-semibold text-slate-900 group-hover:text-accent dark:text-slate-50 dark:group-hover:text-accent">
|
||||
{tag}
|
||||
</h2>
|
||||
<span className="type-small text-slate-600 dark:text-slate-300">
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
export default function Template({ children }: { children: React.ReactNode }) {
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const [prefersReducedMotion, setPrefersReducedMotion] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -14,26 +13,6 @@ export default function Template({ children }: { children: React.ReactNode }) {
|
||||
return () => mq.removeEventListener('change', handler);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const container = containerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
if (prefersReducedMotion) {
|
||||
container.style.animation = 'none';
|
||||
container.style.opacity = '1';
|
||||
container.style.transform = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
// Trigger animation on mount
|
||||
container.style.animation = 'none';
|
||||
void container.offsetHeight;
|
||||
container.style.animation = 'pageEnter 0.45s cubic-bezier(0.32, 0.72, 0, 1) forwards';
|
||||
}, [children, prefersReducedMotion]);
|
||||
|
||||
return (
|
||||
<div ref={containerRef} className="page-transition">
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
// ViewTransitions handles page transitions - no additional wrapper needed
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user