从各种预先设置样式的主题中选择或开发自己的主题。
PrimeVue 是一个与设计无关的库,因此与其他一些 UI 库不同,它不强制执行某种样式,例如材料设计。样式与组件使用主题分离。主题由两部分组成;基础 和 预设。基础是带有 CSS 变量作为占位符的样式规则,而预设是一组设计令牌,通过将令牌映射到 CSS 变量来馈送基础。可以使用不同的预设配置基础,目前 Aura、Material、Lara 和 Nora 是可用的内置选项。
样式化模式架构的核心是基于一个名为设计令牌的概念,预设定义了 3 层令牌配置;原始、语义 和 组件。
原始令牌没有上下文,调色板是原始令牌的一个很好的例子,例如 蓝色-50 到 蓝色-900。名为 蓝色-500 的令牌可以用作主色,消息的背景,但在它本身上,令牌的名称并不表示上下文。通常,它们被语义令牌使用。
语义令牌定义内容,并且它们的名称指示它们的使用位置,语义令牌的一个众所周知的例子是 primary.color。语义令牌映射到原始令牌或其他语义令牌。colorScheme 令牌组是一个特殊变量,用于基于应用程序中处于活动状态的配色方案定义令牌,这允许基于配色方案(如暗模式)定义不同的令牌。
组件令牌是每个组件的隔离令牌,例如映射到语义令牌的 inputtext.background 或 button.color。例如,button.background 组件令牌映射到 primary.color 语义令牌,后者映射到 green.500 原始令牌。
在定义核心调色板时使用原始令牌,并使用语义令牌指定常见的设计元素,例如焦点环、主色和表面。组件令牌仅应在自定义特定组件时使用。通过将您自己的设计令牌定义为自定义预设,您将能够定义自己的样式而无需接触 CSS。使用样式类覆盖 PrimeVue 组件不是最佳实践,应该是最后的手段,设计令牌是建议的方法。
观看 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'
}
定义样式是否应默认在 CSS 层内定义。如果需要,CSS 层将有助于声明自定义级联层以便于自定义。默认为 false。
options: {
cssLayer: {
name: 'primevue',
order: 'app-styles, primevue, another-css-library'
}
}
Aura、Material、Lara 和 Nora 是可用的内置选项,旨在展示与设计无关的主题的强大功能。Aura 是 PrimeTek 自己的愿景,Material 遵循 Google Material Design v2,Lara 基于 Bootstrap,而 Nora 的灵感来自企业应用程序。访问源代码,了解有关预设结构的更多信息。您可以开箱即用地使用它们进行修改,或者在您需要从头开始构建自己的预设时将它们用作参考。
令牌使用点分隔符描述,例如 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 和 extend。
预设的调色板由 primitive 设计令牌组定义。您可以使用 CSS 变量或 $dt 实用程序访问颜色。
// With CSS
var(--p-blue-500)
// With JS
$dt('blue.500').value
PrimeVue 使用 system 作为主题配置中的默认 darkModeSelector。如果您的应用程序中有暗模式开关,请将 darkModeSelector 设置为您使用的选择器,例如 .my-app-dark,以便 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() {
document.documentElement.classList.toggle('my-app-dark');
}
如果你希望始终使用暗黑模式,请在初始时应用 darkModeSelector,并且永远不要更改它。
<html class="my-app-dark">
也可以使用 false 或 none 作为选择器的值来完全禁用暗黑模式。
theme: {
preset: Aura,
options: {
darkModeSelector: false || 'none',
}
}
definePreset 实用程序用于在 PrimeVue 设置期间自定义现有预设。第一个参数是要自定义的预设,第二个参数是要覆盖的设计令牌。
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}'
}
}
});
在浅色和深色模式之间变化的配色方案调色板使用 surface 令牌指定。下面的示例在浅色模式下使用 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}'
}
}
}
}
});
noir 模式是使用 surface 色调作为 primary 并需要额外的 colorScheme 配置来实现的变体的昵称。 一个以黑色和白色变体作为 primary 颜色的示例预设配置:
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)'
}
}
}
}
});
UI 组件的字体设置从应用程序继承,因此没有字体设计。
表单输入组件的设计令牌源自 form.field 令牌组。此自定义示例将悬停时的边框颜色更改为 primary。任何依赖于此语义令牌的组件(如 dropdown.hover.border.color 和 textarea.hover.border.color)都将收到更改。
const MyPreset = definePreset(Aura, {
semantic: {
colorScheme: {
light: {
formField: {
hoverBorderColor: '{primary.color}'
}
},
dark: {
formField: {
hoverBorderColor: '{primary.color}'
}
}
}
}
});
焦点环定义轮廓宽度、样式、颜色和偏移量。让我们使用较粗的环,并使用 primary 颜色作为轮廓。
const MyPreset = definePreset(Aura, {
semantic: {
focusRing: {
width: '2px',
style: 'dashed',
color: '{primary.color}',
offset: '1px'
}
}
});
特定组件的设计令牌在 components 层定义。如果你要构建自己的样式,则不建议覆盖组件令牌,而应优先构建自己的预设。此配置是全局的,适用于所有卡片组件。如果你需要在页面上本地自定义特定组件,请查看“Scoped 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}'
}
}
}
}
}
});
可以通过添加自定义设计令牌和附加样式来扩展主题系统。此功能提供了高度的自定义性,使你可以根据自己的需要调整样式,而不受默认令牌的限制。
示例预设配置添加了一个新的 accent 按钮,其中包含自定义的 button.accent.color 和 button.accent.inverse.color 令牌。还可以全局添加令牌,以便在组件中共享它们。
const MyPreset = definePreset(Aura, {
components: {
// custom button tokens and additional style
button: {
extend: {
accent: {
color: '#f59e0b',
inverseColor: '#ffffff'
}
}
css: ({ dt }) => `
.p-button-accent {
background: ${dt('button.accent.color')};
color: ${dt('button.accent.inverse.color')};
transition-duration: ${dt('my.transition.fast')};
}
`
}
},
// common tokens and styles
extend: {
my: {
transition: {
slow: '0.75s'
normal: '0.5s'
fast: '0.25s'
},
imageDisplay: 'block'
}
},
css: ({ dt }) => `
/* Global CSS */
img {
display: ${dt('my.image.display')};
}
`
});
可以使用 dt 属性将设计令牌的作用域限定为特定组件。在此示例中,第一个开关使用全局令牌,而第二个开关使用自己的令牌覆盖全局令牌。
与 :deep() 相比,这种方法更可取,因为它提供了更简洁的 API,同时避免了 CSS 规则覆盖的麻烦。
<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}');
PrimeVue CSS 层仅在主题配置中显式启用分层时才应用于样式化模式。在非样式化模式下,不包含内置 CSS 类,因此不需要任何层。
@layer 是一种标准的 CSS 功能,用于定义级联层,以便可以自定义优先级顺序。如果你需要更多地了解层,请访问 MDN 上的文档以开始了解。
cssLayer 默认情况下处于禁用状态。在主题配置中启用它时,PrimeVue 会将内置样式类包装在 primevue 级联层下,以使库样式易于覆盖。应用程序中没有层的 CSS 具有最高的 CSS 特异性,因此你将能够覆盖样式,而无需考虑位置或类的编写强度。
层还可以更轻松地使用 CSS Modules,请查看 CSS Modules 指南以获取示例。
如果 PrimeVue 组件在你的应用程序中出现视觉问题,则可能是重置 CSS 造成的。CSS 层是一种有效的解决方案,它涉及启用 PrimeVue 层,将重置 CSS 包装在另一层中,并定义层顺序。这样,你的重置 CSS 不会妨碍 PrimeVue 组件。
/* Order */
@layer reset, primevue;
/* Reset CSS */
@layer reset {
button,
input {
/* CSS to Reset */
}
}
通过在 SFC 中的 style 元素上启用 module 属性,可以支持 CSS modules。使用 $style 关键字将类应用于 PrimeVue 组件。建议在使用 CSS modules 时启用 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;
}