はじめに
少し前にNuxt3 + Tailwind CSS + Storybook ver7の環境を作ったことがありました。
storybookの導入時NuxtのAutoImportの対応が思いの外時間がかかったので備忘録として環境構築の流れを書いてみました。
環境
node v18.15.0
nuxt ^3.3.2
tailwindcss ^3.3.0
storybook ^7.0.5
Nuxt3プロジェクト作成
まずはNuxt3プロジェクトを作ります。
プロジェクト作成
公式ドキュメントに従ってプロジェクトを作成します。
$ npx nuxi init <project-name>
$ cd <project-name>
$ yarn install
インストールできたら動作確認します。
$ yarn dev
http://localhost:3000/
にアクセスするとWelcome to Nuxt!とページが出ました。
これでNuxt3の土台はできました。
Tailwind CSS導入
こちらも公式ドキュメントに従って導入します。
$ yarn add -D tailwindcss postcss autoprefixer
$ npx tailwindcss init
// tailwind.config.jsが作成される
postcss.config.jsを作成
StorybookでTailwind CSSを読み込むためにpostcss.config.js
を作成します。
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
nuxt.config.tsに追記
postcss.config.js
を作成しましたが、Nuxt3では読み込むためにnuxt.config.ts
に書く必要があります。
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
+ postcss: {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+ },
});
tailwind.config.jsのcontentを書き換える
ここはディレクトリ構成によるので、自分の構成に合わせて書いてみてください。一般的な構成の場合は以下でいいと思います。
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
+ "./*.{vue,js,ts,jsx,tsx}",
+ "./components/**/*.{vue,js,ts,jsx,tsx}",
+ "./layouts/**/*.{vue,js,ts,jsx,tsx}",
+ "./pages/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
};
Tailwind CSSをCSSファイルに追加します。
今回はassets/css/main.css
にしました。
@tailwind base;
@tailwind components;
@tailwind utilities;
nuxt.config.tsでmain.cssを読み込みます。
// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
postcss: {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
},
+ css: ["~/assets/css/main.css"],
});
Tailwind CSSが適用されているかapp.vue
を書き換えて確認しましょう。
<template>
<div class="text-red-500">Hello tailwind CSS!</div>
</template>
文字が赤くなったのでうまくいきました。
Storybook導入
一番沼ったところです。Nuxtの自動インポートをStorybookに適応したりする部分が割と面倒ですがやっていきましょう。
インストール
公式ドキュメントを見ながら以下コマンドでインストールします。
$ npx storybook@latest init --type vue3
// .storybookディレクトリとstoriesディレクトリが作成されます。
Storybookを起動して、インストールできているか確認します。
$ yarn dev
http://localhost:6006 にアクセスしてみるとintroductionページが開きました。
Storybookの設定ファイルを書く
今回components
は以下のようなディレクトリ構成にします。
components/
├ <コンポーネント名>/
| ├ index.vue
| └ index.stories.ts
そのため.storybook/main.js
のstoriesを以下のようにします。
ここは自分の構成に合わせて適切に設定してください。
/** @type { import('@storybook/vue3-vite').StorybookConfig } */
const config = {
+ stories: [
+ "../*.stories.@(js|jsx|ts|tsx)",
+ "../components/**/*.stories.@(js|jsx|ts|tsx)",
+ "../layouts/**/*.stories.@(js|jsx|ts|tsx)",
+ "../pages/**/*.stories.@(js|jsx|ts|tsx)",
+ ],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/vue3-vite",
options: {},
},
docs: {
autodocs: "tag",
},
};
export default config;
preview.jsでTailwind CSSを定義したCSSファイルを読み込みます。
/** @type { import('@storybook/vue3').Preview } */
+ import "../assets/css/main.css";
const preview = {
parameters: {
actions: { argTypesRegex: "^on[A-Z].*" },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
},
};
export default preview;
また自動で作成されたstoriesディレクトリは使わないので削除しましょう。
Storybookでも自動インポートに対応する
以下を参考にしました。
VueAPI等の自動インポート
Nuxt3では(Nuxt2もですが)自動インポート機能があります。
import文を書く手間がなくなるので開発がスピーディーに進むのですが、Storybook内では読み込んでくれないのでnot found
になってしまいます。
これを解決するためにunplugin-auto-import
を使います。
$ yarn add -D unplugin-auto-import
以下のように設定し、Storybookでも自動インポートされる関数等を読み込めるようにします。
/** @type { import('@storybook/vue3-vite').StorybookConfig } */
+ const AutoImport = require("unplugin-auto-import/vite");
const config = {
stories: [
"../*.stories.@(js|jsx|ts|tsx)",
"../components/**/*.stories.@(js|jsx|ts|tsx)",
"../layouts/**/*.stories.@(js|jsx|ts|tsx)",
"../pages/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/vue3-vite",
options: {},
},
docs: {
autodocs: "tag",
},
+ viteFinal: async (config) => {
+ if (config.plugins !== undefined) {
+ config.plugins.push(
+ AutoImport({ imports: ["vue"], dts: ".storybook/auto-imports.d.ts" })
+ );
+ }
+ return {
+ ...config,
+ define: {
+ ...config.define,
+ global: "window",
+ },
+ };
+ },
};
export default config;
コンポーネントの自動インポートに対応する
Nuxtにはコンポーネントの自動インポートもあり、こちらはunplugin-auto-components
では解決できません。
コンポーネントの自動インポートに対応するためにはunplugin-vue-components
を使います。
なお、コンポーネントの自動インポートは無効にもできるので無効にする場合、こちらの設定は不要です。
$ yarn add -D unplugin-vue-components
以下のように追記し、Storybookでも自動インポートされる関数等を読み込めるようにします。
/** @type { import('@storybook/vue3-vite').StorybookConfig } */
const AutoImport = require("unplugin-auto-import/vite");
+ const AutoImportComponents = require("unplugin-vue-components/vite");
const config = {
stories: [
"../*.stories.@(js|jsx|ts|tsx)",
"../components/**/*.stories.@(js|jsx|ts|tsx)",
"../layouts/**/*.stories.@(js|jsx|ts|tsx)",
"../pages/**/*.stories.@(js|jsx|ts|tsx)",
],
addons: [
"@storybook/addon-links",
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/vue3-vite",
options: {},
},
docs: {
autodocs: "tag",
},
viteFinal: async (config) => {
if (config.plugins !== undefined) {
config.plugins.push(
AutoImport({ imports: ["vue"], dts: ".storybook/auto-imports.d.ts" })
);
+ config.plugins.push(
+ AutoImportComponents({
+ dirs: ["components"],
+ directoryAsNamespace: false, // trueの場合ディレクトリ名もコンポーネント名に含む
+ dts: ".storybook/components.d.ts",
+ })
+ );
+ }
return {
...config,
define: {
...config.define,
global: "window",
},
};
},
};
export default config;
directoryAsNamespace
はディレクトリが層階構造になっているときにディレクトリ名をコンポーネント名に含むかどうかを決めます。nuxt.comfig.ts側のpathPrefixをfalseにする場合はdirectoryAsNamespaceもfalseにします。
// ディレクトリ名をコンポーネント名に含むとき
// components/Fizz/Bass/index.vueのコンポーネント名は<FizzBuzz />になる
// nuxt.comfig.ts
pathPrefix: true
// .storybook/main.js
directoryAsNamespac:true
// ディレクトリ名をコンポーネント名に含まないとき
// components/Fizz/Bass/index.vueのコンポーネント名は<Buzz />になる
// nuxt.comfig.ts
pathPrefix: false
// .storybook/main.js
directoryAsNamespac:false
自動インポートに対応した際に作成されるファイルはコミットの中にあると若干邪魔にもなるのでgitignoreに追記することをおすすめします。
...
+ .storybook/auto-imports.d.ts
+ .storybook/components.d.ts
ストーリーを作ってみる
ストーリーを作るためにまずコンポーネントを作ります。
今回はかんたんなボタンコンポーネントを作ります。
<script setup lang="ts">
type ButtonColor = "green" | "red";
interface Props {
color?: ButtonColor;
}
const props = withDefaults(defineProps<Props>(), {
color: undefined,
});
const { color } = toRefs(props);
</script>
<template>
<button
class="px-4 py-2 rounded-lg transition-colors"
:class="{
'text-white bg-green-500 hover:bg-green-700': color === 'green',
'text-white bg-red-500 hover:bg-red-700': color === 'red',
'text-black bg-gray-200 hover:bg-gray-400': !color,
}"
>
<slot></slot>
</button>
</template>
なお本記事ではvue3の大きな変更点であるcomposition apiの解説は省略します。
以下のドキュメント等を見てみてください。
コンポーネントができたのでstoriesも作ります。
import Button from "./index.vue";
import type { Meta, StoryObj } from "@storybook/vue3";
const meta: Meta<typeof Button> = {
title: "button",
component: Button,
render: (args) => ({
components: { Button },
tags: ["autodocs"], // docsが不要だったら削除してOK
setup() {
return { args };
},
template: '<Button v-bind="args">ボタン</Button>',
}),
argTypes: {
color: {
control: {
type: "inline-radio",
},
options: ["green", "red", undefined],
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Component: Story = {
args: {
color: "green",
},
};
最後に http://localhost:6006 にアクセスして確認します。
うまく行きました。ControlsでPropsに設定した色の選択もできます。
最後に
今回はNuxt3 + Tailwind CSS + Storybook ver7の環境構築をしました。
Storybook ver7も2023/04/03に正式リリースされ、今後はスタンダートになると思います。
今回紹介した構成を参考にしていただければ幸いです。