
ダークモード固定だった gaipack サイトを、Claude Code と 3 日でデュアルテーマ化しました🌗
ダークモード固定だった gaipack サイト(約 40 ページ)を、ライト / ダーク両対応にリニューアルしました。色のハードコード約 5,000 箇所をセマンティックトークンへ移行した設計と、途中でハマったポイント、Claude Code との協業で工夫したことを紹介します。
背景: 「黒すぎる」サイトをエンタープライズ向けに
きっかけは、社内からの「gaipack サイトが黒すぎるので白くしてほしい」という依頼でした。エンタープライズのお客様に見ていただくには、白基調の見やすいページも必要という趣旨です。当時の gaipack サイトはダークモード固定で、ライトテーマという概念自体がありませんでした。
細かい指示はなかったため、要件は次のように定義しました。
- ライト / ダークの両方に対応する
- デフォルトの挙動はシステム(ブラウザ)の設定に準拠する
- ボタンで手動トグルできるようにする
- ダークモードの見た目は変えない
対象サイトの構成
gaipack サイトの構成は次のとおりです。
| 項目 | 内容 |
|---|---|
| フレームワーク | Next.js |
| スタイリング | Tailwind CSS + shadcn/ui |
| 動的コンテンツ | Contentful(ブログ・お知らせ) |
| ホスティング | Vercel |
| ページ数 | 約 40 |
サイト自体が、gaipack のサービスである「AIDD デザイン」「AIDD CMS」のスキームで構築されています。
課題: 色のハードコードが約 5,000 箇所
実装に入る前に Claude Code のプランモードで現状を調査したところ、bg-[#0B1218] や text-white のような色の直書きがコード全面にあることが分かりました。その数、約 5,000 箇所です。
<!-- どこを切り替えても、この色は変わらない --><div class="bg-[#0B1218] text-white border-white/10">この構造では globals.css で .dark を切り替えても色が変わりません。テーマ切り替えの仕組みを入れる前に、まずハードコードされた色をセマンティックトークンへ移行するリファクタリングから着手しました。
セマンティックトークンとは、色そのものではなく「役割(意味)」に名前をつけた変数のことです。「この色は背景」「この色は本文」という意味づけを CSS 変数として定義し、コンポーネント側は意味を参照します。
設計の肝: ダークを「正」としたトークン化
トークン設計では、ダークテーマを基準(正)に据えました。
--surface/--content/--accent-*などのセマンティックトークンを定義する- 各トークンのダーク値には、元のハードコード値をそのまま割り当てる
- ライト値は、基準となるダーク値からの派生値として新たに定義する
/* ダーク値 = 元のハードコード値。置換してもダークは変わらない */:root { --surface: #0B1218; --content: #FFFFFF;}ダーク値を元の値と一致させているため、ハードコードをトークンに置換してもダークモードの見た目はロスレスで変わりません。「ダークの見た目は変えない」という要件を、置換作業そのものの仕組みで担保できます。
ライト専用の差分は light: バリアントで重ねる
ライトモードだけ見た目を変えたい箇所は、Tailwind CSS の @custom-variant で light: バリアントを用意し、差分だけを上書きする設計にしました。
<!-- before --><div class="bg-[#0B1218] text-white"><!-- after: トークン参照 + ライト差分のみ light: で --><div class="bg-surface text-content light:shadow-none">基本はトークン参照だけでライト / ダークが切り替わり、ライト固有の調整(影を消す等)だけが light: として残ります。差分が明示的になるので、どこがライト専用の調整なのかコード上で追いやすくなりました。
ライトのデザイン方針: フラットに寄せる
ライトテーマのデザインは、同じ KDDIアイレットのブランドである iret / cloudpack のサイトを参考にしたフラット設計としました。Claude Code から「基準となるサイトはありますか」と質問されたので、両サイトの URL を渡して方向性を揃えています。
- ダークの演出(影・グロー・グリッド)はライトでは抑制する
- セクションの区切りは、枠線とトーン差で作る
ハマりどころ
1. hex にアルファを連結するイディオム
既存コードには、16 進数カラーコードに 1A のような 2 桁を連結して透明度を表現するイディオムがありました。トークン化で色が var(--accent) になると、この連結が有効な色になりません。SVG では黒、CSS では透明として描画されてしまいました。
// NG: var(--accent) + "1A" は有効な色にならないconst fill = `${accent}1A`; // SVG: 黒 / CSS: 透明// OK: 不透明度は opacity 属性で分離(SVG)fill={accent} opacity={0.1}// OK: CSS 側は「最初からアルファ入りのトークン」を用意border: "1px solid var(--lp-border)"透明度は opacity 属性やアルファ込みの専用トークンとして分離し、参照する形に修正しました。
2. iframe と SVG には親の CSS が届かない
お問い合わせに使っている HubSpot フォームは iframe 内に描画されるため、Next.js 側で定義したテーマ用 CSS が届きません。ライトモードにするとラベルが白文字のまま残り、読めない状態になっていました。こちらは iframe にライト用の CSS を注入して対処しました。
Recharts のチャートや装飾用の SVG にも、白線・白文字・暗色パネルの直書きが残っていて、ライトでは要素が見えなくなります。これらは個別にテーマ対応を入れました。
3. コードだけでは分からない前提
着手前は「動的コンテンツはすべて Contentful 管理」と思い込んでいましたが、FAQ は実は Zendesk の API を参照していることが後から判明しました。
もうひとつ、LP のひとつである /lp/aidd-camp は Next.js ではなく静的 HTML を rewrite で配信しており、そもそも今回の仕組みでは対象にできない構造でした。コードを眺めただけでは分からない前提が着手後に次々と発覚し、対応範囲を随時見直すことになりました。
Claude Code との進め方: 往復をどう減らすか
最初はトップページから 1 ページずつ「依頼 → 指摘 → 修正」を繰り返していましたが、この往復にかなりの時間を取られました。
転機になったのは、Claude Code 自身への「どういう指示を出したら効率的に進められる?」という質問です。制約・失敗パターン・完了条件を先に固定して一括スキャンするのがよい、という提案を受けて進め方を切り替えました。前提条件をまとめて渡し、機械的に判定できる箇所は一括で処理してもらう方式です。
補足として、作業は Claude のデスクトップアプリ上の Claude Code で行いました。修正結果のスクリーンショットを添付しながら対話する場面が多く、視覚的に確認しやすかったためです。また、セッションのコンテキスト上限に達しそうになり、途中で 2 回 /compact を実行しています。
デグレ検証: ダーク不変を実測で保証する
「ダークは変えていないはず」を信用しない、を検証の大方針にしました。
- 本番サイトの URL を渡し、本番ダークとローカルのカラーパレットを実測で突き合わせる
- 全ページのスクリーンショットから色を抽出して比較する
この機械的な比較は Claude Code に任せました。ただし最終的な見た目の判断は人間の仕事です。本番サイトとローカルを自分の目で何度も見比べて、微修正を繰り返しました。最初にできあがったライトモードは、お問い合わせボタンが明るすぎたりメインメッセージが読みにくかったりと目がチカチカする状態で、そこからの調整の往復が一番泥臭い部分でした。
結果と残タスク
- 全ページでライト対応・ダーク不変を実現しました(AIDD キャンプ LP を除く)
- デザインの正本である
DESIGN.mdをデュアルテーマ前提の内容に更新しました
DESIGN.md には今回のトークン設計を宣言的な設計原則として明文化したので、今後新しいページを作るときはこれを参照するだけでライトテーマに対応しやすくなるはずです。残タスクは、静的 HTML 配信になっている AIDD キャンプ LP の Next.js 移行と、その際のライト対応です。
まとめ
| 学び | 内容 |
|---|---|
| トークン設計 | 基準テーマを固定し、もう一方を派生させる設計がデュアルテーマを安全にする |
| AI との分担 | 機械的な置換・検証は Claude Code に一括で任せ、人は美的判断のレビューに絞る |
| 指示の出し方 | 前提・制約・完了条件を最初の 1 メッセージに込めると往復が激減する |
Claude Code がなければ 1 ヶ月以上かかっていたであろう作業を、3 日で終えられました。
もうひとつの学びは、toC / toB を問わずデュアルテーマは最初から検討しておくべき、ということです。後からの移行は今回のようにリファクタリングの負担が大きいため、当面ダークオンリー(ライトオンリー)で運用するとしても、切り替えやすいトークン設計にしておくことをおすすめします。
gaipack ではこのように、自社サイトを「AIDD デザイン」「AIDD CMS」のスキームで運用しながら改善を続けています。AI を活用した Web サイト構築・運用にご興味があれば、お気軽にお問い合わせください!