細かいことは良いから Figma の Styles を Variables に変換したい（Config 2023 新機能を試す）

## この記事の概要

[Config 2023 で発表された Variables](https://help.figma.com/hc/en-us/articles/15339657135383) は非常に便利そうでした。
新しいもの好きな方なら、まず試してみたくなるような内容です。
ただ、既存のデータでガッチリと Styles を組んでいると、手動での移行が大変そうだなあ……と思った方もいるのではないでしょうか？

ご安心ください。
公式から Styles -> Variables 変換用のコードが提供されています。

事前知識が無いと若干難しそうだったので、やり方をこの記事で解説します。

## やることの概要

1. 空の Plugin を作成する
1. 公式から出ているコードをコピペする
1. Plugin を実行する

説明するを考えた際、まずはコピペが一番分かりやすそうに思えたのでこのやり方を紹介しています。
あまり美しいやり方ではありませんがご了承ください。

また、前提として今回は説明用にこちらのデータを用意しました。
味気ないですが、次のように Color styles を定義しています。

- white
- black
- red
    - light
    - medium
    - dark

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/46a969f0-d2d4-17c0-88df-9869fef3624a.png)

## 空の Plugin を作成する

plugin を通して、既存の styles を variables へと変換します。
そのためまずは新規に作成します。

Toolbar の `Resources` をクリックし、 `Plugins` タブを開きます。
すぐ下のドロップダウンから `Development` を選びます。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/0bc4aaf7-f656-e69e-9295-d58c619495b6.png)

`+ New` をクリックし、 `New plugin...` をクリックします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/1c4ef131-cc65-fddb-6b86-8a6a6a2435e7.png)

`Name` には適当な名前（ここでは `styles-to-variables`）を入れて、 `Figma design` だけの方にチェックを入れて、 `Next` をクリックします。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/56be147e-9f4a-40c3-dd0f-980a726ce789.png)

`Run once` にチェックを入れ、 `Save as` をクリックします。
保存先を聞かれるので、適当なフォルダーを指定します。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/fa1f2229-48fe-47f2-87b8-0e4918591ff1.png)

ひとまずはこれで完了です。
`Done` でウインドウを閉じてしまって構いません。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/3e040c5f-0b80-71c5-e037-0c8c92818c85.png)

## 公式から出ているコードをコピペする

先ほど作成したフォルダーを開くと、次のようなコードの構成になっています。

- .gitignore
- code.js
- code.ts
- manifest.json
- package.json
- README.md
- tsconfig.json

次に、公式の `plugin-samples` というリポジトリの中に、 `styles-to-variables` があるのでアクセスします。

https://github.com/figma/plugin-samples/tree/master/styles-to-variables

この `code.js` を自分のフォルダーにある `code.js` にまるっとコピペします。

:::note warn
Figma Plugin は TypeScript で書いて JavaScript へ変換するのがスタンダードです。ただ、今回は見本のコードが JavaScript でしか提供されていないのと、とにかく簡単に Styles を Variables に変換するのをゴールとしているため、こんな無茶苦茶なやり方をします。くれぐれも日頃の Plugin 開発では真似しないでください。
:::

## Plugin を実行する

ここまで来たら終わったも同様です。
Figma に戻り Plugin を開くと、自分が作ったものが `Run` 可能になっています。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/28efad43-7ef4-cadb-3752-b9129a9c364d.png)

実行した後に `Variables` を確認するとこの通り。
`white` と `black` はトップレベルに、 `red` は 1 つ階層を作って、元々の `Styles` と同様の構成で登録されました。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/214677/b1f4bf7b-38bb-cb34-a467-67c313a27b72.png)

## 余談: TypeScript 化

いくら「細かいことは良いから」なんて記事タイトルにしても、せっかく TypeScript で書けるならそちらで書きたい気持ちはあります。

というわけで、ひとまずエラーが出なくなるようにだけ変えました。
そもそもの処理を変えた方が良い箇所もあるのかもなあ……と思いつつ、私の実力ではそこまで及ばないので今回はここで終わりとします。

```diff_typescript
  console.clear();

+ type TokenData = {
+   color: RGB;
+   hex: string;
+   opacity: number;
+   tokens: string[];
+ };

- const styles = figma.getLocalPaintStyles();
- const tokenDataMap = styles.reduce(styleToTokenDataMap, {});
- const tokenData = Object.values(tokenDataMap);
+ const styles: PaintStyle[] = figma.getLocalPaintStyles();
+ const tokenDataMap: { [key: string]: TokenData } = styles.reduce(styleToTokenDataMap, {});
+ const tokenData: TokenData[] = Object.values(tokenDataMap);
  createTokens(tokenData);
```

```diff_typescript
- function createTokens(tokenData) {
+ function createTokens(tokenData: TokenData[]): void {
    if (tokenData.length <= 0) {
      figma.notify("No convertible styles found. :(");
      return;
    }
-   const collection = figma.variables.createVariableCollection(`Style Tokens`);
-   let aliasCollection;
+   const collection: VariableCollection = figma.variables.createVariableCollection(`Style Tokens`);
+   let aliasCollection: VariableCollection | undefined;
    const modeId = collection.modes[0].modeId;
```

```diff_typescript
- function styleToTokenDataMap(into, current) {
-   const paints = current.paints.filter(
+ function styleToTokenDataMap(into: { [key: string]: TokenData }, current: PaintStyle): { [key: string]: TokenData } {
+   const paints = (current.paints as SolidPaint[]).filter(
      ({ visible, type }) => visible && type === "SOLID"
    );
```

```diff_typescript
- function rgbToHex({ r, g, b }) {
-   const toHex = (value) => {
+ function rgbToHex({ r, g, b }: RGB) {
+   const toHex = (value: number) => {
      const hex = Math.round(value * 255).toString(16);
      return hex.length === 1 ? "0" + hex : hex;
    };
```