SvelteKit + TailwindCSS + Storybookで最近の流行に入門してみる

## はじめに

社内でTailwindCSSとStorybookの紹介があり、
使える案件があれば導入してみたいなあと思っていました。

実案件に導入する前にまずはどんな風に使うか触ってみたかったので、
こちらも実案件では使ったことがないSvelteKitのプロジェクトに導入してみました。

https://kit.svelte.jp/

https://storybook.js.org/

https://tailwindcss.com/

## 本記事の対象者

- とりあえず全部使ったことないから触ってみたい
- TailwindCSSとStorybook気になってるけどどうやってはじめるのかわかんない
- SvelteKitで作成したプロジェクトにTailwindCSSとStorybookを導入したい

各ツールの詳しい使い方や解説は本記事ではしません（そこまで理解できてない）。

## 参考記事

https://qiita.com/oekazuma/items/d6a0dc6f7c3c2d912f64

https://qiita.com/ryotaro76/items/38875593509e69c60d69

https://blog.kinto-technologies.com/posts/Svelte-magazine-003-Svelte%E3%81%A7Storybook%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%82%8B/

## 実行環境

- macOS Ventura `13.2.1`
- Node.js `20.6.0`

パッケージマネージャーはお好みでどうぞ。今回はnpmです。

## プロジェクトの作成

[公式の手順][svelte-create]通りにSvelteKitのプロジェクトを作成します。
プロジェクトの初期設定を対話形式で聞かれますが、お試しなのでなんでもいいです。

```bash
npm create svelte@latest my-app
cd my-app
npm install
npm run dev
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/cf3d55db-243c-2276-158c-0494f41b9b14.png)


今回はテンプレートを`Skeleten project`にしたので、`npm run dev`で以下の画面が立ち上がればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/a46cb0b9-6cb2-22e1-5410-b389c69117a0.png)



## TailwindCSSの導入

作成したプロジェクトにTailwindCSSをインストールします。
TailwindCSSの[公式手順][tailwindcss-install]もありますが、
[Svelte Add][svelte-add]というものを使うと諸々の設定を自動でしてくれるそうなので、こちらでインストールします。
設定内容を追いたい場合は[公式手順][tailwindcss-install]通りにインストールしてください。

https://github.com/svelte-add/tailwindcss

```bash
npx svelte-add@latest tailwindcss
# 依存関係をアップデートする
npm install
```

インストールできたので使ってみます。
`src/routes/+page.svelte`にボタンを追加します。

```svelte
<button class="primary mx-8 my-8"> ボタン </button>

<style lang="postcss">
  .primary {
    @apply bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded;
  }
</style>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/9db958a2-76fa-e61f-2d9b-e28c9b29bb7b.png)

ちゃんとスタイルが適用されていますね。
TailwindCSSはHTMLの中にクラスでスタイルを書いていくものですが、`@apply`を使うことでクラスとしてまとめることができます。
ただし使いすぎるとTailwindCSSの良さを損なうので、用途を絞って使うのが良さそうです。

https://devlog.neton.co.jp/develop/css/tailwind-apply/

上記コードの`primary`は自前クラス、`mx-8 my-8`はTailwindCSSで用意されたクラスです。
今回はボタンをコンポーネント化して、propsで`primary` `warning`（後で作る）等のクラス名を受け取るようにしたいので、`@apply`を利用します。

## Storybookの導入

Storybookの[公式手順][storybook-install]通りに、プロジェクトのルートディレクトリで以下のコマンドを実行します。

```bash
npx storybook@latest init
```

以下の画像のメッセージまで出ればインストールは完了です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/5716ff33-1bb5-187a-b308-0a9eb82103c7.png)

`Ctrl + C`で終了し、`npm run storybook`を実行して以下の画面が立ち上がればOKです。
Storybookのテンプレートが作られています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/08defc85-58b1-ee89-1429-1e8183f73e53.png)

各ツールのインストールはこれで終了です。

## コンポーネントの登録

先程のボタンをコンポーネントとして分割して、Storybookに登録します。

Nuxt.jsだと`components`フォルダにvueファイルを置いていきますが、
SvelteKitの場合はコンポーネントの置き場が決められているわけではない？ので`src/lib`配下に`components`フォルダを作成し、`Button.svelte`を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/1fa37b65-1ff0-1706-41f7-77f7283fabeb.png)

`color` `style` `size`をpropsで受け取るようにします。

