diff --git a/components/right-sidebar.tsx b/components/right-sidebar.tsx
index df967a6..688170c 100644
--- a/components/right-sidebar.tsx
+++ b/components/right-sidebar.tsx
@@ -103,7 +103,7 @@ export function RightSidebar() {
{tag}
diff --git a/components/section-divider.tsx b/components/section-divider.tsx
new file mode 100644
index 0000000..9b44a57
--- /dev/null
+++ b/components/section-divider.tsx
@@ -0,0 +1,53 @@
+'use client';
+
+import { useEffect, useRef, useState, ReactNode } from 'react';
+import clsx from 'clsx';
+
+interface SectionDividerProps {
+ children: ReactNode;
+ className?: string;
+}
+
+export function SectionDivider({ children, className }: SectionDividerProps) {
+ const ref = useRef
(null);
+ const [visible, setVisible] = useState(false);
+
+ useEffect(() => {
+ const el = ref.current;
+ if (!el) return;
+
+ if (!('IntersectionObserver' in window)) {
+ setVisible(true);
+ return;
+ }
+
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setVisible(true);
+ }
+ });
+ },
+ { threshold: 0.15, rootMargin: '0px 0px -20% 0px' }
+ );
+
+ observer.observe(el);
+ return () => observer.disconnect();
+ }, []);
+
+ return (
+
+
+ {children}
+
+ );
+}
diff --git a/styles/globals.css b/styles/globals.css
index e78774a..bdf1d5f 100644
--- a/styles/globals.css
+++ b/styles/globals.css
@@ -7,10 +7,22 @@
--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-size: clamp(15px, 0.65vw + 11px, 19px);
+}
+
+@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 text-[15px] sm:text-base;
+ @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: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang TC', 'Noto Sans TC', 'Microsoft JhengHei', 'Noto Sans', sans-serif;
}
.toc-target-highlight {
@@ -34,6 +46,37 @@ body {
@apply -translate-y-0.5 shadow-md;
}
+.prose {
+ font-size: clamp(1rem, 0.2vw + 0.9rem, 1.2rem);
+ line-height: var(--line-height-body);
+}
+
+.prose h1 {
+ font-size: clamp(2.2rem, 1.4rem + 2.2vw, 3.4rem);
+ line-height: 1.25;
+}
+
+.prose h2 {
+ font-size: clamp(1.8rem, 1.1rem + 1.6vw, 2.8rem);
+ line-height: 1.3;
+}
+
+.prose h3 {
+ font-size: clamp(1.4rem, 0.9rem + 1vw, 2rem);
+ line-height: 1.35;
+}
+
+.prose p,
+.prose li {
+ font-size: clamp(1rem, 0.2vw + 0.9rem, 1.15rem);
+ line-height: var(--line-height-body);
+}
+
+.prose small,
+.prose figcaption {
+ font-size: clamp(0.85rem, 0.2vw + 0.8rem, 0.95rem);
+}
+
.prose h1 > a,
.prose h2 > a,
.prose h3 > a,
@@ -103,6 +146,31 @@ body {
}
@layer components {
+ .type-display {
+ font-size: clamp(2.2rem, 1.6rem + 2.4vw, 3.5rem);
+ line-height: 1.2;
+ }
+
+ .type-title {
+ font-size: clamp(1.6rem, 1.1rem + 1.4vw, 2.6rem);
+ line-height: 1.3;
+ }
+
+ .type-subtitle {
+ font-size: clamp(1.25rem, 0.9rem + 1vw, 1.9rem);
+ line-height: 1.35;
+ }
+
+ .type-body {
+ font-size: clamp(1rem, 0.2vw + 0.9rem, 1.15rem);
+ line-height: var(--line-height-body);
+ }
+
+ .type-small {
+ font-size: clamp(0.85rem, 0.2vw + 0.8rem, 0.95rem);
+ line-height: 1.4;
+ }
+
.motion-card {
transition: transform var(--motion-duration-medium) var(--motion-ease-snappy),
box-shadow var(--motion-duration-medium) var(--motion-ease-snappy),