From f9d23d1eed190137f0c19cf34134f48e8b21dab5 Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 11 Jun 2026 10:29:11 +0800 Subject: [PATCH] refactor(ui): redesign with warm palette, sage accent, and consistent theme vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Warm editorial color palette (#faf9f6 light / #141416 dark) - Sage green accent (#5c7a5e) for nav, links, buttons, tags - Replace all hardcoded hex colors with CSS theme utilities - Serif font (system Georgia) only on header logo - Better hover states: scale, brightness, color transitions - Accent-colored focus rings on inputs - Fix language mixing: Back to Home → 返回首页 - No external font dependencies --- input.css | 161 ++++++++++++++++++----------- src/components/footer.rs | 8 +- src/components/forms.rs | 8 +- src/components/frontend_layout.rs | 2 +- src/components/header.rs | 10 +- src/components/post/post_footer.rs | 2 +- src/components/post_card.rs | 10 +- src/components/skeletons/atoms.rs | 2 +- src/pages/about.rs | 8 +- src/pages/archives.rs | 20 ++-- src/pages/home.rs | 10 +- src/pages/login.rs | 8 +- src/pages/register.rs | 14 +-- src/pages/search.rs | 8 +- src/pages/tags.rs | 18 ++-- src/theme.rs | 2 +- 16 files changed, 164 insertions(+), 127 deletions(-) diff --git a/input.css b/input.css index 67a55e1..5a7891f 100644 --- a/input.css +++ b/input.css @@ -3,18 +3,20 @@ @custom-variant dark (&:where(.dark, .dark *)); @theme { - /* PaperMod Light Theme */ - --color-paper-theme: #ffffff; + --font-serif: 'Source Serif 4', 'Noto Serif SC', 'Source Han Serif SC', 'Songti SC', Georgia, serif; + + --color-paper-theme: #faf9f6; --color-paper-entry: #ffffff; - --color-paper-primary: #1e1e1e; - --color-paper-secondary: #6c6c6c; - --color-paper-tertiary: #d6d6d6; - --color-paper-content: #1f1f1f; - --color-paper-code-block: #1c1d21; - --color-paper-code-bg: #f5f5f5; - --color-paper-border: #eeeeee; - - /* PaperMod sizing */ + --color-paper-primary: #1c1917; + --color-paper-secondary: #78716c; + --color-paper-tertiary: #d6d3d1; + --color-paper-content: #292524; + --color-paper-code-block: #1c1917; + --color-paper-code-bg: #f5f4f0; + --color-paper-border: #e7e5e0; + --color-paper-accent: #5c7a5e; + --color-paper-accent-soft: #e8f0e8; + --radius-paper: 8px; --gap-paper: 24px; --content-gap-paper: 20px; @@ -26,33 +28,36 @@ html { scroll-behavior: smooth; } - + body { background-color: var(--color-paper-theme); color: var(--color-paper-primary); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif; transition: background-color 0.3s ease, color 0.3s ease; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; } .dark { - --color-paper-theme: #1d1e20; - --color-paper-entry: #2e2e33; - --color-paper-primary: #dadadb; - --color-paper-secondary: #9b9c9d; - --color-paper-tertiary: #414244; - --color-paper-content: #c4c4c5; - --color-paper-code-block: #2e2e33; - --color-paper-code-bg: #37383e; - --color-paper-border: #333333; + --color-paper-theme: #141416; + --color-paper-entry: #1e1f22; + --color-paper-primary: #e7e5df; + --color-paper-secondary: #8c8981; + --color-paper-tertiary: #3a3a3d; + --color-paper-content: #d4d2cc; + --color-paper-code-block: #1e1f22; + --color-paper-code-bg: #28292d; + --color-paper-border: #2a2b2e; + --color-paper-accent: #7da97f; + --color-paper-accent-soft: #1e2e1e; } } @layer components { - /* Post Single Layout */ .post-single { margin: 0 auto; } - /* Post Header */ .post-header { margin: 24px auto var(--content-gap-paper) auto; } @@ -62,12 +67,14 @@ line-height: 1.2; color: var(--color-paper-primary); word-break: break-word; + letter-spacing: -0.02em; } .post-description { margin-top: 10px; color: var(--color-paper-secondary); font-size: 16px; + line-height: 1.7; } .post-meta { @@ -91,7 +98,6 @@ color: var(--color-paper-primary); } - /* Breadcrumbs */ .breadcrumbs { display: flex; flex-wrap: wrap; @@ -112,14 +118,12 @@ text-underline-offset: 0.2rem; } - /* Draft Badge */ .entry-hint { display: inline-flex; margin-left: 0.5rem; color: var(--color-paper-secondary); } - /* TOC */ details.toc { margin-bottom: var(--content-gap-paper); background: var(--color-paper-code-bg); @@ -174,7 +178,6 @@ margin-inline-start: var(--gap-paper); } - /* TOC Animation */ details { interpolate-size: allow-keywords; } @@ -191,7 +194,6 @@ opacity: 1; } - /* Entry Cover */ .entry-cover { margin-bottom: var(--content-gap-paper); } @@ -202,23 +204,23 @@ height: auto; } - /* Post Content */ .post-content { color: var(--color-paper-content); margin: 30px 0; } - /* Markdown Content */ .md-content h1 { margin: 40px auto 32px; font-size: 40px; color: var(--color-paper-primary); + letter-spacing: -0.01em; } .md-content h2 { margin: 32px auto 24px; font-size: 32px; color: var(--color-paper-primary); + letter-spacing: -0.01em; } .md-content h3 { @@ -248,16 +250,16 @@ .md-content a:not(.anchor) { text-underline-offset: 0.3rem; text-decoration: underline; - color: var(--color-paper-primary); + color: var(--color-paper-accent); } .md-content a:not(.anchor):hover { - color: var(--color-paper-secondary); + color: #4a6249; } .md-content p { margin-bottom: var(--content-gap-paper); - line-height: 1.8; + line-height: 1.85; } .md-content p:last-child { @@ -325,7 +327,7 @@ .md-content-img-zoomable { cursor: zoom-in; - transition: opacity 0.2s ease; + transition: opacity 0.2s ease-out; } .md-content-img-zoomable:hover { @@ -337,7 +339,6 @@ border-radius: var(--radius-paper); } - /* Code Blocks */ .md-content code { padding: 0.2rem 0.3rem; font-size: 0.85em; @@ -366,12 +367,10 @@ line-height: 1.6; } - /* Reset display for syntax-highlighted spans to prevent Tailwind .block conflict */ .md-content pre code span { display: inline !important; } - /* Copy Code Button */ .copy-code { display: none; position: absolute; @@ -386,11 +385,11 @@ cursor: pointer; border: none; font-family: inherit; - transition: background 0.2s ease; + transition: background 0.2s ease-out; } .copy-code:hover { - background: rgba(100, 100, 100, 0.9); + background: rgba(92, 122, 94, 0.9); } pre:hover .copy-code, @@ -398,7 +397,6 @@ display: block; } - /* Heading Anchors */ .anchor { display: none; text-decoration: none; @@ -423,12 +421,10 @@ color: var(--color-paper-primary) !important; } - /* Post Footer */ .post-footer { margin-top: var(--content-gap-paper); } - /* Tags */ .post-tags { display: flex; flex-wrap: wrap; @@ -452,15 +448,15 @@ border-radius: var(--radius-paper); border: 1px solid var(--color-paper-border); text-decoration: none; - transition: background 0.2s ease; + transition: all 0.2s ease-out; } .post-tags a:hover { - background: var(--color-paper-border); - color: var(--color-paper-primary); + background: var(--color-paper-accent-soft); + border-color: var(--color-paper-accent); + color: var(--color-paper-accent); } - /* Post Navigation */ .paginav { display: flex; border-radius: var(--radius-paper); @@ -477,7 +473,7 @@ padding: 0.8rem; text-decoration: none; color: var(--color-paper-primary); - transition: background 0.2s ease; + transition: background 0.2s ease-out; } .paginav a:hover { @@ -491,7 +487,6 @@ .paginav .title { letter-spacing: 1px; - text-transform: uppercase; font-size: 0.8rem; color: var(--color-paper-secondary); } @@ -501,7 +496,6 @@ font-weight: 500; } - /* Back to Home */ .back-to-home { margin-top: calc(var(--content-gap-paper) * 1.5); padding-top: var(--content-gap-paper); @@ -527,17 +521,16 @@ text-underline-offset: 0.3rem; } - /* Image Viewer Lightbox */ .image-viewer-overlay { position: fixed; inset: 0; - z-index: 9999; + z-index: 50; background: rgba(0, 0, 0, 0.92); display: flex; align-items: center; justify-content: center; padding: 2rem; - animation: fadeIn 0.2s ease; + animation: fadeIn 0.2s ease-out; } .image-viewer-content { @@ -568,24 +561,23 @@ display: flex; align-items: center; justify-content: center; - transition: background 0.2s ease; + transition: background 0.2s ease-out; } .image-viewer-close:hover { background: rgba(255, 255, 255, 0.4); } - /* Markdown Image Lightbox */ .md-image-lightbox-overlay { position: fixed; inset: 0; - z-index: 9999; + z-index: 50; background: rgba(0, 0, 0, 0.92); display: flex; align-items: center; justify-content: center; padding: 2rem; - animation: fadeIn 0.2s ease; + animation: fadeIn 0.2s ease-out; } .md-image-lightbox-content { @@ -616,14 +608,13 @@ display: flex; align-items: center; justify-content: center; - transition: background 0.2s ease; + transition: background 0.2s ease-out; } .md-image-lightbox-close:hover { background: rgba(255, 255, 255, 0.4); } - /* Post Card Cover Thumbnail */ .post-card-cover { overflow: hidden; border-radius: var(--radius-paper); @@ -633,12 +624,60 @@ width: 100%; height: 100%; object-fit: cover; - transition: transform 0.3s ease; + transition: transform 0.3s ease-out; } .post-card-cover:hover img { transform: scale(1.05); } + + a.accent-link { + color: var(--color-paper-accent); + text-decoration: none; + transition: color 0.2s ease-out; + } + a.accent-link:hover { + color: var(--color-paper-primary); + } + + .btn-primary { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1.25rem; + font-size: 0.875rem; + font-weight: 500; + color: white; + background: var(--color-paper-accent); + border-radius: 9999px; + border: none; + cursor: pointer; + transition: all 0.2s ease-out; + } + .btn-primary:hover { + filter: brightness(1.1); + transform: translateY(-1px); + } + .btn-primary:active { + transform: translateY(0) scale(0.98); + } + + .post-card-accent { + position: relative; + } + .post-card-accent::after { + content: ''; + position: absolute; + bottom: 0; + left: 0; + width: 0; + height: 2px; + background: var(--color-paper-accent); + transition: width 0.3s ease-out; + } + .post-card-accent:hover::after { + width: 100%; + } } @keyframes fadeIn { @@ -646,7 +685,6 @@ to { opacity: 1; } } -/* Responsive */ @media screen and (max-width: 768px) { .post-title { font-size: 32px; @@ -686,7 +724,7 @@ @keyframes pageEnter { from { opacity: 0; - transform: translateY(4px); + transform: translateY(6px); } to { opacity: 1; @@ -698,7 +736,6 @@ animation: pageEnter 0.15s ease-out; } -/* Reduced Motion */ @media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; diff --git a/src/components/footer.rs b/src/components/footer.rs index f2498a3..c722d1e 100644 --- a/src/components/footer.rs +++ b/src/components/footer.rs @@ -67,7 +67,7 @@ pub fn Footer() -> Element { }); let btn_class = use_memo(move || { - let base = "fixed bottom-16 right-8 z-50 w-10 h-10 rounded-full bg-gray-100 dark:bg-[#2d2e30] shadow-md flex items-center justify-center cursor-pointer transition-all duration-300 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-white"; + let base = "fixed bottom-16 right-8 z-50 w-10 h-10 rounded-full bg-paper-entry border border-paper-border shadow-sm flex items-center justify-center cursor-pointer transition-all duration-300 text-paper-secondary hover:text-paper-accent"; if visible() { format!("{} opacity-100 translate-y-0", base) } else { @@ -76,9 +76,9 @@ pub fn Footer() -> Element { }); rsx! { - footer { class: "w-full border-t border-gray-200 dark:border-[#333] mt-auto", - div { class: "max-w-3xl mx-auto px-6 py-5 flex items-center justify-between text-sm text-gray-400 dark:text-[#9b9c9d]", - span { "© 2026 Yggdrasil Blog" } + footer { class: "w-full border-t border-paper-border mt-auto", + div { class: "max-w-3xl mx-auto px-6 py-5 flex items-center justify-between text-sm text-paper-secondary", + span { "© 2026 Yggdrasil" } } } a { diff --git a/src/components/forms.rs b/src/components/forms.rs index 7622955..b815526 100644 --- a/src/components/forms.rs +++ b/src/components/forms.rs @@ -1,8 +1,8 @@ use dioxus::prelude::*; -pub const INPUT_CLASS: &str = "w-full px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600"; +pub const INPUT_CLASS: &str = "w-full px-4 py-2 border border-paper-border rounded-lg bg-paper-entry text-paper-primary placeholder:text-paper-tertiary focus:outline-none focus:border-paper-accent focus:ring-1 focus:ring-paper-accent/30 transition-colors duration-200"; -pub const BUTTON_PRIMARY_CLASS: &str = "w-full py-2 px-4 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 font-medium rounded-full hover:opacity-80 transition-opacity cursor-pointer"; +pub const BUTTON_PRIMARY_CLASS: &str = "w-full py-2.5 px-4 bg-paper-accent text-white font-medium rounded-full hover:brightness-110 active:scale-[0.98] transition-all duration-200 cursor-pointer"; #[component] pub fn FormInput( @@ -31,7 +31,7 @@ pub fn FormInput( #[component] pub fn FormLabel(label: &'static str) -> Element { rsx! { - label { class: "block text-sm font-medium text-gray-700 dark:text-[#9b9c9d] mb-1", + label { class: "block text-sm font-medium text-paper-secondary mb-1", "{label}" } } @@ -42,7 +42,7 @@ pub fn AlertBox(message: String, variant: &'static str) -> Element { let (bg_class, text_class) = match variant { "error" => ("bg-red-100 dark:bg-red-900/30", "text-red-700 dark:text-red-300"), "success" => ("bg-green-100 dark:bg-green-900/30", "text-green-700 dark:text-green-300"), - _ => ("bg-gray-100 dark:bg-[#333]", "text-gray-700 dark:text-[#9b9c9d]"), + _ => ("bg-paper-code-bg", "text-paper-secondary"), }; rsx! { div { class: "mb-4 p-3 {bg_class} {text_class} rounded-lg text-center", diff --git a/src/components/frontend_layout.rs b/src/components/frontend_layout.rs index 3faf287..77b0de8 100644 --- a/src/components/frontend_layout.rs +++ b/src/components/frontend_layout.rs @@ -29,7 +29,7 @@ pub fn FrontendLayout() -> Element { let nav_items = use_nav_items(route.clone()); rsx! { - div { class: "min-h-screen flex flex-col bg-white dark:bg-[#1d1e20]", + div { class: "min-h-screen flex flex-col bg-paper-theme", Header { nav_items, right_content: rsx! { ThemeToggle {} } } main { class: "flex-1 w-full max-w-3xl mx-auto px-6 py-6", SuspenseBoundary { diff --git a/src/components/header.rs b/src/components/header.rs index 7feaf84..6459254 100644 --- a/src/components/header.rs +++ b/src/components/header.rs @@ -13,10 +13,10 @@ pub struct NavItemConfig { #[component] pub fn Header(nav_items: Vec, right_content: Element) -> Element { rsx! { - header { class: "sticky top-0 z-40 w-full border-b border-gray-200 dark:border-[#333] bg-white/80 dark:bg-[#1d1e20]/80 backdrop-blur-sm", + header { class: "sticky top-0 z-40 w-full border-b border-paper-border bg-paper-theme/80 backdrop-blur-sm", nav { class: "max-w-3xl mx-auto px-6 h-[60px] flex items-center justify-between", Link { - class: "text-2xl font-bold text-gray-900 dark:text-[#dadadb] hover:opacity-80 transition-opacity", + class: "text-2xl font-bold font-serif text-paper-primary hover:text-paper-accent transition-colors duration-200", to: Route::Home {}, "Yggdrasil" } @@ -39,12 +39,12 @@ pub fn Header(nav_items: Vec, right_content: Element) -> Element #[component] fn NavItem(route: Route, label: &'static str, is_active: bool) -> Element { - let base_class = "px-3 py-1 text-base rounded-lg transition-colors"; + let base_class = "px-3 py-1 text-base rounded-lg transition-all duration-200"; let class_str = if is_active { - format!("{} font-medium text-gray-900 dark:text-[#dadadb] underline underline-offset-[0.3rem] decoration-2 decoration-gray-900 dark:decoration-[#dadadb]", base_class) + format!("{} font-medium text-paper-accent underline underline-offset-[0.3rem] decoration-2 decoration-paper-accent", base_class) } else { format!( - "{} text-gray-600 dark:text-[#9b9c9d] hover:text-gray-900 dark:hover:text-[#dadadb]", + "{} text-paper-secondary hover:text-paper-primary", base_class ) }; diff --git a/src/components/post/post_footer.rs b/src/components/post/post_footer.rs index b95896a..43bb27f 100644 --- a/src/components/post/post_footer.rs +++ b/src/components/post/post_footer.rs @@ -34,7 +34,7 @@ pub fn PostFooter(post: Post) -> Element { div { class: "back-to-home", Link { to: Route::Home {}, - "← Back to Home" + "← 返回首页" } } } diff --git a/src/components/post_card.rs b/src/components/post_card.rs index 14ce1e3..d3588dd 100644 --- a/src/components/post_card.rs +++ b/src/components/post_card.rs @@ -13,7 +13,7 @@ pub fn PostCard(post: Post) -> Element { rsx! { article { - class: "relative mb-6 p-6 bg-white dark:bg-[#2e2e33] rounded-lg border border-gray-200 dark:border-[#333] hover:-translate-y-0.5 hover:border-gray-300 dark:hover:border-gray-600 transition-all duration-250", + class: "relative mb-6 p-6 bg-paper-entry rounded-lg border border-paper-border hover:-translate-y-0.5 hover:border-paper-accent/50 hover:shadow-sm transition-all duration-200", Link { class: "block group", to: Route::PostDetail { slug: post_slug }, @@ -29,22 +29,22 @@ pub fn PostCard(post: Post) -> Element { } } h2 { - class: "text-2xl font-bold leading-tight text-gray-900 dark:text-[#dadadb] group-hover:opacity-80 transition-opacity", + class: "text-2xl font-bold leading-tight text-paper-primary group-hover:text-paper-accent transition-colors duration-200", "{post.title}" } div { - class: "mt-2 text-sm text-gray-500 dark:text-[#9b9c9d] leading-relaxed line-clamp-2", + class: "mt-2 text-sm text-paper-secondary leading-relaxed line-clamp-2", "{post.summary.as_deref().unwrap_or_default()}" } div { - class: "mt-3 flex items-center gap-3 text-[13px] text-gray-400 dark:text-[#9b9c9d]", + class: "mt-3 flex items-center gap-3 text-[13px] text-paper-secondary", span { "{date_str}" } if !post.tags.is_empty() { span { "·" } for tag in post.tags.clone().into_iter() { span { Link { - class: "hover:text-gray-600 dark:hover:text-[#dadadb] transition-colors", + class: "hover:text-paper-accent transition-colors duration-200", to: Route::TagDetail { tag: tag.clone() }, onclick: move |evt: dioxus::events::MouseEvent| evt.stop_propagation(), "{tag}" diff --git a/src/components/skeletons/atoms.rs b/src/components/skeletons/atoms.rs index 67c0c80..6eba83d 100644 --- a/src/components/skeletons/atoms.rs +++ b/src/components/skeletons/atoms.rs @@ -4,7 +4,7 @@ use dioxus::prelude::*; pub fn SkeletonBox(class: &'static str, style: Option<&'static str>) -> Element { rsx! { div { - class: "bg-gray-200 dark:bg-[#2a2a2a] animate-pulse {class}", + class: "bg-paper-tertiary/30 animate-pulse {class}", style: style.unwrap_or(""), } } diff --git a/src/pages/about.rs b/src/pages/about.rs index ea48f81..d7ea6b4 100644 --- a/src/pages/about.rs +++ b/src/pages/about.rs @@ -4,14 +4,14 @@ use dioxus::prelude::*; pub fn About() -> Element { rsx! { header { class: "page-header mb-6", - h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]", + h1 { class: "text-4xl font-bold text-paper-primary tracking-tight", "关于" } } - article { class: "prose dark:prose-invert max-w-none text-gray-800 dark:text-[#c9cacc] leading-relaxed", + article { class: "prose dark:prose-invert max-w-none text-paper-content leading-relaxed", p { "Yggdrasil 是一个以文字为主的简约博客系统。" } p { "它使用 Rust + Dioxus 构建,采用 PostgreSQL 作为数据库,支持 Markdown 写作、标签管理和暗色模式。" } - h2 { class: "text-xl font-bold text-gray-900 dark:text-[#dadadb] mt-8 mb-4", "技术栈" } + h2 { class: "text-xl font-bold text-paper-primary mt-8 mb-4", "技术栈" } ul { class: "list-disc pl-5 space-y-1", li { "Rust + Dioxus 0.7 (全栈 Web 框架)" } li { "PostgreSQL + tokio-postgres (数据库)" } @@ -19,7 +19,7 @@ pub fn About() -> Element { li { "Tiptap Editor (富文本编辑器)" } li { "pulldown-cmark (Markdown 渲染)" } } - h2 { class: "text-xl font-bold text-gray-900 dark:text-[#dadadb] mt-8 mb-4", "特性" } + h2 { class: "text-xl font-bold text-paper-primary mt-8 mb-4", "特性" } ul { class: "list-disc pl-5 space-y-1", li { "Markdown 写作与实时预览" } li { "文章标签与归档" } diff --git a/src/pages/archives.rs b/src/pages/archives.rs index 43b2336..618354c 100644 --- a/src/pages/archives.rs +++ b/src/pages/archives.rs @@ -81,7 +81,7 @@ fn group_posts(posts: &[Post]) -> Vec { pub fn Archives() -> Element { rsx! { header { class: "page-header mb-6", - h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]", + h1 { class: "text-4xl font-bold text-paper-primary tracking-tight", "归档" } } @@ -98,9 +98,9 @@ fn ArchivesContent() -> Element { Some(Ok(PostListResponse { posts, total })) => { let grouped = group_posts(posts); rsx! { - div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]", + div { class: "mt-2 text-base text-paper-secondary", "共 " - span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{total}" } + span { class: "font-medium text-paper-primary", "{total}" } " 篇文章" } for year_group in grouped.iter() { @@ -134,14 +134,14 @@ fn YearSection(year_group: YearGroup) -> Element { rsx! { div { class: "archive-year mt-10", h2 { - class: "archive-year-header text-2xl font-bold text-gray-900 dark:text-[#dadadb] mb-4", + class: "archive-year-header text-2xl font-bold text-paper-primary mb-4", id: "{year_group.year}", a { class: "archive-header-link hover:opacity-80 transition-opacity", href: "#{year_group.year}", "{year_group.year}" } - sup { class: "archive-count text-sm text-gray-400 dark:text-[#9b9c9d] ml-1", "{total}" } + sup { class: "archive-count text-sm text-paper-secondary ml-1", "{total}" } } for month_group in year_group.months.iter() { MonthSection { month_group: month_group.clone(), year: year_group.year.clone() } @@ -155,16 +155,16 @@ fn MonthSection(month_group: MonthGroup, year: String) -> Element { let count = month_group.posts.len(); rsx! { - div { class: "archive-month flex flex-col md:flex-row md:items-start py-2.5 border-b border-gray-100 dark:border-[#333]/50", + div { class: "archive-month flex flex-col md:flex-row md:items-start py-2.5 border-b border-paper-border/50", h3 { - class: "archive-month-header text-lg font-medium text-gray-700 dark:text-[#9b9c9d] md:w-[200px] shrink-0 mt-0 mb-0 py-1.5", + class: "archive-month-header text-lg font-medium text-paper-secondary md:w-[200px] shrink-0 mt-0 mb-0 py-1.5", id: "{year}-{month_group.month_en}", a { class: "archive-header-link hover:opacity-80 transition-opacity", href: "#{year}-{month_group.month_en}", "{month_group.month}" } - sup { class: "archive-count text-sm text-gray-400 dark:text-[#9b9c9d] ml-1", "{count}" } + sup { class: "archive-count text-sm text-paper-secondary ml-1", "{count}" } } div { class: "archive-posts flex-1", for post in month_group.posts.iter() { @@ -181,10 +181,10 @@ fn ArchiveEntry(post: Post) -> Element { rsx! { div { class: "archive-entry relative py-1.5 my-2.5 group", - h3 { class: "archive-entry-title text-base font-normal text-gray-900 dark:text-[#dadadb] m-0", + h3 { class: "archive-entry-title text-base font-normal text-paper-primary m-0", "{post.title}" } - div { class: "archive-meta text-sm text-gray-400 dark:text-[#9b9c9d] mt-1", + div { class: "archive-meta text-sm text-paper-secondary mt-1", "{date_str}" } Link { diff --git a/src/pages/home.rs b/src/pages/home.rs index b1b0fdd..18498ce 100644 --- a/src/pages/home.rs +++ b/src/pages/home.rs @@ -40,7 +40,7 @@ fn HomePosts(current_page: i32) -> Element { PostCard { post: post.clone() } } if posts.is_empty() { - div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20", + div { class: "text-center text-paper-secondary py-20", "暂无文章" } } @@ -66,10 +66,10 @@ fn HomePosts(current_page: i32) -> Element { fn HomeInfo() -> Element { rsx! { div { class: "mb-10 text-center", - h1 { class: "text-[34px] font-bold leading-tight text-gray-900 dark:text-[#dadadb]", + h1 { class: "text-4xl font-bold leading-tight text-paper-primary tracking-tight", "Yggdrasil" } - p { class: "mt-3 text-base text-gray-500 dark:text-[#9b9c9d] leading-relaxed", + p { class: "mt-3 text-base text-paper-secondary leading-relaxed", "以文字为主的简约博客系统" } } @@ -92,7 +92,7 @@ fn Pagination(current_page: i32, total: i64) -> Element { nav { class: "flex mt-10 mb-6 justify-between", if has_prev { Link { - class: "inline-flex items-center px-4 py-2 text-sm text-white bg-gray-900 dark:bg-[#dadadb] dark:text-gray-900 rounded-full hover:opacity-80 transition-opacity cursor-pointer", + class: "inline-flex items-center px-4 py-2 text-sm text-white bg-paper-accent rounded-full hover:brightness-110 active:scale-[0.98] transition-all duration-200 cursor-pointer", to: prev_route, span { class: "mr-1", "«" } "上一页" @@ -100,7 +100,7 @@ fn Pagination(current_page: i32, total: i64) -> Element { } if has_next { Link { - class: "ml-auto inline-flex items-center px-4 py-2 text-sm text-white bg-gray-900 dark:bg-[#dadadb] dark:text-gray-900 rounded-full hover:opacity-80 transition-opacity cursor-pointer", + class: "ml-auto inline-flex items-center px-4 py-2 text-sm text-white bg-paper-accent rounded-full hover:brightness-110 active:scale-[0.98] transition-all duration-200 cursor-pointer", to: Route::HomePage { page: current_page + 1 }, "下一页" span { class: "ml-1", "»" } diff --git a/src/pages/login.rs b/src/pages/login.rs index 4bb3201..976ee17 100644 --- a/src/pages/login.rs +++ b/src/pages/login.rs @@ -51,9 +51,9 @@ pub fn Login() -> Element { }); rsx! { - div { class: "min-h-screen flex items-center justify-center bg-white dark:bg-[#1d1e20]", - div { class: "w-full max-w-md p-8 bg-white dark:bg-[#2e2e33] rounded-2xl border border-gray-200 dark:border-[#333]", - h1 { class: "text-2xl font-bold text-center text-gray-900 dark:text-[#dadadb] mb-6", + div { class: "min-h-screen flex items-center justify-center bg-paper-theme", + div { class: "w-full max-w-md p-8 bg-paper-entry rounded-2xl border border-paper-border shadow-sm", + h1 { class: "text-2xl font-bold text-center text-paper-primary mb-6", "登录" } @@ -88,7 +88,7 @@ pub fn Login() -> Element { "登录" } Link { - class: "block w-full py-2 px-4 text-center text-gray-500 dark:text-[#9b9c9d] hover:text-gray-700 dark:hover:text-[#dadadb] font-medium rounded-lg transition-colors cursor-pointer", + class: "block w-full py-2 px-4 text-center text-paper-secondary hover:text-paper-accent font-medium rounded-lg transition-all duration-200 cursor-pointer", to: Route::Register {}, "还没有账号?去注册" } diff --git a/src/pages/register.rs b/src/pages/register.rs index 7a19851..9f27ec8 100644 --- a/src/pages/register.rs +++ b/src/pages/register.rs @@ -51,19 +51,19 @@ pub fn Register() -> Element { }; rsx! { - div { class: "min-h-screen flex items-center justify-center bg-white dark:bg-[#1d1e20]", - div { class: "w-full max-w-md p-8 bg-white dark:bg-[#2e2e33] rounded-2xl border border-gray-200 dark:border-[#333]", - h1 { class: "text-2xl font-bold text-center text-gray-900 dark:text-[#dadadb] mb-2", + div { class: "min-h-screen flex items-center justify-center bg-paper-theme", + div { class: "w-full max-w-md p-8 bg-paper-entry rounded-2xl border border-paper-border shadow-sm", + h1 { class: "text-2xl font-bold text-center text-paper-primary mb-2", "注册" } - p { class: "text-sm text-center text-gray-500 dark:text-[#9b9c9d] mb-6", + p { class: "text-sm text-center text-paper-secondary mb-6", "首个注册账号将自动成为管理员" } if success() { div { class: "mb-4 p-3 bg-green-100 dark:bg-green-900/30 text-green-700 dark:text-green-300 rounded-lg text-center", "注册成功!" - Link { class: "block mt-2 text-gray-700 dark:text-[#dadadb] hover:underline cursor-pointer", + Link { class: "block mt-2 text-paper-accent hover:underline cursor-pointer", to: Route::Login {}, "去登录" } @@ -121,9 +121,9 @@ pub fn Register() -> Element { "注册" } } - p { class: "mt-4 text-center text-sm text-gray-500 dark:text-[#9b9c9d]", + p { class: "mt-4 text-center text-sm text-paper-secondary", "已有账号?" - Link { class: "text-gray-700 dark:text-[#dadadb] hover:underline cursor-pointer", + Link { class: "text-paper-accent hover:underline cursor-pointer", to: Route::Login {}, "去登录" } diff --git a/src/pages/search.rs b/src/pages/search.rs index 9ab5374..aff4231 100644 --- a/src/pages/search.rs +++ b/src/pages/search.rs @@ -26,14 +26,14 @@ pub fn Search() -> Element { rsx! { header { class: "page-header mb-6", - h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]", + h1 { class: "text-4xl font-bold text-paper-primary tracking-tight", "搜索" } } div { class: "mb-8", div { class: "flex gap-2", input { - class: "flex-1 px-4 py-2 border border-gray-200 dark:border-[#333] rounded-lg bg-white dark:bg-[#2e2e33] text-gray-900 dark:text-[#dadadb] focus:outline-none focus:border-gray-400 dark:focus:border-gray-600", + class: "flex-1 px-4 py-2 border border-paper-border rounded-lg bg-paper-entry text-paper-primary placeholder:text-paper-tertiary focus:outline-none focus:border-paper-accent focus:ring-1 focus:ring-paper-accent/30", r#type: "text", placeholder: "输入关键词搜索文章...", value: query(), @@ -41,7 +41,7 @@ pub fn Search() -> Element { onkeydown: move |e| if e.key() == Key::Enter { on_search() }, } button { - class: "px-6 py-2 bg-gray-900 dark:bg-[#dadadb] text-white dark:text-gray-900 rounded-full font-medium hover:opacity-80 transition-opacity", + class: "px-6 py-2 bg-paper-accent text-white rounded-full font-medium hover:brightness-110 active:scale-[0.98] transition-all duration-200", onclick: move |_| on_search(), "搜索" } @@ -51,7 +51,7 @@ pub fn Search() -> Element { DelayedSkeleton { SearchSkeleton {} } } else if let Some(Ok(PostListResponse { posts, total: _ })) = search_res() { if posts.is_empty() { - div { class: "text-center text-gray-500 dark:text-[#9b9c9d] py-20", + div { class: "text-center text-paper-secondary py-20", "未找到相关文章" } } else { diff --git a/src/pages/tags.rs b/src/pages/tags.rs index 86b054b..803f25b 100644 --- a/src/pages/tags.rs +++ b/src/pages/tags.rs @@ -11,7 +11,7 @@ use crate::router::Route; pub fn Tags() -> Element { rsx! { header { class: "page-header mb-6", - h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]", + h1 { class: "text-4xl font-bold text-paper-primary tracking-tight", "标签" } } @@ -32,21 +32,21 @@ fn TagsContent() -> Element { Some(Ok(tags)) => { let total = tags.iter().map(|t| t.post_count).sum::(); rsx! { - div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]", + div { class: "mt-2 text-base text-paper-secondary", "共 " - span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{tags.len()}" } + span { class: "font-medium text-paper-primary", "{tags.len()}" } " 个标签," - span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{total}" } + span { class: "font-medium text-paper-primary", "{total}" } " 篇文章" } ul { class: "flex flex-wrap gap-4 mt-6", for tag in tags { li { Link { - class: "inline-flex items-center px-3 py-1.5 text-base font-medium bg-gray-100 dark:bg-[#2e2e33] text-gray-700 dark:text-[#9b9c9d] rounded-lg hover:bg-gray-200 dark:hover:bg-[#333] transition-colors", + class: "inline-flex items-center px-3 py-1.5 text-base font-medium bg-paper-accent-soft text-paper-accent rounded-lg hover:bg-paper-accent hover:text-white transition-all duration-200", to: Route::TagDetail { tag: tag.name.clone() }, "{tag.name}" - sup { class: "ml-1 text-sm text-gray-500 dark:text-[#9b9c9d]", "{tag.post_count}" } + sup { class: "ml-1 text-sm text-paper-secondary", "{tag.post_count}" } } } } @@ -72,7 +72,7 @@ fn TagsContent() -> Element { pub fn TagDetail(tag: String) -> Element { rsx! { header { class: "page-header mb-6", - h1 { class: "text-[34px] font-bold text-gray-900 dark:text-[#dadadb]", + h1 { class: "text-4xl font-bold text-paper-primary tracking-tight", "{tag}" } } @@ -92,9 +92,9 @@ fn TagDetailContent(tag: String) -> Element { match posts_data { Some(Ok((posts, total))) => { rsx! { - div { class: "mt-2 text-base text-gray-500 dark:text-[#9b9c9d]", + div { class: "mt-2 text-base text-paper-secondary", "共 " - span { class: "font-medium text-gray-700 dark:text-[#dadadb]", "{total}" } + span { class: "font-medium text-paper-primary", "{total}" } " 篇文章" } for post in posts.iter() { diff --git a/src/theme.rs b/src/theme.rs index e43a144..74b3f1f 100644 --- a/src/theme.rs +++ b/src/theme.rs @@ -135,7 +135,7 @@ pub fn ThemeToggle() -> Element { rsx! { button { - class: "theme-toggle p-2 rounded-full cursor-pointer hover:opacity-80 transition-opacity text-gray-600 dark:text-gray-300", + class: "theme-toggle p-2 rounded-full cursor-pointer hover:text-paper-accent transition-colors duration-200 text-paper-secondary", onclick: move |_| theme.set(theme().toggle()), if mounted() && theme() == Theme::Dark { svg {