スタイルモード

様々なプリスタイルテーマから選択するか、独自のテーマを開発できます。

PrimeVueはデザインに依存しないライブラリであるため、他のUIライブラリとは異なり、マテリアルデザインなど特定のスタイルを強制しません。代わりに、テーマを使用してスタイルをコンポーネントから切り離しています。テーマは「ベース」と「プリセット」の2つの部分で構成されています。ベースはCSS変数をプレースホルダーとして使用するスタイルルールであり、プリセットはトークンをCSS変数にマッピングすることでベースに供給する一連のデザイントークンです。ベースは異なるプリセットで構成できます。現在、Aura、Lara、Noraが利用可能なプリセットであり、今後のバージョンではマテリアルデザインなどのプリセットが追加される予定です。

Architecture

スタイルモードアーキテクチャの中核は、「デザイントークン」と呼ばれる概念に基づいています。プリセットは、3つの階層、「プリミティブ」、「セマンティック」、「コンポーネント」でトークン構成を定義します。

プリミティブトークン

プリミティブトークンにはコンテキストがありません。カラーパレットは、`blue-50`から`blue-900`などのプリミティブトークンの良い例です。`blue-500`というトークンはプライマリーカラーとして使用できますが、メッセージの背景など、トークン名はそれ自体ではコンテキストを示しません。通常、セマンティックトークンによって使用されます。

セマンティックトークン

セマンティックトークンはコンテンツを定義し、その名前は使用場所を示します。セマンティックトークンのよく知られた例は`primary.color`です。セマンティックトークンは、プリミティブトークンまたは他のセマンティックトークンにマップされます。`colorScheme`トークングループは、アプリケーションでアクティブなカラースキームに基づいてトークンを定義するための特別な変数であり、これにより、ダークモードのようなカラースキームに基づいて異なるトークンを定義できます。

コンポーネントトークン

コンポーネントトークンは、`inputtext.background`や`button.color`など、コンポーネントごとに分離されたトークンであり、セマンティックトークンにマップされます。例として、`button.background`コンポーネントトークンは`primary.color`セマンティックトークンにマップされ、さらに`green.500`プリミティブトークンにマップされます。

ベストプラクティス

コアカラーパレットを定義する場合はプリミティブトークンを使用し、フォーカスリング、プライマリーカラー、サーフェスなどの一般的なデザイン要素を指定するにはセマンティックトークンを使用します。コンポーネントトークンは、特定のコンポーネントをカスタマイズする場合にのみ使用してください。独自の設計トークンをカスタムプリセットとして定義することで、CSSに触れることなく独自のスタイルを定義できます。スタイルクラスを使用してPrimeVueコンポーネントをオーバーライドすることはベストプラクティスではなく、最後の手段とするべきです。デザイントークンが推奨されるアプローチです。

`theme`プロパティは、初期テーマのカスタマイズに使用されます。


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

const app = createApp(App);

app.use(PrimeVue, {
    // Default theme configuration
    theme: {
        preset: Aura,
        options: {
            prefix: 'p',
            darkModeSelector: 'system',
            cssLayer: false
        }
    }
 });

`options`プロパティは、CSSがプリセットのデザイントークンからどのように生成されるかを定義します。

prefix

CSS変数のプレフィックス。デフォルトは`p`です。たとえば、`primary.color`デザイントークンは`var(--p-primary-color)`になります。


options: {
    prefix: 'my'
}

darkModeSelector

ダークモードのCSS変数をカプセル化するCSSルール。デフォルトは`system`で、`@media (prefers-color-scheme: dark)`を生成します。ユーザーの選択に基づいてダークモードを切り替え可能にする必要がある場合は、`.app-dark`などのクラスセレクターを定義し、このクラスをドキュメントルートで切り替えます。例については、ダークモードの切り替えセクションを参照してください。


options: {
    darkModeSelector: '.my-app-dark'
}

cssLayer

スタイルをデフォルトで`primeui`という名前のCSSレイヤー内に定義するかどうかを定義します。CSSレイヤーは、より簡単なカスタマイズのためにカスタムカスケードレイヤーを宣言するのに便利です。デフォルトは`false`です。


options: {
    cssLayer: {
        name: 'primevue',
        order: 'tailwind-base, primevue, tailwind-utilities'
    }
}

現在、Aura、Lara、Noraが利用可能なプリセットです。マテリアルデザインに基づいた新しいプリセットは、2024年末までに計画されています。

