😎

デザインシステムのUIプロトタイピング環境としてPlayroomを試す

2024/02/10に公開

はじめに

Chakra UIやTailwind CSSなど、人気のあるUIフレームワークが持つブラウザー上のPlayground環境は、ユーザーがローカルに環境を用意することなくUIフレームワークを試すことができる。

https://play.chakra-ui.com/playground
https://play.tailwindcss.com/

自身でデザインシステムのコンポーネントライブラリーを開発する場合にこのようなPlayground環境があると、そのコンポーネントを用いたプロトタイピングが可能になり良いかなと思うけど、
それを実現できるツールとしてPlayroomを試してみる。

Playroomとは

Design with JSX, powered by your own component library.

https://github.com/seek-oss/playroom

Playroomとは、seekというオーストラリアの企業のOSSの1つで、端的には「コンポーネントライブラリーを用いてJSXでデザインする」ツール。
最近登場したツールというわけではなく、数年前から開発されている。

コンポーネントライブラリーのコンポーネントを全てPlayroomで利用可能な状態にしておくことで、ブラウザーさえあれば、プロダクトで利用される実際のコンポーネントを用いて高速にプロトタイピングできる。
どのようなツールか理解するのに、以下デモのgifが分かりやすい。

Playroomのデモ

https://github.com/seek-oss/playroom から引用

複数のスクリーンサイズで表示確認しながらJSXを記述することができて、その結果に合わせてURLが生成される。そのため、Playroomを共有可能な環境へホスティングしておけば、プロトタイピングの結果を他者と共有することも可能になる。

なお、ソースコードを眺めると、CodeMirrorを利用してブラウザー上でのエディターとライブプレビューを実現していると考えられる。

試す

プロジェクトを作って、GitHub Pagesにデプロイするところまでを試してみる。

プロジェクトを作成してローカルで動かす

まずViteでプロジェクトを作る。

pnpm create vite
.../Library/pnpm/store/v3/tmp/dlx-2072   |   +1 +
.../Library/pnpm/store/v3/tmp/dlx-2072   | Progress: resolved 1, reused 1, downloaded 0, added 1, done
✔ Project name: … try-playroom
✔ Select a framework: › React
✔ Select a variant: › TypeScript + SWC

Scaffolding project in /path/to/try-playroom...

Done. Now run:

  cd try-playroom
  pnpm install
  pnpm run dev

プロジェクトを作成し終わったら、pnpm iを実行して依存ライブラリーを追加しておく。

playroompnpm add -DでプロジェクトのdevDependenciesに追加する。

pnpm add -D playroom
 WARN  1 deprecated subdependencies found: @types/lz-string@1.5.0
Packages: +609
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 810, reused 767, downloaded 0, added 0, done

devDependencies:
+ playroom 0.34.2

Done in 8.9s

package.jsonscriptsplayroom:startplayroom:buildコマンドを追加する。

package.json
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview",
+    "playroom:start": "playroom start",
+    "playroom:build": "playroom build" 
  },

playroom.config.jsファイルを作って、設定を記述する。
必須の項目はcomponentsoutputPathのみ。

playroom.config.js
export default {
  components: './src/components/index.ts', // Playroomで利用するコンポーネントがexportされているファイル
  outputPath: './dist/playroom', // Playroomのビルド結果を出力するパス
}

設定できる項目については、READMEに記載がある。より詳細には、以下の型定義を参照すると良いかもしれない。
https://github.com/seek-oss/playroom/blob/f491105688f8e2ece356490829ec69ec713f2a97/src/index.d.ts

試す用のコンポーネントを用意するため、今回はdaisyUIを用いる。daisyUIはTailwind CSSベースのコンポーネントライブラリー。
まず、Tailwind CSSを公式の案内に従ってセットアップする。

pnpm add -D tailwindcss postcss autoprefixer
 WARN  1 deprecated subdependencies found: @types/lz-string@1.5.0
Packages: +20
++++++++++++++++++++
Progress: resolved 830, reused 787, downloaded 0, added 20, done

devDependencies:
+ autoprefixer 10.4.17
+ postcss 8.4.33
+ tailwindcss 3.4.1

Done in 2.3s

❯ pnpm dlx tailwindcss init -p
Packages: +106
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Progress: resolved 106, reused 106, downloaded 0, added 106, done

Created Tailwind CSS config file: tailwind.config.js
Created PostCSS config file: postcss.config.js
tailwind.config.js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
+     "./index.html",
+     "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}
index.css
+ @tailwind base;
+ @tailwind components;
+ @tailwind utilities;

次にdaisyUIをインストールする。

pnpm add -D daisyui
 WARN  1 deprecated subdependencies found: @types/lz-string@1.5.0
Packages: +4
++++
Progress: resolved 834, reused 791, downloaded 0, added 4, done

devDependencies:
+ daisyui 4.6.0

Done in 1.7s

pluginsdaisyuiを追加する。

tailwind.config.js
+ import daisyui from 'daisyui'

/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
-  plugins: [],
+  plugins: [daisyui],
}

ViteでビルドしたCSSファイルをPlayroomで利用したいけど、ユニークなハッシュが付与されたファイル名になっているとその指定が難しい。
そのため、キャッシュバスティングを無効にするように設定する。

vite.config.ts
export default defineConfig({
  plugins: [react()],
+  build: {
+    rollupOptions: {
+      output: {
+        assetFileNames: 'assets/[name].[ext]'
+      }
+    }
+  }
})

ViteでビルドしたCSSをPlayroomで読み込めるように、loaderを追加する。

pnpm add -E -D css-loader style-loader
 WARN  1 deprecated subdependencies found: @types/lz-string@1.5.0
