feat(canvas): 动效系统与响应式适配 - token落地+断点体系+无障碍 #99
基于 Wayfinder #99 决议,完成动效/响应式/无障碍三大维度落地: 1. 动效 token 迁移(CSS 动画) - 33 处 easing 替换为 var(--motion-ease-out/in-out/linear) - 7 处有限动画 duration 替换为 var(--motion-dur-*-calc) (含 motion-scale 降级,自动联动 prefers-reduced-motion) - canvas-node batch 动画内联值替换为 token 2. aceternityMotion 对齐 + motion/react 硬编码替换 - 添加 JS↔CSS token 映射文档注释 - 3 处 motion/react 硬编码 duration 替换为 aceternityMotion.* 引用 3. infinite 装饰动画 reduced-motion 降级 - L1 (prefers-reduced-motion): 缩短至 0.01ms + 单次播放 - L2 (.no-motion): animation: none 完全禁用 - 覆盖 9 个 infinite 动画类(连线流光/边框流光/极光标题等) 4. 响应式断点体系 - @theme 新增 3 断点 token: --breakpoint-tablet(768)/desktop(1024)/wide(1440) - 修复 canvas-toolbar 外观面板缺 max-w 导致移动端溢出 5. 无障碍 - 画布页新增 skip-to-content 链接(键盘用户跳过侧栏直达画布) - 任务进度面板 aria-live="polite"(屏幕阅读器播报任务数变化) Closes #99
This commit is contained in:
@@ -34,7 +34,7 @@ export function CanvasUploadStatusToast({ status, theme }: { status: CanvasUploa
|
||||
>
|
||||
<div className="absolute inset-x-0 top-0 h-px" style={{ background: `linear-gradient(90deg, transparent, ${accent}, transparent)` }} />
|
||||
<div className="flex items-start gap-3">
|
||||
<motion.span animate={status.done || status.error ? { scale: [0.8, 1.08, 1] } : { y: [0, -2, 0] }} transition={status.done || status.error ? { duration: 0.3 } : { duration: 1.5, repeat: Number.POSITIVE_INFINITY }} className="mt-0.5 grid size-9 shrink-0 place-items-center rounded-[var(--dock-radius)] border" style={{ background: theme.spatial.surface, borderColor: `${accent}44`, color: accent }}>
|
||||
<motion.span animate={status.done || status.error ? { scale: [0.8, 1.08, 1] } : { y: [0, -2, 0] }} transition={status.done || status.error ? { duration: aceternityMotion.duration.panel } : { duration: 1.5, repeat: Number.POSITIVE_INFINITY }} className="mt-0.5 grid size-9 shrink-0 place-items-center rounded-[var(--dock-radius)] border" style={{ background: theme.spatial.surface, borderColor: `${accent}44`, color: accent }}>
|
||||
{status.error ? <TriangleAlert className="size-4" /> : status.done ? <CheckCircle2 className="size-4" /> : <CloudUpload className="size-4" />}
|
||||
</motion.span>
|
||||
<span className="min-w-0 flex-1">
|
||||
|
||||
@@ -1350,7 +1350,9 @@ function InfiniteCanvasPage() {
|
||||
if (!projectLoaded) return <CanvasRefreshShell />;
|
||||
|
||||
return (
|
||||
<main className="flex h-full min-h-0 overflow-hidden" style={{ background: theme.canvas.background, color: theme.node.text }}>
|
||||
<>
|
||||
<a href="#canvas-main" className="sr-only focus:not-sr-only focus:absolute focus:left-4 focus:top-4 focus:z-[var(--z-toast)] focus:rounded-md focus:border focus:bg-background focus:px-4 focus:py-2 focus:text-sm focus:font-medium focus:shadow-lg">跳转到画布主内容</a>
|
||||
<main id="canvas-main" tabIndex={-1} className="flex h-full min-h-0 overflow-hidden outline-none" style={{ background: theme.canvas.background, color: theme.node.text }}>
|
||||
{currentProject?.projectId ? <CanvasProjectSidebar projectId={currentProject.projectId} detail={linkedProjectQuery.data} onAddChapter={handleProjectChapterInsert} onLocateStyle={locateProjectStyleNode} onOpenAssets={() => openProjectAssets()} /> : null}
|
||||
<section className="relative min-w-0 flex-1 overflow-hidden">
|
||||
<CanvasTopBar
|
||||
@@ -1772,5 +1774,6 @@ function InfiniteCanvasPage() {
|
||||
/>
|
||||
) : null}
|
||||
</main>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user