feat(ui): content-first shelf redesign — responsive cover grid, hover overlays, format badges, skeletons, sticky blurred top bar, brand login, SVG icon set, animated toasts

This commit is contained in:
2026-09-08 13:05:40 +08:00
parent 02ac3e5f7f
commit 32b9d3a136
11 changed files with 468 additions and 128 deletions
+35 -4
View File
@@ -1,7 +1,38 @@
import type { Format } from "../api/types";
export const btn =
"rounded-lg bg-stone-800 px-3 py-1.5 text-sm transition-colors hover:bg-stone-700 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500 active:bg-stone-600 disabled:cursor-not-allowed disabled:opacity-50";
"inline-flex items-center gap-1.5 rounded-lg bg-white/[0.06] px-3 py-1.5 text-sm ring-1 ring-white/10 transition-colors hover:bg-white/[0.11] focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500 active:bg-white/[0.15] disabled:cursor-not-allowed disabled:opacity-50";
export const btnPrimary =
"rounded-lg bg-amber-600 px-3 py-1.5 text-sm text-stone-950 transition-colors hover:bg-amber-500 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500 active:bg-amber-400 disabled:cursor-not-allowed disabled:opacity-50";
"inline-flex items-center gap-1.5 rounded-lg bg-amber-500 px-3 py-1.5 text-sm font-medium text-stone-950 shadow-md shadow-amber-500/20 transition-colors hover:bg-amber-400 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500 active:bg-amber-300 disabled:cursor-not-allowed disabled:opacity-50";
export const btnGhost =
"inline-flex items-center gap-1.5 rounded-lg px-2.5 py-1.5 text-sm text-stone-400 transition-colors hover:bg-white/[0.07] hover:text-stone-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500";
export const input =
"rounded-lg border border-stone-700 bg-stone-900 px-3 py-1.5 text-sm outline-none transition-colors placeholder:text-stone-500 focus:border-amber-500/70";
export const card = "rounded-xl border border-stone-800 bg-stone-900";
"rounded-lg bg-white/[0.04] px-3 py-1.5 text-sm ring-1 ring-white/10 outline-none transition placeholder:text-stone-500 focus:bg-white/[0.06] focus:ring-2 focus:ring-amber-500/60";
export const card = "rounded-xl bg-stone-900/70 ring-1 ring-white/[0.07]";
// 库/筛选分段 pill
export const pill =
"rounded-full px-3.5 py-1.5 text-sm text-stone-400 ring-1 ring-transparent transition-colors hover:bg-white/[0.07] hover:text-stone-100 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-amber-500";
export const pillActive = "bg-amber-500/15 text-amber-300 ring-amber-500/30 hover:bg-amber-500/20 hover:text-amber-200";
// 格式徽章:按类型着色,色彩之外始终伴随文字,不依赖颜色单独传义
export const FORMAT_BADGE: Record<Format, string> = {
cbz: "bg-orange-500/15 text-orange-300 ring-orange-500/25",
pdf: "bg-rose-500/15 text-rose-300 ring-rose-500/25",
epub: "bg-sky-500/15 text-sky-300 ring-sky-500/25",
txt: "bg-emerald-500/15 text-emerald-300 ring-emerald-500/25",
md: "bg-teal-500/15 text-teal-300 ring-teal-500/25",
};
export function formatBadge(fmt: Format, extra = ""): string {
return `rounded px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide ring-1 ${FORMAT_BADGE[fmt]} ${extra}`;
}
export function formatSize(bytes: number): string {
if (bytes <= 0) return "";
const units = ["B", "KB", "MB", "GB"];
let i = 0;
let n = bytes;
for (; n >= 1024 && i < units.length - 1; i++) n /= 1024;
return `${n >= 10 || i === 0 ? Math.round(n) : n.toFixed(1)} ${units[i]}`;
}