トークンはドットセパレータで記述されます(例:`primary.color`、`form.field.background`、`checkbox.icon.checked.color`)。プリセットの設定では、ドットセパレータのマッピングにキャメルケースとオブジェクトプロパティが使用されます。以下は、`checkbox.icon.checked.color`を表すチェックボックスコンポーネントトークンの例です。すべての代替案は同じ結果になります。


export default {
    iconCheckedColor: //...,
}

export default {
    icon: {
        checkedColor: //...
    }
}

export default {
    icon: {
        checked: {
            color: //...
        }
    }
}

プリセットスキームでは、`primitive`、`semantic`、`components`、`directives`、`colorscheme`、`light`、`dark`、`common`、`root`、`states`というキーは予約済みであり、トークン名として使用できません。

`definePreset`ユーティリティは、PrimeVueの設定中に既存のプリセットをカスタマイズするために使用されます。最初の引数はカスタマイズするプリセット、2番目の引数はオーバーライドするデザイントークンです。


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

const MyPreset = definePreset(Aura, {
    //Your customizations, see the following sections for examples
});

app.use(PrimeVue, {
    theme: {
        preset: MyPreset
    }
 });

`primary`はメインカラーパレットを定義し、デフォルト値は`emerald`プリミティブトークンにマップされます。代わりに`indigo`を使用するように設定してみましょう。


const MyPreset = definePreset(Aura, {
    semantic: {
        primary: {
            50: '{indigo.50}',
            100: '{indigo.100}',
            200: '{indigo.200}',
            300: '{indigo.300}',
            400: '{indigo.400}',
            500: '{indigo.500}',
            600: '{indigo.600}',
            700: '{indigo.700}',
            800: '{indigo.800}',
            900: '{indigo.900}',
            950: '{indigo.950}'
        }
    }
});

`noir`モードは、黒のトーンをプライマリーとして使用するバリアントのニックネームであり、実装には追加の`colorScheme`設定が必要です。黒と白のバリアントをプライマリーカラーとするサンプルプリセット設定です。


const Noir = definePreset(Aura, {
    semantic: {
        primary: {
            50: '{zinc.50}',
            100: '{zinc.100}',
            200: '{zinc.200}',
            300: '{zinc.300}',
            400: '{zinc.400}',
            500: '{zinc.500}',
            600: '{zinc.600}',
            700: '{zinc.700}',
            800: '{zinc.800}',
            900: '{zinc.900}',
            950: '{zinc.950}'
        },
        colorScheme: {
            light: {
                primary: {
                    color: '{zinc.950}',
                    inverseColor: '#ffffff',
                    hoverColor: '{zinc.900}',
                    activeColor: '{zinc.800}'
                },
                highlight: {
                    background: '{zinc.950}',
                    focusBackground: '{zinc.700}',
                    color: '#ffffff',
                    focusColor: '#ffffff'
                }
            },
            dark: {
                primary: {
                    color: '{zinc.50}',
                    inverseColor: '{zinc.950}',
                    hoverColor: '{zinc.100}',
                    activeColor: '{zinc.200}'
                },
                highlight: {
                    background: 'rgba(250, 250, 250, .16)',
                    focusBackground: 'rgba(250, 250, 250, .24)',
                    color: 'rgba(255,255,255,.87)',
                    focusColor: 'rgba(255,255,255,.87)'
                }
            }
        }
    }
});

ライトモードとダークモードの間で変化するカラースキームパレットは、サーフェストークンで指定されます。以下の例では、ライトモードに`zinc`、ダークモードに`slategray`を使用しています。この設定により、ライトモードはグレースケールトーンになり、ダークモードにはブルートーンが含まれます。


const MyPreset = definePreset(Aura, {
    semantic: {
        colorScheme: {
            light: {
                surface: {
                    0: '#ffffff',
                    50: '{zinc.50}',
                    100: '{zinc.100}',
                    200: '{zinc.200}',
                    300: '{zinc.300}',
                    400: '{zinc.400}',
                    500: '{zinc.500}',
                    600: '{zinc.600}',
                    700: '{zinc.700}',
                    800: '{zinc.800}',
                    900: '{zinc.900}',
                    950: '{zinc.950}'
                }
            },
            dark: {
                surface: {
                    0: '#ffffff',
                    50: '{slate.50}',
                    100: '{slate.100}',
                    200: '{slate.200}',
                    300: '{slate.300}',
                    400: '{slate.400}',
                    500: '{slate.500}',
                    600: '{slate.600}',
                    700: '{slate.700}',
                    800: '{slate.800}',
                    900: '{slate.900}',
                    950: '{slate.950}'
                }
            }
        }
    }
});

