Tailwind CSS

PrimeVueとTailwind CSSの統合(スタイル付きモードとスタイルなしモードの両方)。

Tailwind CSSは、ユーティリティファースト設計に基づいた人気のCSSフレームワークです。コアは、独自のUI要素を構築するための、定義済みのCSSルールを持つ柔軟なCSSクラスを提供します。たとえば、Bootstrapのような意見の強いbtnクラスの代わりに、Tailwindはボタンを適用するためにbg-blue-500roundedp-4などのプリミティブなクラスを提供します。再利用可能なクラスのセットはTailwind CSSコンポーネントとしてグループ化することもでき、このアプローチを採用して、特にTailwind用にコンポーネントを構築するライブラリもいくつか存在します。

Tailwindは優れたCSSライブラリですが、Vue.jsと組み合わせた真に包括的なUIスイートを欠いています。そこでPrimeVueが登場し、幅広くアクセシビリティが高く機能豊富なUIコンポーネントライブラリを提供します。PrimeVueのコアはTailwind CSSに依存しませんが、primeui tailwindプラグインやスタイルなしモードのプリセットなど、必要な統合ポイントを提供しています。

PrimeVueのスタイルなしモードでは、デザイントークンやCSSクラスなどのデフォルトのスタイル要素が無効になるため、パススループロパティを使用してコンポーネントのスタイルを完全に制御できます。この機能は、PrimeVueコンポーネントをラップすることによってカスタムデザインに基づいて独自のUIライブラリを構築する場合、または単にTailwind CSSを使用してPrimeVueコンポーネントのスタイルを設定する場合に非常に役立ちます。

スタイルなしモードも、スタイル付きモードと同様にプリセットの概念を使用してテーマを定義します。スタイル付きモードでは、プリセットはCSS変数で実装されたデザイントークンのセットであるのに対し、スタイルなしモードでは、プリセットはTailwind CSSクラスをコンポーネントに挿入するためのパススルー設定オブジェクトです。デフォルトのスタイル付きモードのテーマAPIを無視して、代わりにTailwind CSSを使用してPrimeVue UIコンポーネントのスタイルを設定する場合は、スタンドアロンのTailwind CSSプリセットプロジェクトウェブサイトで詳細を確認してください。

tailwindcss-primeuiは、PrimeTekによる公式プラグインで、PrimeVueなどのPrime UIライブラリとTailwind CSSの高度な統合を提供します。スタイル付きモードとスタイルなしモードの両方で動作するように設計されています。たとえば、スタイル付きモードでは、プライマリーやサーフェスなどのセマンティックカラーはbg-primarytext-surface-500text-muted-colorなどのTailwindユーティリティとして提供されます。

プラグインはnpmで利用可能です。


npm i tailwindcss-primeui

インストール後、Tailwind設定ファイルでプラグインを設定します。


// tailwind.config.js
module.exports = {
    // ...
    plugins: [require('tailwindcss-primeui')]
};

このプラグインは、新しいユーティリティセットを使用してデフォルトの設定を拡張します。すべてのバリアントとブレークポイントはサポートされています(例:dark:sm:hover:bg-primary)。

カラーパレット

クラスプロパティ
primary-[50-950]プライマリーカラーパレット。
surface-[0-950]サーフェスカラーパレット。
primaryデフォルトのプライマリーカラー。
primary-contrastデフォルトのプライマリーコントラストカラー。
primary-emphasisデフォルトのプライマリーエンファシスカラー。
border-surfaceデフォルトのプライマリーエンファシスカラー。
bg-emphasisエンファシス背景(例:ホバーされた要素)。
bg-highlightハイライト背景。
bg-highlight-emphasisエンファシス付きのハイライト背景。
rounded-borderボーダー半径。
text-colorエンファシス付きのテキストカラー。
text-color-emphasisデフォルトのプライマリーエンファシスカラー。
text-muted-colorセカンダリテキストカラー。
text-muted-color-emphasisエンファシス付きのセカンダリテキストカラー。

スタイル付きモードでは、CSSの特異性のために、Tailwindユーティリティでデフォルトのスタイルをオーバーライドできない場合があります。解決策は2つあります。

重要

スタイルを強制するには、!をプレフィックスとして使用します。


<InputText placeholder="Overriden" class="!p-8" />

CSSレイヤー

PrimeVue CSSレイヤーを有効にし、レイヤーを使用してTailwindスタイルの特異性を高めます。これにより、!プレフィックスは不要になります。


import PrimeVue from 'primevue/config';
import Aura from '@primevue/themes/aura';

const app = createApp(App);

app.use(PrimeVue, {
    theme: {
        preset: Aura,
        options: {
            cssLayer: {
                name: 'primevue',
                order: 'tailwind-base, primevue, tailwind-utilities'
            }
        }
    }
 });


@layer tailwind-base, primevue, tailwind-utilities;

@layer tailwind-base {
  @tailwind base;
}

@layer tailwind-utilities {
  @tailwind components;
  @tailwind utilities;
}

