#reveal,Class,Variance
Featured Content Ads
add advertising hereClass Variance Authority
Featured Content Ads
add advertising here
Featured Content Ads
add advertising hereIntroduction
CSS-in-TS libraries comparable to Stitches and Vanilla Extract are unbelievable alternate options for building form-fine UI system; casting off the final worries of class names and StyleSheet composition.
…but CSS-in-TS (or CSS-in-JS) will not be for all individuals.
You’ll possess stout protect a watch on over your StyleSheet output. Your job would possibly perchance maybe well maybe require you to make utilize of a framework comparable to Tailwind CSS. It is most likely you’ll maybe well maybe correct prefer writing your individual CSS.
Increasing variants with the “ancient” CSS advance can change into an arduous assignment; manually matching classes to props and manually including varieties.
cva
goals to set up these peril aspects away, allowing you to point of curiosity on the extra relaxing parts of UI pattern.
Acknowledgements
- Stitches (Modulz)
Large as a result of the Modulz group for pioneering thevariants
API motion – your birth-source contributions are immensely most standard - clb (Bill Criswell)
This project first and major place started out with the device of merging into the unbelievableclb
library, but after some discussion with Bill, we felt it used to be most efficient to poke down the route of a separate project.
I’m so grateful to Bill for sharing his work publicly and for getting me desirous about building a kind-fine variants API for classes. Have to you’ve a moment, please poke and giant name the project on GitHub. Thank you Bill!
Installation
npm i class-variance-authority
“Net I even prefer to write such a protracted bundle name for every import?”
Unfortunately, yes. Originally, the idea used to be the publish the bundle as cva
, but this name has been taken and marked as a “placeholder”. I’ve reached out to the author and NPM beef up, but possess but to hear back.
In the duration in-between, you would possibly perchance maybe well maybe moreover always alias the bundle in your convenience…
Aliasing in TypeScript
-
Add the alias to your
tsconfig.json
paths
:{ "compilerOptions": { "baseUrl": ".", "paths": { "cva": ["node_modules/class-variance-authority"] } } }
-
Then import savor so:
import { cva } from "cva"; // …
Getting Began
Disclaimer: Even supposing
cva
is a tiny library, or not it is most efficient to make utilize of in a SSR/SSG ambiance – your user doubtlessly would not need this JavaScript, significantly for static system.
Your First Factor
To kick things off, let’s manufacture a “overall” button
side, utilizing cva
to address our variant’s classes
Indicate: Employ of Tailwind CSS will not be necessary
button({ intent: “secondary”, size: “tiny” });
// => “font-semibold border rounded bg-white textual vow material-gray-800 border-gray-400 hover:bg-gray-100 textual vow material-sm py-1 px-2″”>
import { cva } from "class-variance-authority"; const button = cva(["font-semibold", "border", "rounded"], { variants: { intent: { predominant: [ "bg-blue-500", "text-white", "border-transparent", "hover:bg-blue-600", ], // or // predominant: "bg-blue-500 textual vow material-white border-clear hover:bg-blue-600", secondary: [ "bg-white", "text-gray-800", "border-gray-400", "hover:bg-gray-100", ], }, size: { tiny: ["text-sm", "py-1", "px-2"], medium: ["text-base", "py-2", "px-4"], }, }, compoundVariants: [{ intent: "primary", size: "medium", class: "uppercase" }], defaultVariants: { intent: "predominant", size: "medium", }, }); button(); // => "font-semibold border rounded bg-blue-500 textual vow material-white border-clear hover:bg-blue-600 textual vow material-adversarial py-2 px-4 uppercase" button({ intent: "secondary", size: "tiny" }); // => "font-semibold border rounded bg-white textual vow material-gray-800 border-gray-400 hover:bg-gray-100 textual vow material-sm py-1 px-2"
TypeScript Helpers
cva
affords the VariantProps
helper to extract variant varieties
export const yourComponent = cva(/… */);”>
// styles/system.ts import form { VariantProps } from "class-variance-authority"; import { cva, cx } from "class-variance-authority"; / YourComponent */ export form YourComponentProps = VariantProps<typeof yourComponent>; export const yourComponent = cva(/... */);
Composing Classes
Whereas cva
would not but offer a built-in methodology for composing classes, it does offer the instruments to lengthen system in your individual phrases…
As an illustration; two cva
styles, concatenated along with cx
:
export const box = cva([“box”, “box-border”], {
variants: {
margin: { 0: “m-0”, 2: “m-2”, 4: “m-4”, 8: “m-8” },
padding: { 0: “p-0”, 2: “p-2”, 4: “p-4”, 8: “p-8” },
},
defaultVariants: {
margin: 0,
padding: 0,
},
});
/Card export interface CardProps extends BoxProps, CardBaseProps {}
*/
type CardBaseProps = VariantProps
const cardBase = cva([“card”, “border-solid”, “border-slate-300”, “rounded”], {
variants: {
shadow: {
md: “drop-shadow-md”,
lg: “drop-shadow-lg”,
xl: “drop-shadow-xl”,
},
},
});
export const card = ({ margin, padding, shadow }: CardProps = {}) =>
cx(field({ margin, padding }), cardBase({ shadow }));”>
// styles/system.ts
import form { VariantProps } from "class-variance-authority";
import { cva, cx } from "class-variance-authority";
/
Box
*/
export form BoxProps = VariantProps<typeof box>;
export const field = cva(["box", "box-border"], {
variants: {
margin: { 0: "m-0", 2: "m-2", 4: "m-4", 8: "m-8" },
padding: { 0: "p-0", 2: "p-2", 4: "p-4", 8: "p-8" },
},
defaultVariants: {
margin: 0,
padding: 0,
},
});
/
Card
*/
form CardBaseProps = VariantProps<typeof cardBase>;
const cardBase = cva(["card", "border-solid", "border-slate-300", "rounded"], {
variants: {
shadow: {
md: "fall-shadow-md",
lg: "fall-shadow-lg",
xl: "fall-shadow-xl",
},
},
});
export interface CardProps extends BoxProps, CardBaseProps {}
export const card = ({ margin, padding, shadow }: CardProps = {}) =>
cx(field({ margin, padding }), cardBase({ shadow }));
API Reference
cva
Builds a class variance authority
const side = cva("adversarial", alternate options);
adversarial
: the adversarial class name (string
,string[]
ornull
)alternate options
(not necessary)variants
: your variants schemacompoundVariants
: variants basically based on a combination of previously outlined variantsdefaultVariants
: place default values for previously outlined variants
cx
Concatenates class names
const className = cx(classes);
classes
: array of classes to be concatenated
Examples
⚠️ Warning: The examples under are purely demonstrative and have not been examined thoroughly (but)
BEM
/styles.css */ .button { // } .button--predominant { // } .button--secondary { // } .button--tiny { // } .button--medium { // } .button--predominant-tiny { // }
button({ intent: “secondary”, size: “tiny” });
// => “button button–secondary button–tiny””>
import { cva } from "class-variance-authority"; const button = cva("button", { variants: { intent: { predominant: "button--predominant", secondary: "button--secondary", }, size: { tiny: "button--tiny", medium: "button--medium", }, }, compoundVariants: [ { intent: "primary", size: "medium", class: "button--primary-small" }, ], defaultVariants: { intent: "predominant", size: "medium", }, }); button(); // => "button button--predominant button--medium" button({ intent: "secondary", size: "tiny" }); // => "button button--secondary button--tiny"
11ty (with Tailwind)
React (with CSS Modules)
/button.css */ .adversarial { // } .predominant { // } .secondary { // } .tiny { // } .medium { // } .primaryMedium { // }
export const Button: React.FC
);”>
// button.tsx
import React from "react";
import { cva } from "class-variance-authority";
import form { VariantProps } from "class-variance-authority";
import {
adversarial,
predominant,
secondary,
tiny,
medium,
primaryMedium,
} from "./button.css";
// ⚠️ Disclaimer: Employ of Tailwind CSS will not be necessary
const button = cva(adversarial, {
variants: {
intent: {
predominant,
secondary,
},
size: {
tiny,
medium,
},
},
compoundVariants: [
{ intent: "primary", size: "medium", class: primaryMedium },
],
defaultVariants: {
intent: "predominant",
size: "medium",
},
});
export form ButtonProps = VariantProps<typeof button>;
export const Button: React.FC<ButtonProps> = ({ in