UIコンポーネントはアプリケーションからフォント設定を継承するため、フォントのデザインはありません。

フォーム入力コンポーネントのデザイントークンは、`form.field`トークングループから派生しています。このカスタマイズ例では、ホバー時にボーダーの色をプライマリーに変更します。`dropdown.hover.border.color`や`textarea.hover.border.color`など、このセマンティックトークンに依存するコンポーネントはすべて変更を受けます。


const MyPreset = definePreset(Aura, {
    semantic: {
        colorScheme: {
            light: {
                formField: {
                    hoverBorderColor: '{primary.color}'
                }
            },
            dark: {
                formField: {
                    hoverBorderColor: '{primary.color}'
                }
            }
        }
    }
});

フォーカスリングは、アウトラインの幅、スタイル、色、オフセットを定義します。アウトラインにプライマリーカラーを使用して、より太いリングを使用してみましょう。


const MyPreset = definePreset(Aura, {
    semantic: {
        focusRing: {
            width: '2px',
            style: 'dashed',
            color: '{primary.color}',
            offset: '1px'
        }
    }
});

特定のコンポーネントのデザイントークンは、`components`レイヤーで定義されます。独自のスタイルを作成する場合は、コンポーネントトークンをオーバーライドすることをお勧めしません。代わりに、独自のプリセットを作成することをお勧めします。この設定はグローバルであり、すべてのカードコンポーネントに適用されます。ページで特定のコンポーネントをローカルにカスタマイズする必要がある場合は、スコープ付きCSSセクションの例を参照してください。


const MyPreset = definePreset(Aura, {
    components: {
        card: {
            colorScheme: {
                light: {
                    root: {
                        background: '{surface.0}',
                        color: '{surface.700}'
                    },
                    subtitle: {
                        color: '{surface.500}'
                    }
                },
                dark: {
                    root: {
                        background: '{surface.900}',
                        color: '{surface.0}'
                    },
                    subtitle: {
                        color: '{surface.400}'
                    }
                }
            }
        }
    }
});

デザイントークンは、CSS変数を使用して特定のコンポーネントにスコープできます。この例では、最初のスイッチはグローバルトークンを使用し、2番目のスイッチはグローバルトークンを独自のトークンでオーバーライドします。


<template>
    <div>
        <ToggleSwitch v-model="checked1" />
        <ToggleSwitch v-model="checked2" :dt="amberSwitch" />
    </div>
</template>

<script setup>
import { ref } from 'vue';

const checked1 = ref(true);
const checked2 = ref(true);
const amberSwitch = ref({
    handle: {
        borderRadius: '4px'
    },
    colorScheme: {
        light: {
            root: {
                checkedBackground: '{amber.500}',
                checkedHoverBackground: '{amber.600}',
                borderRadius: '4px'
            },
            handle: {
                checkedBackground: '{amber.50}',
                checkedHoverBackground: '{amber.100}'
            }
        },
        dark: {
            root: {
                checkedBackground: '{amber.400}',
                checkedHoverBackground: '{amber.300}',
                borderRadius: '4px'
            },
            handle: {
                checkedBackground: '{amber.900}',
                checkedHoverBackground: '{amber.800}'
            }
        }
    }
});
</script>


現在のプリセットを完全に置き換えます。一般的なユースケースは、実行時にプリセットを動的に変更することです。


import { usePreset } from '@primevue/themes';

const onButtonClick() {
    usePreset(MyPreset);
}

提供されたトークンを現在のプリセットにマージします。例としては、プライマリーカラーパレットを動的に変更することです。


import { updatePreset } from '@primevue/themes';

const changePrimaryColor() {
    updatePreset({
        semantic: {
            primary: {
                50: '{indigo.50}',
                100: '{indigo.100}',
                200: '{indigo.200}',
                300: '{indigo.300}',
                400: '{indigo.400}',
                500: '{indigo.500}',
                600: '{indigo.600}',
                700: '{indigo.700}',
                800: '{indigo.800}',
                900: '{indigo.900}',
                950: '{indigo.950}'
            }
        }
    })
}

`updatePreset`を使用して同じ更新を行うためのショートハンドです。


import { updatePrimaryPalette } from '@primevue/themes';

