lzwcc/ ui-motion-lab
No.003 · Motion · 2026.10.14刷到自 Claude Design System Storybook
作品 / 动画

Claude 风格的文字显现与输入框交互

逐字模糊消解、输入框的悬浮边框,以及聚焦时的双环反馈。

复刻css

有些界面的顺滑感,来自很小的细节:文字如何出现,按钮何时变得可用,输入框如何回应焦点。这篇文章把两个效果拆成可以直接操作的演示。

Grapheme Reveal:文字从模糊到清晰

点击「重新播放」,观察文字的边缘。每个字符用 90ms 从 blur(6px) 消解到清晰,同时由透明变为可见。相邻字符默认错开 30ms,所以整句话像一条波浪展开。

Demo · 动手试试
把复杂的问题,变成清晰的下一步。 Let's build something thoughtful. ✨

90ms 模糊消解 · 每个字符错开 30ms

试着调节字符间隔,或输入中文、英文和 emoji。这里按照字素拆分文字,👨‍👩‍👧‍👦 和 👍🏽 都会作为一个单元出现。演示播放的是已有文字,并没有连接流式接口。

/* 每个字符的 delay = index × 字符间隔 */
animation: grapheme-reveal 90ms cubic-bezier(0.215, 0.61, 0.355, 1) both;

Chat Composer:从悬浮到聚焦

先把鼠标移到下面的输入框上,观察边框轻微加深;再点击输入框,内环和外环一起变得清晰,外部弥散阴影也随之增强。移走鼠标后,只要焦点还在输入框里,聚焦反馈就会保留。

点击文章的其他位置后,输入框回到默认状态;如果鼠标仍在框内,则回到悬浮状态。三种状态之间使用 200ms 的过渡。

Demo · 动手试试
Chat Composer

先悬浮,再点击输入框,对比边框加深与聚焦双环的变化。

输入文字后可发送;内容仅在此演示中展示。

输入文字后,陶土色发送按钮从淡色变为可用。点击发送,会在演示下方显示这条消息;刷新页面后内容消失。焦点留在发送按钮上时,双环反馈也会保留。

双环聚焦的「呼吸」体现在哪里

这里的呼吸感来自焦点进入与离开时的阴影变化,不会持续循环。圆角保持 20px,组件通过 CSS :hover 响应鼠标悬浮,通过 :focus-within 响应内部控件的焦点。悬浮态轻于聚焦态,触屏设备不启用鼠标悬浮效果。

/* 聚焦状态:一层柔和投影,加上内外各 1px 的环。 */
box-shadow:
  0 0.25rem 1.25rem rgba(0, 0, 0, 0.075),
  inset 0 0 0 1px rgba(0, 0, 0, 0.16),
  0 0 0 1px rgba(0, 0, 0, 0.16);

transition: box-shadow 200ms;

可以用站点的主题按钮查看深色效果。系统开启「减少动态效果」时,文字直接完整显示,输入框保留聚焦反馈并关闭动画过渡。

参考:Claude Design System Storybook。两个演示依据该参考站的参数改写为 Vue 组件,悬浮边框状态为本演示补充;它们与官方 Claude 的一致性未作验证。

提示词

复制给 Claude、ChatGPT 或编码 agent,可以复现本页的两个效果。

实现两个 Claude 风格的微交互,用原生 HTML/CSS/JS 或任意前端框架都可以。

一、逐字显现
1. 用 Intl.Segmenter(granularity: "grapheme")把文字拆成字素,每个字素包进一个 display: inline-block、white-space: pre 的 span,这样 emoji 组合(👨‍👩‍👧‍👦、👍🏽)和带重音的字母不会被拆开。另放一份完整文本给屏幕阅读器,拆开的 span 所在容器设 aria-hidden。
2. 每个字素播放动画:从 opacity 0、filter: blur(6px) 到 opacity 1、blur(0),时长 90ms,缓动 cubic-bezier(0.215, 0.61, 0.355, 1),fill-mode 为 both。第 i 个字素的 animation-delay = i × 30ms。
3. 重新播放:重新创建这组节点(比如换掉外层元素的 key),不用定时器。
4. prefers-reduced-motion 时关闭动画,文字直接完整显示。

二、聊天输入框
1. 结构:一个 form,圆角 20px,内边距 14px,纵向排列、间距 8px。上面是无边框、透明背景的 textarea(3 行,最小高 72px,最大高 240px,字号 15px,行高 1.5,只能纵向拉伸,去掉 outline);下面一行左边是小号品牌文字,右边是 32×32、圆角 8px 的发送按钮(背景 #d97757,悬浮 #cc5a36,按下 scale(0.95))。
2. 边框全部用 box-shadow 画,box-shadow 过渡 200ms。定义变量 --ring(浅色模式 0, 0, 0;深色模式 255, 255, 255):
   - 默认:0 0.25rem 1.25rem rgb(0 0 0 / 0.035), inset 0 0 0 1px rgba(var(--ring), 0.08)
   - 悬浮:投影 0.055,内环 0.12。只写在 @media (hover: hover) and (pointer: fine) 里,避免触屏出现粘住的悬浮态。
   - 聚焦(:focus-within):投影 0.075,内环 inset 0 0 0 1px 和外环 0 0 0 1px 都是 0.16。写在悬浮规则之后,聚焦时优先显示双环,移走指针也保持。
   深色模式下三档环的透明度分别是 0.12、0.18、0.24。
3. 输入内容去掉首尾空白后为空时,发送按钮 disabled,opacity 0.2。
4. prefers-reduced-motion 时去掉所有过渡和按下缩放,保留聚焦双环。