Packages: +1
+
Progress: resolved 829, reused 795, downloaded 0, added 1, done

devDependencies:
+ css-loader 6.10.0
+ style-loader 3.3.4

Done in 1.9s
playroom.config.js
export default {
  components: './src/components/index.ts', // Playroomで利用するコンポーネントがexportされているファイル
  outputPath: './dist/playroom', // Playroomのビルド結果を出力するパス
+  webpackConfig: () => ({
+    module: {
+      rules: [
+        {
+          test: /\.css$/i, // iがないと動かなかった 🤔
+          exclude: /node_modules/,
+          use: [
+            'style-loader',
+            'css-loader',
+          ],
+        }
+      ],
+    },
+  }),
}

Playroomのサーバーを動かす前にViteでビルドするようにplayroom:startの内容を書き換える。

package.json
-    "playroom:start": "playroom start",
+    "playroom:start": "pnpm run build && playroom start",
-    "playroom:build": "playroom build"
+    "playroom:build": "pnpm run build && playroom build"

playroom:buildの方も事前にViteのビルドが必要なことは同じなので、合わせて変更する。

tsxを解釈できるようにloaderなどを追加する。

pnpm add -E -D babel-loader @babel/preset-env @babel/preset-react @babel/preset-typescript
 WARN  1 deprecated subdependencies found: @types/lz-string@1.5.0
Already up to date
Progress: resolved 829, reused 795, downloaded 0, added 0, done

devDependencies:
+ @babel/preset-env 7.23.9
+ @babel/preset-react 7.23.3
+ @babel/preset-typescript 7.23.3
+ babel-loader 9.1.3

Done in 3.7s
playroom.config.js
      rules: [
+        {
+          test: /\.tsx?$/,
+          exclude: /node_modules/,
+          use: [
+            {
+              loader: 'babel-loader',
+              options: {
+                presets: [
+                  '@babel/preset-env',
+                  ['@babel/preset-react', { runtime: 'automatic' }],
+                  '@babel/preset-typescript'
+                ],
+              },
+            },
+          ],
+        },

後は、UIコンポーネントを作ってplayroom.config.jscomponentsで設定したsrc/components/index.tsにおいてコンポーネントをexportするだけ。

index.ts
export { Button } from './Button'
export { Link } from './Link'
export { Card } from './Card'
.
.
.

ひとまず試すだけなのでコンポーネントの実装は適当な形で行う。

src/components/Button/index.tsx
import { PropsWithChildren } from "react";

export const Button = ({children}: PropsWithChildren) => (
  <button className="btn">{children}</button>
)

このままだとCSSファイルが読み込めないので、frameComponentのオプションで外枠となるコンポーネントを用意して、ViteでビルドしたCSSを読み込むようにする。
frameComponentオプションは、Providerコンポーネントで全体を囲いたいときなどに有用なもの。

playroom.config.js
  components: './src/components/index.ts', // Playroomで利用するコンポーネントがexportされているファイル
  outputPath: './playroom', // Playroomのビルド結果を出力するパス
+  frameComponent: './.playroom/Frame.tsx',
.playroom/Frame.tsx
import React, { PropsWithChildren } from "react";
import '../dist/assets/index.css'

export default ({children}: PropsWithChildren) => (
  <div>{children}</div>
)

以上でローカル環境においてPlayroomを動かすことが出来る。npm run playrom:startすればhttp://localhost:9000/で確認可能となる。

GitHub Pagesへデプロイ

GitHub Pagesへのデプロイは、GitHub Actionsから行う。
以下の通り、ワークフローのファイルを用意しておけば、mainブランチへpushしたらワークフローが動いてデプロイされる。

.github/workflows/deploy.yml
name: Deploy Playroom

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout repository
        uses: actions/checkout@v3

      - name: Set up pnpm
        uses: pnpm/action-setup@v2
        with:
          version: 8

      - name: Set up Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 20

      - name: Install dependencies
        run: pnpm i

      - name: Build Playroom
        run: pnpm run playroom:build

      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./playroom

pnpm iで依存ライブラリーをインストールして、ViteとPlayroomのビルドを行い、そのビルド成果物をpeaceiris/actions-gh-pagesでGitHub Pagesへデプロイするというだけの流れ。

なお、該当のGitHubリポジトリーでGitHub Pagesのブランチ設定はgh-pagesブランチを利用するようにしておく。

GitHub Pagesの設定

今回は以下のGitHubリポジトリーにおいたので、デプロイは https://makotot.github.io/try-playroom へ行われる。

https://github.com/makotot/try-playroom

Storybook

UIコンポーネント開発ツールとしては、Storybookが非常に多くの機能を持っていると思う。ただ、把握している限り、Storybook自体にはPlayroomのようなブラウザー上でプロトタイピングできるような機能がない。コンポーネントのパラメーターの値の変更はControlsアドオンで可能であるものの、公式のアドオンでよりPlayroomに類似するものは見当たらない。

Playroom自体をStorybookで利用できるようにしたアドオンは、個人で開発されているものがある。

https://github.com/rbardini/storybook-addon-playroom

まとめ

1通りの流れを適当なコンポーネントを用意して試しただけではあるものの、環境を用意してなくてもブラウザーだけで高速に実現したいUIを試作したり、それを共有したり、有効活用できそうなイメージが持てた。
ただ、何をやるにもある程度コンポーネントがないと作れないので、コンポーネントが充実していないのであれば、まずその整備が必要になる。コンポーネントライブラリーとしてコンポーネント不足を明らかにする1つの手段としても考えられるかもしれない。

参考

GitHubで編集を提案

Discussion