feat(ui): 补齐分层设计 token 并迁移共享变体(对标 Claude DESIGN.md)

- 面色阶 surface-soft/card/strong + 文字 5 级 body-strong/body/muted-soft
- 边框 line-soft、coral 状态 cinnabar-active/disabled(按压变深 + 奶油化禁用)
- 圆角刻度 xs..xl/full:按钮 rounded-md、卡片 rounded-lg、徽章 pill
- 排版档 display/title/caption/eyebrow/prose(衬线负字距)
- 动效 token duration-fast/base + ease-standard,transitionUi 常量统一过渡
- 焦点环统一 cinnabar/35;双主题成对定义
This commit is contained in:
Yaojia Wang
2026-07-11 07:16:00 +02:00
parent 57b3183564
commit b9f9134748
5 changed files with 117 additions and 24 deletions

View File

@@ -23,30 +23,31 @@ export function cn(...classes: Array<string | false | null | undefined>): string
return classes.filter(Boolean).join(" ");
}
// 统一焦点环(与 buttonBase 现有焦点环一致),供导航/输入等可聚焦元素复用
// 统一焦点环(供按钮/输入/导航等所有可聚焦元素复用,统一到 cinnabar/35
export const focusRing =
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cinnabar/35";
// 手稿正文统一排版:衬线 18px、行高 1.9
export const proseBody = "font-serif text-[18px] leading-[1.9]";
// 统一交互过渡(颜色变化 + 动效 token供按钮/输入/导航复用,全局可调
export const transitionUi = "transition-colors duration-fast ease-standard";
// 手稿正文统一排版:衬线 18pxtext-prose 档)、行高 1.9。
export const proseBody = "font-serif text-prose";
// Drawer / CommandPalette 共用的全屏遮罩。
export const overlayScrim = "fixed inset-0 z-40 bg-black/30";
const buttonBase =
"inline-flex items-center justify-center gap-1.5 rounded border text-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-cinnabar/35 disabled:cursor-not-allowed disabled:opacity-45";
const buttonBase = `inline-flex items-center justify-center gap-1.5 rounded-md border text-sm disabled:cursor-not-allowed ${transitionUi} ${focusRing}`;
// 非主按钮的禁用淡化(主按钮改用奶油化禁用底,见下)。
const disabledFade = "disabled:opacity-45";
const buttonVariants: Record<ButtonVariant, string> = {
primary:
"border-cinnabar bg-cinnabar text-panel shadow-paper hover:bg-cinnabar/95",
secondary:
"border-line bg-panel text-ink hover:border-cinnabar hover:text-cinnabar",
outline:
"border-cinnabar bg-transparent text-cinnabar hover:bg-[var(--color-cinnabar-wash)]",
ghost:
"border-transparent bg-transparent text-ink-soft hover:bg-[var(--color-cinnabar-wash)] hover:text-cinnabar",
danger:
"border-conflict bg-transparent text-conflict hover:bg-conflict/10",
"border-cinnabar bg-cinnabar text-panel shadow-paper hover:bg-cinnabar-active active:bg-cinnabar-active disabled:border-cinnabar-disabled disabled:bg-cinnabar-disabled disabled:text-muted-soft",
secondary: `border-line bg-panel text-ink hover:border-cinnabar hover:text-cinnabar ${disabledFade}`,
outline: `border-cinnabar bg-transparent text-cinnabar hover:bg-[var(--color-cinnabar-wash)] active:bg-[var(--color-cinnabar-wash)] ${disabledFade}`,
ghost: `border-transparent bg-transparent text-ink-soft hover:bg-[var(--color-cinnabar-wash)] hover:text-cinnabar ${disabledFade}`,
danger: `border-conflict bg-transparent text-conflict hover:bg-conflict/10 ${disabledFade}`,
};
const buttonSizes: Record<ButtonSize, string> = {
@@ -68,7 +69,7 @@ export function buttonClass({
}
const badgeBase =
"inline-flex items-center gap-1 rounded border px-2 py-0.5 text-xs leading-5";
"inline-flex items-center gap-1 rounded-full border px-2.5 py-0.5 text-xs leading-5";
const badgeVariants: Record<BadgeVariant, string> = {
neutral: "border-line bg-bg text-ink-soft",
@@ -91,7 +92,7 @@ export function badgeClass({
}
export function cardClass(className?: string): string {
return cn("rounded border border-line bg-panel shadow-paper", className);
return cn("rounded-lg border border-line bg-panel shadow-paper", className);
}
const fieldTextBase = "block text-sm font-medium text-ink";
@@ -108,8 +109,7 @@ export function fieldErrorClass(className?: string): string {
return cn("mt-1 text-xs leading-5 text-conflict", className);
}
const inputBase =
"w-full rounded border bg-bg text-ink transition-colors placeholder:text-ink-soft/65 focus:outline-none focus-visible:ring-2 focus-visible:ring-cinnabar/30 disabled:cursor-not-allowed disabled:bg-line/20 disabled:text-ink-soft";
const inputBase = `w-full rounded-md border bg-bg text-ink placeholder:text-ink-soft/80 disabled:cursor-not-allowed disabled:bg-line/20 disabled:text-ink-soft ${transitionUi} ${focusRing}`;
const inputStates: Record<InputState, string> = {
default: "border-line focus:border-cinnabar",