PenpotとHeadless UIの融合:デザインシステムからアクセシブルなReactコンポーネントを爆速で生成するパイプライン
テックリードの皆さん、日々のフロントエンド開発において「デザインの変更がコードに反映されるまでのタイムラグ」や、「Figma(あるいは昨今急成長しているオープンソースのPenpot)で作られたデザインと、実装されたReactコンポーネントのアクセシビリティ(a11y)の乖離」に頭を悩ませていないだろうか?
特に、Penpotのようなオープンで拡張性の高いデザインツールを軸に据える場合、プロプライエタリなツール特有のブラックボックスを排除し、デザインからコードへのシームレスなパイプラインを自社で完全にコントロールできる。
本記事では、Penpotのデザイン変数(Design Tokens)と、W3C ARIA準拠のアクセシビリティを完全に担保するHeadless UI(およびTailwind CSS)を緻密に紐付け、デザイン変更が即座にプロダクションコードへ反映される「爆速かつ堅牢なコンポーネント生成パイプライン」の構築手法を、実戦的なコードと設定ファイルと共に完全解説する。
—
1. 概念設計:なぜ「Penpot × Headless UI」なのか?
多くのチームが陥る罠は、デザインツール上で描かれた「見た目のコンポーネント」をそのままエンジニアが手動でコード化することだ。これはスケールしない。
真にスケーラブルなアプローチは以下のレイヤー分離にある:
1. デザインシステム層(Penpot): カラー、タイポグラフィ、スペーシングなどの「デザイン変数」と、コンポーネントの振る舞いの定義。
2. プリミティブ層(Headless UI): フォーカス管理、キーボードナビゲーション、ARIA属性などの「インタラクションとアクセシビリティの骨格」。
3. スタイリング層(Tailwind CSS): デザイン変数をマッピングしたユーティリティクラスによる「見た目の具現化」。
Penpotの強力な「Design Tokens(変数)」機能と、Headless UIの非制御(Uncontrolled)コンポーネント思想をトークンレベルで同期させれば、「デザインをいじれば、アクセシビリティを微塵も損なわずにコード側が追従する」理想郷が手に入る。
—
2. 開発スピードを極限まで高める:Penpotのプロフェッショナル術
パイプライン構築の前に、デザイン側の基盤を整えるための実務知見を共有する。ここを手を抜くと、後続のコード生成が破綻する。
隠れた神ショートカット & 操作の極意
- `Shift + V`:コンポーネントのバリアント切り替えを瞬時に行う。
- `Ctrl + /` (Macは `Cmd + /`):コマンドパレットの呼び出し。マウスを触らずにレイアウトグリッドや変数の適用を行え。
- コンポーネントの「Grid / Flexレイント」の厳格化: PenpotのCSS Grid / Flexbox実装は本物のCSSと完全に同等だ。デザイナーに「絶対値(px)での絶対配置」を禁止し、必ずFlex/Gridレイアウトで組ませること。これがそのままTailwindの `flex`, `grid`, `gap-` に変換される。
導入必須:Penpotプラグイン & 連携拡張
- Tokens Studio for Penpot(非公式コミュニティプラグイン等を活用したJSON同期):
デザイン変数をJSONとしてエクスポート・インポートするパイプラインの要。Figma Tokensと同様のスキーマをPenpot上でも維持する。
—
3. 実践:デザイン変数からTailwind & Headless UIへの同期パイプライン
ここからが本題だ。Penpotから出力されたデザイン変数を、TypeScriptとTailwind CSSの型安全な世界へ流し込む。
ステップ1: Penpot TokensのJSONエクスポート
Penpotからエクスポートされたデザイン変数(`tokens.json`)は、以下のような構造を持っていると仮定する。
{
“color”: {
“primary”: {
“value”: “#2563EB”,
“type”: “color”
},
“primary-foreground”: {
“value”: “#FFFFFF”,
“type”: “color”
}
},
“spacing”: {
“md”: {
“value”: “16px”,
“type”: “spacing”
}
}
}
ステップ2: トークン変換スクリプト(Style Dictionaryの活用)
このJSONを、Tailwindのコンフィグに変換する。`style-dictionary` などのツールをカスタムし、ビルド時に自動変換するパイプラインを組む。
以下は、プロジェクトルートに配置する `tailwind.config.ts` のベストプラクティス構成例だ。Penpotのトークンと完全に直結している。
// tailwind.config.ts
import type { Config } from ‘tailwindcss’;
import headlessuiPlugin from ‘@headlessui/tailwindcss’;
// Penpotから同期されたデザイントークン(実際はビルド時にJSONから自動生成を推奨)
import designTokens from ‘./tokens/penpot-tokens.json’;
const config: Config = {
content: [
“./src/pages//.{js,ts,jsx,tsx,mdx}”,
“./src/components//.{js,ts,jsx,tsx,mdx}”,
“./src/app//.{js,ts,jsx,tsx,mdx}”,
],
theme: {
extend: {
// Penpotのデザイン変数をTailwindの色空間にマッピング
colors: {
brand: {
DEFAULT: designTokens.color.primary.value,
foreground: designTokens.color[‘primary-foreground’].value,
},
},
spacing: {
penpotMd: designTokens.spacing.md.value,
},
},
},
plugins: [
headlessuiPlugin, // Headless UIの状態変化クラス(data-headlessui-state等)と連携
],
};
export default config;
—
4. 爆速かつアクセシブルなReactコンポーネントの実装
デザイン変数がコード化されたら、次はHeadless UIをベースにしたコンポーネントの実装だ。今回は実務で最も頻出する「アクセシブルなドロップダウン(Menu)」を例に取る。
このコンポーネントは、Penpot上のデザイン定義(フォーカス状態、ホバー状態、トランジション)を、Headless UIのアクセシビリティ(WAI-ARIA準拠のキーボード操作、フォーカストラップ)の上に完全に構築している。
// src/components/AccessibleDropdown.tsx
import React, { Fragment } from ‘react’;
import { Menu, Transition } from ‘@headlessui/react’;
import { ChevronDownIcon } from ‘@heroicons/react/20/solid’;
interface DropdownItem {
label: string;
href: string;
onClick?: () => void;
}
interface AccessibleDropdownProps {
buttonText: string;
items: DropdownItem[];
}
/
- PenpotのデザインシステムトークンとHeadless UIを融合させたアクセシブルなドロップダウン
- – スクリーンリーダー対応(aria-属性はHeadless UIが自動制御)
- – キーボードナビゲーション(矢印キーでの移動、Escでの閉じる動作)完全対応
/
export const AccessibleDropdown: React.FC
buttonText,
items,
}) => {
return (
{buttonText}
{/ Menu.Itemsは自動的にrole=”menu”を持ちます /}
{({ active }) => (
)}
))}
);
};
—
5. チーム開発で役立つ設定の共有化ルール
属人性を排し、デザイナーとエンジニアが共通言語を持つための「ガバナンスルール」をチームに導入しよう。
1. 命名規則の厳格化(Design Token Naming Convention)
FigmaやPenpot、そしてコードの間で齟齬を生む最大の原因は命名の不一致だ。以下の階層構造(BEMにインスパイアされたトークン構造)をLintで強制する。
- `{category}-{property}-{modifier}`
- 例: `color-primary-default`
- 例: `spacing-component-md`
CI/CDパイプラインでの自動検証(GitHub Actions)
デザイナーがPenpot上で変数を更新し、JSONがPR経由でリポジトリに投げ込まれた際、以下のバリデーションをCIで走らせる。
.github/workflows/design-tokens-lint.yml
name: Validate Design Tokens
on:
pull_request:
paths:
- ‘tokens/’
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: ’20’
- name: Install dependencies
run: npm ci
- name: Lint Design Tokens against Schema
run: npm run tokens:validate
- name: Check Tailwind Build
run: npm run build:css
—
6. テックリードからの総括
Penpotのオープンなデザイン変数と、Headless UIの堅牢なアクセシビリティプリミティブを組み合わせることで、「デザイン変更に強く、かつアクセシビリティがデフォルトで担保されたReactコンポーネント」を爆速で量産するパイプラインが完成する。
商用ツールへのベンダーロックインを避けつつ、デザインからコードへの透明性の高いワークフローを手に入れたいチームにとって、この構成は現在のベストソリューションの一つだ。
明日からのスプリントで、まずは配色のデザイントークンの同期から試してみてほしい。開発速度とコード品質の劇的な変化に、チーム全員が驚くはずだ。