feat(back-button): enhance button styles and add variant support; refactor back navigation in sidebar and coins rules screen
This commit is contained in:
@@ -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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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}`}>
|
||||||
|
|||||||
Reference in New Issue
Block a user