🤝

エンジニア・デザイナーに知ってもらいたいFigmaとReactの違い

2024/07/15に公開

Figmaは非常に優秀で、Reactのコンポーネントをよく表現できると日々感じています。
だからこそ、Reactを実装しているエンジニアとFigmaで作成しているデザイナーとで、微妙に噛み合わない…シーンが出てきます。

お互いに「あ!ここが違うのか!」ということを知っておくと、コミュニケーションが楽になるのでは?と思ったので、まとめてみます。

こんな人におすすめ

  • Reactは実装できるが、Figmaではどんなことができるのかよく知らない
  • Figmaでコンポーネントを作成することはできるが、どう実装されるのかよくわかっていない

できるだけFigmaの設定とReactでの表現を合わせて書くようにするので、それぞれの対応表みたいな形で使ってもらえると嬉しいです。

プロパティ・Props編

Figma:プロパティ と React: props は同じものと考えてよい

コンポーネントという単位で考えると、ほぼ同じものを指していると考えて良さそうです。

プロパティ

https://help.figma.com/hc/en-us/articles/5579474826519-Explore-component-properties

右のメニューにある↑のこと。
コンポーネントにバリエーションを与える機能として説明してあります。

Props

https://ja.react.dev/learn/passing-props-to-a-component

親コンポーネントから子コンポーネントに情報を伝えるものとしてpropsが紹介されています。

比較

タイプ・型
Figma ・バリアント
・ブール値
・インスタンスの入れ替え
・テキスト
React ・string
・number
・boolean
・array
・object
など

Figma: ブール値 と React: boolean は別物

同じtrue/falseをとる値を指しますが、別物と考えたほうが良さそうです。
Figmaの方が使い方が限定されています。

Figma: レイヤーの表示/非表示を制御する

https://help.figma.com/hc/en-us/articles/5579474826519-Explore-component-properties#h_01G2Q5GA6DEB604H2E5H5C5TA4

Figmaのブール値はレイヤーの表示/非表示(=Nodeの有無)を制御するためにしか利用できません。
例えばアイコン付きのボタンなのか、アイコンがないボタンなのか、アイコンの有無を表すプロパティとして利用されます。
そのため、表示するアイコンを選択するためのプロパティを別途用意する必要があります。

ReactのTypeで表すと、以下のようなイメージです。

ButtonType.ts
type ButtonProps = {
  hasStartIcon: boolean;
  startIcon: React.ReactNode;
}

しかし、実際に実装されるコードは以下のようになります。

ButtonType.ts
type ButtonProps = {
  startIcon?: React.ReactNode;
}

Reactでは存在しないかもしれないprops(=optional)を定義することができます。
存在しない=表示しないということができるため、Figmaのブール値は省略し、表示するアイコンを指定するためのpropsのみ定義される場合が多いです。

React: 何にでも使える

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Boolean

Figmaのブール値とは違い、表示/非表示以外にも何にでも使えます。

  • trueの時には表示し、falseの時には非表示にする
  • trueの時には文字色を赤くし、falseの時には文字色をグレーにする
  • trueの時にはpaddingを0にし、falseの時にはpaddingを16にする

など、使用用途は様々です。

Figmaでいうバリアントプロパティにtrueの場合と、falseの場合が存在するという使い方がもっとも近いです。

Figmaでは"プロパティに値が存在しない"という状態を作ることができない

上記のブール値でも述べていますが、

  • Figma: プロパティの値は必ず存在する
  • React: propsには値がない状態を作ることができる

という違いがあります。

ただし、Figmaでは「ブール値」と組み合わせることでプロパティ自体を表示させないということが可能です。

Buttonを例とすると、

  • hasStartIcon: ブール値
  • startIcon: インスタンスの入れ替え

というプロパティを用意しているとします。

  • hasStartIcon:trueの場合、startIconを選択するためのプロパティが表示される
  • hasStartIcon:falseの場合、startIconを選択するためのプロパティが表示されない

ブール値によって非表示になった場合、そのレイヤー自体が存在しなくなるため、プロパティも同様に非表示となります。

Figma: インスタンスの入れ替え と Reat: ReactNode は同じものと考えてよい

インスタンスの入れ替え

https://help.figma.com/hc/en-us/articles/5579474826519-Explore-component-properties#h_01G2Q5FYN2ADEDQ3ZSB1KKY8Z0

コンポーネント内に含まれるコンポーネントを変更するためのプロパティのタイプ。
コンポーネント化されていないものとは変更することができません。

ReactNode

https://github.com/microsoft/TypeScript/blob/main/tests/lib/react18/react18.d.ts#L231

ReactElement, string, number, boolean, null などを渡すことができるpropsの型。

Figma: ネストされたインスタンス と React: 子コンポーネントへのpropsの受け渡し

コンポーネントに含まれるコンポーネントへのプロパティの設定と、propsの受け渡しには若干の違いがあります。

Figma: ネストされたインスタンス

https://help.figma.com/hc/en-us/articles/5579474826519-Explore-component-properties#exposed-instances

Figmaでは親のコンポーネントで子のコンポーネントのプロパティを変更させるには、ネストされたインスタンスを公開する形で実現することができます。

  • ネストされたインスタンスのプロパティを、部分的に公開することはできない
  • ネストされたインスタンスのプロパティ名を変更することはできない
    そのため、同じコンポーネントを利用したインスタンスを複数公開すると、同じプロパティ名が並んでしまいます

React: propsの受け渡し

