安装 Windi CSS
$ npm i -D vite-plugin-windicss windicss
vite.config.js
// vite.config.js
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import WindiCSS from "vite-plugin-windicss"; // <==
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue(), WindiCSS()], // <==
});
Windi CSS 文件
- main.ts
import 'virtual:windi.css'
import 'virtual:windi-devtools'
+ windi.config.ts
import { defineConfig } from 'vite-plugin-windicss'
import primary from './src/assets/styles/theme.less'
export default defineConfig({
darkMode: 'class',
plugins: [createEnterPlugin()],
theme: {
extend: {
zIndex: {
'-1': '-1'
},
colors: {
primary: primary['primary-color']
},
screens: {
sm: '576px',
md: '768px',
lg: '992px',
xl: '1200px',
'2xl': '1600px'
}
}
}
})
/**
* Used for animation when the element is displayed.
* @param maxOutput The larger the maxOutput output, the larger the generated css volume.
*/
function createEnterPlugin(maxOutput = 6) {
const createCss = (index: number, d = 'x') => {
const upd = d.toUpperCase()
return {
[`*> .enter-${d}:nth-child(${index})`]: {
transform: `translate${upd}(50px)`
},
[`*> .-enter-${d}:nth-child(${index})`]: {
transform: `translate${upd}(-50px)`
},
[`* > .enter-${d}:nth-child(${index}),* > .-enter-${d}:nth-child(${index})`]: {
'z-index': `${10 - index}`,
opacity: '0',
animation: `enter-${d}-animation 0.4s ease-in-out 0.3s`,
'animation-fill-mode': 'forwards',
'animation-delay': `${(index * 1) / 10}s`
}
}
}
const handler = ({ addBase }) => {
const addRawCss = {}
for (let index = 1; index < maxOutput; index++) {
Object.assign(addRawCss, {
...createCss(index, 'x'),
...createCss(index, 'y')
})
}
addBase({
...addRawCss,
'@keyframes enter-x-animation': {
to: {
opacity: '1',
transform: 'translateX(0)'
}
},
'@keyframes enter-y-animation': {
to: {
opacity: '1',
transform: 'translateY(0)'
}
}
})
}
return { handler }
}
+ stylelint.config.ts
module.exports = {
root: true,
plugins: ['stylelint-order'],
extends: ['stylelint-config-standard', 'stylelint-config-prettier'],
customSyntax: 'postcss-html',
rules: {
'function-no-unknown': null,
'selector-class-pattern': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global']
}
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep']
}
],
'at-rule-no-unknown': [
true,
{
ignoreAtRules: [
'tailwind',
'apply',
'variants',
'responsive',
'screen',
'function',
'if',
'each',
'include',
'mixin'
]
}
],
'no-empty-source': null,
'string-quotes': null,
'named-grid-areas-no-invalid': null,
'unicode-bom': 'never',
'no-descending-specificity': null,
'font-family-no-missing-generic-family-keyword': null,
'declaration-colon-space-after': 'always-single-line',
'declaration-colon-space-before': 'never',
// 'declaration-block-trailing-semicolon': 'always',
'rule-empty-line-before': [
'always',
{
ignore: ['after-comment', 'first-nested']
}
],
'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
'order/order': [
[
'dollar-variables',
'custom-properties',
'at-rules',
'declarations',
{
type: 'at-rule',
name: 'supports'
},
{
type: 'at-rule',
name: 'media'
},
'rules'
],
{ severity: 'warning' }
]
},
ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
overrides: [
{
files: ['*.vue', '**/*.vue', '*.html', '**/*.html'],
extends: ['stylelint-config-recommended'],
rules: {
'keyframes-name-pattern': null,
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['deep', 'global']
}
],
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', 'v-global', 'v-slotted']
}
]
}
},
{
files: ['*.less', '**/*.less'],
customSyntax: 'postcss-less',
extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue']
}
]
}
基础使用
过渡效果 enter-x enter-y 宽高适配 xl:w-100px xl:h-200px
<div class="text a1 w-full xl:w-100px xl:h-200px enter-x"></div>
<div class="text a2 w-full enter-y"></div>
<div class="text a3 w-full enter-y"></div>