
くらにゃんLINEミニアプリをFigmaのVariablesとMCPを使ってダークモードに対応してみた
こんばんは、リテールアプリ共創部マッハチームの西田です
先日、弊社のマスコットキャラクターである、くらにゃんの公式ショップのLINEミニアプリがリリースされました
今回はくらにゃんLINEミニアプリを、AIを活用し高速でダークモードに対応したので、その過程をご紹介させていただきます
全体の仕組み
利用している技術
- Next.js ⇒ LINEミニアプリ
- Tailwind 4 ⇒ CSS
- Claude Code ⇒ コーディング補助
- AI Starter ⇒ 雑務、壁打ち
全体像
Dark Mode を反映したデザイントークンをFigmaで定義し、
それをMCPとClaude Codeを使って Next.js で作成されたLINEミニアプリに反映しました
デザイントークンをFigmaのVariablesで定義
FigmaのVariablesを使い、デザイントークンを定義します。Variablesを使うことにより、Figmaで作成するデザイン内で再利用可能で、一貫性のあるデザインの作成を楽にします。
Variablesはデザインシステムの構築にも利用でき、今回のダークモード対応ではデザインシステムのデザイントークンを定義します。
デザイントークンは2種類登録します
- プリミティブトークン
- セマンティックトークン
プリミティブトークンはデザインシステム内で使われる設定値を定義します。今回はダークモード対応のために色のトークンを定義します
primitive/color/grey/300
⇒#DDDDDD
primitive/color/grey/700
⇒#444444
プリミティブトークンをFigmaのデザイン上で使い回すことで、色味の一貫性を保ちやすくします
セマンティックトークンは意味を持った名前で、デザイン上でどこに使用するのかのコンテキストを含む名前にします。Figma上のデザインからはこのセマンティックを参照し、プリミティブトークンは、セマンティックトークンからエイリアス先として設定します
semantic/color/text
⇒primitive/color/grey/950
semantic/color/heading/secondary
⇒primitive/color/grey/900
FigmaのデザインにVariablesを適用
FigmaのデザインにVariablesを適用していきます。色を選択するところで、 Libraries から選択することができます
Variablesをエクスポートし、AIに渡してダークモードの色を指定
Figma の Plugin を使って、Variablesの定義をエクスポートします
今回は上記の Plugin を使って、エクスポートしました。JSON形式のファイルがエクスポートされるので、そのファイルをAIに渡し、ダークモードの定義を出力してもらいました
VariablesのMode機能でDarkモードを表現する
Variables の Mode 機能を使って、Figma上でDarkモードを表現できるようにします。モードを追加し「Dark」と名前をつけます。セマンティックトークのDarkモード時に参照するプリミティブトークンを設定します
デザインに戻り、Appearance からModeを切り替えることで、LightモードとDarkモードを切り替え、見た目を確認することができます
Variablesをエクスポートし、リポジトリに含める
先ほどエクスポートしたJSONをリポジトリに含め、 AIから参照できるようにします
デザイントークンを Tailwind で使えるようにする
デザイントークン定義をエクスポートしたJSONを Tailwind で使えるようにします。今回は Tailwind v4 を使ってるので、通常のCSS変数として取り込みます。最初 AI (Claude Code) に読み込ますだけでいい感じに css ファイルに追記してくれると想定していましたが、JSONファイルが大きすぎるためか、なかなかうまくいかず、最終的には、出力されたJSONから CSS ファイルに変換するスクリプトをAIに出力させ、それを実行しCSSファイルに変換しました。
出力されたCSSは以下のようなCSSです
:root {
/* Colors from Figma */
--color-grey-50: #f5f5f5;
--color-grey-950: #0a0a0a;
--color-text-primary: var(--color-grey-950);
--color-background-page: var(--color-grey-50);
/* ...省略... */
}
.dark {
/* Dark mode color overrides */
--color-text-primary: var(--color-grey-50);
--color-background-page: var(--color-grey-950);
/* ...省略... */
}
CSSの変数として、プリミティブカラー、セマンティックカラーの定義が出力され、ダークモード対応用の .dark
CSSクラスの配下であればセマンティックカラーの変数を上書きするようになっています。
スクリプト自体は汎用性がないもののため、ここには記載しませんが、出力結果を見ながらAIに指示を出し調整しました
Next.js上でOSの設定に合わせてダークモードを切り替えれるようにする
OSの設定からダークモードの設定を取得し、その内容に応じて、html要素にcssクラス light
か dark
を設定しています。また、LINEミニアプリが実行されるLIFFブラウザでは、ダークモードに直接対応していないので、手動でもダークモードを切り替えられるようにしました。手動で切り替えた設定は localStorage に保存するようにしてます
"use client";
import { createContext, useContext, useEffect, useState } from "react";
type Theme = "light" | "dark" | "system";
type ThemeContextType = {
theme: Theme;
setTheme: (theme: Theme) => void;
};
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
export function useTheme() {
const context = useContext(ThemeContext);
if (!context) {
throw new Error("useTheme must be used within a ThemeProvider");
}
return context;
}
export function ThemeProvider({ children }: { children: React.ReactNode }) {
const [theme, setTheme] = useState<Theme>("system");
useEffect(() => {
const storedTheme = localStorage.getItem("theme") as Theme | null;
if (storedTheme) {
setTheme(storedTheme);
}
}, []);
useEffect(() => {
const root = window.document.documentElement;
root.classList.remove("light", "dark");
const applyTheme = () => {
if (theme === "system") {
const isDark = window.matchMedia("(prefers-color-scheme: dark)").matches;
root.classList.add(isDark ? "dark" : "light");
} else {
root.classList.add(theme);
}
};
applyTheme();
if (theme === "system") {
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
const handleChange = () => applyTheme();
mediaQuery.addEventListener("change", handleChange);
return () => mediaQuery.removeEventListener("change", handleChange);
}
}, [theme]);
const handleThemeChange = (newTheme: Theme) => {
setTheme(newTheme);
localStorage.setItem("theme", newTheme);
};
return (
<ThemeContext.Provider value={{ theme, setTheme: handleThemeChange }}>
{children}
</ThemeContext.Provider>
);
}
Figma MCPを使いコンポーネントにTailwindのクラスを当てる
Figma MCPを設定
Figma MCPはFigma のディスクトップアプリの Local Server を使用します。ディスクトップアプリを起動し、メニューから Preferences → Enable local MCP Server でMCPサーバーを起動します
その後、MCPサーバーを Claude Code に登録します
claude mcp add --transport sse figma-dev-mode-mcp-server http://127.0.0.1:3845/sse
以下のコマンドを実行し
claude mcp list
以下の出力が確認できればOKです
figma-dev-mode-mcp-server: http://127.0.0.1:3845/sse (SSE) - ✓ Connected
MCPを使ってコンポーネントにクラスを適用
Figma MCPを使うとディスクトップアプリ上で選択した Node をClaude Codeから参照できるようになります
Claude Code に指示を出すときに、明示的に「Figma MCPを使用して」と付け加えると、Figma MCPを利用する確率が高まります。
Figma MCPは選択したノードの配下のノードの情報まで一度に取得できます。最初、一番上位のフレームを選択して一度に全てのCSSクラスを適用する想定でしたが以下の問題が発生しできませんでした
- Tailwind v4 のテーマに設定したクラス名をAIが使用しない
- AIがFigma上のデザインとReactコンポーネントとの対応がわからず、よくわからない修正を繰り返してしまう
そのため、まず、 Tailwind v4 のテーマに設定したクラス名に手動で修正し、修正内容をプロンプトで伝えました。
その後、同じセッションでコンテキストを保ったまま、React コンポーネントに対応する Figma 上のNodeを選択し、Claude Code 上で @
で対応するコンポーネントのファイルを指定し、一つ一つ指示を出すことで、思った通りの修正を行うことができました
最後に
今回はAIを使って、LINEミニアプリをダークモードに対応してみました。基本的には Plan Mode ⇒ Auto Edit Mode の順で開発を進めていますが、Plan Mode の情報量では確認しきれない部分も多く、出力し、動きを確認し、修正を繰り返すというステップで開発を進めました。
今回のダークモード実装には大体半日程度かかり、そのほとんどを慣れないFigmaの作業が占めています。AIを使って10倍の生産性まで、まだ遠い感じがしますが、案件作業中の合間で、こういった作業も行えるのは、AIなしでは考えられなかったと思います。
この記事が誰かの役に立てれば幸いです