PrimeVueとTailwind CSSの使用例。

ユーティリティクラスとしてのPrimeVueカラーパレット。

  • primary
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • surface
    0
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
primary
highlight
box

PrimeVueコンポーネントを含むフォームのレスポンシブレイアウトにTailwindユーティリティを使用する。


<div class="flex flex-col gap-6 w-full sm:w-auto">
    <div class="flex flex-col sm:flex-row sm:items-center gap-6">
        <div class="flex-auto">
            <label for="firstname" class="block font-semibold mb-2">Firstname</label>
            <InputText id="firstname" class="w-full" />
        </div>
        <div class="flex-auto">
            <label for="lastname" class="block font-semibold mb-2">Lastname</label>
            <InputText id="lastname" class="w-full" />
        </div>
    </div>
    <div class="flex flex-col sm:flex-row sm:items-center gap-6">
        <div class="flex-1">
            <label for="date" class="block font-semibold mb-2">Date</label>
            <DatePicker inputId="date" class="w-full" />
        </div>
        <div class="flex-1">
            <label for="country" class="block font-semibold mb-2">Country</label>
            <Select v-model="selectedCountry" inputId="country" :options="countries" optionLabel="name" placeholder="Select a Country" class="w-full">
                <template #value="slotProps">
                    <div v-if="slotProps.value" class="flex items-center">
                        <img :alt="slotProps.value.label" src="https://primefaces.org/cdn/primevue/images/flag/flag_placeholder.png" :class="`mr-2 flag flag-${slotProps.value.code.toLowerCase()}`" style="width: 18px" />
                        <div>{{ slotProps.value.name }}</div>
                    </div>
                    <span v-else>
                        {{ slotProps.placeholder }}
                    </span>
                </template>
                <template #option="slotProps">
                    <div class="flex items-center">
                        <img :alt="slotProps.option.label" src="https://primefaces.org/cdn/primevue/images/flag/flag_placeholder.png" :class="`mr-2 flag flag-${slotProps.option.code.toLowerCase()}`" style="width: 18px" />
                        <div>{{ slotProps.option.name }}</div>
                    </div>
                </template>
            </Select>
        </div>
    </div>
    <div class="flex-auto">
        <label for="message" class="block font-semibold mb-2">Message</label>
        <Textarea id="message" class="w-full" rows="4" />
    </div>
</div>

カスタムUIを持つヘッドレスPrimeVueダイアログ。


<Button label="Login" icon="pi pi-user" @click="visible = true" />

<Dialog v-model:visible="visible" pt:root:class="!border-0 !bg-transparent" pt:mask:class="backdrop-blur-sm">
    <template #container="{ closeCallback }">
        <div class="flex flex-col px-8 py-8 gap-6 rounded-2xl" style="background-image: radial-gradient(circle at left top, var(--p-primary-400), var(--p-primary-700))">
            <svg width="35" height="40" viewBox="0 0 35 40" fill="none" xmlns="http://www.w3.org/2000/svg" class="block mx-auto">
                <path
                    d="M25.87 18.05L23.16 17.45L25.27 20.46V29.78L32.49 23.76V13.53L29.18 14.73L25.87 18.04V18.05ZM25.27 35.49L29.18 31.58V27.67L25.27 30.98V35.49ZM20.16 17.14H20.03H20.17H20.16ZM30.1 5.19L34.89 4.81L33.08 12.33L24.1 15.67L30.08 5.2L30.1 5.19ZM5.72 14.74L2.41 13.54V23.77L9.63 29.79V20.47L11.74 17.46L9.03 18.06L5.72 14.75V14.74ZM9.63 30.98L5.72 27.67V31.58L9.63 35.49V30.98ZM4.8 5.2L10.78 15.67L1.81 12.33L0 4.81L4.79 5.19L4.8 5.2ZM24.37 21.05V34.59L22.56 37.29L20.46 39.4H14.44L12.34 37.29L10.53 34.59V21.05L12.42 18.23L17.45 26.8L22.48 18.23L24.37 21.05ZM22.85 0L22.57 0.69L17.45 13.08L12.33 0.69L12.05 0H22.85Z"
                    fill="var(--p-primary-700)"
                />
                <path
                    d="M30.69 4.21L24.37 4.81L22.57 0.69L22.86 0H26.48L30.69 4.21ZM23.75 5.67L22.66 3.08L18.05 14.24V17.14H19.7H20.03H20.16H20.2L24.1 15.7L30.11 5.19L23.75 5.67ZM4.21002 4.21L10.53 4.81L12.33 0.69L12.05 0H8.43002L4.22002 4.21H4.21002ZM21.9 17.4L20.6 18.2H14.3L13 17.4L12.4 18.2L12.42 18.23L17.45 26.8L22.48 18.23L22.5 18.2L21.9 17.4ZM4.79002 5.19L10.8 15.7L14.7 17.14H14.74H15.2H16.85V14.24L12.24 3.09L11.15 5.68L4.79002 5.2V5.19Z"
                    fill="var(--p-primary-200)"
                />
            </svg>
            <div class="inline-flex flex-col gap-2">
                <label for="username" class="text-primary-50 font-semibold">Username</label>
                <InputText id="username" class="!bg-white/20 !border-0 !p-4 !text-primary-50 w-80"></InputText>
            </div>
            <div class="inline-flex flex-col gap-2">
                <label for="password" class="text-primary-50 font-semibold">Password</label>
                <InputText id="password" class="!bg-white/20 !border-0 !p-4 !text-primary-50 w-80" type="password"></InputText>
            </div>
            <div class="flex items-center gap-4">
                <Button label="Cancel" @click="closeCallback" text class="!p-4 w-full !text-primary-50 !border !border-white/30 hover:!bg-white/10"></Button>
                <Button label="Sign-In" @click="closeCallback" text class="!p-4 w-full !text-primary-50 !border !border-white/30 hover:!bg-white/10"></Button>
            </div>
        </div>
    </template>
