- - {page.title} - + + + + diff --git a/src/pages/archives/index.astro b/src/pages/archives/index.astro index 424366f..5b56899 100644 --- a/src/pages/archives/index.astro +++ b/src/pages/archives/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import { getAllPosts } from '@/lib/posts'; const posts = getAllPosts(); @@ -7,7 +8,7 @@ const posts = getAllPosts(); - 归档 + {posts.map((post) => ( diff --git a/src/pages/categories/[category].astro b/src/pages/categories/[category].astro index 741540b..7104d64 100644 --- a/src/pages/categories/[category].astro +++ b/src/pages/categories/[category].astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import PostCard from '@/components/PostCard.astro'; import { getAllCategories, getAllPosts } from '@/lib/posts'; @@ -16,7 +17,7 @@ const posts = getAllPosts().filter((post) => post.categories.includes(category)) - {category} + {posts.map((post) => )} diff --git a/src/pages/categories/index.astro b/src/pages/categories/index.astro index b3e7944..fc75480 100644 --- a/src/pages/categories/index.astro +++ b/src/pages/categories/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import { getAllCategories, getAllPosts } from '@/lib/posts'; const posts = getAllPosts(); @@ -8,7 +9,7 @@ const categories = getAllCategories(); - 分类 + {categories.map((category) => ( diff --git a/src/pages/index.astro b/src/pages/index.astro index 1a7b927..7a590e6 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,9 +1,12 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import HomeSidebar from '@/components/HomeSidebar.astro'; import PostCard from '@/components/PostCard.astro'; -import { getAllPosts } from '@/lib/posts'; +import { getAllCategories, getAllPosts, getAllTags } from '@/lib/posts'; const posts = getAllPosts(); +const tags = getAllTags(); +const categories = getAllCategories(); --- @@ -14,13 +17,16 @@ const posts = getAllPosts(); 把折腾、学习和生活片段安静地收在这里。 - - - 最新文章 - 查看全部 - - - {posts.slice(0, 16).map((post) => )} + + + + 最新文章 + 查看全部 + + + {posts.slice(0, 16).map((post) => )} + + diff --git a/src/pages/posts/[slug].astro b/src/pages/posts/[slug].astro index 47e8f31..b0a676c 100644 --- a/src/pages/posts/[slug].astro +++ b/src/pages/posts/[slug].astro @@ -1,4 +1,5 @@ --- +import ArticleSidebar from '@/components/ArticleSidebar.astro'; import BaseLayout from '@/layouts/BaseLayout.astro'; import { getAllPosts } from '@/lib/posts'; @@ -11,20 +12,93 @@ export function getStaticPaths() { const { post } = Astro.props; const { Content } = post; +const allPosts = getAllPosts(); + +function slugifyHeading(value: string): string { + return value + .trim() + .toLowerCase() + .replace(/<[^>]+>/g, '') + .replace(/[`*_~[\]()]/g, '') + .replace(/[^\p{Letter}\p{Number}\s-]/gu, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); +} + +function makeToc(raw: string) { + const used = new Map(); + let inFence = false; + + return raw + .split(/\r?\n/) + .flatMap((line) => { + if (/^\s*```/.test(line)) { + inFence = !inFence; + return []; + } + + if (inFence) return []; + + const match = /^(#{1,3})\s+(.+?)\s*#*$/.exec(line); + if (!match) return []; + + const text = match[2].replace(/<[^>]+>/g, '').trim(); + if (!text) return []; + + const baseId = slugifyHeading(text) || 'section'; + const count = used.get(baseId) ?? 0; + used.set(baseId, count + 1); + + return [{ + depth: match[1].length, + text, + id: count === 0 ? baseId : `${baseId}-${count}`, + }]; + }); +} + +const toc = makeToc(post.rawContent?.() ?? ''); +const relatedPosts = allPosts + .filter((item) => item.slug !== post.slug) + .filter((item) => item.categories.some((category) => post.categories.includes(category))) + .slice(0, 8); --- - - - {post.dateText} - {post.title} - - {post.categories.map((category) => {category})} - {post.tags.map((tag) => #{tag})} + + + + {post.dateText} + {post.title} + + {post.categories.map((category) => {category})} + {post.tags.map((tag) => #{tag})} + + + + - - - - - + + + + + diff --git a/src/pages/tags/[tag].astro b/src/pages/tags/[tag].astro index b7aae79..921b30c 100644 --- a/src/pages/tags/[tag].astro +++ b/src/pages/tags/[tag].astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import PostCard from '@/components/PostCard.astro'; import { getAllPosts, getAllTags } from '@/lib/posts'; @@ -16,7 +17,7 @@ const posts = getAllPosts().filter((post) => post.tags.includes(tag)); - #{tag} + {posts.map((post) => )} diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro index 5b20782..d9bc971 100644 --- a/src/pages/tags/index.astro +++ b/src/pages/tags/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import { getAllPosts, getAllTags } from '@/lib/posts'; const posts = getAllPosts(); @@ -8,7 +9,7 @@ const tags = getAllTags(); - 标签 + {tags.map((tag) => ( diff --git a/src/styles/site.css b/src/styles/site.css index a23e266..f1b34bd 100644 --- a/src/styles/site.css +++ b/src/styles/site.css @@ -8,6 +8,11 @@ --accent: #0f766e; --accent-strong: #14532d; --shadow: 0 14px 45px rgba(58, 51, 38, 0.1); + --page-gutter: clamp(20px, 4vw, 64px); + --wide-width: 1440px; + --content-width: 1280px; + --article-width: 1080px; + --narrow-width: 1000px; } * { @@ -33,45 +38,106 @@ a { .site-header { position: sticky; - top: 0; + top: 12px; z-index: 10; - border-bottom: 1px solid rgba(222, 216, 204, 0.8); - background: rgba(255, 253, 248, 0.88); - backdrop-filter: blur(14px); + pointer-events: none; } .nav { - width: min(1120px, calc(100% - 32px)); - min-height: 64px; + position: relative; + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); + min-height: 58px; margin: 0 auto; + padding: 0 18px; display: flex; align-items: center; justify-content: space-between; gap: 24px; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.56); + border-radius: 999px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.52), rgba(255, 255, 255, 0.2)), + rgba(255, 255, 255, 0.28); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + inset 0 -18px 38px rgba(255, 255, 255, 0.16), + 0 18px 54px rgba(44, 55, 63, 0.18); + backdrop-filter: blur(22px) saturate(180%); + -webkit-backdrop-filter: blur(22px) saturate(180%); + pointer-events: auto; +} + +.nav::before { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.72), transparent 30%), + linear-gradient(90deg, rgba(255, 255, 255, 0.42), transparent 38%, rgba(255, 255, 255, 0.22)); + opacity: 0.74; + pointer-events: none; +} + +.nav::after { + content: ""; + position: absolute; + top: 7px; + right: 16px; + left: 16px; + height: 1px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.78); + pointer-events: none; } .brand { + position: relative; + z-index: 1; font-size: 1.05rem; font-weight: 800; } .nav-links { + position: relative; + z-index: 1; display: flex; align-items: center; - gap: 18px; + gap: 6px; color: var(--muted); font-size: 0.94rem; white-space: nowrap; } +.nav-links a { + border: 1px solid transparent; + border-radius: 999px; + padding: 7px 12px; + transition: + color 0.2s ease, + background-color 0.2s ease, + border-color 0.2s ease, + box-shadow 0.2s ease, + transform 0.2s ease; +} + .nav-links a:hover, .site-footer a:hover, .section-heading a:hover { color: var(--accent); } +.nav-links a:hover { + border-color: rgba(255, 255, 255, 0.62); + background: rgba(255, 255, 255, 0.38); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.72), + 0 8px 18px rgba(15, 118, 110, 0.1); + transform: translateY(-1px); +} + .hero { - width: min(1120px, calc(100% - 32px)); + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); min-height: 360px; margin: 0 auto; display: grid; @@ -99,12 +165,96 @@ a { } .content-wrap { - width: min(980px, calc(100% - 32px)); + width: min(var(--content-width), calc(100% - (var(--page-gutter) * 2))); margin: 0 auto 72px; } .content-wrap.narrow { - width: min(780px, calc(100% - 32px)); + width: min(var(--narrow-width), calc(100% - (var(--page-gutter) * 2))); +} + +.page-header-card { + position: relative; + overflow: hidden; + margin: 40px 0 26px; + border: 1px solid rgba(255, 255, 255, 0.58); + border-radius: 8px; + padding: clamp(26px, 5vw, 44px); + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.78), transparent 36%), + linear-gradient(135deg, rgba(255, 255, 255, 0.58), rgba(238, 250, 243, 0.36)), + rgba(255, 253, 248, 0.72); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + 0 18px 48px rgba(58, 51, 38, 0.12); + backdrop-filter: blur(18px) saturate(165%); + -webkit-backdrop-filter: blur(18px) saturate(165%); +} + +.page-header-card::after { + content: ""; + position: absolute; + top: 10px; + right: 18px; + left: 18px; + height: 1px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.76); +} + +.page-header-eyebrow { + margin: 0 0 8px; + color: var(--accent); + font-size: 0.86rem; + font-weight: 700; +} + +.page-header-card h1, +.content-wrap .page-header-card h1 { + margin: 0; + color: #20242a; + font-size: clamp(2rem, 5vw, 3.6rem); + line-height: 1.08; +} + +.page-header-subtitle { + max-width: 620px; + margin: 12px 0 0; + color: var(--muted); + font-size: 1rem; +} + +.page-with-card { + margin-bottom: 0; +} + +.page-article { + margin-top: 0; +} + +.post-layout { + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); + margin: 40px auto 84px; + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + align-items: start; + gap: 28px; +} + +.post-layout .article { + width: 100%; + margin: 0; +} + +.home-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + align-items: start; + gap: 24px; +} + +.home-main { + min-width: 0; } .section-heading { @@ -150,6 +300,396 @@ a { color: var(--muted); } +.home-sidebar { + display: grid; + gap: 20px; +} + +.sticky-sidebar { + position: sticky; + top: 84px; + display: grid; + gap: 20px; +} + +.sidebar-card { + overflow: hidden; + border: 1px solid rgba(222, 216, 204, 0.72); + border-radius: 8px; + padding: 18px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.72), rgba(242, 251, 237, 0.78)), + rgba(255, 253, 248, 0.76); + box-shadow: var(--shadow); + backdrop-filter: blur(12px); +} + +.sidebar-card h2 { + display: flex; + align-items: center; + gap: 8px; + margin: 0 0 14px; + color: #4d5258; + font-size: 1.08rem; + font-weight: 700; +} + +.sidebar-card h2 em { + margin-left: auto; + min-width: 28px; + border-radius: 999px; + padding: 2px 8px; + color: #6d9cc2; + font-size: 0.78rem; + font-style: normal; + text-align: center; + background: rgba(102, 200, 245, 0.18); +} + +.profile-card { + text-align: center; +} + +.profile-avatar { + width: 112px; + height: 112px; + margin: 0 auto 14px; + border-radius: 999px; + object-fit: cover; + box-shadow: 0 12px 30px rgba(15, 118, 110, 0.18); +} + +.profile-name { + margin-bottom: 16px; + font-family: Georgia, "Times New Roman", serif; + color: #585b5e; + font-size: 1.9rem; + letter-spacing: 0; +} + +.profile-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + margin-bottom: 18px; +} + +.profile-stats a { + display: grid; + gap: 3px; + color: var(--muted); +} + +.profile-stats strong { + color: #2f4358; + font-size: 1.22rem; + font-weight: 600; +} + +.profile-stats a:first-child strong, +.notice-card strong, +.schedule-left span, +.calendar-summary span { + color: #66c8f5; +} + +.follow-button { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 44px; + border-radius: 8px; + color: #fff; + font-weight: 700; + background: #3eb8be; + transition: + transform 0.2s ease, + background-color 0.2s ease; +} + +.follow-button:hover { + color: #fff; + transform: translateY(-1px); + background: #279fa5; +} + +.github-mark { + font-size: 0.9rem; +} + +.profile-links { + display: flex; + justify-content: center; + gap: 18px; + margin-top: 16px; + color: #3f5368; + font-weight: 700; +} + +.notice-card p { + margin: 0.65em 0; + color: #596068; + line-height: 1.9; +} + +.calendar-widget, +.schedule-widget { + display: grid; + grid-template-columns: 0.9fr 1.1fr; + gap: 16px; + align-items: center; +} + +.calendar-summary, +.schedule-left { + display: grid; + gap: 4px; + text-align: center; +} + +.calendar-summary strong, +.schedule-left strong { + color: #4b5560; + font-size: 0.9rem; +} + +.calendar-summary span, +.schedule-left span { + font-size: 2.25rem; + line-height: 1.1; + font-weight: 800; +} + +.calendar-summary small, +.schedule-left small { + color: var(--muted); + font-size: 0.78rem; +} + +.calendar-grid { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + gap: 5px 7px; + color: #8aa6be; + font-size: 0.78rem; + text-align: center; +} + +.calendar-grid span { + display: grid; + place-items: center; + min-height: 20px; + border-radius: 999px; +} + +.calendar-grid .muted { + opacity: 0.45; +} + +.calendar-grid .today { + color: #fff; + background: #66c8f5; +} + +.schedule-bars { + display: grid; + gap: 8px; +} + +.schedule-row { + display: grid; + grid-template-columns: 34px 1fr 64px; + align-items: center; + gap: 8px; + color: var(--muted); + font-size: 0.78rem; +} + +.schedule-row progress { + width: 100%; + height: 18px; + overflow: hidden; + border: 0; + border-radius: 4px; + background: rgba(102, 200, 245, 0.18); +} + +.schedule-row progress::-webkit-progress-bar { + background: rgba(102, 200, 245, 0.18); +} + +.schedule-row progress::-webkit-progress-value { + background: rgba(102, 200, 245, 0.58); +} + +.schedule-row progress::-moz-progress-bar { + background: rgba(102, 200, 245, 0.58); +} + +.schedule-row em { + font-style: normal; +} + +.sidebar-posts { + display: grid; +} + +.sidebar-post { + display: grid; + grid-template-columns: 70px minmax(0, 1fr); + gap: 12px; + align-items: center; + padding: 12px 0; + border-top: 1px dashed rgba(104, 111, 120, 0.28); +} + +.sidebar-post:first-child { + border-top: 0; + padding-top: 0; +} + +.sidebar-post img, +.post-thumb-fallback { + width: 70px; + aspect-ratio: 1; + border-radius: 6px; + object-fit: cover; + background: linear-gradient(135deg, rgba(62, 184, 190, 0.32), rgba(102, 200, 245, 0.34)); +} + +.sidebar-post strong { + display: -webkit-box; + overflow: hidden; + color: #4d5258; + font-size: 0.94rem; + font-weight: 500; + line-height: 1.45; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.sidebar-post time { + display: block; + margin-top: 3px; +} + +.article-sidebar { + position: sticky; + top: 90px; + display: grid; + gap: 20px; +} + +.article-author-card { + text-align: center; + background: + radial-gradient(circle at 50% 0%, rgba(102, 200, 245, 0.32), transparent 42%), + linear-gradient(135deg, rgba(255, 255, 255, 0.58), rgba(214, 238, 255, 0.3)), + rgba(255, 253, 248, 0.62); +} + +.article-author-card .profile-avatar { + width: 88px; + height: 88px; +} + +.article-author-card .profile-name { + margin-bottom: 8px; + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-size: 1.45rem; + font-weight: 800; +} + +.article-author-meta { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 8px; + color: var(--muted); + font-size: 0.86rem; +} + +.article-author-meta a { + color: var(--accent); +} + +.toc-list, +.same-category-list { + display: grid; + gap: 6px; +} + +.toc-list { + position: relative; + padding-top: 10px; +} + +.toc-list::before { + content: ""; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 5px; + border-radius: 999px; + background-image: radial-gradient(circle, rgba(102, 200, 245, 0.72) 2px, transparent 2px); + background-size: 14px 5px; +} + +.toc-list a, +.same-category-list a { + border-radius: 8px; + padding: 9px 11px; + color: #50637a; + line-height: 1.45; + transition: + color 0.2s ease, + background-color 0.2s ease, + transform 0.2s ease; +} + +.toc-list a:hover, +.same-category-list a:hover { + color: #1f618d; + background: rgba(102, 200, 245, 0.16); + transform: translateX(2px); +} + +.toc-depth-2 { + padding-left: 22px !important; +} + +.toc-depth-3 { + padding-left: 34px !important; + font-size: 0.9rem; +} + +.same-category-list a { + display: grid; + gap: 3px; + border-top: 1px dashed rgba(104, 111, 120, 0.22); +} + +.same-category-list a:first-child { + border-top: 0; +} + +.same-category-list strong { + display: -webkit-box; + overflow: hidden; + font-size: 0.94rem; + font-weight: 600; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.sidebar-empty { + margin: 0; + color: var(--muted); + font-size: 0.92rem; +} + time { color: var(--muted); font-size: 0.9rem; @@ -172,13 +712,20 @@ time { } .article { - width: min(820px, calc(100% - 32px)); + width: min(var(--article-width), calc(100% - (var(--page-gutter) * 2))); margin: 64px auto 84px; - border: 1px solid var(--line); + border: 1px solid rgba(255, 255, 255, 0.58); border-radius: 8px; padding: clamp(24px, 5vw, 48px); - background: rgba(255, 253, 248, 0.93); - box-shadow: var(--shadow); + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.72), transparent 34%), + linear-gradient(145deg, rgba(255, 255, 255, 0.62), rgba(226, 248, 241, 0.35)), + rgba(255, 253, 248, 0.68); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + 0 24px 64px rgba(44, 55, 63, 0.16); + backdrop-filter: blur(18px) saturate(165%); + -webkit-backdrop-filter: blur(18px) saturate(165%); } .article-header { @@ -194,6 +741,7 @@ time { } .prose :where(h2, h3, h4) { + scroll-margin-top: 110px; margin-top: 2em; line-height: 1.35; } @@ -256,7 +804,7 @@ time { } .site-footer { - width: min(1120px, calc(100% - 32px)); + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); margin: 0 auto; padding: 32px 0 44px; display: flex; @@ -265,25 +813,80 @@ time { color: var(--muted); } +@media (min-width: 960px) { + .post-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 1100px) { + .post-layout { + grid-template-columns: 1fr; + } + + .article-sidebar { + position: static; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .toc-card { + grid-column: 1 / -1; + } + + .home-layout { + grid-template-columns: 1fr; + } + + .home-sidebar { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .sticky-sidebar { + position: static; + } +} + @media (max-width: 720px) { + :root { + --page-gutter: 16px; + } + .nav { min-height: auto; - padding: 14px 0; + padding: 12px 14px; align-items: flex-start; flex-direction: column; + border-radius: 24px; } .nav-links { width: 100%; overflow-x: auto; - gap: 14px; + gap: 6px; padding-bottom: 2px; } + .nav-links a { + flex: 0 0 auto; + } + .hero { min-height: 300px; } + .home-sidebar { + grid-template-columns: 1fr; + } + + .calendar-widget, + .schedule-widget { + grid-template-columns: 1fr; + } + + .article-sidebar { + grid-template-columns: 1fr; + } + .archive-list a { grid-template-columns: 1fr; gap: 2px;
diff --git a/src/pages/archives/index.astro b/src/pages/archives/index.astro index 424366f..5b56899 100644 --- a/src/pages/archives/index.astro +++ b/src/pages/archives/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import { getAllPosts } from '@/lib/posts'; const posts = getAllPosts(); @@ -7,7 +8,7 @@ const posts = getAllPosts(); - 归档 + {posts.map((post) => ( diff --git a/src/pages/categories/[category].astro b/src/pages/categories/[category].astro index 741540b..7104d64 100644 --- a/src/pages/categories/[category].astro +++ b/src/pages/categories/[category].astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import PostCard from '@/components/PostCard.astro'; import { getAllCategories, getAllPosts } from '@/lib/posts'; @@ -16,7 +17,7 @@ const posts = getAllPosts().filter((post) => post.categories.includes(category)) - {category} + {posts.map((post) => )} diff --git a/src/pages/categories/index.astro b/src/pages/categories/index.astro index b3e7944..fc75480 100644 --- a/src/pages/categories/index.astro +++ b/src/pages/categories/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import { getAllCategories, getAllPosts } from '@/lib/posts'; const posts = getAllPosts(); @@ -8,7 +9,7 @@ const categories = getAllCategories(); - 分类 + {categories.map((category) => ( diff --git a/src/pages/index.astro b/src/pages/index.astro index 1a7b927..7a590e6 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,9 +1,12 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import HomeSidebar from '@/components/HomeSidebar.astro'; import PostCard from '@/components/PostCard.astro'; -import { getAllPosts } from '@/lib/posts'; +import { getAllCategories, getAllPosts, getAllTags } from '@/lib/posts'; const posts = getAllPosts(); +const tags = getAllTags(); +const categories = getAllCategories(); --- @@ -14,13 +17,16 @@ const posts = getAllPosts(); 把折腾、学习和生活片段安静地收在这里。 - - - 最新文章 - 查看全部 - - - {posts.slice(0, 16).map((post) => )} + + + + 最新文章 + 查看全部 + + + {posts.slice(0, 16).map((post) => )} + + diff --git a/src/pages/posts/[slug].astro b/src/pages/posts/[slug].astro index 47e8f31..b0a676c 100644 --- a/src/pages/posts/[slug].astro +++ b/src/pages/posts/[slug].astro @@ -1,4 +1,5 @@ --- +import ArticleSidebar from '@/components/ArticleSidebar.astro'; import BaseLayout from '@/layouts/BaseLayout.astro'; import { getAllPosts } from '@/lib/posts'; @@ -11,20 +12,93 @@ export function getStaticPaths() { const { post } = Astro.props; const { Content } = post; +const allPosts = getAllPosts(); + +function slugifyHeading(value: string): string { + return value + .trim() + .toLowerCase() + .replace(/<[^>]+>/g, '') + .replace(/[`*_~[\]()]/g, '') + .replace(/[^\p{Letter}\p{Number}\s-]/gu, '') + .replace(/\s+/g, '-') + .replace(/-+/g, '-') + .replace(/^-|-$/g, ''); +} + +function makeToc(raw: string) { + const used = new Map(); + let inFence = false; + + return raw + .split(/\r?\n/) + .flatMap((line) => { + if (/^\s*```/.test(line)) { + inFence = !inFence; + return []; + } + + if (inFence) return []; + + const match = /^(#{1,3})\s+(.+?)\s*#*$/.exec(line); + if (!match) return []; + + const text = match[2].replace(/<[^>]+>/g, '').trim(); + if (!text) return []; + + const baseId = slugifyHeading(text) || 'section'; + const count = used.get(baseId) ?? 0; + used.set(baseId, count + 1); + + return [{ + depth: match[1].length, + text, + id: count === 0 ? baseId : `${baseId}-${count}`, + }]; + }); +} + +const toc = makeToc(post.rawContent?.() ?? ''); +const relatedPosts = allPosts + .filter((item) => item.slug !== post.slug) + .filter((item) => item.categories.some((category) => post.categories.includes(category))) + .slice(0, 8); --- - - - {post.dateText} - {post.title} - - {post.categories.map((category) => {category})} - {post.tags.map((tag) => #{tag})} + + + + {post.dateText} + {post.title} + + {post.categories.map((category) => {category})} + {post.tags.map((tag) => #{tag})} + + + + - - - - - + + + + + diff --git a/src/pages/tags/[tag].astro b/src/pages/tags/[tag].astro index b7aae79..921b30c 100644 --- a/src/pages/tags/[tag].astro +++ b/src/pages/tags/[tag].astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import PostCard from '@/components/PostCard.astro'; import { getAllPosts, getAllTags } from '@/lib/posts'; @@ -16,7 +17,7 @@ const posts = getAllPosts().filter((post) => post.tags.includes(tag)); - #{tag} + {posts.map((post) => )} diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro index 5b20782..d9bc971 100644 --- a/src/pages/tags/index.astro +++ b/src/pages/tags/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import { getAllPosts, getAllTags } from '@/lib/posts'; const posts = getAllPosts(); @@ -8,7 +9,7 @@ const tags = getAllTags(); - 标签 + {tags.map((tag) => ( diff --git a/src/styles/site.css b/src/styles/site.css index a23e266..f1b34bd 100644 --- a/src/styles/site.css +++ b/src/styles/site.css @@ -8,6 +8,11 @@ --accent: #0f766e; --accent-strong: #14532d; --shadow: 0 14px 45px rgba(58, 51, 38, 0.1); + --page-gutter: clamp(20px, 4vw, 64px); + --wide-width: 1440px; + --content-width: 1280px; + --article-width: 1080px; + --narrow-width: 1000px; } * { @@ -33,45 +38,106 @@ a { .site-header { position: sticky; - top: 0; + top: 12px; z-index: 10; - border-bottom: 1px solid rgba(222, 216, 204, 0.8); - background: rgba(255, 253, 248, 0.88); - backdrop-filter: blur(14px); + pointer-events: none; } .nav { - width: min(1120px, calc(100% - 32px)); - min-height: 64px; + position: relative; + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); + min-height: 58px; margin: 0 auto; + padding: 0 18px; display: flex; align-items: center; justify-content: space-between; gap: 24px; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.56); + border-radius: 999px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.52), rgba(255, 255, 255, 0.2)), + rgba(255, 255, 255, 0.28); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + inset 0 -18px 38px rgba(255, 255, 255, 0.16), + 0 18px 54px rgba(44, 55, 63, 0.18); + backdrop-filter: blur(22px) saturate(180%); + -webkit-backdrop-filter: blur(22px) saturate(180%); + pointer-events: auto; +} + +.nav::before { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.72), transparent 30%), + linear-gradient(90deg, rgba(255, 255, 255, 0.42), transparent 38%, rgba(255, 255, 255, 0.22)); + opacity: 0.74; + pointer-events: none; +} + +.nav::after { + content: ""; + position: absolute; + top: 7px; + right: 16px; + left: 16px; + height: 1px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.78); + pointer-events: none; } .brand { + position: relative; + z-index: 1; font-size: 1.05rem; font-weight: 800; } .nav-links { + position: relative; + z-index: 1; display: flex; align-items: center; - gap: 18px; + gap: 6px; color: var(--muted); font-size: 0.94rem; white-space: nowrap; } +.nav-links a { + border: 1px solid transparent; + border-radius: 999px; + padding: 7px 12px; + transition: + color 0.2s ease, + background-color 0.2s ease, + border-color 0.2s ease, + box-shadow 0.2s ease, + transform 0.2s ease; +} + .nav-links a:hover, .site-footer a:hover, .section-heading a:hover { color: var(--accent); } +.nav-links a:hover { + border-color: rgba(255, 255, 255, 0.62); + background: rgba(255, 255, 255, 0.38); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.72), + 0 8px 18px rgba(15, 118, 110, 0.1); + transform: translateY(-1px); +} + .hero { - width: min(1120px, calc(100% - 32px)); + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); min-height: 360px; margin: 0 auto; display: grid; @@ -99,12 +165,96 @@ a { } .content-wrap { - width: min(980px, calc(100% - 32px)); + width: min(var(--content-width), calc(100% - (var(--page-gutter) * 2))); margin: 0 auto 72px; } .content-wrap.narrow { - width: min(780px, calc(100% - 32px)); + width: min(var(--narrow-width), calc(100% - (var(--page-gutter) * 2))); +} + +.page-header-card { + position: relative; + overflow: hidden; + margin: 40px 0 26px; + border: 1px solid rgba(255, 255, 255, 0.58); + border-radius: 8px; + padding: clamp(26px, 5vw, 44px); + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.78), transparent 36%), + linear-gradient(135deg, rgba(255, 255, 255, 0.58), rgba(238, 250, 243, 0.36)), + rgba(255, 253, 248, 0.72); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + 0 18px 48px rgba(58, 51, 38, 0.12); + backdrop-filter: blur(18px) saturate(165%); + -webkit-backdrop-filter: blur(18px) saturate(165%); +} + +.page-header-card::after { + content: ""; + position: absolute; + top: 10px; + right: 18px; + left: 18px; + height: 1px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.76); +} + +.page-header-eyebrow { + margin: 0 0 8px; + color: var(--accent); + font-size: 0.86rem; + font-weight: 700; +} + +.page-header-card h1, +.content-wrap .page-header-card h1 { + margin: 0; + color: #20242a; + font-size: clamp(2rem, 5vw, 3.6rem); + line-height: 1.08; +} + +.page-header-subtitle { + max-width: 620px; + margin: 12px 0 0; + color: var(--muted); + font-size: 1rem; +} + +.page-with-card { + margin-bottom: 0; +} + +.page-article { + margin-top: 0; +} + +.post-layout { + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); + margin: 40px auto 84px; + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + align-items: start; + gap: 28px; +} + +.post-layout .article { + width: 100%; + margin: 0; +} + +.home-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + align-items: start; + gap: 24px; +} + +.home-main { + min-width: 0; } .section-heading { @@ -150,6 +300,396 @@ a { color: var(--muted); } +.home-sidebar { + display: grid; + gap: 20px; +} + +.sticky-sidebar { + position: sticky; + top: 84px; + display: grid; + gap: 20px; +} + +.sidebar-card { + overflow: hidden; + border: 1px solid rgba(222, 216, 204, 0.72); + border-radius: 8px; + padding: 18px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.72), rgba(242, 251, 237, 0.78)), + rgba(255, 253, 248, 0.76); + box-shadow: var(--shadow); + backdrop-filter: blur(12px); +} + +.sidebar-card h2 { + display: flex; + align-items: center; + gap: 8px; + margin: 0 0 14px; + color: #4d5258; + font-size: 1.08rem; + font-weight: 700; +} + +.sidebar-card h2 em { + margin-left: auto; + min-width: 28px; + border-radius: 999px; + padding: 2px 8px; + color: #6d9cc2; + font-size: 0.78rem; + font-style: normal; + text-align: center; + background: rgba(102, 200, 245, 0.18); +} + +.profile-card { + text-align: center; +} + +.profile-avatar { + width: 112px; + height: 112px; + margin: 0 auto 14px; + border-radius: 999px; + object-fit: cover; + box-shadow: 0 12px 30px rgba(15, 118, 110, 0.18); +} + +.profile-name { + margin-bottom: 16px; + font-family: Georgia, "Times New Roman", serif; + color: #585b5e; + font-size: 1.9rem; + letter-spacing: 0; +} + +.profile-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + margin-bottom: 18px; +} + +.profile-stats a { + display: grid; + gap: 3px; + color: var(--muted); +} + +.profile-stats strong { + color: #2f4358; + font-size: 1.22rem; + font-weight: 600; +} + +.profile-stats a:first-child strong, +.notice-card strong, +.schedule-left span, +.calendar-summary span { + color: #66c8f5; +} + +.follow-button { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 44px; + border-radius: 8px; + color: #fff; + font-weight: 700; + background: #3eb8be; + transition: + transform 0.2s ease, + background-color 0.2s ease; +} + +.follow-button:hover { + color: #fff; + transform: translateY(-1px); + background: #279fa5; +} + +.github-mark { + font-size: 0.9rem; +} + +.profile-links { + display: flex; + justify-content: center; + gap: 18px; + margin-top: 16px; + color: #3f5368; + font-weight: 700; +} + +.notice-card p { + margin: 0.65em 0; + color: #596068; + line-height: 1.9; +} + +.calendar-widget, +.schedule-widget { + display: grid; + grid-template-columns: 0.9fr 1.1fr; + gap: 16px; + align-items: center; +} + +.calendar-summary, +.schedule-left { + display: grid; + gap: 4px; + text-align: center; +} + +.calendar-summary strong, +.schedule-left strong { + color: #4b5560; + font-size: 0.9rem; +} + +.calendar-summary span, +.schedule-left span { + font-size: 2.25rem; + line-height: 1.1; + font-weight: 800; +} + +.calendar-summary small, +.schedule-left small { + color: var(--muted); + font-size: 0.78rem; +} + +.calendar-grid { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + gap: 5px 7px; + color: #8aa6be; + font-size: 0.78rem; + text-align: center; +} + +.calendar-grid span { + display: grid; + place-items: center; + min-height: 20px; + border-radius: 999px; +} + +.calendar-grid .muted { + opacity: 0.45; +} + +.calendar-grid .today { + color: #fff; + background: #66c8f5; +} + +.schedule-bars { + display: grid; + gap: 8px; +} + +.schedule-row { + display: grid; + grid-template-columns: 34px 1fr 64px; + align-items: center; + gap: 8px; + color: var(--muted); + font-size: 0.78rem; +} + +.schedule-row progress { + width: 100%; + height: 18px; + overflow: hidden; + border: 0; + border-radius: 4px; + background: rgba(102, 200, 245, 0.18); +} + +.schedule-row progress::-webkit-progress-bar { + background: rgba(102, 200, 245, 0.18); +} + +.schedule-row progress::-webkit-progress-value { + background: rgba(102, 200, 245, 0.58); +} + +.schedule-row progress::-moz-progress-bar { + background: rgba(102, 200, 245, 0.58); +} + +.schedule-row em { + font-style: normal; +} + +.sidebar-posts { + display: grid; +} + +.sidebar-post { + display: grid; + grid-template-columns: 70px minmax(0, 1fr); + gap: 12px; + align-items: center; + padding: 12px 0; + border-top: 1px dashed rgba(104, 111, 120, 0.28); +} + +.sidebar-post:first-child { + border-top: 0; + padding-top: 0; +} + +.sidebar-post img, +.post-thumb-fallback { + width: 70px; + aspect-ratio: 1; + border-radius: 6px; + object-fit: cover; + background: linear-gradient(135deg, rgba(62, 184, 190, 0.32), rgba(102, 200, 245, 0.34)); +} + +.sidebar-post strong { + display: -webkit-box; + overflow: hidden; + color: #4d5258; + font-size: 0.94rem; + font-weight: 500; + line-height: 1.45; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.sidebar-post time { + display: block; + margin-top: 3px; +} + +.article-sidebar { + position: sticky; + top: 90px; + display: grid; + gap: 20px; +} + +.article-author-card { + text-align: center; + background: + radial-gradient(circle at 50% 0%, rgba(102, 200, 245, 0.32), transparent 42%), + linear-gradient(135deg, rgba(255, 255, 255, 0.58), rgba(214, 238, 255, 0.3)), + rgba(255, 253, 248, 0.62); +} + +.article-author-card .profile-avatar { + width: 88px; + height: 88px; +} + +.article-author-card .profile-name { + margin-bottom: 8px; + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-size: 1.45rem; + font-weight: 800; +} + +.article-author-meta { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 8px; + color: var(--muted); + font-size: 0.86rem; +} + +.article-author-meta a { + color: var(--accent); +} + +.toc-list, +.same-category-list { + display: grid; + gap: 6px; +} + +.toc-list { + position: relative; + padding-top: 10px; +} + +.toc-list::before { + content: ""; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 5px; + border-radius: 999px; + background-image: radial-gradient(circle, rgba(102, 200, 245, 0.72) 2px, transparent 2px); + background-size: 14px 5px; +} + +.toc-list a, +.same-category-list a { + border-radius: 8px; + padding: 9px 11px; + color: #50637a; + line-height: 1.45; + transition: + color 0.2s ease, + background-color 0.2s ease, + transform 0.2s ease; +} + +.toc-list a:hover, +.same-category-list a:hover { + color: #1f618d; + background: rgba(102, 200, 245, 0.16); + transform: translateX(2px); +} + +.toc-depth-2 { + padding-left: 22px !important; +} + +.toc-depth-3 { + padding-left: 34px !important; + font-size: 0.9rem; +} + +.same-category-list a { + display: grid; + gap: 3px; + border-top: 1px dashed rgba(104, 111, 120, 0.22); +} + +.same-category-list a:first-child { + border-top: 0; +} + +.same-category-list strong { + display: -webkit-box; + overflow: hidden; + font-size: 0.94rem; + font-weight: 600; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.sidebar-empty { + margin: 0; + color: var(--muted); + font-size: 0.92rem; +} + time { color: var(--muted); font-size: 0.9rem; @@ -172,13 +712,20 @@ time { } .article { - width: min(820px, calc(100% - 32px)); + width: min(var(--article-width), calc(100% - (var(--page-gutter) * 2))); margin: 64px auto 84px; - border: 1px solid var(--line); + border: 1px solid rgba(255, 255, 255, 0.58); border-radius: 8px; padding: clamp(24px, 5vw, 48px); - background: rgba(255, 253, 248, 0.93); - box-shadow: var(--shadow); + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.72), transparent 34%), + linear-gradient(145deg, rgba(255, 255, 255, 0.62), rgba(226, 248, 241, 0.35)), + rgba(255, 253, 248, 0.68); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + 0 24px 64px rgba(44, 55, 63, 0.16); + backdrop-filter: blur(18px) saturate(165%); + -webkit-backdrop-filter: blur(18px) saturate(165%); } .article-header { @@ -194,6 +741,7 @@ time { } .prose :where(h2, h3, h4) { + scroll-margin-top: 110px; margin-top: 2em; line-height: 1.35; } @@ -256,7 +804,7 @@ time { } .site-footer { - width: min(1120px, calc(100% - 32px)); + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); margin: 0 auto; padding: 32px 0 44px; display: flex; @@ -265,25 +813,80 @@ time { color: var(--muted); } +@media (min-width: 960px) { + .post-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 1100px) { + .post-layout { + grid-template-columns: 1fr; + } + + .article-sidebar { + position: static; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .toc-card { + grid-column: 1 / -1; + } + + .home-layout { + grid-template-columns: 1fr; + } + + .home-sidebar { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .sticky-sidebar { + position: static; + } +} + @media (max-width: 720px) { + :root { + --page-gutter: 16px; + } + .nav { min-height: auto; - padding: 14px 0; + padding: 12px 14px; align-items: flex-start; flex-direction: column; + border-radius: 24px; } .nav-links { width: 100%; overflow-x: auto; - gap: 14px; + gap: 6px; padding-bottom: 2px; } + .nav-links a { + flex: 0 0 auto; + } + .hero { min-height: 300px; } + .home-sidebar { + grid-template-columns: 1fr; + } + + .calendar-widget, + .schedule-widget { + grid-template-columns: 1fr; + } + + .article-sidebar { + grid-template-columns: 1fr; + } + .archive-list a { grid-template-columns: 1fr; gap: 2px;
- - {post.dateText} - {post.title} - - {post.categories.map((category) => {category})} - {post.tags.map((tag) => #{tag})} + + + + {post.dateText} + {post.title} + + {post.categories.map((category) => {category})} + {post.tags.map((tag) => #{tag})} + + + + - - - - - + + + + + diff --git a/src/pages/tags/[tag].astro b/src/pages/tags/[tag].astro index b7aae79..921b30c 100644 --- a/src/pages/tags/[tag].astro +++ b/src/pages/tags/[tag].astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import PostCard from '@/components/PostCard.astro'; import { getAllPosts, getAllTags } from '@/lib/posts'; @@ -16,7 +17,7 @@ const posts = getAllPosts().filter((post) => post.tags.includes(tag)); - #{tag} + {posts.map((post) => )} diff --git a/src/pages/tags/index.astro b/src/pages/tags/index.astro index 5b20782..d9bc971 100644 --- a/src/pages/tags/index.astro +++ b/src/pages/tags/index.astro @@ -1,5 +1,6 @@ --- import BaseLayout from '@/layouts/BaseLayout.astro'; +import PageHeaderCard from '@/components/PageHeaderCard.astro'; import { getAllPosts, getAllTags } from '@/lib/posts'; const posts = getAllPosts(); @@ -8,7 +9,7 @@ const tags = getAllTags(); - 标签 + {tags.map((tag) => ( diff --git a/src/styles/site.css b/src/styles/site.css index a23e266..f1b34bd 100644 --- a/src/styles/site.css +++ b/src/styles/site.css @@ -8,6 +8,11 @@ --accent: #0f766e; --accent-strong: #14532d; --shadow: 0 14px 45px rgba(58, 51, 38, 0.1); + --page-gutter: clamp(20px, 4vw, 64px); + --wide-width: 1440px; + --content-width: 1280px; + --article-width: 1080px; + --narrow-width: 1000px; } * { @@ -33,45 +38,106 @@ a { .site-header { position: sticky; - top: 0; + top: 12px; z-index: 10; - border-bottom: 1px solid rgba(222, 216, 204, 0.8); - background: rgba(255, 253, 248, 0.88); - backdrop-filter: blur(14px); + pointer-events: none; } .nav { - width: min(1120px, calc(100% - 32px)); - min-height: 64px; + position: relative; + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); + min-height: 58px; margin: 0 auto; + padding: 0 18px; display: flex; align-items: center; justify-content: space-between; gap: 24px; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.56); + border-radius: 999px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.52), rgba(255, 255, 255, 0.2)), + rgba(255, 255, 255, 0.28); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + inset 0 -18px 38px rgba(255, 255, 255, 0.16), + 0 18px 54px rgba(44, 55, 63, 0.18); + backdrop-filter: blur(22px) saturate(180%); + -webkit-backdrop-filter: blur(22px) saturate(180%); + pointer-events: auto; +} + +.nav::before { + content: ""; + position: absolute; + inset: 0; + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.72), transparent 30%), + linear-gradient(90deg, rgba(255, 255, 255, 0.42), transparent 38%, rgba(255, 255, 255, 0.22)); + opacity: 0.74; + pointer-events: none; +} + +.nav::after { + content: ""; + position: absolute; + top: 7px; + right: 16px; + left: 16px; + height: 1px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.78); + pointer-events: none; } .brand { + position: relative; + z-index: 1; font-size: 1.05rem; font-weight: 800; } .nav-links { + position: relative; + z-index: 1; display: flex; align-items: center; - gap: 18px; + gap: 6px; color: var(--muted); font-size: 0.94rem; white-space: nowrap; } +.nav-links a { + border: 1px solid transparent; + border-radius: 999px; + padding: 7px 12px; + transition: + color 0.2s ease, + background-color 0.2s ease, + border-color 0.2s ease, + box-shadow 0.2s ease, + transform 0.2s ease; +} + .nav-links a:hover, .site-footer a:hover, .section-heading a:hover { color: var(--accent); } +.nav-links a:hover { + border-color: rgba(255, 255, 255, 0.62); + background: rgba(255, 255, 255, 0.38); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.72), + 0 8px 18px rgba(15, 118, 110, 0.1); + transform: translateY(-1px); +} + .hero { - width: min(1120px, calc(100% - 32px)); + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); min-height: 360px; margin: 0 auto; display: grid; @@ -99,12 +165,96 @@ a { } .content-wrap { - width: min(980px, calc(100% - 32px)); + width: min(var(--content-width), calc(100% - (var(--page-gutter) * 2))); margin: 0 auto 72px; } .content-wrap.narrow { - width: min(780px, calc(100% - 32px)); + width: min(var(--narrow-width), calc(100% - (var(--page-gutter) * 2))); +} + +.page-header-card { + position: relative; + overflow: hidden; + margin: 40px 0 26px; + border: 1px solid rgba(255, 255, 255, 0.58); + border-radius: 8px; + padding: clamp(26px, 5vw, 44px); + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.78), transparent 36%), + linear-gradient(135deg, rgba(255, 255, 255, 0.58), rgba(238, 250, 243, 0.36)), + rgba(255, 253, 248, 0.72); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + 0 18px 48px rgba(58, 51, 38, 0.12); + backdrop-filter: blur(18px) saturate(165%); + -webkit-backdrop-filter: blur(18px) saturate(165%); +} + +.page-header-card::after { + content: ""; + position: absolute; + top: 10px; + right: 18px; + left: 18px; + height: 1px; + border-radius: 999px; + background: rgba(255, 255, 255, 0.76); +} + +.page-header-eyebrow { + margin: 0 0 8px; + color: var(--accent); + font-size: 0.86rem; + font-weight: 700; +} + +.page-header-card h1, +.content-wrap .page-header-card h1 { + margin: 0; + color: #20242a; + font-size: clamp(2rem, 5vw, 3.6rem); + line-height: 1.08; +} + +.page-header-subtitle { + max-width: 620px; + margin: 12px 0 0; + color: var(--muted); + font-size: 1rem; +} + +.page-with-card { + margin-bottom: 0; +} + +.page-article { + margin-top: 0; +} + +.post-layout { + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); + margin: 40px auto 84px; + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + align-items: start; + gap: 28px; +} + +.post-layout .article { + width: 100%; + margin: 0; +} + +.home-layout { + display: grid; + grid-template-columns: minmax(0, 1fr) 320px; + align-items: start; + gap: 24px; +} + +.home-main { + min-width: 0; } .section-heading { @@ -150,6 +300,396 @@ a { color: var(--muted); } +.home-sidebar { + display: grid; + gap: 20px; +} + +.sticky-sidebar { + position: sticky; + top: 84px; + display: grid; + gap: 20px; +} + +.sidebar-card { + overflow: hidden; + border: 1px solid rgba(222, 216, 204, 0.72); + border-radius: 8px; + padding: 18px; + background: + linear-gradient(135deg, rgba(255, 255, 255, 0.72), rgba(242, 251, 237, 0.78)), + rgba(255, 253, 248, 0.76); + box-shadow: var(--shadow); + backdrop-filter: blur(12px); +} + +.sidebar-card h2 { + display: flex; + align-items: center; + gap: 8px; + margin: 0 0 14px; + color: #4d5258; + font-size: 1.08rem; + font-weight: 700; +} + +.sidebar-card h2 em { + margin-left: auto; + min-width: 28px; + border-radius: 999px; + padding: 2px 8px; + color: #6d9cc2; + font-size: 0.78rem; + font-style: normal; + text-align: center; + background: rgba(102, 200, 245, 0.18); +} + +.profile-card { + text-align: center; +} + +.profile-avatar { + width: 112px; + height: 112px; + margin: 0 auto 14px; + border-radius: 999px; + object-fit: cover; + box-shadow: 0 12px 30px rgba(15, 118, 110, 0.18); +} + +.profile-name { + margin-bottom: 16px; + font-family: Georgia, "Times New Roman", serif; + color: #585b5e; + font-size: 1.9rem; + letter-spacing: 0; +} + +.profile-stats { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 10px; + margin-bottom: 18px; +} + +.profile-stats a { + display: grid; + gap: 3px; + color: var(--muted); +} + +.profile-stats strong { + color: #2f4358; + font-size: 1.22rem; + font-weight: 600; +} + +.profile-stats a:first-child strong, +.notice-card strong, +.schedule-left span, +.calendar-summary span { + color: #66c8f5; +} + +.follow-button { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + min-height: 44px; + border-radius: 8px; + color: #fff; + font-weight: 700; + background: #3eb8be; + transition: + transform 0.2s ease, + background-color 0.2s ease; +} + +.follow-button:hover { + color: #fff; + transform: translateY(-1px); + background: #279fa5; +} + +.github-mark { + font-size: 0.9rem; +} + +.profile-links { + display: flex; + justify-content: center; + gap: 18px; + margin-top: 16px; + color: #3f5368; + font-weight: 700; +} + +.notice-card p { + margin: 0.65em 0; + color: #596068; + line-height: 1.9; +} + +.calendar-widget, +.schedule-widget { + display: grid; + grid-template-columns: 0.9fr 1.1fr; + gap: 16px; + align-items: center; +} + +.calendar-summary, +.schedule-left { + display: grid; + gap: 4px; + text-align: center; +} + +.calendar-summary strong, +.schedule-left strong { + color: #4b5560; + font-size: 0.9rem; +} + +.calendar-summary span, +.schedule-left span { + font-size: 2.25rem; + line-height: 1.1; + font-weight: 800; +} + +.calendar-summary small, +.schedule-left small { + color: var(--muted); + font-size: 0.78rem; +} + +.calendar-grid { + display: grid; + grid-template-columns: repeat(7, minmax(0, 1fr)); + gap: 5px 7px; + color: #8aa6be; + font-size: 0.78rem; + text-align: center; +} + +.calendar-grid span { + display: grid; + place-items: center; + min-height: 20px; + border-radius: 999px; +} + +.calendar-grid .muted { + opacity: 0.45; +} + +.calendar-grid .today { + color: #fff; + background: #66c8f5; +} + +.schedule-bars { + display: grid; + gap: 8px; +} + +.schedule-row { + display: grid; + grid-template-columns: 34px 1fr 64px; + align-items: center; + gap: 8px; + color: var(--muted); + font-size: 0.78rem; +} + +.schedule-row progress { + width: 100%; + height: 18px; + overflow: hidden; + border: 0; + border-radius: 4px; + background: rgba(102, 200, 245, 0.18); +} + +.schedule-row progress::-webkit-progress-bar { + background: rgba(102, 200, 245, 0.18); +} + +.schedule-row progress::-webkit-progress-value { + background: rgba(102, 200, 245, 0.58); +} + +.schedule-row progress::-moz-progress-bar { + background: rgba(102, 200, 245, 0.58); +} + +.schedule-row em { + font-style: normal; +} + +.sidebar-posts { + display: grid; +} + +.sidebar-post { + display: grid; + grid-template-columns: 70px minmax(0, 1fr); + gap: 12px; + align-items: center; + padding: 12px 0; + border-top: 1px dashed rgba(104, 111, 120, 0.28); +} + +.sidebar-post:first-child { + border-top: 0; + padding-top: 0; +} + +.sidebar-post img, +.post-thumb-fallback { + width: 70px; + aspect-ratio: 1; + border-radius: 6px; + object-fit: cover; + background: linear-gradient(135deg, rgba(62, 184, 190, 0.32), rgba(102, 200, 245, 0.34)); +} + +.sidebar-post strong { + display: -webkit-box; + overflow: hidden; + color: #4d5258; + font-size: 0.94rem; + font-weight: 500; + line-height: 1.45; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.sidebar-post time { + display: block; + margin-top: 3px; +} + +.article-sidebar { + position: sticky; + top: 90px; + display: grid; + gap: 20px; +} + +.article-author-card { + text-align: center; + background: + radial-gradient(circle at 50% 0%, rgba(102, 200, 245, 0.32), transparent 42%), + linear-gradient(135deg, rgba(255, 255, 255, 0.58), rgba(214, 238, 255, 0.3)), + rgba(255, 253, 248, 0.62); +} + +.article-author-card .profile-avatar { + width: 88px; + height: 88px; +} + +.article-author-card .profile-name { + margin-bottom: 8px; + font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + font-size: 1.45rem; + font-weight: 800; +} + +.article-author-meta { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 8px; + color: var(--muted); + font-size: 0.86rem; +} + +.article-author-meta a { + color: var(--accent); +} + +.toc-list, +.same-category-list { + display: grid; + gap: 6px; +} + +.toc-list { + position: relative; + padding-top: 10px; +} + +.toc-list::before { + content: ""; + position: absolute; + top: 0; + right: 0; + left: 0; + height: 5px; + border-radius: 999px; + background-image: radial-gradient(circle, rgba(102, 200, 245, 0.72) 2px, transparent 2px); + background-size: 14px 5px; +} + +.toc-list a, +.same-category-list a { + border-radius: 8px; + padding: 9px 11px; + color: #50637a; + line-height: 1.45; + transition: + color 0.2s ease, + background-color 0.2s ease, + transform 0.2s ease; +} + +.toc-list a:hover, +.same-category-list a:hover { + color: #1f618d; + background: rgba(102, 200, 245, 0.16); + transform: translateX(2px); +} + +.toc-depth-2 { + padding-left: 22px !important; +} + +.toc-depth-3 { + padding-left: 34px !important; + font-size: 0.9rem; +} + +.same-category-list a { + display: grid; + gap: 3px; + border-top: 1px dashed rgba(104, 111, 120, 0.22); +} + +.same-category-list a:first-child { + border-top: 0; +} + +.same-category-list strong { + display: -webkit-box; + overflow: hidden; + font-size: 0.94rem; + font-weight: 600; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; +} + +.sidebar-empty { + margin: 0; + color: var(--muted); + font-size: 0.92rem; +} + time { color: var(--muted); font-size: 0.9rem; @@ -172,13 +712,20 @@ time { } .article { - width: min(820px, calc(100% - 32px)); + width: min(var(--article-width), calc(100% - (var(--page-gutter) * 2))); margin: 64px auto 84px; - border: 1px solid var(--line); + border: 1px solid rgba(255, 255, 255, 0.58); border-radius: 8px; padding: clamp(24px, 5vw, 48px); - background: rgba(255, 253, 248, 0.93); - box-shadow: var(--shadow); + background: + radial-gradient(circle at 18% 0%, rgba(255, 255, 255, 0.72), transparent 34%), + linear-gradient(145deg, rgba(255, 255, 255, 0.62), rgba(226, 248, 241, 0.35)), + rgba(255, 253, 248, 0.68); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.82), + 0 24px 64px rgba(44, 55, 63, 0.16); + backdrop-filter: blur(18px) saturate(165%); + -webkit-backdrop-filter: blur(18px) saturate(165%); } .article-header { @@ -194,6 +741,7 @@ time { } .prose :where(h2, h3, h4) { + scroll-margin-top: 110px; margin-top: 2em; line-height: 1.35; } @@ -256,7 +804,7 @@ time { } .site-footer { - width: min(1120px, calc(100% - 32px)); + width: min(var(--wide-width), calc(100% - (var(--page-gutter) * 2))); margin: 0 auto; padding: 32px 0 44px; display: flex; @@ -265,25 +813,80 @@ time { color: var(--muted); } +@media (min-width: 960px) { + .post-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 1100px) { + .post-layout { + grid-template-columns: 1fr; + } + + .article-sidebar { + position: static; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .toc-card { + grid-column: 1 / -1; + } + + .home-layout { + grid-template-columns: 1fr; + } + + .home-sidebar { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } + + .sticky-sidebar { + position: static; + } +} + @media (max-width: 720px) { + :root { + --page-gutter: 16px; + } + .nav { min-height: auto; - padding: 14px 0; + padding: 12px 14px; align-items: flex-start; flex-direction: column; + border-radius: 24px; } .nav-links { width: 100%; overflow-x: auto; - gap: 14px; + gap: 6px; padding-bottom: 2px; } + .nav-links a { + flex: 0 0 auto; + } + .hero { min-height: 300px; } + .home-sidebar { + grid-template-columns: 1fr; + } + + .calendar-widget, + .schedule-widget { + grid-template-columns: 1fr; + } + + .article-sidebar { + grid-template-columns: 1fr; + } + .archive-list a { grid-template-columns: 1fr; gap: 2px;
+ + {post.dateText} + {post.title} + + {post.categories.map((category) => {category})} + {post.tags.map((tag) => #{tag})} + + + + - - - - -