Penpotのコンポーネントバリアント応用術:状態遷移をスマートに管理するネスト構造の設計手法
テックリードの君なら、こんな絶望を味わったことが一度はあるはずだ。
「デザインファイル上では綺麗に並んでいたコンポーネントが、いざコードに落とし込む段階になって破綻している」「ちょっとした状態(ホバー、フォーカス、ローディング)を追加しただけで、FigmaやPenpotのバリアント数が爆発し、デザイナーもエンジニアも迷子になっている」――。
オープンソースの旗手であるPenpotは、SVGをネイティブフォーマットとして採用し、CSSのメンタルモデル(FlexboxやGrid)をそのままデザインツールに持ち込んだ、我々エンジニアにとって最高のご褒美のようなツールだ。しかし、その自由度ゆえに、適当にコンポーネントを作ると「メンテ不能のゴミ山」がすぐに出現する。
今回は、Penpotのポテンシャルを極限まで引き出し、「エンジニアのコーディング規約と1:1で同期する」コンポーネント設計の極意を伝授する。バリアントの限界を突破し、状態遷移を美しく制御するネスト構造の構築手法をマスターしよう。
—
1. バリアント機能の限界を超えるネスト構造の活用法
単一のコンポーネントにすべての状態(Default, Hover, Focus, Disabled, Loading…)を詰め込もうとするのは、アンチパターンだ。プロパティの直積(組み合わせ)でバリアント数が爆発し、デザイナーはプロパティ選択地獄に陥り、エンジニアは何を参照すべきか迷う。
ここで導入すべきのが、「アトミック・ステート・ネスト(Atomic State Nesting)」という設計思想だ。
思考のフレームワーク:構造と状態の分離
コンポーネントの本質は以下の2つに分解できる。
1. ストラクチャ(構造): アイコン、テキスト、ボーダー、背景など、見た目の骨組み。
2. ステート(状態): ホバー時の色変化、フォーカス時のリング、無効時の透過度など。
Penpotでは、「ロジカル・コンポーネント(親)」の中に「ステートレスなビジュアル・コンポーネント(子)」をネストさせることで、バリアントの爆発を防ぐ。
実践:プライマリボタンのネスト構造
- Level 1 (Atom): `_Button/Base`(Auto-Layoutのパディング、タイポグラフィ、角丸のみを持つ。状態を持たない)
- Level 2 (Molecule): `Button/Primary`(`_Button/Base`を内包し、Variantプロパティとして `State [Default / Hover / Focus / Disabled]` のみをコントロールする)
親コンポーネントは状態の見た目(CSSの疑似クラスに相当する部分)のみを管理し、実際のコンテンツ(ラベルやアイコン)はオーバーライド(インスタンス上での書き換え)に委ねる。これにより、バリアント数を最小限($O(N)$のオーダー)に抑え込むことができる。
—
2. ホバー、フォーカス、ディセーブル状態を効率よく管理する命名規則
チーム開発、特にデザイナーとエンジニアが混ざるプロジェクトにおいて、命名規則の欠如は致命傷になる。Penpotのレイヤーパネルおよびコンポーネント名規約は、そのままCSS/Tailwind CSSのクラス設計と直結させるべきだ。
スラッシュ(`/`)を用いたFigma/Penpot共通の階層命名規則
Penpotのプロパティ機能とオートレイアウトを最大限に活かすため、以下の命名スキームをチームの絶対ルールとして定めよ。
> `[カテゴリー] / [コンポーネント名] / [バリアント属性]=[値]`
具体例:
- `Components / Button / Type=Primary, State=Default`
- `Components / Button / Type=Primary, State=Hover`
- `Forms / Input / Size=Md, State=Error`
開発スピードを加速させるキーボードショートカット(Penpot版)
マウス操作でレイヤーをポチポチしている時間はエンジニアの寿命を削る。以下のショートカットを指に覚え込ませろ。
| ショートカット (Mac / Windows) | アクション | テックリードの活用文脈 |
| :— | :— | :— |
| `⌥ + ⌘ + K` / `Alt + Ctrl + K` | コンポーネント化 (Create Component) | 素早くアトミックパーツを切り出す時 |
| `⌥ + ⏎` / `Alt + Enter` | コンポーネントのメインに移動 (Go to main) | インスタンスから親の構造を瞬時に確認・修正する時 |
| `Shift + A` | オートレイアウト追加 (Add Auto Layout) | Flexbox構造をコンポーネントに即座に適用する時 |
| `⌘ + G` / `Ctrl + G` | グループ化 | レイヤー構造を整理し、ネストの境界を明確にする時 |
—
3. エンジニアのコーディング規約と完全に一致させるコンポーネント設計のコツ
優秀なUI/UXエンジニアならこう考えるはずだ。「このPenpotのデザイン、そのままTailwind CSSのコンポーネント設計に落とし込めるよな?」と。
デザインとコードの乖離をゼロにするために、Penpotのパラメータ設計をコンポーネント指向フレームワーク(React / Vue / Svelte等)のPropsと完全同期させる。
設計の同期ルール
1. Penpotの「Booleanプロパティ」 $\rightarrow$ Reactの `isLoading?: boolean`, `hasIcon?: boolean`
2. Penpotの「Variantプロパティ」 $\rightarrow$ Reactの `variant?: ‘primary’ | ‘secondary’`, `size?: ‘sm’ | ‘md’ | ‘lg’`
3. Penpotの「Textプロパティ」 $\rightarrow$ Reactの `children: string`
この規約を徹底することで、デザイナーがPenpot上で設定したプロパティ名と、エンジニアが書くコンポーネントのインタフェース(TypeScriptの型定義)が完全に一致する。
—
🛠️ 生産性を極限まで高める神プラグイン & チーム設定共有ルール
1. 絶対入れるべき拡張機能・プラグイン
Penpotはエコシステムが急速に拡大している。以下のプラグイン(または公式提供の拡張機能・ワークフロー)をチーム全員に強制インストールさせろ。
- Design Token Exporter: Penpot上のカラー、タイポグラフィ、スペーシングをJSONとしてエクスポートし、GitHub Actions経由でコード側のCSS変数(Design Tokens)と自動同期させるパイプラインを構築せよ。
- Accessibility Checker (Contrast Tools): WCAG 2.1のコントラスト比をバリアントごとにリアルタイム検証し、アクセシビリティ負債の発生をデザイン段階でブロックする。
2. チーム開発で役立つ設定の共有化ルール
- カラーパレットとタイポグラフィの「Shared Libraries」化:
プロジェクト固有の自由な色入力を禁止する。すべてのカラーはDesign Tokens(Primitives / Semantic)としてライブラリ登録し、デザイナーは必ずセマンティックトークン(例: `color-action-primary-default`)を使用する。
- グリッド・スペーシングの強制:
スペーシングは `4px` または `8px` のグリッドシステム($4, 8, 12, 16, 24, 32, 48, 64$)に厳格に縛る。PenpotのGrid/Layout設定でこれを視覚的に強制する。
—
📄 実用的な設定ファイル(JSON)のベストプラクティス構成例
デザインシステムとコードベースの架け橋となる、Design TokensのJSONファイル(W3C Design Tokens Community Group形式準拠)のベストプラクティスを提示する。これをPenpotのエクスポート結果と紐付け、エンジニアリング側にシームレスに流し込む。
{
“button”: {
“primary”: {
“background”: {
“value”: “{color.brand.indigo.600}”,
“type”: “color”,
“comment”: “プライマリボタンのデフォルト背景色。Tailwindのbg-indigo-600に対応”
},
“background-hover”: {
“value”: “{color.brand.indigo.700}”,
“type”: “color”,
“comment”: “ホバー時の背景色。CSSの :hover 疑似クラスに直結”
},
“text”: {
“value”: “{color.neutral.white}”,
“type”: “color”,
“comment”: “ボタン内のテキストカラー”
},
“border-radius”: {
“value”: “{spacing.radius.md}”,
“type”: “borderRadius”,
“comment”: “角丸の設定。rounded-md (6px) に対応”
}
}
},
“color”: {
“brand”: {
“indigo”: {
“600”: { “value”: “#4F46E5”, “type”: “color” },
“700”: { “value”: “#4338CA”, “type”: “color” }
}
},
“neutral”: {
“white”: { “value”: “#FFFFFF”, “type”: “color” }
}
},
“spacing”: {
“radius”: {
“md”: { “value”: “6px”, “type”: “dimension” }
}
}
}
—
結び:ツールに使われるな、ツールを飼いならせ
優れたUI/UXエンジニアは、ツールの機能表を眺めて満足したりはしない。「このツールは内部でどういうDOM構造/SVGを描画し、どのようなCSSモデルで動いているか」を理解し、その特性をハックしてチームの生産性を最大化する。
Penpotのネスト構造と厳格な命名規則、そしてコードとの同期。これらを今日から君のチームのワークフローに導入しろ。
無駄な修正指示のチャットが消え、デザインと実装のズレに悩む夜は二度と訪れなくなるはずだ。さあ、エディタを開き、システムを再構築しよう。