Files
UHD_Media_Tool/apps/web/app/components/ui/button.tsx
T
rootandClaude Opus 5.5 caf5a1f2f4 feat(web): 作品详情头图改为剧照铺顶渐隐到页面底色(方案 A)
- MediaHero 去掉深色圆角卡片:剧照铺在内容区顶部,上下与右缘渐隐成透明;左侧文字区压近乎不透明的页面底色、右侧露出剧照,窄屏改为自上而下压底色;文字用常规前景色,日夜同一结构;没有背景图时不渲染剧照层
- 新增材质参数 --mat-hero-*(浅 / 深两套)与 .media-hero-backdrop
- 「正常」/ Emby / 库存等徽标并进「连载中」所在的事实行
- 操作按钮(订阅 / 搜索资源 / 查看订阅 / 合并作品 / 插槽入口如补字幕 / 命名覆盖)移进头图区外链行下方,库作品页与 TMDB 详情页一致
- 订阅改为主色实心(Button variant="solid",.pill-solid / --mat-pill-solid-*),作为头图区唯一主操作;设计文档记为例外
- 简介折叠:去掉省略号 + 单独一行「展开」的重复,改为最后一行行尾渐隐、「展开」压在渐隐处;按实际排版测量是否需要折叠
- 补 MediaHero / ClampText 渲染测试

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
2026-10-07 01:44:32 +08:00

71 lines
3.0 KiB
TypeScript

import { cva, type VariantProps } from "class-variance-authority";
import { Slot } from "radix-ui";
import type * as React from "react";
import { cn } from "~/lib/utils.ts";
const buttonVariants = cva(
// 全站只有这一套按钮(与动漫站同一思路):形状一律胶囊 / 正圆,颜色与材质只由 variant 决定,
// 调用处只给布局(宽度、高度、间距),不改圆角、底色、边框、投影和字色。
// 不是 <button> 的元素(Link、a)用 buttonVariants() 拿同一套类名
"inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-full text-sm font-medium whitespace-nowrap transition-[transform,background-color,border-color,color,box-shadow,opacity] duration-base ease-spring outline-none active:scale-[0.96] focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
// 主操作:半透明胶囊 + 沿边框流转的棱镜光谱。样式见 app.css 的 .pill-primary(--mat-pill-primary-*、--prism-*)
default: "pill-primary",
// 实心主操作:只给详情页头图区的唯一主操作(订阅)用,见 app.css 的 .pill-solid(--mat-pill-solid-*)
solid: "pill-solid",
// 次操作:描边胶囊。outline 与 secondary 是同一个样子,保留两个名字只为兼容旧调用
outline: "pill-secondary",
secondary: "pill-secondary",
// 危险操作:同一胶囊,淡红底红字
destructive:
"border border-destructive/35 bg-destructive/10 text-destructive hover:bg-destructive/15 focus-visible:ring-destructive/25 dark:focus-visible:ring-destructive/40",
// 只有文字 / 图标,悬停出淡底
ghost: "text-muted-foreground hover:bg-foreground/[0.06] hover:text-foreground",
link: "text-primary underline-offset-4 hover:underline active:scale-100",
},
size: {
default: "h-9 px-4 py-2 has-[>svg]:px-3",
xs: "h-6 gap-1 px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-8 gap-1.5 px-3 has-[>svg]:px-2.5",
lg: "h-11 px-6 has-[>svg]:px-5",
icon: "size-9",
"icon-xs": "size-6 [&_svg:not([class*='size-'])]:size-3",
"icon-sm": "size-8",
"icon-lg": "size-10",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
},
);
function Button({
className,
variant = "default",
size = "default",
asChild = false,
...props
}: React.ComponentProps<"button"> &
VariantProps<typeof buttonVariants> & {
asChild?: boolean;
}) {
const Comp = asChild ? Slot.Root : "button";
return (
<Comp
data-slot="button"
data-variant={variant}
data-size={size}
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
);
}
export { Button, buttonVariants };