Works
Cubic Bezier Input
スタイルが適用されておらず、高い composability を備えたキュービックベジェ入力コンポーネント。
概要
cubic-bezier-input は、スタイルを持たないヘッドレスな React コンポーネントです。Radix や shadcn/ui などのライブラリで広まった composable なコンポーネントパターンに従い、開発者がベジェ曲線エディタのルックアンドフィールを完全に制御できるようにします。
課題
既存のベジェ曲線入力にはいくつかの制限がありました:
- 特定のデザインシステムに依存 – ほとんどの実装が特定の UI ライブラリ向けに作られており、既存のプロジェクトに組み込むのが困難でした。
- スタイルの変更が難しい – 組み込みのビジュアルがカスタムデザインと衝突し、上書きするのが大変でした。
- コンポーザブルではない – グリッド、曲線、コントロールハンドル — すべてが単一のモノリシックなコンポーネントで、カスタマイズの選択肢が限られていました。
Tailwind でスタイルを適用でき、Radix のような composability を持つものが欲しいと思いました。そうすれば、他人のデザイン決定と戦うことなく、どんなプロジェクトにも統合できます。
解決策
コンポーネント構造は compound component パターンに従います:
- Root – ベジェ曲線の値に関する状態とコンテキストを管理します。
- Controller – 2つの制御点のうちの1つを表し、それぞれにインタラクティブな Thumb と接続する Line を持ちます。
- Curve – 調整可能な End 点を持つベジェ曲線のパスを描画します。
<CubicBezier.Root>
<CubicBezier.Grid />
<CubicBezier.Controller index={1}>
<CubicBezier.Line />
<CubicBezier.Thumb />
</CubicBezier.Controller>
<CubicBezier.Controller index={2}>
<CubicBezier.Line />
<CubicBezier.Thumb />
</CubicBezier.Controller>
<CubicBezier.Curve>
<CubicBezier.End />
</CubicBezier.Curve>
</CubicBezier.Root>
すべてのコンポーネントは className プロパティを受け付け、ゼロスタイルで出荷されるため、Tailwind や他の CSS アプローチを使って簡単にスタイルを適用できます。曲線とグリッドは SVG で描画されるため、fill-* や stroke-* などのユーティリティクラスを使って外観をカスタマイズできます。
開発と実装
このコンポーネントは、Vercel の AI コーディングツールである v0 を使って構築しました。まず API サーフェスを設計し、コンポーネント階層、公開するプロパティ、各パーツが受け付けるべきもの、そしてコンテキストを通じて状態をどのように流すかを検討しました。それらの設計が固まったら、v0 に内容を伝えてコードを生成させました。
API は Radix を参考にモデリングしました。状態管理は制御された(controlled)パターンと制御されていない(uncontrolled)パターンの両方をサポートし、onValueChange でリアルタイム更新、onValueCommit でユーザーの操作完了をハンドリングします。minY や maxY のような制約は、ベジェ値が特定の範囲内に収まる必要があるケースをサポートするために追加しました。
最後に
API を設計し、その仕様に基づいてエージェントに構築させるのがこれほど簡単になったことはありません。また、コードをどのように構造化してほしいかを具体的に指定することの重要性も増していると感じます。エージェントはコードを生成できますが、あなたが導かなければ良い設計判断を下すことはできません。
サードパーティのライブラリの設計決定に依存せずに、ミニライブラリや内部コンポーネントを素早く構築することは、エージェントの優れた活用方法です。自分のニーズに正確に合ったものを作れます。このアプローチは今後ますます使っていきたいと考えています。