抄 Claude 的界面?这5个组件值得你今晚就动手
抄 Claude 的界面?这5个组件值得你今晚就动手
大多数人做 AI 聊天界面,第一步就做错了——他们在想"功能",Claude 在想"感觉"。
你有没有注意到,用 Claude 的时候有一种说不清楚的"顺滑感"?消息出来的方式、输入框的反馈、空白页的引导……每一处都克制,但每一处都让你觉得"对,就该这样"。这不是偶然,是有人在背后做了大量有意识的设计决策。
好消息是:有人把这套设计系统拆解开源了。
open-claude-design 是 GitHub 上一个复现 Claude 界面组件的开源项目,MIT 协议,可以直接商用。我花了 2 小时跑通环境,把所有组件翻了一遍,筛出了 5 个最值得"抄"进你自己项目的组件——本文就是这次探索的完整记录。
---
一、为什么 Claude 的界面"感觉就是不一样"
AI 产品的界面同质化已经到了一个荒谬的程度:左边侧边栏、右边对话区、底部输入框,连配色都在互相抄。
但 Claude 的界面在这堆产品里显得格外"安静"。没有大量的按钮、没有功能入口轰炸,就连消息气泡的圆角弧度都比别人小一点点——这种克制感反而带来了更强的专注体验。
这背后是 Anthropic 设计团队有意识构建的一套交互语言:信息密度适中、动效有目的性、每一个元素存在都有理由。
open-claude-design 这个项目做的事情就是把这套语言拆解出来,用 React + Tailwind 复现成可独立使用的组件库。项目目前在 GitHub 上保持活跃更新,采用 MIT 协议,意味着你可以直接把它的代码用进商业项目里。
---
二、2小时跑起来的完整过程(含踩坑记录)
先说结论:整体不难,但有几个坑会让你白白浪费半小时。
克隆与安装(约15分钟)
git clone https://github.com/open-claude-design/open-claude-design.git
cd open-claude-design
npm install
第一个坑在这里就出现了——Node 版本。
项目要求 Node >= 18.0,但如果你本地还挂着 16.x 的老版本(很多人的 Mac 上都是),npm install 会在某个原生模块上报错,错误信息指向 sharp,让人一头雾水。
解决方式很简单:
nvm use 18
或者
nvm install 18 && nvm use 18
本地启动(约5分钟)
npm run dev
第二个坑:样式未加载。第一次打开 localhost:3000,你可能看到一片白色无样式的 HTML。原因是 Tailwind 的 JIT 模式在冷启动时需要额外几秒编译。等 10 秒刷新,样式就全回来了。不要慌,也不要去改配置。
避坑速查表
| 问题 | 原因 | 解决方案 | |npm install 报 sharp 错误 | Node 版本 < 18 | nvm use 18 |
| 首次启动样式不显示 | Tailwind JIT 冷启动延迟 | 等 10 秒后刷新页面 |
| 热更新不生效 | 文件路径含中文或空格 | 把项目放到纯英文路径下 |
| 字体加载慢 | 默认走 Google Fonts CDN | 在 _document.tsx 里换成本地字体或国内 CDN |
跑通之后,你会看到一个完整的仿 Claude 界面在本地运行。那一刻确实有点奇妙——和真正的 Claude 放在一起截图,普通用户很难分辨。
---
三、最值得"抄"的5个组件
1. 消息气泡的打字机动效
解决了什么问题: AI 回复如果一次性渲染出来,用户会感觉"突兀",像是在看一堵文字墙。打字机效果让信息"流动"起来,符合对话的自然节奏,同时给用户一个"AI 正在思考"的心理暗示。有 UX 研究表明,流式输出相比批量输出能显著降低用户的"等待焦虑感"——因为你看到内容在动,就不会觉得在干等。
核心实现(CSS + JS,约18行):/ 光标闪烁动效 /
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0; }
}
.typing-cursor::after {
content: '▋';
animation: blink 1s step-end infinite;
margin-left: 2px;
}
// 打字机逻辑核心
function typewriter(text, setter, speed = 20) {
let index = 0;
const timer = setInterval(() => {
setter(text.slice(0, index + 1));
index++;
if (index >= text.length) clearInterval(timer);
}, speed);
}
适合嵌入哪类项目: 所有 AI 对话类应用。这个组件是优先级最高的,没有之一。
---
2. 侧边栏对话历史折叠逻辑
解决了什么问题: 对话历史多了之后,侧边栏会变成一个信息垃圾堆。Claude 的做法是按时间分组(今天、昨天、过去7天、更早),并且在折叠状态下只显示标题前20个字,悬停才展开完整内容。 关键实现思路: 这不是一个复杂的算法,但分组逻辑需要注意:用date-fns 做时间计算,而不是手写 timestamp 比较,否则跨时区会出问题。
import { isToday, isYesterday, subDays, isAfter } from 'date-fns';
function groupConversations(conversations) {
return {
today: conversations.filter(c => isToday(new Date(c.updatedAt))),
yesterday: conversations.filter(c => isYesterday(new Date(c.updatedAt))),
lastWeek: conversations.filter(c =>
isAfter(new Date(c.updatedAt), subDays(new Date(), 7)) &&
!isToday(new Date(c.updatedAt)) &&
!isYesterday(new Date(c.updatedAt))
),
};
}
适合嵌入哪类项目: 任何需要管理多轮对话历史的应用,包括客服系统、AI 写作工具。
---
3. 代码块的一键复制 + 语言标签
解决了什么问题: 这是一个"小而美"的设计——代码块右上角显示语言类型(python、javascript 等),同时有一个复制按钮,点击后变成"已复制"并在 2 秒后复原。
这个交互逻辑看起来简单,但很多开发者自己实现时会踩一个坑:复制按钮的状态重置没有清理副作用,导致组件卸载后 setTimeout 还在跑,控制台报 warning。
项目里的实现用 useRef 存储 timer id 并在 useEffect 的清理函数里 clearTimeout,这个细节值得直接抄。
---
4. 输入框的自适应高度 + 快捷键提示
解决了什么问题: 固定高度的 是 AI 聊天界面的通病——用户打长文本时需要在小框里滚动,体验极差。Claude 的输入框会随内容自动增高,但有最大高度限制,超出后才出现滚动条。
核心实现:
function useAutoResize(ref) {
useEffect(() => {
const el = ref.current;
if (!el) return;
const resize = () => {
el.style.height = 'auto';
el.style.height = Math.min(el.scrollHeight, 200) + 'px';
};
el.addEventListener('input', resize);
return () => el.removeEventListener('input', resize);
}, [ref]);
}
快捷键提示(↵ 发送 / Shift+↵ 换行)用绝对定位放在输入框右下角,颜色用 text-gray-400,不抢焦点但随时可见。
---
5. 空状态页的引导卡片设计
解决了什么问题: 新用户第一次打开对话界面,面对一个空白输入框往往不知道从哪里开始。Claude 的空状态页用几张卡片展示"你可以问我什么",每张卡片是一个可点击的示例 prompt。这个设计的精髓不在于技术,而在于选题逻辑:示例 prompt 要覆盖不同使用场景,并且要足够具体("帮我写一封拒绝加班的邮件"比"帮我写邮件"好10倍)。
项目里的卡片组件支持自定义 prompt 列表,直接传 props 就能替换成你自己产品的场景。
适合嵌入哪类项目: 面向普通用户的 AI 应用,尤其是用户群体对 AI 不熟悉的场景。---
四、哪些组件看起来好看但不适合直接用
客观说,这个项目也有局限,有几个组件我建议谨慎使用或改造后再用:
与 Claude 品牌色强绑定的组件项目里的主题色是 #CC785C(一种暖橙色),这是 Claude 的品牌色。如果你的产品有自己的设计规范,直接用这些组件会让界面看起来像是"Claude 的山寨版"。建议在 tailwind.config.js 里替换掉 primary color 再用。
侧边栏的展开/收起动效用了 transition: all 300ms cubic-bezier(...),在低端 Android 设备上(尤其是 WebView 环境)会有明显掉帧。建议加上 @media (prefers-reduced-motion: reduce) 的降级处理。
项目的硬编码文案全是英文,没有 i18n 方案。如果你的产品面向中文用户,需要自己做一遍文案替换,工作量不小。好在组件结构清晰,找起来不难。
---
五、把组件接进真实 AI 项目的最后一步
界面有了,但没有真实的 AI 接口,这些组件只是一个"会动的 mockup"。
打字机动效组件要真正"活起来",需要对接流式 API(Streaming)。以下是一个最简版的对接示例:
async function streamChat(message, onChunk) {
const response = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
// 解析 SSE 格式,提取 delta content
const lines = chunk.split('\n').filter(l => l.startsWith('data:'));
for (const line of lines) {
const data = JSON.parse(line.slice(5));
if (data.choices?.[0]?.delta?.content) {
onChunk(data.choices[0].delta.content);
}
}
}
}
把 onChunk 回调接到打字机组件的状态更新函数上,流式输出的每一个 token 就会实时渲染到界面上——这才是真正的"活了"。
组件跑通之后,我用的是 [api.884819.xyz](https://api.884819.xyz) 来对接 Claude 的流式接口——国内直连,响应速度稳定,对接方式和官方 API 完全一致,改一行 baseURL 就切过来了。打字机动效配上真实的流式输出,那个感觉才算真的"活了"。
// 就这一行,从官方切到国内入口
const client = new OpenAI({
baseURL: 'https://api.884819.xyz/v1',
apiKey: 'your-key-here',
});
平台新用户注册即送体验 token,国产模型(Deepseek、千问等)完全免费,没有月租,按量付费。注册只需要用户名 + 密码,30 秒搞定,直接就能开始验证你的 Demo。
---
最后
从"抄组件"到"跑通 Demo",这条路比你想象的短:
1. git clone 项目,按避坑速查表配好环境
2. 优先集成打字机动效 + 自适应输入框这两个组件
3. 接上流式 API,让界面真正动起来
今晚就能有一个能用的 Demo。这不是夸张,是我自己跑过的路径。
---
顺带一提,我在翻 open-claude-design 源码的时候,发现了一个很有意思的东西——它的深色模式切换逻辑和市面上所有的实现方式都不一样,没有闪烁、没有延迟,甚至在 SSR 场景下也完美。这个单独值得写一篇,下次聊。
---
本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。#AI开发 #前端开发 #Claude #开源项目 #React #UI组件 #8848AI #AI工具