PrimeVue ベースの UI コンポーネントライブラリ Volt を使ってみよう
はじめに
Volt とは PrimeVue の Unstyled モードを利用した新しい UI ライブラリです。
Volt はスタイリングを完全にコントロールできる PrimeVue ベースの UI ライブラリであり、UI コンポーネントはアプリケーションのコードベースに存在します。
Volt の各コンポーネントは Unstyled オプションを有効にした PrimeVue コンポーネントをラップしており、コンポーネントのスタイリングを完全にコントロールできるようになります。
このとき、Volt では Tailwind CSS を利用してスタイリングを行います。
Volt のコンポーネントはアプリケーションのコードベースに追加され、直接編集できます。
具体的には、コンポーネント追加のコマンドを実行することによって src/volt
にコンポーネントの .vue
ファイルがダウンロードされます。
今回はドキュメントに沿って Volt を導入し、コンポーネントを表示してみます。
Volt を導入する
Vue.js + Vite の構成で、アプリケーションの作成から Volt コンポーネントを 1 つ表示するところまでを示します。
コマンド例でのパッケージマネージャーは npm を利用します。
Node.js: v20.13.1
vue: v3.5.13
tailwindcss: v4.0.17
primevue: v4.3.3
今回の検証に利用したリポジトリは以下になります。
Vue アプリケーションを作成する
Vue.js のドキュメントより、create-vue
を利用してアプリケーションの作成します。
npm create vue@latest
今回のコード例では、TypeScript のみが有効になっています:
[+] TypeScript
[ ] JSX Support
[ ] Router (SPA development)
[ ] Pinia (state management)
[ ] Vitest (unit testing)
[ ] End-to-End Testing
[ ] ESLint (error prevention)
[ ] Prettier (code formatting)
Tailwind CSS を導入する
パッケージのインストール
Tailwind CSS に関連する以下のパッケージをインストールします。
-
tailwindcss
: Tailwind CSS 本体 -
@tailwindcss/vite
: Vite 向けのプラグイン -
tailwindcss-primeui
: PrimeVue 向けのプラグイン -
tailwind-merge
: Volt が提供するコード内で利用
npm install tailwindcss @tailwindcss/vite tailwindcss-primeui tailwind-merge
Vite プラグインの設定
先ほどインストールした Tailwind CSS の Vite プラグイン(@tailwindcss/vite
)を vite.config.ts
に追加します。
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import vueDevTools from 'vite-plugin-vue-devtools'
+ import tailwindcss from '@tailwindcss/vite'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
vueDevTools(),
+ tailwindcss()
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
},
},
})
CSS ファイルへの設定
CSS ファイルへ Tailwind CSS と PrimeVue 向けプラグインのインポート文を追記します。
+ @import 'tailwindcss';
+ @import 'tailwindcss-primeui';
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
...
PrimeVue を導入
パッケージをインストールし、Vue プラグインをインストールするコードを記述します。
npm install primevue
import './assets/main.css'
import { createApp } from 'vue'
import App from './App.vue'
+ import PrimeVue from 'primevue/config'
- createApp(App).mount('#app')
+ const app = createApp(App)
+ app.use(PrimeVue, {
+ unstyled: true,
+ });
+
+ app.mount('#app');
CSS Variables の定義
tailwind-primeui
が利用する CSS Variables のデフォルト値を定義します。
このステップは、将来的には primeui tailwind plugin が暗黙的に行うようになるようです。[1]
CSS Variables はドキュメントに記載されているものを src/assets/base.css
に追記します。
今回は既に記述されているセレクタ内に追記する形で実装します。
@import 'tailwindcss';
@import 'tailwindcss-primeui';
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
+ --p-primary-50: #ecfdf5;
+ --p-primary-100: #d1fae5;
+ --p-primary-200: #a7f3d0;
+ --p-primary-300: #6ee7b7;
+ --p-primary-400: #34d399;
+ --p-primary-500: #10b981;
+ --p-primary-600: #059669;
+ --p-primary-700: #047857;
+ --p-primary-800: #065f46;
+ --p-primary-900: #064e3b;
+ --p-primary-950: #022c22;
+ --p-surface-0: #ffffff;
+ --p-surface-50: #fafafa;
+ --p-surface-100: #f4f4f5;
+ --p-surface-200: #e4e4e7;
+ --p-surface-300: #d4d4d8;
+ --p-surface-400: #a1a1aa;
+ --p-surface-500: #71717a;
+ --p-surface-600: #52525b;
+ --p-surface-700: #3f3f46;
+ --p-surface-800: #27272a;
+ --p-surface-900: #18181b;
+ --p-surface-950: #09090b;
+ --p-content-border-radius: 6px;
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
+ --p-primary-color: var(--p-primary-500);
+ --p-primary-contrast-color: var(--p-surface-0);
+ --p-primary-hover-color: var(--p-primary-600);
+ --p-primary-active-color: var(--p-primary-700);
+ --p-content-border-color: var(--p-surface-200);
+ --p-content-hover-background: var(--p-surface-100);
+ --p-content-hover-color: var(--p-surface-800);
+ --p-highlight-background: var(--p-primary-50);
+ --p-highlight-color: var(--p-primary-700);
+ --p-highlight-focus-background: var(--p-primary-100);
+ --p-highlight-focus-color: var(--p-primary-800);
+ --p-text-color: var(--p-surface-700);
+ --p-text-hover-color: var(--p-surface-800);
+ --p-text-muted-color: var(--p-surface-500);
+ --p-text-hover-muted-color: var(--p-surface-600);
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
+ --p-primary-color: var(--p-primary-400);
+ --p-primary-contrast-color: var(--p-surface-900);
+ --p-primary-hover-color: var(--p-primary-300);
+ --p-primary-active-color: var(--p-primary-200);
+ --p-content-border-color: var(--p-surface-700);
+ --p-content-hover-background: var(--p-surface-800);
+ --p-content-hover-color: var(--p-surface-0);
+ --p-highlight-background: color-mix(in srgb, var(--p-primary-400), transparent 84%);
+ --p-highlight-color: rgba(255, 255, 255, 0.87);
+ --p-highlight-focus-background: color-mix(in srgb, var(--p-primary-400), transparent 76%);
+ --p-highlight-focus-color: rgba(255, 255, 255, 0.87);
+ --p-text-color: var(--p-surface-0);
+ --p-text-hover-color: var(--p-surface-0);
+ --p-text-muted-color: var(--p-surface-400);
+ --p-text-hover-muted-color: var(--p-surface-300);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
Volt コンポーネントのダウンロード
Volt コンポーネントは volt-vue
コマンドを実行することで追加できます。
npx volt-vue コンポーネント名
で指定したコンポーネントをダウンロードできます。
また、コンポーネント名を all
とするとすべてのコンポーネントを一括でダウンロードします。
コマンドを実行すると、PrimeVue の公式リポジトリから最新のコンポーネントファイルがダウンロードされます。[2]
コンポーネント名はドキュメントを確認してください。
GitHub リポジトリにすべてのコンポーネントの Vue ファイルが存在するため、これらも参考にできます。
今回は Button
コンポーネントをダウンロードし、後続の手順で表示します。
npx volt-vue add Button
コマンドの実行が完了すると src/volt/Button.vue
が追加されています。
Volt コンポーネントの表示
Vue の SFC で Volt コンポーネントを利用してみます。
今回は src/components/TheWelcome.vue
の先頭に先ほどダウンロードした Button.vue
を表示させます。
<script setup lang="ts">
import WelcomeItem from './WelcomeItem.vue'
import DocumentationIcon from './icons/IconDocumentation.vue'
import ToolingIcon from './icons/IconTooling.vue'
import EcosystemIcon from './icons/IconEcosystem.vue'
import CommunityIcon from './icons/IconCommunity.vue'
import SupportIcon from './icons/IconSupport.vue'
+ import Button from '@/volt/Button.vue'
const openReadmeInEditor = () => fetch('/__open-in-editor?file=README.md')
</script>
<template>
+ <Button label="Hello World" />
<WelcomeItem>
<template #icon>
<DocumentationIcon />
</template>
<template #heading>Documentation</template>
...
これにより表示ができているはずです。
Dev サーバーを起動して確認してみます。
npm run dev
追加された Volt コンポーネントを眺めてみる
アプリケーションコードにダウンロードしたコンポーネントファイルの中身を見てみます。
src/volt/Button.vue
は以下のようなファイルです。
<template>
<Button
unstyled
:pt="theme"
:ptOptions="{
mergeProps: ptViewMerge
}"
>
<template v-for="(_, slotName) in $slots" v-slot:[slotName]="slotProps">
<slot :name="slotName" v-bind="slotProps ?? {}" />
</template>
</Button>
</template>
<script setup lang="ts">
import Button, { type ButtonPassThroughOptions, type ButtonProps } from 'primevue/button';
import { ref } from 'vue';
import { ptViewMerge } from './utils';
interface Props extends /* @vue-ignore */ ButtonProps {}
defineProps<Props>();
const theme = ref<ButtonPassThroughOptions>({
root: `inline-flex cursor-pointer select-none items-center justify-center overflow-hidden relative
px-3 py-2 gap-2 rounded-md disabled:pointer-events-none disabled:opacity-60 transition-colors duration-200
bg-primary enabled:hover:bg-primary-emphasis enabled:active:bg-primary-emphasis-alt text-primary-contrast
border border-primary enabled:hover:border-primary-emphasis enabled:active:border-primary-emphasis-alt
focus-visible:outline focus-visible:outline-1 focus-visible:outline-offset-2 focus-visible:outline-primary
p-vertical:flex-col p-fluid:w-full p-fluid:p-icon-only:w-10
p-icon-only:w-10 p-icon-only:px-0 p-icon-only:gap-0
p-icon-only:p-rounded:rounded-full p-icon-only:p-rounded:h-10
p-small:text-sm p-small:px-[0.625rem] p-small:py-[0.375rem]
p-large:text-[1.125rem] p-large:px-[0.875rem] p-large:py-[0.625rem]
p-raised:shadow-sm p-rounded:rounded-[2rem]
p-outlined:bg-transparent enabled:hover:p-outlined:bg-primary-50 enabled:active:p-outlined:bg-primary-100
p-outlined:border-primary-200 enabled:hover:p-outlined:border-primary-200 enabled:active:p-outlined:border-primary-200
p-outlined:text-primary enabled:hover:p-outlined:text-primary enabled:active:p-outlined:text-primary
dark:p-outlined:bg-transparent dark:enabled:hover:p-outlined:bg-primary/5 dark:enabled:active:p-outlined:bg-primary/15
dark:p-outlined:border-primary-700 dark:enabled:hover:p-outlined:border-primary-700 dark:enabled:active:p-outlined:border-primary-700
dark:p-outlined:text-primary dark:enabled:hover:p-outlined:text-primary dark:enabled:active:p-outlined:text-primary
p-text:bg-transparent enabled:hover:p-text:bg-primary-50 enabled:active:p-text:bg-primary-100
p-text:border-transparent enabled:hover:p-text:border-transparent enabled:active:p-text:border-transparent
p-text:text-primary enabled:hover:p-text:text-primary enabled:active:p-text:text-primary
dark:p-text:bg-transparent dark:enabled:hover:p-text:bg-primary/5 dark:enabled:active:p-text:bg-primary/15
dark:p-text:border-transparent dark:enabled:hover:p-text:border-transparent dark:enabled:active:p-text:border-transparent
dark:p-text:text-primary dark:enabled:hover:p-text:text-primary dark:enabled:active:p-text:text-primary
`,
loadingIcon: ``,
icon: `p-right:order-1 p-bottom:order-2`,
label: `font-medium p-icon-only:invisible p-icon-only:w-0
p-small:text-sm p-large:text-[1.125rem]`,
pcBadge: {
root: `min-w-4 h-4 leading-4 bg-primary-contrast rounded-full text-primary text-xs font-bold`
}
});
</script>
- PrimeVue のコンポーネントを
unstyled
プロパティを付けて利用している- PrimeVue を導入 で Unstyled モードの設定をしなくても(おそらく)変わらず[3]動作する
-
theme
変数のroot
に登録されている Tailwind CSS のクラス名を変更することでスタイリングを変更できる
おわりに
公式ドキュメントでも触れられていますが、PrimeVue と Volt はそれぞれ異なる要件を満たすために作成されています。
Volt はコンポーネントのスタイリングを完全にコントロールできますが、必要最低限のコンポーネントのみを実装しています。
また、PrimeVue に Tailwind CSS を導入する使い方から一歩進んで、Volt ではコンポーネントを Tailwind CSS でテーマ化できます。
- | PrimeVue | Volt |
---|---|---|
コンポーネントの位置 | node_modules | アプリケーションコードベース |
スタイリング |
@primevue/themes とデザイントークン |
Tailwind CSS のユーティリティ |
ダウンロード元 | NPM レジストリ | GitHub のリポジトリ |
コンポーネント数 | 90 個程度 | 50 個程度 |
用途に合わせて使い分けることができそうです。
-
With a future update of the primeui tailwind plugin, this step will be done implicitly. ↩︎
-
this tool automatically fetches the latest components from the official PrimeVue repository ↩︎
-
Unstyled モードをオフにしても見た目には変化がありませんでした。機能面の細かい部分については検証していません。 ↩︎
Discussion