</Dialog>

このプラグインは、styleclassおよびanimateonscrollディレクティブで使用できる拡張アニメーションユーティリティも追加します。


<Select v-model="animation" :options="animations" placeholder="Select One" class="w-full sm:w-44" />
<div class="py-8 overflow-hidden">
    <div :class="`rounded-border bg-primary w-16 h-16 mx-auto animate-${animation} animate-once animate-duration-1000`"></div>
</div>

アニメーション

クラスプロパティ
animate-fadeinfadein 0.15s linear
animate-fadeoutfadeout 0.15s linear
animate-slidedownslidedown 0.45s ease-in-out
animate-slideupslideup 0.45s cubic-bezier(0, 1, 0, 1)
animate-scaleinscalein 0.15s linear
animate-fadeinleftfadeinleft 0.15s linear
animate-fadeoutleftfadeoutleft 0.15s linear
animate-fadeinrightfadeinright 0.15s linear
animate-fadeoutrightfadeoutright 0.15s linear
animate-fadeinupfadeinup 0.15s linear
animate-fadeoutupfadeoutup 0.15s linear
animate-fadeindownfadeindown 0.15s linear
animate-fadeoutupfadeoutup 0.15s linear
animate-widthwidth 0.15s linear
animate-flipflip 0.15s linear
animate-flipupflipup 0.15s linear
animate-flipleftfadein 0.15s linear
animate-fliprightflipright 0.15s linear
animate-zoominzoomin 0.15s linear
animate-zoomindownzoomindown 0.15s linear
animate-zoominleftzoominleft 0.15s linear
animate-zoominrightzoominright 0.15s linear
animate-zoominupzoominup 0.15s linear

アニメーション時間

クラスプロパティ
animate-duration-0animation-duration: 0s
animate-duration-75animation-duration: 75ms
animate-duration-100animation-duration: 100ms
animate-duration-200animation-duration: 200ms
animate-duration-300animation-duration: 300ms
animate-duration-400animation-duration: 400ms
animate-duration-500animation-duration: 500ms
animate-duration-700animation-duration: 700ms
animate-duration-1000animation-duration: 1000ms
animate-duration-2000animation-duration: 2000ms
animate-duration-3000animation-duration: 300ms

アニメーション遅延

クラスプロパティ
animate-delay-noneanimation-duration: 0s
animate-delay-75animation-delay: 75ms
animate-delay-100animation-delay: 100ms
animate-delay-150animation-delay: 150ms
animate-delay-200animation-delay: 200ms
animate-delay-300animation-delay: 300ms
animate-delay-400animation-delay: 400ms
animate-delay-500animation-delay: 500ms
animate-delay-700animation-delay: 700ms
animate-delay-1000animation-delay: 1000ms

反復回数

クラスプロパティ
animate-infiniteanimation-iteration-count: infinite
animate-onceanimation-iteration-count: 1
animate-twiceanimation-iteration-count: 2

方向

クラスプロパティ
animate-normalanimation-direction: normal
animate-reverseanimation-direction: reverse
animate-alternateanimation-direction: alternate
animate-alternate-reverseanimation-direction: alternate-reverse

タイミング関数

クラスプロパティ
animate-ease-linearanimation-timing-function: linear
animate-ease-inanimation-timing-function: cubic-bezier(0.4, 0, 1, 1)
animate-ease-outanimation-timing-function: cubic-bezier(0, 0, 0.2, 1)
animate-ease-in-outanimation-timing-function: cubic-bezier(0.4, 0, 0.2, 1)

塗りつぶしモード

クラスプロパティ
animate-fill-noneanimation-fill-mode: normal
animate-fill-forwardsanimation-fill-mode: forwards
animate-fill-backwardsanimation-fill-mode: backwards
animate-fill-bothanimation-fill-mode: both

再生状態

クラスプロパティ
animate-runninganimation-play-state: running
animate-pausedanimation-play-state: paused

背面の可視状態

クラスプロパティ
backface-visiblebackface-visibility: visible
backface-hiddenbackface-visibility: hidden