feat(web): 设计规范与 uhd_web 同步(截至 2026-10-08)
- 材质统一边光:slab / float / pill / pill-selected / seg-thumb / chrome-side 改为左上亮右下暗的 1px 边光 + 外侧暗线;新增 lens / field / chip 参数与 pg-float-modal - 主按钮:转动光谱环改为静态左上光谱高光,悬停沿边扫过一小段;次按钮同为 lens 玻璃 - Input / Textarea / SelectTrigger 挂 .field-glass,原生下拉与实体选择器同步;对话框按钮行 gap-3 - 提示条改 pg-float(状态色只在图标、展开不叠放);新增 GlassTooltip 接管 title 提示 - 触屏判断 input-modality.ts、touch / hover 自定义变体;animate-pg-pop 改 backwards;scroll-fade-end - 控制台外壳:侧栏无硬分隔线、折叠开关进标志行;顶栏去磨砂,右侧控件收进玻璃胶囊 - 登录页居中单卡 auth-card;详情头图「订阅」改回普通主按钮,删除 pill-solid;发现页海报角标改 glass-chip-media - 引入 simple-liquid-glass(liquid-surface.tsx);更新 docs/design 三份规范与 CHANGELOG Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
d842bb7fb4
commit
09a2adb637
38 files changed
+941
-356
No files matched your search
@@ -0,0 +1,137 @@
|
||||
import { useTheme } from "next-themes";
|
||||
import { LiquidGlass, type LiquidGlassProps } from "simple-liquid-glass";
|
||||
import { useTouchInput } from "~/lib/input-modality.ts";
|
||||
import { cn } from "~/lib/utils.ts";
|
||||
|
||||
/**
|
||||
* 液态玻璃面:simple-liquid-glass 的薄封装,材质按 meniscus 的尺寸分档。
|
||||
* - lens:小控件(图标键、头像),几乎不糊,只在边带折射;
|
||||
* - regular:栏(顶栏胶囊、播放器底栏);
|
||||
* - frost:大块、字多的悬浮层(菜单)。只有真模糊,不做折射(见下方 effectMode)。
|
||||
* 折射只有 Chromium 桌面有(SVG 位移进 backdrop-filter);Safari / Firefox / 触屏设备走包自带的磨砂。
|
||||
* 只放在压着图片、视频、海报的层上;背后没细节的内容卡继续用 pg-slab。
|
||||
*/
|
||||
export type LiquidTier = "lens" | "regular" | "frost";
|
||||
|
||||
const TIERS: Record<
|
||||
LiquidTier,
|
||||
{
|
||||
blur: number;
|
||||
displacement: number;
|
||||
fill: { dark: string; light: string };
|
||||
}
|
||||
> = {
|
||||
lens: { blur: 1, displacement: 6, fill: { dark: "rgba(0,0,0,0.14)", light: "rgba(255,255,255,0.08)" } },
|
||||
regular: { blur: 5, displacement: 10, fill: { dark: "rgba(10,11,14,0.30)", light: "rgba(255,255,255,0.16)" } },
|
||||
frost: { blur: 20, displacement: 8, fill: { dark: "rgba(14,15,19,0.58)", light: "rgba(255,255,255,0.5)" } },
|
||||
};
|
||||
|
||||
const FROST_BLUR = "blur(24px) saturate(160%)";
|
||||
// 模糊之上再压一层底色,浅色字(次要说明、退出登录)压在暖色画面上也读得清
|
||||
const FROST_FILL = { dark: "rgba(14,15,19,0.55)", light: "rgba(255,255,255,0.6)" };
|
||||
|
||||
interface Props extends Omit<LiquidGlassProps, "radius"> {
|
||||
tier?: LiquidTier;
|
||||
radius: number;
|
||||
/** 播放器等恒暗区域固定用夜间参数 */
|
||||
forceDark?: boolean;
|
||||
/** 不画包自带的边光(主按钮的棱镜高光就是它的边光) */
|
||||
rimless?: boolean;
|
||||
/** 折射边缘带色散:背后画面在棱边被拆成彩色(只在 Chromium 桌面、背后有画面时看得到) */
|
||||
dispersive?: boolean;
|
||||
/** 只要玻璃外观与模糊,不做折射(测速页:实时曲线在重绘,不加 SVG 滤镜开销) */
|
||||
noRefraction?: boolean;
|
||||
}
|
||||
|
||||
export function LiquidSurface({
|
||||
tier = "regular",
|
||||
radius,
|
||||
forceDark,
|
||||
rimless,
|
||||
dispersive,
|
||||
noRefraction,
|
||||
className,
|
||||
style,
|
||||
children,
|
||||
...rest
|
||||
}: Props) {
|
||||
const { resolvedTheme } = useTheme();
|
||||
const dark = forceDark || resolvedTheme !== "light";
|
||||
// 触屏设备(手机、平板、车机)不跑 SVG 折射,退回同参数的磨砂:车机这类 Linux 触屏会被包当成桌面
|
||||
const touch = useTouchInput();
|
||||
// 不折射、只模糊的情况:包在这个模式下不铺 glassColor,还会在顶部画一道 50% 白的光泽(夜间压住第一行字),
|
||||
// 底色由这里铺,光泽在 app.css 里按档收淡 / 去掉
|
||||
const blurOnly = touch || noRefraction || tier === "frost";
|
||||
const t = TIERS[tier];
|
||||
return (
|
||||
<LiquidGlass
|
||||
mode="custom"
|
||||
refraction="lens"
|
||||
lensProfile="material"
|
||||
quality={dispersive ? "high" : "standard"}
|
||||
radius={radius}
|
||||
blur={t.blur}
|
||||
displacementScale={dispersive ? t.displacement + 4 : t.displacement}
|
||||
saturation={150}
|
||||
dispersion={50}
|
||||
aberrationIntensity={dispersive ? 0.4 : 0.06}
|
||||
frost={0}
|
||||
// frost 档(字多的悬浮层)不做折射:Chromium 里同一个 backdrop-filter 同时有 blur 和 SVG 位移时,
|
||||
// 模糊不生效,菜单底下的画面原样透出来、字看不清。frost 只要真模糊,折射留给栏和按钮
|
||||
effectMode={blurOnly ? "blur" : "auto"}
|
||||
glassColor={dark ? t.fill.dark : t.fill.light}
|
||||
borderColor={rimless ? "rgba(255,255,255,0)" : dark ? "rgba(255,255,255,0.22)" : "rgba(255,255,255,0.65)"}
|
||||
className={cn("liquid-surface", `liquid-surface-${tier}`, blurOnly && "liquid-surface-blur", className)}
|
||||
style={{
|
||||
// frost 档(菜单、搜索结果等字多的悬浮层)自己垫一层固定模糊:包退回磨砂时只给 8~11px,
|
||||
// 菜单压在画面上看不清,这里统一保证 24px
|
||||
...(tier === "frost"
|
||||
? {
|
||||
backdropFilter: FROST_BLUR,
|
||||
WebkitBackdropFilter: FROST_BLUR,
|
||||
backgroundColor: dark ? FROST_FILL.dark : FROST_FILL.light,
|
||||
}
|
||||
: blurOnly
|
||||
? { backgroundColor: dark ? t.fill.dark : t.fill.light }
|
||||
: null),
|
||||
// 1px 暗边线在亮棱外侧,投影只留贴边一层
|
||||
boxShadow: dark
|
||||
? "0 0 0 1px rgba(0,0,0,0.28), 0 8px 24px -14px rgba(0,0,0,0.5)"
|
||||
: "0 0 0 1px rgba(20,22,28,0.08), 0 8px 24px -16px rgba(20,22,28,0.22)",
|
||||
...style,
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
{children}
|
||||
</LiquidGlass>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给压在大图上的按钮加真折射:外面套一层 lens 玻璃,按钮本身挂 .btn-liquid 去掉自己的底和模糊,
|
||||
* 只留字、图标和主按钮的彩虹环。radius 取按钮高度的一半。
|
||||
*/
|
||||
export function LiquidLens({
|
||||
radius,
|
||||
primary,
|
||||
className,
|
||||
children,
|
||||
}: {
|
||||
radius: number;
|
||||
primary?: boolean;
|
||||
className?: string;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<LiquidSurface
|
||||
tier="lens"
|
||||
radius={radius}
|
||||
rimless={primary}
|
||||
dispersive={primary}
|
||||
className={className}
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
>
|
||||
{children}
|
||||
</LiquidSurface>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user