返回顶部按钮迁移为 PaperMod 风格
- button "↑" → <a href="#top"> + 内联 SVG 双上箭头图标 - 滚动超过一屏才显示,添加 opacity/translate-y 过渡动画 - 平滑滚动到顶部并清除 URL hash(history.replaceState) - 无障碍属性:aria-label、title、accesskey="g" Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
61c1ec7282
commit
a15394c935
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="m296-224-56-56 240-240 240 240-56 56-184-183-184 183Zm0-240-56-56 240-240 240 240-56 56-184-183-184 183Z"/></svg>
|
||||
|
After Width: | Height: | Size: 229 B |
@ -21,7 +21,6 @@
|
||||
--color-blue-600: oklch(54.6% 0.245 262.881);
|
||||
--color-blue-700: oklch(48.8% 0.243 264.376);
|
||||
--color-gray-50: oklch(98.5% 0.002 247.839);
|
||||
--color-gray-100: oklch(96.7% 0.003 264.542);
|
||||
--color-gray-200: oklch(92.8% 0.006 264.531);
|
||||
--color-gray-300: oklch(87.2% 0.01 258.338);
|
||||
--color-gray-400: oklch(70.7% 0.022 261.325);
|
||||
@ -205,6 +204,12 @@
|
||||
}
|
||||
}
|
||||
@layer utilities {
|
||||
.pointer-events-none {
|
||||
pointer-events: none;
|
||||
}
|
||||
.visible {
|
||||
visibility: visible;
|
||||
}
|
||||
.relative {
|
||||
position: relative;
|
||||
}
|
||||
@ -301,6 +306,14 @@
|
||||
.flex-1 {
|
||||
flex: 1;
|
||||
}
|
||||
.translate-y-0 {
|
||||
--tw-translate-y: calc(var(--spacing) * 0);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.translate-y-2 {
|
||||
--tw-translate-y: calc(var(--spacing) * 2);
|
||||
translate: var(--tw-translate-x) var(--tw-translate-y);
|
||||
}
|
||||
.cursor-pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -368,9 +381,6 @@
|
||||
.bg-gray-50 {
|
||||
background-color: var(--color-gray-50);
|
||||
}
|
||||
.bg-gray-100 {
|
||||
background-color: var(--color-gray-100);
|
||||
}
|
||||
.bg-gray-900 {
|
||||
background-color: var(--color-gray-900);
|
||||
}
|
||||
@ -511,6 +521,12 @@
|
||||
.underline-offset-\[0\.3rem\] {
|
||||
text-underline-offset: 0.3rem;
|
||||
}
|
||||
.opacity-0 {
|
||||
opacity: 0%;
|
||||
}
|
||||
.opacity-100 {
|
||||
opacity: 100%;
|
||||
}
|
||||
.shadow {
|
||||
--tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
|
||||
box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
|
||||
@ -576,13 +592,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-gray-200 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: var(--color-gray-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
.hover\:bg-red-700 {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
@ -665,11 +674,6 @@
|
||||
background-color: #2e2e33;
|
||||
}
|
||||
}
|
||||
.dark\:bg-\[\#333\] {
|
||||
&:where(.dark, .dark *) {
|
||||
background-color: #333;
|
||||
}
|
||||
}
|
||||
.dark\:bg-\[\#dadadb\] {
|
||||
&:where(.dark, .dark *) {
|
||||
background-color: #dadadb;
|
||||
@ -759,15 +763,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.dark\:hover\:bg-\[\#444\] {
|
||||
&:where(.dark, .dark *) {
|
||||
&:hover {
|
||||
@media (hover: hover) {
|
||||
background-color: #444;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.dark\:hover\:text-\[\#dadadb\] {
|
||||
&:where(.dark, .dark *) {
|
||||
&:hover {
|
||||
@ -787,6 +782,21 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@property --tw-translate-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-translate-y {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-translate-z {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-space-y-reverse {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
@ -910,24 +920,12 @@
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
}
|
||||
@property --tw-translate-x {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-translate-y {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@property --tw-translate-z {
|
||||
syntax: "*";
|
||||
inherits: false;
|
||||
initial-value: 0;
|
||||
}
|
||||
@layer properties {
|
||||
@supports ((-webkit-hyphens: none) and (not (margin-trim: inline))) or ((-moz-orient: inline) and (not (color:rgb(from red r g b)))) {
|
||||
*, ::before, ::after, ::backdrop {
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-translate-z: 0;
|
||||
--tw-space-y-reverse: 0;
|
||||
--tw-border-style: solid;
|
||||
--tw-leading: initial;
|
||||
@ -956,9 +954,6 @@
|
||||
--tw-backdrop-saturate: initial;
|
||||
--tw-backdrop-sepia: initial;
|
||||
--tw-duration: initial;
|
||||
--tw-translate-x: 0;
|
||||
--tw-translate-y: 0;
|
||||
--tw-translate-z: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -172,14 +172,69 @@ fn Pagination() -> Element {
|
||||
|
||||
#[component]
|
||||
fn Footer() -> Element {
|
||||
let mut visible = use_signal(|| false);
|
||||
|
||||
use_effect(move || {
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
if let Some(window) = web_sys::window() {
|
||||
let closure = wasm_bindgen::prelude::Closure::wrap(Box::new(move || {
|
||||
if let Some(w) = web_sys::window() {
|
||||
let threshold = w.inner_height().ok()
|
||||
.and_then(|h| h.as_f64())
|
||||
.unwrap_or(0.0);
|
||||
let scroll_y = w.scroll_y().unwrap_or(0.0);
|
||||
let new_visible = scroll_y > threshold;
|
||||
visible.set(new_visible);
|
||||
}
|
||||
}) as Box<dyn FnMut()>);
|
||||
|
||||
let _ = window.add_event_listener_with_callback("scroll", wasm_bindgen::JsCast::unchecked_ref(closure.as_ref()));
|
||||
|
||||
let threshold = window.inner_height().ok()
|
||||
.and_then(|h| h.as_f64())
|
||||
.unwrap_or(0.0);
|
||||
let scroll_y = window.scroll_y().unwrap_or(0.0);
|
||||
visible.set(scroll_y > threshold);
|
||||
|
||||
closure.forget();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let link_class = use_memo(move || {
|
||||
let base = "p-2 rounded-full cursor-pointer hover:opacity-80 transition-all duration-300 text-gray-600 dark:text-gray-300";
|
||||
if visible() {
|
||||
format!("{} opacity-100 translate-y-0", base)
|
||||
} else {
|
||||
format!("{} opacity-0 translate-y-2 pointer-events-none", base)
|
||||
}
|
||||
});
|
||||
|
||||
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" }
|
||||
button {
|
||||
class: "p-2 rounded-full bg-gray-100 dark:bg-[#333] hover:bg-gray-200 dark:hover:bg-[#444] transition-colors",
|
||||
onclick: move |_| scroll_to_top(),
|
||||
"↑"
|
||||
a {
|
||||
class: "{link_class}",
|
||||
href: "#top",
|
||||
aria_label: "go to top",
|
||||
title: "Go to Top (Alt + G)",
|
||||
accesskey: "g",
|
||||
onclick: move |evt| {
|
||||
evt.prevent_default();
|
||||
scroll_to_top();
|
||||
},
|
||||
svg {
|
||||
xmlns: "http://www.w3.org/2000/svg",
|
||||
height: "24px",
|
||||
view_box: "0 -960 960 960",
|
||||
width: "24px",
|
||||
fill: "currentColor",
|
||||
path {
|
||||
d: "m296-224-56-56 240-240 240 240-56 56-184-183-184 183Zm0-240-56-56 240-240 240 240-56 56-184-183-184 183Z",
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -194,10 +249,10 @@ fn scroll_to_top() {
|
||||
options.top(0.0);
|
||||
options.behavior(web_sys::ScrollBehavior::Smooth);
|
||||
let _ = window.scroll_to_with_scroll_to_options(&options);
|
||||
|
||||
if let Ok(history) = window.history() {
|
||||
let _ = history.replace_state_with_url(&wasm_bindgen::JsValue::NULL, "", Some(" "));
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let _ = ();
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user