Files
blog-nextjs/styles/globals.css
gbanyan 62090c7742 perf: 优化字体加载性能和字间距
- 添加 adjustFontFallback: false 优化 CLS(累积布局偏移)
- 调整霞鹜文楷字间距:从负值改为正值,让手写风格字体更自然
  - h1: -0.03em → 0.01em
  - h2: -0.02em → 0.015em
  - type-display: 添加 0.01em
  - type-title: 0.02em → 0.025em
- 改善字体加载时的视觉稳定性

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 16:08:26 +08:00

709 lines
16 KiB
CSS

@import "tailwindcss";
@plugin "@tailwindcss/typography";
/* Class-based dark mode for next-themes */
@custom-variant dark (&:where(.dark, .dark *));
/* Auto-detect content in the submodule */
@source "../content";
/* Tailwind v4 CSS-first theme configuration */
@theme {
/* Custom colors (CSS variable references) */
--color-accent: var(--color-accent);
--color-accent-soft: var(--color-accent-soft);
--color-accent-textLight: var(--color-accent-text-light);
--color-accent-textDark: var(--color-accent-text-dark);
/* Custom font families */
--font-serif-eng: var(--font-serif-eng), serif;
--font-serif-cn: var(--font-serif-cn), "Songti SC", "Noto Serif TC", "SimSun", serif;
/* Custom transition timing */
--ease-snappy: cubic-bezier(0.32, 0.72, 0, 1);
/* Custom transition durations */
--duration-180: 180ms;
--duration-260: 260ms;
/* Custom box shadows */
--shadow-lifted: 0 12px 30px -14px rgba(15, 23, 42, 0.25);
--shadow-outline: 0 0 0 1px rgba(59, 130, 246, 0.25);
/* Custom keyframes */
--animate-fade-in-up: fade-in-up 0.6s ease-out both;
--animate-float-soft: float-soft 12s ease-in-out infinite;
}
@keyframes fade-in-up {
0% { opacity: 0; transform: translateY(8px) scale(0.98); }
100% { opacity: 1; transform: translateY(0) scale(1); }
}
@keyframes float-soft {
0% { transform: translate3d(0,0,0) scale(1); }
50% { transform: translate3d(4px,-6px,0) scale(1.03); }
100% { transform: translate3d(0,0,0) scale(1); }
}
:root {
--motion-duration-short: 180ms;
--motion-duration-medium: 260ms;
--motion-ease-snappy: cubic-bezier(0.32, 0.72, 0, 1);
--card-translate-y: -6px;
--line-height-body: clamp(1.5, 0.15vw + 1.45, 1.65);
--font-weight-regular: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-system-sans: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'SF Pro Display', 'PingFang TC', 'PingFang SC', 'Hiragino Sans', 'Hiragino Kaku Gothic ProN', 'Segoe UI Variable Text', 'Segoe UI', 'Microsoft JhengHei', 'Microsoft YaHei', 'Noto Sans TC', 'Noto Sans SC', 'Noto Sans CJK TC', 'Noto Sans CJK SC', 'Source Han Sans TC', 'Source Han Sans SC', 'Roboto', 'Ubuntu', 'Cantarell', 'Inter', 'Helvetica Neue', Arial, sans-serif;
/* Ink + accent palette */
--color-ink-strong: #0f172a;
--color-ink-body: #1f2937;
--color-ink-muted: #475569;
--color-accent: #7c3aed;
--color-accent-soft: #f4f0ff;
font-size: clamp(15px, 0.65vw + 11px, 19px);
}
.dark {
--color-ink-strong: #e2e8f0;
--color-ink-body: #cbd5e1;
--color-ink-muted: #94a3b8;
--color-accent: #a78bfa;
--color-accent-soft: #1f1a3d;
}
@media (min-width: 2560px) {
:root {
font-size: 20px;
}
}
body {
@apply bg-white text-gray-900 transition-colors duration-200 ease-snappy dark:bg-gray-950 dark:text-gray-100;
font-size: 1rem;
line-height: var(--line-height-body);
font-family: var(--font-system-sans);
color: var(--color-ink-body);
}
@keyframes timeline-scroll {
0% {
transform: translate(-50%, -10%);
opacity: 0;
}
15% {
opacity: 1;
}
85% {
opacity: 1;
}
100% {
transform: translate(-50%, 110%);
opacity: 0;
}
}
@keyframes pageEnter {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.page-transition {
opacity: 0;
}
/* Scroll reveal animations - CSS only */
.scroll-reveal {
opacity: 0;
transform: translateY(12px);
transition: opacity 0.5s cubic-bezier(0.32, 0.72, 0, 1),
transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
}
.scroll-reveal.is-visible {
opacity: 1;
transform: translateY(0);
}
/* Respect user's motion preferences */
@media (prefers-reduced-motion: reduce) {
.scroll-reveal {
opacity: 1;
transform: none;
transition: none;
}
}
.toc-target-highlight {
@apply bg-yellow-50/60 dark:bg-yellow-900/40 transition-colors duration-500;
}
/* Subtle hover for article elements */
.prose blockquote {
@apply transition-transform transition-shadow duration-180 ease-snappy;
border-left: 4px solid var(--color-accent, #2563eb);
background: linear-gradient(135deg, rgba(124, 58, 237, 0.06), rgba(124, 58, 237, 0.1));
padding: 1.2rem 1.5rem;
font-style: italic;
color: rgba(15, 23, 42, 0.78);
position: relative;
}
.dark .prose blockquote {
background: linear-gradient(135deg, rgba(167, 139, 250, 0.12), rgba(124, 58, 237, 0.08));
color: rgba(226, 232, 240, 0.85);
border-left-color: rgba(167, 139, 250, 0.9);
}
.prose blockquote:hover {
@apply -translate-y-0.5 shadow-sm;
}
.prose blockquote::before {
content: '\201C';
position: absolute;
top: 0.5rem;
left: 0.8rem;
font-size: 3rem;
font-family: 'Times New Roman', 'Noto Serif TC', serif;
color: rgba(37, 99, 235, 0.25);
pointer-events: none;
}
.prose pre {
@apply transition-transform transition-shadow duration-180 ease-snappy;
}
.prose pre:hover {
@apply -translate-y-0.5 shadow-md;
}
/* Typography plugin prose overrides */
.prose {
font-size: clamp(1rem, 0.2vw + 0.9rem, 1.2rem);
line-height: var(--line-height-body);
color: var(--color-ink-body);
}
.prose a {
color: var(--color-accent-text-light);
}
.prose a:hover {
color: var(--color-accent);
}
.dark .prose a {
color: var(--color-accent-text-dark);
}
.dark .prose a:hover {
color: var(--color-accent);
}
.prose h1 {
font-size: clamp(2.2rem, 1.4rem + 2.2vw, 3.4rem);
line-height: 1.25;
color: var(--color-ink-strong);
font-weight: 700;
letter-spacing: 0.01em; /* 手写风格字体需要稍微增加字间距 */
font-family: var(--font-serif-cn), var(--font-serif-eng), "Songti SC", serif;
}
.prose h2 {
font-size: clamp(1.8rem, 1.1rem + 1.6vw, 2.8rem);
line-height: 1.3;
color: var(--color-ink-strong);
font-weight: 600;
letter-spacing: 0.015em; /* 手写风格字体需要稍微增加字间距 */
font-family: var(--font-serif-cn), var(--font-serif-eng), "Songti SC", serif;
}
.prose h3 {
font-size: clamp(1.4rem, 0.9rem + 1vw, 2rem);
line-height: 1.35;
color: var(--color-ink-strong);
}
.prose p,
.prose li {
font-size: clamp(1rem, 0.2vw + 0.9rem, 1.15rem);
line-height: var(--line-height-body);
color: var(--color-ink-body);
}
.prose small,
.prose figcaption {
font-size: clamp(0.85rem, 0.2vw + 0.8rem, 0.95rem);
}
/* Dark mode prose overrides (replaces prose-dark) */
.dark .prose {
color: var(--color-ink-body);
}
.dark .prose strong,
.dark .prose b {
color: #f8fafc;
font-weight: 700;
}
.dark .prose em {
color: #f1f5f9;
}
.dark .prose h1,
.dark .prose h2 {
color: #f8fafc;
}
.prose h1>a,
.prose h2>a,
.prose h3>a,
.prose h4>a,
.prose h5>a,
.prose h6>a {
text-decoration: none !important;
color: inherit !important;
}
.hero-title {
position: relative;
display: inline-flex;
overflow: hidden;
}
.hero-title__sweep {
position: absolute;
inset: 0;
background: linear-gradient(120deg, transparent 10%, rgba(59, 130, 246, 0.35) 45%, transparent 90%);
transform: translateX(-120%);
animation: hero-sweep 4s var(--motion-ease-snappy) infinite;
pointer-events: none;
}
@keyframes hero-sweep {
0% {
transform: translateX(-120%);
opacity: 0;
}
30% {
opacity: 0.4;
}
60% {
transform: translateX(120%);
opacity: 0;
}
100% {
transform: translateX(120%);
opacity: 0;
}
}
.tag-chip {
position: relative;
overflow: hidden;
transition: color var(--motion-duration-short) var(--motion-ease-snappy), background-color var(--motion-duration-short) var(--motion-ease-snappy);
}
.tag-chip::after {
content: '';
position: absolute;
left: 50%;
bottom: 4px;
width: 0;
height: 2px;
background: currentColor;
opacity: 0.5;
transition: width var(--motion-duration-short) var(--motion-ease-snappy), left var(--motion-duration-short) var(--motion-ease-snappy);
}
.tag-chip:hover::after,
.tag-chip:focus-visible::after {
width: 100%;
left: 0;
}
/* TOC transitions - replaces Framer Motion */
.toc-sidebar {
transition: opacity 0.3s ease-in-out, transform 0.3s ease-in-out;
will-change: opacity, transform;
}
.toc-sidebar-enter {
opacity: 0;
transform: translateX(20px);
}
.toc-sidebar-enter-active {
opacity: 1;
transform: translateX(0);
}
.toc-sidebar-exit {
opacity: 1;
transform: translateX(0);
}
.toc-sidebar-exit-active {
opacity: 0;
transform: translateX(20px);
}
.toc-mobile {
transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
will-change: opacity, transform;
}
.toc-mobile-enter {
opacity: 0;
transform: translateY(20px);
}
.toc-mobile-enter-active {
opacity: 1;
transform: translateY(0);
}
.toc-mobile-exit {
opacity: 1;
transform: translateY(0);
}
.toc-mobile-exit-active {
opacity: 0;
transform: translateY(20px);
}
.toc-button {
transition: all 0.2s ease-in-out;
}
.toc-button:active {
transform: scale(0.95);
}
.toc-button:hover {
transform: scale(1.05);
}
@layer components {
.type-display {
font-size: clamp(2.2rem, 1.6rem + 2.4vw, 3.5rem);
line-height: 1.2;
font-weight: var(--font-weight-semibold);
font-family: var(--font-serif-cn), var(--font-serif-eng), "Songti SC", serif;
letter-spacing: 0.01em; /* 手写风格字体需要稍微增加字间距 */
}
.type-title {
font-size: clamp(1.6rem, 1.1rem + 1.4vw, 2.6rem);
line-height: 1.3;
font-weight: var(--font-weight-semibold);
font-family: var(--font-serif-cn), var(--font-serif-eng), "Songti SC", serif;
letter-spacing: 0.025em; /* 手写风格字体需要稍微增加字间距 */
}
.type-subtitle {
font-size: clamp(1.25rem, 0.9rem + 1vw, 1.9rem);
line-height: 1.35;
font-weight: var(--font-weight-medium);
font-family: var(--font-serif-cn), var(--font-serif-eng), "Songti SC", serif;
letter-spacing: 0.02em; /* 保持原有字间距 */
}
.type-body {
font-size: clamp(1rem, 0.2vw + 0.9rem, 1.15rem);
line-height: var(--line-height-body);
font-weight: var(--font-weight-regular);
}
.type-small {
font-size: clamp(0.85rem, 0.2vw + 0.8rem, 0.95rem);
line-height: 1.4;
font-weight: var(--font-weight-regular);
}
h1 {
font-weight: 700;
letter-spacing: 0.01em; /* 手写风格字体需要稍微增加字间距 */
font-family: var(--font-serif-cn), var(--font-serif-eng), "Songti SC", serif;
}
h2 {
font-weight: 600;
letter-spacing: 0.015em; /* 手写风格字体需要稍微增加字间距 */
font-family: var(--font-serif-cn), var(--font-serif-eng), "Songti SC", serif;
}
.type-nav {
font-size: clamp(0.95rem, 0.2vw + 0.85rem, 1.05rem);
font-weight: var(--font-weight-medium);
letter-spacing: 0.04em;
}
.motion-card {
transition: transform var(--motion-duration-medium) var(--motion-ease-snappy),
box-shadow var(--motion-duration-medium) var(--motion-ease-snappy),
background-color var(--motion-duration-medium) var(--motion-ease-snappy),
border-color var(--motion-duration-medium) var(--motion-ease-snappy);
}
.motion-card:hover {
transform: translateY(var(--card-translate-y));
}
.motion-link {
transition: color var(--motion-duration-short) var(--motion-ease-snappy),
transform var(--motion-duration-short) var(--motion-ease-snappy);
}
}
/* Pagefind Search Styles - Use CSS variables to override defaults */
:root {
--pagefind-ui-scale: 1;
--pagefind-ui-primary: #2563eb;
--pagefind-ui-text: #0f172a;
--pagefind-ui-background: #ffffff;
--pagefind-ui-border: #e2e8f0;
--pagefind-ui-tag: #f1f5f9;
--pagefind-ui-border-width: 1px;
--pagefind-ui-border-radius: 0.5rem;
--pagefind-ui-font: var(--font-system-sans);
}
.dark {
--pagefind-ui-primary: #60a5fa;
--pagefind-ui-text: #f1f5f9;
--pagefind-ui-background: #0f172a;
--pagefind-ui-border: #475569;
--pagefind-ui-tag: #334155;
}
/* Enhanced text colors for better readability */
.pagefind-ui__result-title {
color: var(--pagefind-ui-text) !important;
}
.dark .pagefind-ui__result-title {
color: #f8fafc !important;
}
.pagefind-ui__result-excerpt {
color: #475569 !important;
}
.dark .pagefind-ui__result-excerpt {
color: #cbd5e1 !important;
}
.pagefind-ui__result-link {
color: var(--pagefind-ui-primary) !important;
}
.dark .pagefind-ui__result-link {
color: #93c5fd !important;
}
/* Additional custom styling for highlights */
.pagefind-ui__result-excerpt mark {
@apply bg-yellow-200 font-semibold text-slate-900 dark:bg-yellow-600 dark:text-slate-100;
padding: 0.125rem 0.25rem;
border-radius: 0.25rem;
}
.pagefind-ui__search-input:focus {
@apply ring-2 ring-blue-500 dark:ring-blue-400;
}
/* Code Syntax Highlighting Styles (rehype-pretty-code) */
.prose pre {
@apply overflow-x-auto rounded-lg border border-slate-200 dark:border-slate-700;
padding: 1rem 1.2rem;
margin: 1.5rem 0;
background-color: #f8fafc;
}
.dark .prose pre {
background-color: #0f172a;
}
.prose pre > code {
@apply grid;
counter-reset: line;
font-size: 0.9em;
line-height: 1.7;
}
.prose pre > code > [data-line] {
padding: 0 1rem;
border-left: 2px solid transparent;
}
.prose pre > code > [data-line]::before {
counter-increment: line;
content: counter(line);
display: inline-block;
width: 1.5rem;
margin-right: 1.5rem;
text-align: right;
color: #94a3b8;
user-select: none;
}
.dark .prose pre > code > [data-line]::before {
color: #475569;
}
/* Highlighted lines */
.prose pre > code > [data-highlighted-line] {
background-color: rgba(59, 130, 246, 0.1);
border-left-color: rgb(59, 130, 246);
}
.dark .prose pre > code > [data-highlighted-line] {
background-color: rgba(96, 165, 250, 0.15);
border-left-color: rgb(96, 165, 250);
}
/* Inline code */
.prose :not(pre) > code {
@apply rounded bg-slate-100 px-1.5 py-0.5 text-sm font-semibold text-slate-800 dark:bg-slate-800 dark:text-slate-200;
white-space: nowrap;
}
/* Code title (if specified in markdown: ```js title="example.js") */
.prose [data-rehype-pretty-code-title] {
@apply rounded-t-lg border border-b-0 border-slate-200 bg-slate-100 px-4 py-2 text-sm font-semibold text-slate-700 dark:border-slate-700 dark:bg-slate-800 dark:text-slate-300;
margin-bottom: 0;
}
.prose [data-rehype-pretty-code-title] + pre {
@apply mt-0 rounded-t-none;
}
/* GitHub-style Callouts/Alerts */
.prose .callout {
@apply my-6 rounded-lg border-l-4 p-4 shadow-sm;
background: linear-gradient(135deg, var(--callout-bg-start), var(--callout-bg-end));
}
.prose .callout-header {
@apply mb-3 flex items-center gap-2;
}
.prose .callout-icon {
@apply text-2xl;
line-height: 1;
}
.prose .callout-title {
@apply text-sm font-bold uppercase tracking-wider;
color: var(--callout-title-color);
letter-spacing: 0.05em;
}
.prose .callout-content {
@apply text-sm leading-relaxed;
}
.prose .callout-content > *:first-child {
@apply mt-0;
}
.prose .callout-content > *:last-child {
@apply mb-0;
}
/* NOTE - Blue */
.prose .callout-note {
--callout-bg-start: rgba(59, 130, 246, 0.08);
--callout-bg-end: rgba(59, 130, 246, 0.04);
--callout-title-color: #2563eb;
@apply border-blue-500;
}
.dark .prose .callout-note {
--callout-bg-start: rgba(96, 165, 250, 0.12);
--callout-bg-end: rgba(96, 165, 250, 0.06);
--callout-title-color: #93c5fd;
@apply border-blue-400;
}
/* TIP - Green */
.prose .callout-tip {
--callout-bg-start: rgba(34, 197, 94, 0.08);
--callout-bg-end: rgba(34, 197, 94, 0.04);
--callout-title-color: #16a34a;
@apply border-green-500;
}
.dark .prose .callout-tip {
--callout-bg-start: rgba(74, 222, 128, 0.12);
--callout-bg-end: rgba(74, 222, 128, 0.06);
--callout-title-color: #86efac;
@apply border-green-400;
}
/* IMPORTANT - Purple */
.prose .callout-important {
--callout-bg-start: rgba(168, 85, 247, 0.08);
--callout-bg-end: rgba(168, 85, 247, 0.04);
--callout-title-color: #9333ea;
@apply border-purple-500;
}
.dark .prose .callout-important {
--callout-bg-start: rgba(192, 132, 252, 0.12);
--callout-bg-end: rgba(192, 132, 252, 0.06);
--callout-title-color: #c084fc;
@apply border-purple-400;
}
/* WARNING - Orange/Yellow */
.prose .callout-warning {
--callout-bg-start: rgba(251, 191, 36, 0.08);
--callout-bg-end: rgba(251, 191, 36, 0.04);
--callout-title-color: #d97706;
@apply border-yellow-500;
}
.dark .prose .callout-warning {
--callout-bg-start: rgba(253, 224, 71, 0.12);
--callout-bg-end: rgba(253, 224, 71, 0.06);
--callout-title-color: #fde047;
@apply border-yellow-400;
}
/* CAUTION - Red */
.prose .callout-caution {
--callout-bg-start: rgba(239, 68, 68, 0.08);
--callout-bg-end: rgba(239, 68, 68, 0.04);
--callout-title-color: #dc2626;
@apply border-red-500;
}
.dark .prose .callout-caution {
--callout-bg-start: rgba(248, 113, 113, 0.12);
--callout-bg-end: rgba(248, 113, 113, 0.06);
--callout-title-color: #fca5a5;
@apply border-red-400;
}