```html:src/lib/components/Button.svelte
<script>
  /** @type {'primary' | 'secondary' | 'warning' | 'danger'} */
  export let color = "primary"

  /** @type {'default' | 'pill' | 'outline'} */
  export let style = "default"

  /** @type {'small' | 'medium' | 'large'} */
  export let size = "medium"
</script>

<button class={[color, style, `button--${size}`, "shadow-none"].join(" ")}>
  ボタン
</button>

<style lang="postcss">
  .primary {
    @apply bg-blue-500 text-blue-500 hover:bg-blue-600 border border-blue-500 hover:border-transparent;
  }

  .secondary {
    @apply bg-sky-400 text-sky-400 hover:bg-sky-500 border border-sky-400 hover:border-transparent;
  }

  .warning {
    @apply bg-orange-500 text-orange-500 hover:bg-orange-600 border border-orange-500 hover:border-transparent;
  }

  .danger {
    @apply bg-red-500 text-red-500 hover:bg-red-600 border border-red-500 hover:border-transparent;
  }

  .default {
    @apply text-white font-bold rounded;
  }

  .pill {
    @apply text-white font-bold rounded-full;
  }

  .outline {
    @apply bg-transparent font-semibold hover:text-white rounded;
  }

  .button--small {
    @apply text-sm py-1 px-2;
  }
  .button--medium {
    @apply text-base py-2 px-4;
  }
  .button--large {
    @apply text-lg py-3 px-6;
  }
</style>

```

呼び出し側

```html:src/routes/+page.svelte
<script>
  import Button from "$lib/components/Button.svelte"
</script>

<div class="mx-8 my-8">
  <Button color="primary" style="outline" size="large" />
</div>
```

こんな感じで表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/596225ae-081e-d193-d054-26fed0696068.png)


次にこのボタンをStorybookに登録します。
インストール時に`src`配下に`stories`フォルダが作成されています。
この中の`*.stories.js`に設定を書いていきます。

`src/lib/components/Button.svelte`を新しく登録したいので、元々ある`Button.stories.js`以外は全て削除します。

そして`Button.stories.js`を次のように書き換えます。

```js:src/stories/Button.stories.js
import Button from "$lib/components/Button.svelte"

// More on how to set up stories at: https://storybook.js.org/docs/svelte/writing-stories/introduction
export default {
  title: "Example/Button",
  component: Button,
  tags: ["autodocs"],
  argTypes: {
    color: {
      control: {type: "select"},
      options: ["primary", "secondary", "warning", "danger"],
    },
    size: {
      control: {type: "select"},
      options: ["small", "medium", "large"],
    },
    style: {
      control: {type: "select"},
      options: ["default", "pill", "outline"],
    },
  },
}

// More on writing stories with args: https://storybook.js.org/docs/svelte/writing-stories/args
export const Primary = {
  args: {
    color: "primary",
    style: "default",
  },
}

export const Secondary = {
  args: {
    color: "secondary",
    style: "pill",
  },
}

export const Warning = {
  args: {
    color: "warning",
    style: "pill",
  },
}

export const Danger = {
  args: {
    color: "danger",
    style: "outline",
  },
}

```

こんな感じでボタンが登録されて、UIライブラリのサイトのようにボタンの見た目を変更して確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/82b38676-b387-c67a-c4c0-4b6a3f3b6f3a.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/c2cf325f-be12-3e39-d35e-ccbb6ead079e.png)

以上がSvelteKitにTailwindCSSとStorybookを導入する手順でした。

## おわりに

実務で扱うにはまだまだ勉強不足ですが、とりあえず各ツールの使い方はわかりました。
お試しで触ってみたい方の参考になれば幸いです。

## おまけ

TailwindCSSのクラス名は最初わからないので、[公式ドキュメント][tailwindcss]を探しながら書いていたのですが、[Github Copilot][github-copilot]を利用していると大変便利だったので紹介します。

既存のCSSがあってそれをTailwindCSSのクラス名に変換したい場合は、
以下のようにコメントにCSSを記載しておけばGithub Copilotがクラス名を生成してくれます。

![画面2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1649149/5ba93d8d-cab2-14c8-0b3d-8d856615d23f.gif)

公開サイトからコピーしてきたCSSを使いたい時などに便利そうです。




[svelte-create]: https://kit.svelte.dev/docs/creating-a-project
[tailwindcss]: https://tailwindcss.com/docs/installation
[tailwindcss-install]: https://tailwindcss.com/docs/guides/sveltekit
[svelte-add]: https://github.com/svelte-add/svelte-add
[storybook-install]: https://storybook.js.org/docs/react/get-started/install
[github-copilot]: https://docs.github.com/ja/copilot/getting-started-with-github-copilot