親コンポーネントから子コンポーネントへ値を受け渡すことができます。

  • 親コンポーネントで指定できる子コンポーネントのpropsを制限することができる
  • 親コンポーネントと子コンポーネントのprops名は必ずしも一致する必要はない

以下の例ではAlertコンポーネントに2つのTypogprahyが含まれています。

const Typography: React.FC<{ text: string; fontSize: 16 | 14 }> = ({ text, fontSize }) => {
  return <p style={{ fontSize: fontSize }}>{text}</p>;
};

const Alert: React.FC<{ title: string; description: string; }> = ({ title, description }) => {
  return (
    <div>
      <Typography fontSize={16} text={title} />
      <Typography fontSize={14} text={description} />
    </div>
  );
};
  • fontSizeはAlertコンポーネントのpropsとしては変更できない
  • Typographyに渡されるそれぞれのtextは、Alertコンポーネントとしてはtitle, descriptionとい名前がついている

というふうに解釈することができます。

構造編

Reactというより、FigmaとHTML/CSSとの違いになりますが、知っておくと良いかと思います。

Figma: 線 と CSS: border がheight/wigthに与える影響は違う

一見線とborderは同じに見えますが、height/widthに与える影響は違います。

以下の要素に線/borderを付与した例で説明します。

<Figma>

<HTML/CSS>

<div class="label">保存</div>
.label {
  box-sizeing: border-box;
  display: "inline-flex";
  padding: 8px 16px;
  background: #D9D9D9;
}

Figma: 線 height/widthは変わらず、中の要素にかぶる

4pxの線を追加した場合、figmaではpaddingにかぶる形で4pxの線が追加されます。
そのため、線の有無によってheight/widthが変わることはありません。

CSS: border 中の要素に被らず、height/widthが変わる

ボックスモデルによると、borderはpaddingを囲む形で表現されます。
https://developer.mozilla.org/ja/docs/Learn/CSS/Building_blocks/The_box_model#css_ボックスモデルとは

そのため、borderの有無によりborderの太さ分height/widthの値が変わります

どうすれば合わせられるのか?

Figmaのコンポーネントに線を入れるためのレイヤーを増やす

一番汎用性が高いのは、この方法だと思います。

  • プロパティによって要素の数が増減し、height/widthは内容に合わせて可変となってほしい
  • コンポーネントに含まれるテキストが改行し、行数は可変する

などのシーンでも対応可能です。
オートレイアウトを使ってボックスモデルを再現する方法です。

  1. 線を入れたい要素に対して、オートレイアウトを追加する
  2. オートレイアウトの余白は線の太さと同じ値にする
  3. 線を入れる
Figmaも実装もwidht/heightを固定値とする
  • 要素が増減しない
  • テキストが改行しない

という場合に使える方法で、対応自体はとてもシンプルです。
Buttonなどのコンポーネントには使いやすい方法かと思います。

Figma: 横幅いっぱいになったら改行する という動作が再現できない

デザイナーが"1行の時""複数行の時"というプロパティを用意していたので、なんだろう?と思ったのがきっかけで気がつきました。

<div class="wrapper">
  <div class="label">テキストを入力</div>
</div>
.wrapper {
  width: "200px";
  padding: "8px";
  border: "1px solid";
}

.label {
  display: "inline-flex";
  padding: "8px";
  background: "#D9D9D9";
}

HTML/CSSでは上記のように書くだけで再現できる以下の動作が、Figmaではプロパティの変更なしで再現することができません。

  • テキストが短い時には、テキストが存在する範囲にのみ背景色をつける
  • テキストが親要素いっぱいまで入力されたら、テキストを改行する

figmaの動作

「コンテンツを内包」を使う場合
  • 背景を持つレイヤー: コンテンツを内包
  • テキストを持つレイヤー: コンテンツを内包

背景色を持つレイヤーを「内包」にすることで、テキストが短い時の動作を再現することができます。
しかし、テキストが長くなると親要素からはみ出てしまいます。

「コンテナに合わせて拡大」を使う場合
  • 背景を持つレイヤー: コンテナに合わせて拡大
  • テキストを持つレイヤー: コンテナに合わせて拡大

テキストが長い場合に、テキストを改行するという動作を再現することができます。
しかし、テキストが短い場合においても、背景色がコンテナいっぱいに広がってしまいます。

どうすればいいのか?

以下のどちらかになるかと思います。

  • 1行/2行以上 というプロパティを用意する
    デザイナーが取っていた手法ですが、個人的にはあまり好きではありません。
    • "行数"という定義がエンジニアにとってはあまりにも曖昧であり、実装で実現させるには面倒。
    • "行数"プロパティによって変化するものを、width/heighにのみ影響するかどうか保証できない。(デザイナーが行数によってpaddingを変更させていた過去がある)
  • コンポーネントとしてはコンテンツを内包にしておく
    個人的にはこちらの方が良いと思っています。画面設計時に上書きをする形で変更する形になります。
    • 改行しないほうが、理想のデータ量である場合が多い
    • 「コンテナに合わせて拡大」の設定は親要素がないと選択できないため、コンポーネントの段階では設定できない場合もある
    • コンポーネントの実装としては揃えやすい

おわりに

お互いの違いを知らなければ、それぞれ別々の道を進んでしまい、デザイナーとエンジニアの距離がどんどん遠くなっていってしまいます。
今回紹介したツールの1つ1つの違いは小さいものですが、できることを勘違いしたままだと「あいつは何もわかっていない」と思う瞬間が増えていきます。

お互いが見ているものを少しずつチューニングしていくことが、デザイナー・エンジニア双方が幸せになれる方法だと思っています。

少しでも参考になれば幸いです。

Discussion