What is cva
cva
stands for class-variance-authority
. It is a class library that is very suitable for creating and controlling Css
variant methods. It is very suitable for atomic ideas like tailwindcss
.
Many times we encapsulate components, especially using the idea of atomization to write css
, and then encapsulate the components, just use it!
Example
So how to use it for packaging? Let’s take the example of encapsulating an intuitive vue
component Button
:
<template>
<button>
<slot></slot>
</button>
</template>
Next, we need to control its style based on the passed Props
of this component, including color type, size, status, shape, etc.
So if we use styles to control these, we usually write like this:
<template>
<button :class="className">
<slot></slot>
</button>
</template>
<script setup lang="ts">
import { computed } from 'vue';
function getButtonClass(props){
let classNames = [/* base */]
// do something with props like push, splice, unshift ...
return classNames.join(' ')
}
const props = withDefaults(defineProps<{
// ...
}>(), {
// ...
})
const className = computed(() => {
return getButtonClass(props)
})
</script>
And cva
is a method that helps us generate this getButtonClass
function.
Parameters
The input parameters of a cva
usually include 4
parts: base
, variants
, compoundVariants
, defaultVariants
:
import { cva } from 'class-variance-authority'
// ⬇️ base
const button = cva(['font-semibold', 'border', 'rounded'], {
// ⬇️ variants
variants: {
intent: {
primary: ['bg-blue-500', 'text-white', 'border-transparent', 'hover:bg-blue-600'],
secondary: ['bg-white', 'text-gray-800', 'border-gray-400', 'hover:bg-gray-100']
},
size: {
small: ['text-sm', 'py-1', 'px-2'],
medium: ['text-base', 'py-2', 'px-4']
}
},
// ⬇️ compoundVariants
compoundVariants: [
{
intent: 'primary',
size: 'medium',
class: 'uppercase'
}
],
// ⬇️ defaultVariants
defaultVariants: {
intent: 'primary',
size: 'medium'
}
})
button()
// => "font-semibold border rounded bg-blue-500 text-white border-transparent hover:bg-blue-600 text-base py-2 px-4 uppercase"
button({ intent: 'secondary', size: 'small' })
// => "font-semibold border rounded bg-white text-gray-800 border-gray-400 hover:bg-gray-100 text-sm py-1 px-2"
Many times we can construct such a function to encapsulate our components. This will make our code very intuitive. What should control the style will control the style, and what should control the behavior will control the behavior. So it is good to use cva
to transform the vue
Button
component just now.
But what if our project does not use the atomic Css
class library? Or if we want to generate the cva
function when writing Css
, then you have to use postcss-cva
.
postcss-cva
postcss-cva
is a postcss
plugin based on css ast
analysis.
It can convert the Css
comments you write into cva
functions.
css example
Let’s look at a simple example. During the encapsulation process of the Button
component, the following Css
is written
/* @meta path="./buttonClass" */
/* @dv size="md" */
.btn {
/* @b */
font-size: 16px;
background: gray;
border-radius: 4px;
}
.btn-primary {
/* @v type="primary" */
background: blue;
color: white;
}
.btn-secondary {
/* @v type="secondary" */
font-size: 22px;
color: yellow;
}
.btn-disabled {
/* @cv type="primary" size="xs" */
cursor: not-allowed;
}
.btn-md {
/* @v size="md" */
padding: 6px 10px;
font-size: 16px;
}
.btn-xs {
/* @v size="xs" */
padding: 2px 6px;
font-size: 14px;
}
.btn-sm {
/* @v size="sm" */
padding: 4px 8px;
font-size: 12px;
}
Atomic design
In it we define:
- Its base class:
base
:.btn
- Its variants:
variants
:-
.btn-primary
,.btn-secondary
to control color types -
.btn-md
,.btn-xs
,.btn-sm
to control size - More to control, shape or other etc.
-
- Its compound variants:
compoundVariants
:.btn-disabled
(triggered when the passed parametertype="primary" size="xs"
is met) - Its default variant:
defaultVariants
:size="md"
(usesize="md"
when no parameters are passed by default)
Annotated reference
Then follow the comment reference of postcss-cva
:
keyword | target | type | description |
---|---|---|---|
@b |
base |
node | add current node selector to base |
@gb |
base |
global | define base |
@v |
variants |
node | add current node selector to variants |
@gv |
variants |
global | define variants |
@cv |
compoundVariants |
node | add current node selector to compoundVariants |
@gcv |
compoundVariants |
global | define defaultVariants |
@dv |
defaultVariants |
global | define defaultVariants |
@meta |
meta |
global | define metadata |
We define all variants by adding corresponding annotations.
defined by
@meta path="./buttonClass"
At the same time, the file output directory of the cva
function is also defined as the buttonClass.ts
file in the directory where the current Button.vue
file is located (the default format is ts
)
Generate cva function
In this way, when we run the main function and introduce the css
file where the vue
component/comment is located, a cva
function is generated:
import { cva, VariantProps } from "class-variance-authority";
const index = cva(["btn"], {
variants: {
"type": {
"primary": ["btn-primary"],
"secondary": ["btn-secondary"]
},
"size": {
"md": ["btn-md"],
"xs": ["btn-xs"],
"sm": ["btn-sm"]
}
},
compoundVariants: [{
"class": ["btn-disabled"],
"type": ["primary"],
"size": ["xs"]
}],
defaultVariants: {
"size": "md"
}
});
export type Props = VariantProps<typeof index>;
export default index;
然后,我们就可以直接引入进行封装了!
<template>
<button :class="className">
<slot>postcss-cva</slot>
</button>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import buttonClass, { Props as ButtonProps } from './buttonClass'
const props = withDefaults(defineProps<{
// ButtonProps
type?: 'primary' | 'secondary',
size?: 'md' | 'sm' | 'xs'
}>(), {})
const className = computed(() => {
return buttonClass(props)
})
</script>
postcss-cva
allows you to plan the cva
function when designing and writing css
.
Now you can not only use it directly as an external postcss
plug-in, but it has also been integrated into IceStack inside.
Use it to manage and generate your Css UI
now.
Top comments (0)