fix(web): active lib highlight, rail a11y names, card focus ring, AA accent + destructive tokens, touch-reachable menu, brand wordmark, chrome unify
This commit is contained in:
@@ -27,12 +27,12 @@ function BookCard({ book, index, onOpen, onDelete }: { book: Book; index: number
|
||||
const pct = Math.round(book.percent * 100);
|
||||
return (
|
||||
<div className="group fx-rise relative flex flex-col" style={{ animationDelay: `${Math.min(index, 23) * 25}ms` }}>
|
||||
<button onClick={onOpen} className="block w-full p-0 text-left focus-visible:outline-none">
|
||||
<button onClick={onOpen} className="block w-full rounded-md p-0 text-left outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2">
|
||||
<span className="relative block aspect-[3/4] overflow-hidden rounded-md border bg-muted shadow-sm transition group-hover:shadow-md group-hover:ring-2 group-hover:ring-accent/50">
|
||||
<Cover book={book} className="size-full" />
|
||||
<span className={formatBadge(book.format, "absolute left-1.5 top-1.5")}>{book.format}</span>
|
||||
{book.state === "error" && (
|
||||
<span className="absolute right-1.5 top-1.5 flex items-center gap-1 rounded bg-destructive px-1.5 py-0.5 text-[10px] font-semibold text-primary-foreground" title={book.error}>
|
||||
<span className="absolute right-1.5 top-1.5 flex items-center gap-1 rounded bg-destructive px-1.5 py-0.5 text-[10px] font-semibold text-destructive-foreground" title={book.error}>
|
||||
<TriangleAlert className="size-3" /> 损坏
|
||||
</span>
|
||||
)}
|
||||
@@ -47,7 +47,7 @@ function BookCard({ book, index, onOpen, onDelete }: { book: Book; index: number
|
||||
{book.path.includes("/") ? book.path.slice(0, book.path.lastIndexOf("/")) : `${book.pages > 0 ? book.pages + " 页 · " : ""}${formatSize(book.size)}`}
|
||||
</span>
|
||||
</button>
|
||||
<div className="absolute right-1 top-1 z-10 opacity-0 transition-opacity focus-within:opacity-100 group-hover:opacity-100">
|
||||
<div className="absolute right-1 top-1 z-10 opacity-100 transition-opacity md:opacity-0 md:focus-within:opacity-100 md:group-hover:opacity-100">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="secondary" size="icon" className="size-7 bg-background/80 backdrop-blur" aria-label={`《${book.title}》操作`}>
|
||||
@@ -55,11 +55,11 @@ function BookCard({ book, index, onOpen, onDelete }: { book: Book; index: number
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={onOpen}>打开</DropdownMenuItem>
|
||||
<DropdownMenuItem onSelect={onOpen}>打开</DropdownMenuItem>
|
||||
{isAdmin && (
|
||||
<>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem className="text-destructive focus:text-destructive" onClick={onDelete}>
|
||||
<DropdownMenuItem variant="destructive" onSelect={onDelete}>
|
||||
删除
|
||||
</DropdownMenuItem>
|
||||
</>
|
||||
@@ -74,7 +74,7 @@ function BookCard({ book, index, onOpen, onDelete }: { book: Book; index: number
|
||||
function SectionHead({ label, count }: { label: string; count: number }) {
|
||||
if (!label) return null;
|
||||
return (
|
||||
<h2 className="sticky top-0 z-10 mb-3 flex items-center gap-2 bg-background/90 py-1.5 text-sm font-medium backdrop-blur">
|
||||
<h2 className="sticky top-0 z-10 mb-3 flex items-center gap-2 bg-background/85 py-1.5 text-sm font-medium backdrop-blur">
|
||||
<FolderOpen className="size-4 text-muted-foreground" />
|
||||
{label}
|
||||
<span className="text-xs tabular-nums text-muted-foreground">{count}</span>
|
||||
@@ -148,7 +148,7 @@ export default function Shelf() {
|
||||
|
||||
return (
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-col px-4 pb-20 sm:px-6 md:pb-6">
|
||||
<div className="sticky top-0 z-20 -mx-4 mb-6 flex flex-wrap items-center gap-2 border-b bg-background/90 px-4 py-3 backdrop-blur sm:-mx-6 sm:px-6">
|
||||
<div className="sticky top-0 z-20 -mx-4 mb-6 flex flex-wrap items-center gap-2 border-b bg-background/85 px-4 py-3 backdrop-blur sm:-mx-6 sm:px-6">
|
||||
<div className="relative min-w-0 flex-1 sm:max-w-64">
|
||||
<Search className="pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input className="pl-8" type="search" aria-label="搜索标题" placeholder="搜索标题…" value={qRaw} onChange={(e) => setQRaw(e.target.value)} />
|
||||
@@ -163,7 +163,7 @@ export default function Shelf() {
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuLabel>排序</DropdownMenuLabel>
|
||||
{SORTS.map((k) => (
|
||||
<DropdownMenuItem key={k} onClick={() => setParam("sort", k === "added" ? "" : k)} className={k === sort ? "font-medium text-accent" : ""}>
|
||||
<DropdownMenuItem key={k} onSelect={() => setParam("sort", k === "added" ? "" : k)} className={k === sort ? "font-medium text-accent-strong" : ""}>
|
||||
{SORT_LABEL[k]}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
@@ -190,7 +190,7 @@ export default function Shelf() {
|
||||
{lib === LIB0 && <p className="sr-only" aria-live="polite">{`当前筛选:${libName}`}</p>}
|
||||
|
||||
{(libsQ.data ?? []).length === 0 && !libsQ.isPending && !searching && (
|
||||
<p className="mb-6 flex items-center gap-2 rounded-md border border-accent/30 bg-accent/10 px-3 py-2 text-sm text-accent">
|
||||
<p className="mb-6 flex items-center gap-2 rounded-md border border-accent/30 bg-accent/10 px-3 py-2 text-sm text-accent-strong">
|
||||
<LibraryBig className="size-4 shrink-0" />
|
||||
还没有书库。admin 请到「库管理」建库并扫描。
|
||||
</p>
|
||||
@@ -215,7 +215,7 @@ export default function Shelf() {
|
||||
<button onClick={() => nav(`/book/${b.id}`)} className="group flex w-56 items-center gap-3 rounded-md border bg-card p-2 text-left transition-colors hover:bg-secondary focus-visible:outline-2 focus-visible:outline-accent">
|
||||
<Cover book={b} className="aspect-[3/4] w-11 shrink-0 rounded" />
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="block truncate text-sm font-medium group-hover:text-accent">{b.title}</span>
|
||||
<span className="block truncate text-sm font-medium group-hover:text-accent-strong">{b.title}</span>
|
||||
<span className="mt-0.5 flex justify-between text-[11px] text-muted-foreground">
|
||||
<span>{r.library}</span>
|
||||
<span className="tabular-nums">{pct}%</span>
|
||||
@@ -282,7 +282,7 @@ export default function Shelf() {
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction
|
||||
className="bg-destructive text-primary-foreground hover:bg-destructive/90"
|
||||
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
|
||||
onClick={() => {
|
||||
if (confirmDel) del.mutate(confirmDel.id);
|
||||
setConfirmDel(null);
|
||||
|
||||
Reference in New Issue
Block a user