feat(back-button): enhance button styles and add variant support; refactor back navigation in sidebar and coins rules screen

This commit is contained in:
2026-06-29 14:54:03 +08:00
parent 096978e703
commit ef18500de3
9 changed files with 104 additions and 119 deletions
+57 -12
View File
@@ -1,29 +1,74 @@
.backButton { .backButton {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0;
color: var(--color-auth-text-primary);
cursor: pointer;
border: 0;
border-radius: 999px;
text-decoration: none;
transition:
background 0.15s ease,
box-shadow 0.18s ease,
transform 0.05s ease;
}
.floating {
position: absolute; position: absolute;
top: var(--spacing-xl); top: var(--spacing-xl);
left: var(--spacing-xl); left: var(--spacing-xl);
z-index: 2; z-index: 2;
display: inline-flex;
align-items: center;
justify-content: center;
width: var(--back-button-size); width: var(--back-button-size);
height: var(--back-button-size); height: var(--back-button-size);
padding: 0 2px 0 0; padding: 0 2px 0 0;
color: var(--color-auth-text-primary);
cursor: pointer;
background: var(--color-auth-surface); background: var(--color-auth-surface);
border: none;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition:
background 0.15s ease,
transform 0.05s ease;
} }
.backButton:hover { .soft {
width: 42px;
height: 42px;
color: #21191d;
border: 1px solid rgba(25, 19, 22, 0.08);
background: rgba(255, 255, 255, 0.82);
box-shadow: 0 10px 22px rgba(50, 30, 40, 0.08);
}
.ghost {
width: 32px;
height: 32px;
color: #000000;
background: transparent;
}
.floating:hover {
background: rgba(0, 0, 0, 0.04); background: rgba(0, 0, 0, 0.04);
} }
.backButton:active { .soft:hover {
background: #ffffff;
box-shadow: 0 14px 26px rgba(50, 30, 40, 0.12);
transform: translateY(-1px);
}
.ghost:hover {
opacity: 0.7;
}
.floating:active {
transform: scale(0.96); transform: scale(0.96);
} }
.soft:active {
transform: translateY(1px);
}
.ghost:active {
transform: scale(0.96);
}
.backButton:focus-visible {
outline: 2px solid #f657a0;
outline-offset: 3px;
}
+40 -4
View File
@@ -1,28 +1,64 @@
"use client"; "use client";
import { ChevronLeft } from "lucide-react"; import { ChevronLeft } from "lucide-react";
import Link, { type LinkProps } from "next/link";
import styles from "./back-button.module.css"; import styles from "./back-button.module.css";
export interface BackButtonProps { interface BackButtonBaseProps {
onClick: () => void;
className?: string; className?: string;
ariaLabel?: string; ariaLabel?: string;
iconSize?: number;
variant?: "floating" | "soft" | "ghost";
} }
type BackButtonLinkProps = BackButtonBaseProps & {
href: LinkProps["href"];
onClick?: never;
};
type BackButtonActionProps = BackButtonBaseProps & {
href?: never;
onClick: () => void;
};
export type BackButtonProps = BackButtonLinkProps | BackButtonActionProps;
export function BackButton({ export function BackButton({
href,
onClick, onClick,
className, className,
ariaLabel = "Back", ariaLabel = "Back",
iconSize = 24,
variant = "floating",
}: BackButtonProps) { }: BackButtonProps) {
const buttonClassName = [
styles.backButton,
styles[variant],
className,
]
.filter(Boolean)
.join(" ");
const icon = (
<ChevronLeft size={iconSize} strokeWidth={2.5} aria-hidden="true" />
);
if (href) {
return (
<Link href={href} className={buttonClassName} aria-label={ariaLabel}>
{icon}
</Link>
);
}
return ( return (
<button <button
type="button" type="button"
className={[styles.backButton, className].filter(Boolean).join(" ")} className={buttonClassName}
onClick={onClick} onClick={onClick}
aria-label={ariaLabel} aria-label={ariaLabel}
> >
<ChevronLeft size={20} strokeWidth={2.5} aria-hidden="true" /> {icon}
</button> </button>
); );
} }
@@ -15,36 +15,6 @@
gap: 18px; gap: 18px;
} }
.backLink {
display: inline-flex;
width: 42px;
height: 42px;
align-items: center;
justify-content: center;
color: #21191d;
text-decoration: none;
border: 1px solid rgba(25, 19, 22, 0.08);
border-radius: 999px;
background: rgba(255, 255, 255, 0.82);
box-shadow: 0 10px 22px rgba(50, 30, 40, 0.08);
transition: background 0.18s ease, box-shadow 0.18s ease, transform 0.16s ease;
}
.backLink:hover {
background: #ffffff;
box-shadow: 0 14px 26px rgba(50, 30, 40, 0.12);
transform: translateY(-1px);
}
.backLink:active {
transform: translateY(1px);
}
.backLink:focus-visible {
outline: 2px solid #f657a0;
outline-offset: 3px;
}
.hero { .hero {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
+4 -7
View File
@@ -1,7 +1,6 @@
"use client"; "use client";
import { import {
ArrowLeft,
Coins, Coins,
Gift, Gift,
ImageIcon, ImageIcon,
@@ -10,8 +9,8 @@ import {
Phone, Phone,
Sparkles, Sparkles,
} from "lucide-react"; } from "lucide-react";
import Link from "next/link";
import { BackButton } from "@/app/_components";
import { MobileShell } from "@/app/_components/core"; import { MobileShell } from "@/app/_components/core";
import { ROUTES } from "@/router/routes"; import { ROUTES } from "@/router/routes";
@@ -69,13 +68,11 @@ export function CoinsRulesScreen() {
<MobileShell background="#fcf3f4"> <MobileShell background="#fcf3f4">
<main className={styles.screen}> <main className={styles.screen}>
<header className={styles.header}> <header className={styles.header}>
<Link <BackButton
href={ROUTES.sidebar} href={ROUTES.sidebar}
className={styles.backLink} variant="soft"
aria-label="Back to sidebar" aria-label="Back to sidebar"
> />
<ArrowLeft size={22} strokeWidth={2.4} aria-hidden="true" />
</Link>
<div className={styles.hero}> <div className={styles.hero}>
<span className={styles.heroIcon} aria-hidden="true"> <span className={styles.heroIcon} aria-hidden="true">
@@ -1,28 +0,0 @@
.bar {
padding: var(--spacing-md, 12px) 0;
display: flex;
align-items: center;
}
.link {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
color: #000000;
text-decoration: none;
background: none;
border: 0;
padding: 0;
cursor: pointer;
}
.link:hover {
opacity: 0.7;
}
.icon {
color: currentColor;
flex-shrink: 0;
}
-34
View File
@@ -1,34 +0,0 @@
"use client";
import { ChevronLeft } from "lucide-react";
import Link from "next/link";
import { ROUTES } from "@/router/routes";
import styles from "./back-bar.module.css";
/**
* 顶部返回栏:图标按钮,链接至聊天页。
*
* 视觉规格(与设计稿对齐):
* - 黑色文本(浅色主题下)
* - 居左对齐,顶部留白
*/
export function BackBar() {
return (
<header className={styles.bar}>
<Link
href={ROUTES.chat}
className={styles.link}
aria-label="Back"
>
<ChevronLeft
size={24}
strokeWidth={2.5}
aria-hidden="true"
className={styles.icon}
/>
</Link>
</header>
);
}
-1
View File
@@ -2,7 +2,6 @@
* @file Automatically generated by barrelsby. * @file Automatically generated by barrelsby.
*/ */
export * from "./back-bar";
export * from "./sidebar-wallet-card"; export * from "./sidebar-wallet-card";
export * from "./user-header"; export * from "./user-header";
export * from "./vip-benefits-card"; export * from "./vip-benefits-card";
@@ -49,7 +49,7 @@
} }
.topBar { .topBar {
margin: 4px 0 -2px; margin: 10px 0 -2px;
} }
.hero { .hero {
+2 -2
View File
@@ -4,13 +4,13 @@ import { LogOut } from "lucide-react";
import { signOut } from "next-auth/react"; import { signOut } from "next-auth/react";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { BackButton } from "@/app/_components";
import { MobileShell } from "@/app/_components/core"; import { MobileShell } from "@/app/_components/core";
import { ROUTES } from "@/router/routes"; import { ROUTES } from "@/router/routes";
import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context"; import { useAuthDispatch, useAuthState } from "@/stores/auth/auth-context";
import { useUserDispatch, useUserState } from "@/stores/user/user-context"; import { useUserDispatch, useUserState } from "@/stores/user/user-context";
import { import {
BackBar,
SidebarWalletCard, SidebarWalletCard,
UserHeader, UserHeader,
type SidebarUserState, type SidebarUserState,
@@ -56,7 +56,7 @@ export function SidebarScreen() {
<div className={styles.bgOrbTwo} aria-hidden="true" /> <div className={styles.bgOrbTwo} aria-hidden="true" />
<div className={styles.topBar}> <div className={styles.topBar}>
<BackBar /> <BackButton href={ROUTES.chat} variant="soft" />
</div> </div>
<section className={`${styles.userSlot} ${styles.revealOne}`}> <section className={`${styles.userSlot} ${styles.revealOne}`}>