🐍

コード0行、20分の対話だけ。AIでサイトを大改造した記録

に公開

コード0行、20分の対話だけ。AIでサイトを大改造した記録

はじめに

以前、「一切コーディングせずにAI連携だけでWebサイトを作った話」という記事で、Zennとnoteの記事を一箇所で見られるサイトを完全ノーコードで作成しました。

あれから数ヶ月、AIのコーディング能力が大幅に向上したことで、「そろそろサイトをアップデートしたいな」と思い立ちました。

今回も私は一行もコードを書いていません。

すべてClaude Sonnet 4.5(Cursor経由)との対話だけで、8つの新機能を実装しました。その過程と結果を記録します。

完成サイト

Before(旧版)

旧版サイト

  • シンプルなカードレイアウト
  • 記事タイトルと説明文のみ
  • ライトモードのみ

After(新版)

新版サイト

  • サムネイル付きリッチカード
  • フィルター機能でZenn/note切り替え
  • ダークモード対応
  • ホバーアニメーション

実装した8つの新機能

1. 📸 記事のサムネイル表示

サムネイル表示

実装内容:

  • ZennのRSSフィード(<enclosure>タグ)から画像URL取得
  • noteのRSSフィード(<media:thumbnail>タグ)から画像URL取得
  • 横長サムネイルを文章の上部に配置

技術的ポイント:

# RSSフィードからの画像抽出処理
# enclosureタグ、media:thumbnail、descriptionのimgタグから順に探索
# media:thumbnailはテキストとして格納されているケースに対応
media_thumbnail = item.find("{http://search.yahoo.com/mrss/}thumbnail")
if media_thumbnail is not None:
    if media_thumbnail.text:
        image_url = media_thumbnail.text.strip()

2. 🏷️ 記事の出典バッジ

