様々なプリスタイルテーマから選択するか、独自のテーマを開発できます。
PrimeVueはデザインに依存しないライブラリであるため、他のUIライブラリとは異なり、マテリアルデザインなど特定のスタイルを強制しません。代わりに、テーマを使用してスタイルをコンポーネントから切り離しています。テーマは「ベース」と「プリセット」の2つの部分で構成されています。ベースはCSS変数をプレースホルダーとして使用するスタイルルールであり、プリセットはトークンをCSS変数にマッピングすることでベースに供給する一連のデザイントークンです。ベースは異なるプリセットで構成できます。現在、Aura、Lara、Noraが利用可能なプリセットであり、今後のバージョンではマテリアルデザインなどのプリセットが追加される予定です。
スタイルモードアーキテクチャの中核は、「デザイントークン」と呼ばれる概念に基づいています。プリセットは、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がプリセットのデザイントークンからどのように生成されるかを定義します。
CSS変数のプレフィックス。デフォルトは`p`です。たとえば、`primary.color`デザイントークンは`var(--p-primary-color)`になります。
options: {
prefix: 'my'
}
ダークモードのCSS変数をカプセル化するCSSルール。デフォルトは`system`で、`@media (prefers-color-scheme: dark)`を生成します。ユーザーの選択に基づいてダークモードを切り替え可能にする必要がある場合は、`.app-dark`などのクラスセレクターを定義し、このクラスをドキュメントルートで切り替えます。例については、ダークモードの切り替えセクションを参照してください。
options: {
darkModeSelector: '.my-app-dark'
}
スタイルをデフォルトで`primeui`という名前のCSSレイヤー内に定義するかどうかを定義します。CSSレイヤーは、より簡単なカスタマイズのためにカスタムカスケードレイヤーを宣言するのに便利です。デフォルトは`false`です。
options: {
cssLayer: {
name: 'primevue',
order: 'tailwind-base, primevue, tailwind-utilities'
}
}
現在、Aura、Lara、Noraが利用可能なプリセットです。マテリアルデザインに基づいた新しいプリセットは、2024年末までに計画されています。
import accordion from '@primevue/themes/aura/accordion';
// ... imports of other component tokens
export default {
primitive: {
borderRadius: {
none: '0',
xs: '2px',
sm: '4px',
md: '6px',
lg: '8px',
xl: '12px'
},
emerald: { 50: '#ecfdf5', 100: '#d1fae5', 200: '#a7f3d0', 300: '#6ee7b7', 400: '#34d399', 500: '#10b981', 600: '#059669', 700: '#047857', 800: '#065f46', 900: '#064e3b', 950: '#022c22' },
green: { 50: '#f0fdf4', 100: '#dcfce7', 200: '#bbf7d0', 300: '#86efac', 400: '#4ade80', 500: '#22c55e', 600: '#16a34a', 700: '#15803d', 800: '#166534', 900: '#14532d', 950: '#052e16' },
lime: { 50: '#f7fee7', 100: '#ecfccb', 200: '#d9f99d', 300: '#bef264', 400: '#a3e635', 500: '#84cc16', 600: '#65a30d', 700: '#4d7c0f', 800: '#3f6212', 900: '#365314', 950: '#1a2e05' },
red: { 50: '#fef2f2', 100: '#fee2e2', 200: '#fecaca', 300: '#fca5a5', 400: '#f87171', 500: '#ef4444', 600: '#dc2626', 700: '#b91c1c', 800: '#991b1b', 900: '#7f1d1d', 950: '#450a0a' },
orange: { 50: '#fff7ed', 100: '#ffedd5', 200: '#fed7aa', 300: '#fdba74', 400: '#fb923c', 500: '#f97316', 600: '#ea580c', 700: '#c2410c', 800: '#9a3412', 900: '#7c2d12', 950: '#431407' },
amber: { 50: '#fffbeb', 100: '#fef3c7', 200: '#fde68a', 300: '#fcd34d', 400: '#fbbf24', 500: '#f59e0b', 600: '#d97706', 700: '#b45309', 800: '#92400e', 900: '#78350f', 950: '#451a03' },
yellow: { 50: '#fefce8', 100: '#fef9c3', 200: '#fef08a', 300: '#fde047', 400: '#facc15', 500: '#eab308', 600: '#ca8a04', 700: '#a16207', 800: '#854d0e', 900: '#713f12', 950: '#422006' },
teal: { 50: '#f0fdfa', 100: '#ccfbf1', 200: '#99f6e4', 300: '#5eead4', 400: '#2dd4bf', 500: '#14b8a6', 600: '#0d9488', 700: '#0f766e', 800: '#115e59', 900: '#134e4a', 950: '#042f2e' },
cyan: { 50: '#ecfeff', 100: '#cffafe', 200: '#a5f3fc', 300: '#67e8f9', 400: '#22d3ee', 500: '#06b6d4', 600: '#0891b2', 700: '#0e7490', 800: '#155e75', 900: '#164e63', 950: '#083344' },
sky: { 50: '#f0f9ff', 100: '#e0f2fe', 200: '#bae6fd', 300: '#7dd3fc', 400: '#38bdf8', 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1', 800: '#075985', 900: '#0c4a6e', 950: '#082f49' },
blue: { 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa', 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a', 950: '#172554' },
indigo: { 50: '#eef2ff', 100: '#e0e7ff', 200: '#c7d2fe', 300: '#a5b4fc', 400: '#818cf8', 500: '#6366f1', 600: '#4f46e5', 700: '#4338ca', 800: '#3730a3', 900: '#312e81', 950: '#1e1b4b' },
violet: { 50: '#f5f3ff', 100: '#ede9fe', 200: '#ddd6fe', 300: '#c4b5fd', 400: '#a78bfa', 500: '#8b5cf6', 600: '#7c3aed', 700: '#6d28d9', 800: '#5b21b6', 900: '#4c1d95', 950: '#2e1065' },
purple: { 50: '#faf5ff', 100: '#f3e8ff', 200: '#e9d5ff', 300: '#d8b4fe', 400: '#c084fc', 500: '#a855f7', 600: '#9333ea', 700: '#7e22ce', 800: '#6b21a8', 900: '#581c87', 950: '#3b0764' },
fuchsia: { 50: '#fdf4ff', 100: '#fae8ff', 200: '#f5d0fe', 300: '#f0abfc', 400: '#e879f9', 500: '#d946ef', 600: '#c026d3', 700: '#a21caf', 800: '#86198f', 900: '#701a75', 950: '#4a044e' },
pink: { 50: '#fdf2f8', 100: '#fce7f3', 200: '#fbcfe8', 300: '#f9a8d4', 400: '#f472b6', 500: '#ec4899', 600: '#db2777', 700: '#be185d', 800: '#9d174d', 900: '#831843', 950: '#500724' },
rose: { 50: '#fff1f2', 100: '#ffe4e6', 200: '#fecdd3', 300: '#fda4af', 400: '#fb7185', 500: '#f43f5e', 600: '#e11d48', 700: '#be123c', 800: '#9f1239', 900: '#881337', 950: '#4c0519' },
slate: { 50: '#f8fafc', 100: '#f1f5f9', 200: '#e2e8f0', 300: '#cbd5e1', 400: '#94a3b8', 500: '#64748b', 600: '#475569', 700: '#334155', 800: '#1e293b', 900: '#0f172a', 950: '#020617' },
gray: { 50: '#f9fafb', 100: '#f3f4f6', 200: '#e5e7eb', 300: '#d1d5db', 400: '#9ca3af', 500: '#6b7280', 600: '#4b5563', 700: '#374151', 800: '#1f2937', 900: '#111827', 950: '#030712' },
zinc: { 50: '#fafafa', 100: '#f4f4f5', 200: '#e4e4e7', 300: '#d4d4d8', 400: '#a1a1aa', 500: '#71717a', 600: '#52525b', 700: '#3f3f46', 800: '#27272a', 900: '#18181b', 950: '#09090b' },
neutral: { 50: '#fafafa', 100: '#f5f5f5', 200: '#e5e5e5', 300: '#d4d4d4', 400: '#a3a3a3', 500: '#737373', 600: '#525252', 700: '#404040', 800: '#262626', 900: '#171717', 950: '#0a0a0a' },
stone: { 50: '#fafaf9', 100: '#f5f5f4', 200: '#e7e5e4', 300: '#d6d3d1', 400: '#a8a29e', 500: '#78716c', 600: '#57534e', 700: '#44403c', 800: '#292524', 900: '#1c1917', 950: '#0c0a09' }
},
semantic: {
transitionDuration: '0.2s',
focusRing: {
width: '1px',
style: 'solid',
color: '{primary.color}',
offset: '2px',
shadow: 'none'
},
disabledOpacity: '0.6',
iconSize: '1rem',
anchorGutter: '2px',
primary: {
50: '{emerald.50}',
100: '{emerald.100}',
200: '{emerald.200}',
300: '{emerald.300}',
400: '{emerald.400}',
500: '{emerald.500}',
600: '{emerald.600}',
700: '{emerald.700}',
800: '{emerald.800}',
900: '{emerald.900}',
950: '{emerald.950}'
},
formField: {
paddingX: '0.75rem',
paddingY: '0.5rem',
borderRadius: '{border.radius.md}',
focusRing: {
width: '0',
style: 'none',
color: 'transparent',
offset: '0',
shadow: 'none'
},
transitionDuration: '{transition.duration}'
},
list: {
padding: '0.25rem 0.25rem',
gap: '2px',
header: {
padding: '0.5rem 0.75rem 0.25rem 0.75rem'
},
option: {
padding: '0.5rem 0.75rem',
borderRadius: '{border.radius.sm}'
},
optionGroup: {
padding: '0.5rem 0.75rem',
fontWeight: '600'
}
},
content: {
borderRadius: '{border.radius.md}'
},
mask: {
transitionDuration: '0.15s'
},
navigation: {
list: {
padding: '0.25rem 0.25rem',
gap: '2px'
},
item: {
padding: '0.5rem 0.75rem',
borderRadius: '{border.radius.sm}',
gap: '0.5rem'
},
submenuLabel: {
padding: '0.5rem 0.75rem',
fontWeight: '600'
},
submenuIcon: {
size: '0.875rem'
}
},
overlay: {
select: {
borderRadius: '{border.radius.md}',
shadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)'
},
popover: {
borderRadius: '{border.radius.md}',
padding: '0.75rem',
shadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)'
},
modal: {
borderRadius: '{border.radius.xl}',
padding: '1.25rem',
shadow: '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1)'
},
navigation: {
shadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)'
}
},
colorScheme: {
light: {
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}'
},
primary: {
color: '{primary.500}',
contrastColor: '#ffffff',
hoverColor: '{primary.600}',
activeColor: '{primary.700}'
},
highlight: {
background: '{primary.50}',
focusBackground: '{primary.100}',
color: '{primary.700}',
focusColor: '{primary.800}'
},
mask: {
background: 'rgba(0,0,0,0.4)',
color: '{surface.200}'
},
formField: {
background: '{surface.0}',
disabledBackground: '{surface.200}',
filledBackground: '{surface.50}',
filledFocusBackground: '{surface.50}',
borderColor: '{surface.300}',
hoverBorderColor: '{surface.400}',
focusBorderColor: '{primary.color}',
invalidBorderColor: '{red.400}',
color: '{surface.700}',
disabledColor: '{surface.500}',
placeholderColor: '{surface.500}',
floatLabelColor: '{surface.500}',
floatLabelFocusColor: '{surface.500}',
floatLabelInvalidColor: '{red.400}',
iconColor: '{surface.400}',
shadow: '0 0 #0000, 0 0 #0000, 0 1px 2px 0 rgba(18, 18, 23, 0.05)'
},
text: {
color: '{surface.700}',
hoverColor: '{surface.800}',
mutedColor: '{surface.500}',
hoverMutedColor: '{surface.600}'
},
content: {
background: '{surface.0}',
hoverBackground: '{surface.100}',
borderColor: '{surface.200}',
color: '{text.color}',
hoverColor: '{text.hover.color}'
},
overlay: {
select: {
background: '{surface.0}',
borderColor: '{surface.200}',
color: '{text.color}'
},
popover: {
background: '{surface.0}',
borderColor: '{surface.200}',
color: '{text.color}'
},
modal: {
background: '{surface.0}',
borderColor: '{surface.200}',
color: '{text.color}'
}
},
list: {
option: {
focusBackground: '{surface.100}',
selectedBackground: '{highlight.background}',
selectedFocusBackground: '{highlight.focus.background}',
color: '{text.color}',
focusColor: '{text.hover.color}',
selectedColor: '{highlight.color}',
selectedFocusColor: '{highlight.focus.color}',
icon: {
color: '{surface.400}',
focusColor: '{surface.500}'
}
},
optionGroup: {
background: 'transparent',
color: '{text.muted.color}'
}
},
navigation: {
item: {
focusBackground: '{surface.100}',
activeBackground: '{surface.100}',
color: '{text.color}',
focusColor: '{text.hover.color}',
activeColor: '{text.hover.color}',
icon: {
color: '{surface.400}',
focusColor: '{surface.500}',
activeColor: '{surface.500}'
}
},
submenuLabel: {
background: 'transparent',
color: '{text.muted.color}'
},
submenuIcon: {
color: '{surface.400}',
focusColor: '{surface.500}',
activeColor: '{surface.500}'
}
}
},
dark: {
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}'
},
primary: {
color: '{primary.400}',
contrastColor: '{surface.900}',
hoverColor: '{primary.300}',
activeColor: '{primary.200}'
},
highlight: {
background: 'color-mix(in srgb, {primary.400}, transparent 84%)',
focusBackground: 'color-mix(in srgb, {primary.400}, transparent 76%)',
color: 'rgba(255,255,255,.87)',
focusColor: 'rgba(255,255,255,.87)'
},
mask: {
background: 'rgba(0,0,0,0.6)',
color: '{surface.200}'
},
formField: {
background: '{surface.950}',
disabledBackground: '{surface.700}',
filledBackground: '{surface.800}',
filledFocusBackground: '{surface.800}',
borderColor: '{surface.700}',
hoverBorderColor: '{surface.600}',
focusBorderColor: '{primary.color}',
invalidBorderColor: '{red.300}',
color: '{surface.0}',
disabledColor: '{surface.400}',
placeholderColor: '{surface.400}',
floatLabelColor: '{surface.400}',
floatLabelFocusColor: '{surface.400}',
floatLabelInvalidColor: '{red.300}',
iconColor: '{surface.400}',
shadow: '0 0 #0000, 0 0 #0000, 0 1px 2px 0 rgba(18, 18, 23, 0.05)'
},
text: {
color: '{surface.0}',
hoverColor: '{surface.0}',
mutedColor: '{surface.400}',
hoverMutedColor: '{surface.300}'
},
content: {
background: '{surface.900}',
hoverBackground: '{surface.800}',
borderColor: '{surface.700}',
color: '{text.color}',
hoverColor: '{text.hover.color}'
},
overlay: {
select: {
background: '{surface.900}',
borderColor: '{surface.700}',
color: '{text.color}'
},
popover: {
background: '{surface.900}',
borderColor: '{surface.700}',
color: '{text.color}'
},
modal: {
background: '{surface.900}',
borderColor: '{surface.700}',
color: '{text.color}'
}
},
list: {
option: {
focusBackground: '{surface.800}',
selectedBackground: '{highlight.background}',
selectedFocusBackground: '{highlight.focus.background}',
color: '{text.color}',
focusColor: '{text.hover.color}',
selectedColor: '{highlight.color}',
selectedFocusColor: '{highlight.focus.color}',
icon: {
color: '{surface.500}',
focusColor: '{surface.400}'
}
},
optionGroup: {
background: 'transparent',
color: '{text.muted.color}'
}
},
navigation: {
item: {
focusBackground: '{surface.800}',
activeBackground: '{surface.800}',
color: '{text.color}',
focusColor: '{text.hover.color}',
activeColor: '{text.hover.color}',
icon: {
color: '{surface.500}',
focusColor: '{surface.400}',
activeColor: '{surface.400}'
}
},
submenuLabel: {
background: 'transparent',
color: '{text.muted.color}'
},
submenuIcon: {
color: '{surface.500}',
focusColor: '{surface.400}',
activeColor: '{surface.400}'
}
}
}
}
},
components: {
accordion,
// ... other components
}
};
トークンはドットセパレータで記述されます(例:`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
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には、標準要素のCSSをリセットするrebootユーティリティがあります。このユーティリティを含める場合は、インポート時にレイヤーを指定できます。
@layer bootstrap-reboot, primevue;
@import "bootstrap-reboot.css" layer(bootstrap-rebooot);
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は、標準要素の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;
}