const changePrimaryColor() {
    updatePrimaryPalette({
        50: '{indigo.50}',
        100: '{indigo.100}',
        200: '{indigo.200}',
        300: '{indigo.300}',
        400: '{indigo.400}',
        500: '{indigo.500}',
        600: '{indigo.600}',
        700: '{indigo.700}',
        800: '{indigo.800}',
        900: '{indigo.900}',
        950: '{indigo.950}'
    });
}

`updatePreset`を使用して同じ更新を行うためのショートハンドです。


import { updateSurfacePalette } from '@primevue/themes';

const changeSurfaces() {
    //changes surfaces both in light and dark mode
    updateSurfacePalette({
        50: '{zinc.50}',
        // ...
        950: '{zinc.950}'
    });
}

const changeLightSurfaces() {
    //changes surfaces only in light
    updateSurfacePalette({
        light: {
            50: '{zinc.50}',
            // ...
            950: '{zinc.950}'
        }
    });
}

const changeDarkSurfaces() {
    //changes surfaces only in dark mode
    updateSurfacePalette({
        dark: {
            50: '{zinc.50}',
            // ...
            950: '{zinc.950}'
        }
    });
}

`$dt`関数は、完全なパスと値などのトークンの情報を返します。これは、トークンにプログラムでアクセスする必要がある場合に役立ちます。


import { $dt } from '@primevue/themes';

const duration = $dt('transition.duration');
/*
    duration: {
        name: '--transition-duration',
        variable: 'var(--p-transition-duration)',
        value: '0.2s'
    }
*/

const primaryColor = $dt('primary.color');
/*
    primaryColor: {
        name: '--primary-color',
        variable: 'var(--p-primary-color)',
        value: {
        light: {
            value: '#10b981',
            paths: {
                name: 'semantic.primary.color',
                binding: {
                    name: 'primitive.emerald.500'
                }
            }
        },
        dark: {
            value: '#34d399',
            paths: {
                name: 'semantic.primary.color',
                binding: {
                    name: 'primitive.emerald.400'
                }
            }
        }
    }
}
*/

50から950までの特定の色合いのシェードとティントをオブジェクトとして返します。


import { palette } from '@primevue/themes';

// custom color
const values1 = palette('#10b981');

// copy an existing token set
const primaryColor = palette('{blue}');

プリセットのカラーパレットは、`primitive`デザイントークングループによって定義されます。デフォルトの色は、スタイルなしモードのTailwind Presetsプロジェクトと一貫性を保つために、Tailwindの色を拡張して導き出されています。

カラーは、CSSでは変数として、プログラムでは`$dt`ユーティリティを使用してアクセスできます。


// With CSS
var(--p-blue-500)

// With JS
$dt('blue.500').value

  • emerald
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • グリーン
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • ライム
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • レッド
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • オレンジ
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • アンバー
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • イエロー
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • ティール
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • シアン
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • スカイ
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • ブルー
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • インディゴ
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • バイオレット
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • パープル
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • フューシャ
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • ピンク
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • ローズ
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • スレート
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • グレー
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • 亜鉛色(ジンク)
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • ニュートラル
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950
  • ストーン
    50
    100
    200
    300
    400
    500
    600
    700
    800
    900
    950

PrimeVueは、テーマ設定において、デフォルトのdarkModeSelectorとしてsystemを使用します。アプリケーションにダークモード切り替えがある場合は、.my-app-darkなど、使用しているセレクタにdarkModeSelectorを設定することで、PrimeVueをライト/ダーク切り替えとシームレスに統合できます。


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

const app = createApp(App);

app.use(PrimeVue, {
    // Default theme configuration
    theme: {
        preset: Aura,
        options: {
            darkModeSelector: '.my-app-dark',
        }
    }
 });

以下は、ダークモード切り替えスイッチの非常に基本的な実装例です。prefers-color-schemeを使用してシステムから初期値を取得し、localStorageを使用して状態を維持することで、さらに拡張できます。詳細については、この記事を参照してください。


<Button label="Toggle Dark Mode" @click="toggleDarkMode()" />


function toggleDarkMode() {
    const element = document.querySelector('html');
    element.classList.toggle('my-app-dark');
}

常にダークモードを使用する場合は、最初にdarkModeSelectorを適用し、変更しないようにしてください。

PrimeVueのCSSレイヤーは、テーマ設定でレイヤーが明示的に有効になっている場合にのみ、スタイル付きモードに適用されます。スタイルなしモードでは、組み込みのCSSクラスは含まれず、そのためレイヤーは不要です。

