【実務・中級編】PenpotとHeadless UIの融合:デザインシステムからアクセシブルなReactコンポーネントを爆速で生成する手法 – UI/UX・デザインツール活用バイブル

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 (

{/ Headless UIのMenu.Buttonは自動的にaria-haspopupやaria-expandedが付与されます /}

{buttonText}


{/ Menu.Itemsは自動的にrole=”menu”を持ちます /}

{items.map((item, index) => (

{({ 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コンポーネント」を爆速で量産するパイプラインが完成する。

商用ツールへのベンダーロックインを避けつつ、デザインからコードへの透明性の高いワークフローを手に入れたいチームにとって、この構成は現在のベストソリューションの一つだ。

明日からのスプリントで、まずは配色のデザイントークンの同期から試してみてほしい。開発速度とコード品質の劇的な変化に、チーム全員が驚くはずだ。

タイトルとURLをコピーしました