記事の出典バッジ
実装内容:

  • サムネイル右上にZenn(青 #3EA8FF)/note(緑 #41C9B4)のバッジを表示
  • 一目でどのプラットフォームの記事かわかる
  • absolute配置で画像の上に重ねて表示

3. ⚡ 画像のLazy Loading

実装内容:

  • loading="lazy" 属性を追加
  • 画面に表示される直前に画像を読み込み
  • パフォーマンス向上とデータ通信量削減

4. 🎛️ フィルター機能

フィルター機能

実装内容:

  • 「すべて (37)」「Zenn (12)」「note (25)」の切り替えボタン
  • Reactのstateで動的フィルタリング
  • Material Designの丸みのあるボタンデザイン
  • 記事数をリアルタイムでカウント表示

技術的ポイント:

const [filter, setFilter] = useState('all');
const filteredArticles = filter === 'all' 
  ? articles 
  : articles.filter(article => article.source === filter);

5. 🖱️ カード全体クリック対応

実装内容:

  • カード全体を<a>タグでラップ
  • サムネイル、タイトル、説明文どこをクリックしても記事に遷移
  • リンクのネストを避けるため"Read more"を<span>に変更

6. 📏 カードの高さ統一

カードの高さ統一

実装内容:

  • タイトル:最大3行固定(WebkitLineClamp: 3、高さ 4.2em
  • 説明文:最大3行固定(WebkitLineClamp: 3、高さ 4.2em
  • グリッドレイアウトで同じ行のカードを揃える

技術的ポイント:

style={{
  height: "4.2em",
  lineHeight: "1.4",
  display: "-webkit-box",
  WebkitLineClamp: 3,
  WebkitBoxOrient: "vertical",
  overflow: "hidden"
}}

7. ✨ カードのホバーアニメーション強化

実装内容:

  • マウスホバーで8px浮き上がる(translateY(-8px)
  • 影を強化(通常 0 2px 8px → ホバー 0 8px 24px
  • Material Designの標準イージング関数(cubic-bezier(0.4, 0, 0.2, 1))採用
  • 0.3秒のスムーズなトランジション

8. 🌙 ダークモード対応

ダークモード

実装内容:

  • 右下に固定のトグルボタン(🌙/☀️)
  • CSS Custom Propertiesでテーマ切り替え
  • LocalStorageで設定を永続化
  • システムのダークモード設定を自動検出(prefers-color-scheme
  • MutationObserverでクラス変更を監視し、全コンポーネントに反映

カラーパレット:

/* ライトモード */
--bg-primary: #f6f8fa;
--bg-secondary: #fff;
--text-primary: #222;
--accent: #1976d2;

/* ダークモード */
--bg-primary: #121212;
--bg-secondary: #1e1e1e;
--text-primary: #e0e0e0;
--accent: #BB86FC;  /* Material Design 3 Purple */

今回の開発フロー

私がやったこと

  1. 「サムネイルを表示したい」と日本語で伝える
  2. 「ダークモードを実装して」と依頼
  3. 「カードの高さを揃えて」「3行にして」と微調整
  4. ブラウザで確認してOKを出す

Claude Sonnet 4.5 がやったこと

  1. RSSフィードの構造を分析し、画像抽出ロジックを実装
  2. Reactコンポーネントを作成(ArticleFilter.jsx、DarkModeToggle.jsx)
  3. CSS Custom Propertiesでテーマシステムを構築
  4. LocalStorageとMutationObserverでダークモード永続化
  5. レスポンシブ対応とアクセシビリティ対応

Claude Sonnet 4.5 の進化ポイント

以前(他のAI)との比較

項目 以前(o3/Codex/Cursor組み合わせ) 今回(Claude Sonnet 4.5単体)
AI切り替え 4つのAIを手動で切り替え 1つのAIで完結
コンテキスト理解 仕様書を細かく書く必要 曖昧な要望でも意図を汲む
デバッグ エラーログをコピペして質問 自発的に動作確認まで提案
UI調整 「16px空けて」など具体的指示 「いい感じに」で伝わる
実装時間 半日〜1日 20分

特に優れていた点

  1. コンテキスト保持能力

    • 100以上のやり取りでも過去の変更を覚えている
    • 「さっきの修正を戻して」が通じる
    • 「やっぱ3行」のような言い直しにも対応
  2. 段階的実装の提案

    • 「1つずつ実装しましょう」と自発的に提案
    • 各機能の優先度と実装時間を見積もり
    • おすすめの実装順序まで提示
  3. エラー予測と対策

    • 「この変更でレイアウトが崩れるかも」と事前警告
    • 代替案を複数提示
    • 修正後の動作確認方法まで案内
  4. ユーザー体験への配慮

    • 「カードの高さを揃えた方が見やすい」と提案
    • Lazy Loadingなどパフォーマンス最適化を自発的に実装
    • アクセシビリティ対応(aria-label等)も自動で追加

開発時間

フェーズ 時間 内容
サムネイル表示 3分 RSS解析→画像取得→表示
バッジ表示 2分 色とポジショニング
Lazy Loading 1分 属性追加のみ
フィルター機能 3分 React state管理
全体クリック 2分 a タグラップ
高さ統一 5分 3行固定調整含む
ホバー効果 2分 transform追加
ダークモード 3分 テーマシステム構築
合計 約20分 すべて対話のみ

従来なら半日〜1日かかる作業量が、コーヒー1杯飲む時間で完了。

まとめ — AIコーディングの新時代

できるようになったこと

  • ✅ 複雑な状態管理(React state + MutationObserver)
  • ✅ CSS Custom Propertiesのテーマシステム
  • ✅ アクセシビリティ対応(aria-label等)
  • ✅ パフォーマンス最適化(Lazy Loading)
  • ✅ レスポンシブ対応の自動調整

それでも人間に残る役割

  • 🎯 「何を作りたいか」のビジョン
  • 👀 最終的なUIの良し悪しの判断
  • 🔄 優先順位の決定(「次はダークモード」)
  • ✨ UXの細かい調整(「3行がいい」「やっぱ3行」)

次の展開

今回実装しなかった機能候補:

  • 検索機能
  • 記事の統計情報表示
  • OGP画像の自動生成
  • 読了時間の推定

Claude Sonnet 4.5 なら、これらも対話だけで実装できそうです。


付録 — 実装コード抜粋

RSSから画像取得(fetch_feeds.py)

# media:thumbnailから取得(noteで使用)
# テキストとして格納されている場合に対応
media_thumbnail = item.find("{http://search.yahoo.com/mrss/}thumbnail")
if media_thumbnail is not None:
    if media_thumbnail.text:
        image_url = media_thumbnail.text.strip()
    else:
        # 属性として格納されている場合も考慮
        image_url = media_thumbnail.get("url", "")

ダークモードトグル(DarkModeToggle.jsx)

const toggleDarkMode = () => {
  const newIsDark = !isDark;
  setIsDark(newIsDark);
  
  if (newIsDark) {
    document.documentElement.classList.add('dark-mode');
    localStorage.setItem('theme', 'dark');
  } else {
    document.documentElement.classList.remove('dark-mode');
    localStorage.setItem('theme', 'light');
  }
};

// 初期状態を取得(システム設定も考慮)
useEffect(() => {
  const savedTheme = localStorage.getItem('theme');
  const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
  const shouldBeDark = savedTheme === 'dark' || (!savedTheme && prefersDark);
  
  setIsDark(shouldBeDark);
  if (shouldBeDark) {
    document.documentElement.classList.add('dark-mode');
  }
}, []);

カードのホバー効果(ArticleCard.jsx)

style={{
  transition: "all 0.3s cubic-bezier(0.4, 0, 0.2, 1)",
  transform: "translateY(0)"
}}
onMouseOver={e => {
  e.currentTarget.style.boxShadow = "0 8px 24px rgba(0,0,0,0.15)";
  e.currentTarget.style.transform = "translateY(-8px)";
}}
onMouseOut={e => {
  e.currentTarget.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
  e.currentTarget.style.transform = "translateY(0)";
}}

フィルター機能(ArticleFilter.jsx)

const [filter, setFilter] = useState('all');
const [isDark, setIsDark] = useState(false);

// MutationObserverでダークモードの変更を監視
React.useEffect(() => {
  const checkDarkMode = () => {
    setIsDark(document.documentElement.classList.contains('dark-mode'));
  };
  
  checkDarkMode();
  
  const observer = new MutationObserver(checkDarkMode);
  observer.observe(document.documentElement, {
    attributes: true,
    attributeFilter: ['class']
  });
  
  return () => observer.disconnect();
}, []);

const filteredArticles = filter === 'all' 
  ? articles 
  : articles.filter(article => article.source === filter);

コードを書かない開発は、もはや「実験」ではなく「実用」になりました。
Claude Sonnet 4.5 は、アイデアを20分で形にする時代を実現しています。
次は、あなたの番です。

リバナレテックブログ

Discussion