ClaudeのArtifacts機能でチャットアプリを作ろう!
こんにちは!DiningXの吉波です!
ありがたいことにXのアカウントをフォローしてくださる方も増えてきて、とても嬉しい限りです!
今回は、ClaudeのArtifacts機能のチャットUIやSlack UIを作る方法を紹介します。実際、SlackのチャットUIに関する投稿は、9時間で12万インプレッションという脅威の数字を叩き出しました。
はじまり
まずはいつも通り、作りたいアプリをClaudeにお願いしましょう!
実際のアプリ画像を見せる
Claudeとの最初の会話で、大まかなUIの知識は教えてくれますが、詳細な部分は出力してくれないことが多いです。そこで、実際のUI画像を渡すことで、再現しやすくしてもらいます
UIの再現を依頼する
先ほど渡した画像をもとに再現をお願いします
他のチャット履歴も紹介
エラーの対処
プレビューできない場合の対処
最終的に生成されたコード
import React, { useState, useEffect } from 'react';
import { Search, ChevronDown, Hash, Lock, Home, MessageCircle, Bell, ArrowLeft, UserPlus, Link } from 'lucide-react';
const AnthropicSlackUI = () => {
const [currentView, setCurrentView] = useState('home');
const [selectedChannel, setSelectedChannel] = useState(null);
const [messages, setMessages] = useState([]);
useEffect(() => {
if (currentView === 'channel' && selectedChannel) {
setMessages([
{ id: 1, user: 'Kintaro Endo', time: '14:47', content: '#team-winterに参加しました' },
{ id: 2, user: 'Saburo Nakamura', time: '15:46', content: '#team-winterに参加しました' },
{ id: 3, user: 'Hanako Yoshida', time: '15:48', content: '#team-winterに参加しました' },
{ id: 4, user: 'Kintaro Endo', time: '16:13', content: '「冬のキャンペーン」から「team-winter」にチャンネル名を変更しました' },
{ id: 5, user: 'Hanako Yoshida', time: '16:15', content: '来週の打ち合わせの資料よろしく', reactions: [{ emoji: '👍', count: 1 }] }
]);
const timer = setTimeout(() => {
setMessages(prevMessages => [
...prevMessages,
{ id: 6, user: 'Anthropic Bot', time: '今', content: 'We are Hiring! 🎉' }
]);
}, 5000);
return () => clearTimeout(timer);
}
}, [currentView, selectedChannel]);
const handleChannelSelect = (channel) => {
setSelectedChannel(channel);
setCurrentView('channel');
};
const HomeView = () => (
<>
<div className="flex p-4 space-x-4 border-b border-gray-200">
<div className="flex-1 bg-gray-100 rounded-md p-2 text-center">
<div className="font-bold">Threads</div>
<div className="text-sm text-purple-600">1 new</div>
</div>
<div className="flex-1 bg-gray-100 rounded-md p-2 text-center">
<div className="font-bold">Later</div>
<div className="text-sm text-red-500">1 overdue</div>
</div>
<div className="flex-1 bg-gray-100 rounded-md p-2 text-center">
<div className="font-bold">Drafts & Sent</div>
<div className="text-sm">3 scheduled</div>
</div>
</div>
<div className="p-4">
<div className="flex items-center justify-between mb-2">
<h2 className="font-bold">Mentions</h2>
<ChevronDown size={20} />
</div>
<ul>
<li className="flex items-center py-2" onClick={() => handleChannelSelect('design-team')}>
<Hash size={20} className="mr-2" />
<span>design-team</span>
<span className="ml-auto bg-purple-600 text-white rounded-full w-5 h-5 flex items-center justify-center text-xs">2</span>
</li>
<li className="flex items-center py-2" onClick={() => handleChannelSelect('The Home Team')}>
<div className="w-5 h-5 bg-green-500 rounded-full mr-2"></div>
<span>The Home Team</span>
</li>
<li className="flex items-center py-2" onClick={() => handleChannelSelect('team-finance')}>
<Lock size={20} className="mr-2" />
<span>team-finance</span>
</li>
<li className="flex items-center py-2" onClick={() => handleChannelSelect('project-brand-campaign')}>
<Hash size={20} className="mr-2" />
<span>project-brand-campaign</span>
</li>
</ul>
</div>
<div className="p-4">
<div className="flex items-center justify-between mb-2">
<h2 className="font-bold">Channels</h2>
<ChevronDown size={20} />
</div>
<ul>
<li className="flex items-center py-2" onClick={() => handleChannelSelect('general')}>
<Hash size={20} className="mr-2" />
<span>general</span>
</li>
<li className="flex items-center py-2" onClick={() => handleChannelSelect('sales')}>
<Lock size={20} className="mr-2" />
<span>sales</span>
</li>
<li className="flex items-center py-2" onClick={() => handleChannelSelect('The Home Team')}>
<div className="w-5 h-5 bg-green-500 rounded-full mr-2"></div>
<span>The Home Team</span>
</li>
</ul>
</div>
</>
);
const ChannelView = () => (
<div className="flex-1 flex flex-col">
<div className="bg-white border-b border-gray-200 p-4 flex items-center justify-between">
<div className="flex items-center">
<ArrowLeft size={24} className="mr-4 text-gray-600" onClick={() => setCurrentView('home')} />
<h2 className="font-bold text-lg">#{selectedChannel}</h2>
<span className="ml-2 text-sm text-gray-500">3メンバー</span>
</div>
<div className="flex items-center space-x-4">
<UserPlus size={20} className="text-gray-600" />
<Link size={20} className="text-gray-600" />
</div>
</div>
<div className="flex-1 p-4 overflow-y-auto bg-white">
{messages.map(message => (
<div key={message.id} className="mb-4">
<div className="flex items-center mb-1">
<div className="w-8 h-8 bg-gray-300 rounded-full mr-2"></div>
<span className="font-bold">{message.user}</span>
<span className="text-sm text-gray-500 ml-2">{message.time}</span>
</div>
<p className="ml-10">{message.content}</p>
{message.reactions && (
<div className="ml-10 mt-1">
<span className="bg-gray-100 text-sm px-2 py-1 rounded-full">
👍 {message.reactions[0].count}
</span>
</div>
)}
</div>
))}
</div>
<div className="p-4 border-t border-gray-200 bg-white">
<div className="bg-gray-100 rounded-full p-2 flex items-center">
<input
type="text"
placeholder={`#${selectedChannel}にメッセージを送信`}
className="bg-transparent flex-1 outline-none"
/>
<MessageCircle size={20} className="text-gray-400 ml-2" />
</div>
</div>
</div>
);
return (
<div className="flex flex-col h-screen bg-white text-gray-800">
<header className="bg-purple-700 text-white p-4 flex items-center justify-between">
<div className="flex items-center">
<div className="w-8 h-8 bg-yellow-400 rounded-md flex items-center justify-center mr-2">
<span className="text-purple-700 font-bold">A</span>
</div>
<h1 className="font-bold text-lg">Anthropic Inc</h1>
</div>
<div className="w-8 h-8 bg-purple-600 rounded-full overflow-hidden">
<img src="/api/placeholder/32/32" alt="Profile" className="w-full h-full object-cover" />
</div>
</header>
<div className="bg-purple-700 px-4 pb-4">
<div className="bg-purple-600 rounded-md flex items-center p-2">
<Search size={20} className="text-purple-300 mr-2" />
<input
type="text"
placeholder="Search..."
className="bg-transparent text-white placeholder-purple-300 flex-1 outline-none"
/>
</div>
</div>
<div className="flex-1 overflow-y-auto">
{currentView === 'home' ? <HomeView /> : <ChannelView />}
</div>
<nav className="bg-white border-t border-gray-200 flex justify-around p-4">
<button className="flex flex-col items-center">
<Home size={24} />
<span className="text-xs mt-1">Home</span>
</button>
<button className="flex flex-col items-center">
<MessageCircle size={24} />
<span className="text-xs mt-1">DMs</span>
</button>
<button className="flex flex-col items-center">
<Bell size={24} />
<span className="text-xs mt-1">Activity</span>
</button>
</nav>
<button className="absolute bottom-20 right-4 w-14 h-14 bg-purple-600 rounded-full flex items-center justify-center shadow-lg">
<MessageCircle size={24} className="text-white" />
</button>
</div>
);
};
export default AnthropicSlackUI;
ClaudeのチャットUIを作成するTips
続いて、Claude Artifacts機能のチャットUIを作成するポイントを紹介します!
1. 基本的なチャットUIの作成
これには以下の要素が含まれています:
- メッセージ表示エリア
- 入力フィールド
- 送信ボタン
2.スタイリングの調整
UIをより魅力的にするため、以下のようなスタイリングを行いました:
- メッセージの吹き出しデザイン
- 背景色の設定(クリーム色)
- 送信ボタンのカスタマイズ(丸みを帯びた四角形、赤みがかった茶色)
3.コード生成機能の実装
「コード生成」というキーワードに反応して、サンプルコードを生成する機能を追加しました。
4.コード表示エリアの作成
生成されたコードを表示するための専用エリアを作成しました:
- 黒背景に緑文字のターミナル風デザイン
5.アニメーション効果の追加
UIをより動的で魅力的にするため、アニメーション効果を追加しました:
- ボットの返答を1文字ずつ表示 - コードも1文字ずつ表示
6.レスポンシブデザインの適用
様々な画面サイズに対応するため、レスポンシブデザインを適用しました。
7.細かな調整
ユーザーフィードバックを基に、以下のような細かな調整を行いました:
- メッセージの配置を左揃えに統一
- ユーザーとボットのメッセージの色分け
- プレースホルダーテキストの変更
まとめ
チャットアプリとClaude Artifacts機能は相性抜群ですね!今後ともClaudeに関する知見を発信していきますので、よければフォローお願いします!
ClaudeのチャットUIを作成するコード全体
ここから先は
この記事が気に入ったらサポートをしてみませんか?