@layerは、優先順位のカスタマイズ可能な順序を定義するための標準的なCSS機能です。レイヤーについて詳しく知りたい場合は、MDNのドキュメントをご覧ください。スタイル付きモードで、テーマ設定でcssLayerオプションが有効になっている場合、PrimeVueは組み込みのスタイルクラスをprimevueカスケードレイヤーでラップして、ライブラリのスタイルを簡単に上書きできるようにします。レイヤーのないアプリケーションのCSSは、最高のCSS特異度を持つため、位置やクラスの強度に関わらず、スタイルを上書きできます。cssLayerは、互換性に関する問題を回避するためにデフォルトで無効になっています。これは、次のリセットセクションで説明する互換性のためにレイヤー設定を必要とするサードパーティのCSSライブラリとの互換性の問題を避けるためです。

たとえば、使用中のテーマで定義されているToggleSwitchコンポーネントの丸みを帯びた境界線を削除する必要があるとします。これを実現するには、.p-toggleswitch .p-toggleswitch-sliderセレクタを上書きする必要があります。レイヤーがない場合、より強力なCSSを使用するか、!importantを使用する必要がありますが、レイヤーを使用すると、my-switch-sliderなどのより簡単なクラス名でPrimeVueを常に上書きできるため、問題は発生しません。このアプローチのもう1つの利点は、コンポーネントの組み込みクラス名を特定する必要がないことです。


<template>
    <ToggleSwitch v-model="checked" :pt="{ slider: 'my-switch-slider' }" />
</template>

<script>
import { ref } from "vue";

const checked = ref(false);
</script>

<style>
.my-switch-slider {
    border-radius: 0;
}

.my-switch-slider:before {
    border-radius: 0;
}
</style>

レイヤーを使用すると、CSSモジュールを使用することも容易になります。例については、CSSモジュールのガイドを参照してください。

PrimeVueでも使用されている入力やボタンなどのHTML要素にグローバルスタイルがある場合、カスタマイズの容易さが問題になる可能性があります。これは、範囲が広いグローバルスタイル(例:button { })とレイヤーがない場合、常にPrimeVueコンポーネントを上書きして予期しない結果につながるためです。標準的なHTML要素に適用されるグローバルスタイルの一般的なユースケースは、ブラウザのデフォルトのスタイルを削除するためのCSSリセットユーティリティです。この場合、ベストプラクティスは、resetのようなレイヤーでCSSをラップし、primevueがそのレイヤーの後に来るようにすることです。これにより、リセットCSSがPrimeVueコンポーネントの邪魔になりません。


/* Order */
@layer reset, primevue;

/* Reset CSS */
@layer reset {
    button,
    input {
        /* CSS to Reset */
    }
}

一般的なCSSライブラリのレイヤー設定例。

Bootstrap

Bootstrapには、標準要素のCSSをリセットするrebootユーティリティがあります。このユーティリティを含める場合は、インポート時にレイヤーを指定できます。


@layer bootstrap-reboot, primevue;

@import "bootstrap-reboot.css" layer(bootstrap-rebooot);

Tailwind

Tailwind CSSには、baseと呼ばれるpreflightというリセットユーティリティが含まれています。この機能を使用する場合は、baseとユーティリティを別々のレイヤーでラップし、primevueレイヤーがbaseの後に来るようにしてください。


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

@layer tailwind-base {
    @tailwind base;
}

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

Normalize

Normalizeは、標準要素のCSSをリセットする別のユーティリティです。CSSファイルのインポート時に、レイヤーを割り当て、primevueが正規化されたレイヤーの後に来るようにレイヤーの順序を定義します。


@layer normalize, primevue;

@import "normalize.css" layer(normalize-reset);

CSSモジュールは、SFC内のスタイル要素でmoduleプロパティを有効にすることでサポートされています。PrimeVueコンポーネントにクラスを適用するには、$styleキーワードを使用します。CSSモジュールを使用する場合は、cssLayerを有効にして、PrimeVueスタイルのCSS特異度を低くすることをお勧めします。


<style module>
.myinput {
    border-radius: 2rem;
    padding: 1rem 2rem;
    border-width: 2px;
}
</style>


<template>
    <InputText :class="$style.myinput" placeholder="Search" />
</template>

PrimeVue UIコンポーネントはrem単位を使用します。1remはhtml要素のフォントサイズ(デフォルトは16px)に等しくなります。コンポーネントのサイズをグローバルに調整するには、ルートフォントサイズを使用します。このウェブサイトではベースとして14pxを使用しているため、ベースフォントサイズが異なる場合は、アプリケーションとは異なる場合があります。


html {
    font-size: 14px;
}