class-variance-authority
Advanced tools
Comparing version 0.0.0 to 0.0.1
{ | ||
"name": "class-variance-authority", | ||
"version": "0.0.0", | ||
"version": "0.0.1", | ||
"description": "Class Variance Authority 🧬", | ||
@@ -26,9 +26,10 @@ "author": "Joe Bell (https://joebell.co.uk)", | ||
"files": [ | ||
"dist", | ||
"README.md" | ||
"dist/*.js", | ||
"dist/*.js.map", | ||
"dist/*.d.ts" | ||
], | ||
"scripts": { | ||
"build": "npm-run-all build:*", | ||
"build:cjs": "esbuild src/index.ts --format=cjs --outfile=dist/index.cjs.js", | ||
"build:esm": "esbuild src/index.ts --format=esm --outfile=dist/index.esm.js", | ||
"build:cjs": "npx swc ./src/index.ts --config-file ./.config/.swcrc -o dist/index.cjs.js -C module.type=commonjs", | ||
"build:esm": "npx swc ./src/index.ts --config-file ./.config/.swcrc -o dist/index.esm.js -C module.type=es6 ", | ||
"build:tsc": "tsc --project .config/tsconfig.json --emitDeclarationOnly", | ||
@@ -38,20 +39,20 @@ "check:tsc": "tsc --project .config/tsconfig.json --noEmit", | ||
"check": "run-p check:*", | ||
"ci": "npm run check && npm run build && npm run test", | ||
"test": "run-p test:*", | ||
"test:jest": "jest --config .config/jest.config.ts --coverage", | ||
"test:size": "run-p build:cjs build:esm && npx bundlesize -f 'dist/*.js' -s 910B", | ||
"dev": "jest --config .config/jest.config.ts --watch" | ||
"test:size": "run-p build:cjs build:esm && npx bundlesize -f 'dist/*.js' -s 800B", | ||
"dev": "jest --config .config/jest.config.ts --watch", | ||
"prepare": "husky install", | ||
"prepublishOnly": "npm run build" | ||
}, | ||
"devDependencies": { | ||
"@babel/preset-env": "7.16.5", | ||
"@babel/preset-typescript": "7.16.5", | ||
"@types/jest": "27.0.3", | ||
"@swc/cli": "0.1.55", | ||
"@swc/core": "1.2.136", | ||
"@swc/jest": "0.2.17", | ||
"@types/jest": "27.4.0", | ||
"@types/react": "17.0.38", | ||
"@types/react-dom": "17.0.11", | ||
"bundlesize": "0.18.1", | ||
"esbuild": "0.14.9", | ||
"husky": "7.0.4", | ||
"jest": "27.2.0", | ||
"lint-staged": "12.1.4", | ||
"np": "7.5.0", | ||
"jest": "27.4.7", | ||
"lint-staged": "12.3.2", | ||
"npm-run-all": "4.1.5", | ||
@@ -62,4 +63,4 @@ "prettier": "2.5.1", | ||
"ts-node": "10.4.0", | ||
"typescript": "4.5.4" | ||
"typescript": "4.5.5" | ||
} | ||
} |
362
README.md
@@ -0,19 +1,24 @@ | ||
![CVA](/.github/assets/meta.png) | ||
<h1 align="center">cva</h1> | ||
<p align="center"> | ||
<strong>C</strong>lass <a href="https://www.youtube.com/watch?v=9ZcyoZlY0aU"><strong>V</strong>ariance <strong>A</strong>uthority</a> 🧬 | ||
<strong>C</strong>lass <a href="https://www.youtube.com/watch?v=9ZcyoZlY0aU"><strong>V</strong>ariance <strong>A</strong>uthority</a> | ||
</p> | ||
<p align="center"> | ||
<a href="https://www.npmjs.com/package/@joebell/cva"> | ||
<img alt="NPM Version" src="https://badgen.net/npm/v/@joebell/cva" /> | ||
<a href="https://www.npmjs.com/package/class-variance-authority"> | ||
<img alt="NPM Version" src="https://badgen.net/npm/v/class-variance-authority" /> | ||
</a> | ||
<a href="https://www.npmjs.com/package/@joebell/cva"> | ||
<img alt="Types Included" src="https://badgen.net/npm/types/@joebell/cva" /> | ||
<a href="https://www.npmjs.com/package/class-variance-authority"> | ||
<img alt="Types Included" src="https://badgen.net/npm/types/class-variance-authority" /> | ||
</a> | ||
<a href="https://bundlephobia.com/result?p=class-variance-authority"> | ||
<img alt="Minizipped Size" src="https://img.shields.io/bundlephobia/minzip/class-variance-authority" /> | ||
</a> | ||
<a href="https://github.com/joe-bell/cva/blob/main/LICENSE"> | ||
<img alt="Apache-2.0 License" src="https://badgen.net/github/license/joe-bell/cva" /> | ||
</a> | ||
<a href="https://www.npmjs.com/package/@joebell/cva"> | ||
<img alt="NPM Downloads" src="https://badgen.net/npm/dm/@joebell/cva" /> | ||
<a href="https://www.npmjs.com/package/class-variance-authority"> | ||
<img alt="NPM Downloads" src="https://badgen.net/npm/dm/class-variance-authority" /> | ||
</a> | ||
@@ -29,41 +34,71 @@ <a href="https://twitter.com/joebell_"> | ||
Coming from BEM… | ||
CSS-in-TS libraries such as [Stitches](https://stitches.dev/docs/variants) and [Vanilla Extract](https://vanilla-extract.style/documentation/) are **fantastic** options for building type-safe UI components; taking away all the worries of class names and StyleSheet composition. | ||
```ts | ||
import { cva } from "@joebell/cva"; | ||
…but CSS-in-TS (or CSS-in-JS) isn't for everyone. | ||
const button = cva("button", { | ||
variants: { | ||
intent: { | ||
primary: "button--primary", | ||
secondary: "button--secondary", | ||
warning: "button--warning", | ||
danger: "button--danger", | ||
}, | ||
size: { | ||
small: "button--small", | ||
medium: "button--medium", | ||
large: "button--large", | ||
}, | ||
}, | ||
compoundVariants: [ | ||
{ intent: "primary", size: "medium", class: "button--primary-small" }, | ||
], | ||
defaultVariants: { | ||
intent: "primary", | ||
size: "medium", | ||
}, | ||
}); | ||
You may need full control over your StyleSheet output. Your job might require you to use a framework such as Tailwind CSS. You might just prefer writing your own CSS. | ||
button(); | ||
// => "button button--primary button--medium" | ||
Creating variants with the "traditional" CSS approach can become an arduous task; manually matching classes to props and manually adding types. | ||
button({ intent: "secondary", size: "small" }); | ||
// => "button button--secondary button--small" | ||
CVA aims to take those pain points away, allowing you to focus on the more fun aspects of UI development. | ||
## Acknowledgements | ||
- [**Stitches**](https://stitches.dev/) ([Modulz](http://modulz.app)) | ||
Huge thanks to the Modulz team for pioneering the `variants` API movement – your open-source contributions are immensely appreciated | ||
- [**clb**](https://github.com/crswll/clb) ([Bill Criswell](https://github.com/crswll)) | ||
This project originally started out with the intention of merging into the wonderful [`clb`](https://github.com/crswll/clb) library, but after some discussion with Bill, we felt it was best to go down the route of a separate project. | ||
I'm so grateful to Bill for sharing his work publicly and for getting me excited about building a type-safe variants API for classes. If you have a moment, please go and [star the project on GitHub](https://github.com/crswll/clb). Thank you Bill! | ||
## Installation | ||
```sh | ||
npm i class-variance-authority | ||
``` | ||
Tailwind | ||
<details> | ||
<summary>"Do I really have to write such a long package name for every import?"</summary> | ||
Unfortunately, yes. Originally, the plan was the publish the package as `cva`, but this name [has been taken and marked as a "placeholder"](https://www.npmjs.com/package/cva). I've reached out to the author and NPM support, but have yet to hear back. | ||
In the meantime, you can always alias the package for your convenience… | ||
### Aliasing in TypeScript | ||
1. Add the alias to your [`tsconfig.json`](https://www.typescriptlang.org/tsconfig#paths) `paths`: | ||
```json | ||
{ | ||
"compilerOptions": { | ||
"baseUrl": ".", | ||
"paths": { | ||
"cva": ["node_modules/class-variance-authority"] | ||
} | ||
} | ||
} | ||
``` | ||
2. Then import like so: | ||
```ts | ||
import { cva } from "cva"; | ||
// … | ||
``` | ||
</details> | ||
## Getting Started | ||
> **Disclaimer**: Although `cva` is a [**tiny**](https://bundlephobia.com/package/class-variance-authority) library, it's best to use in a SSR/SSG environment – your user probably doesn't need this JavaScript, especially for static components. | ||
### Your First Component | ||
To kick things off, let's build a "basic" `button` component, using `cva` to handle our variant's classes | ||
> **Note:** Use of Tailwind CSS is optional | ||
```ts | ||
import { cva } from "@joebell/cva"; | ||
import { cva } from "class-variance-authority"; | ||
@@ -87,14 +122,2 @@ const button = cva(["font-semibold", "border", "rounded"], { | ||
], | ||
warning: [ | ||
"bg-yellow-500", | ||
"text-black", | ||
"border-transparent", | ||
"hover:bg-yellow-600", | ||
], | ||
danger: [ | ||
"bg-red-500", | ||
"text-white", | ||
"border-transparent", | ||
"hover:bg-red-600", | ||
], | ||
}, | ||
@@ -104,3 +127,2 @@ size: { | ||
medium: ["text-base", "py-2", "px-4"], | ||
large: ["text-lg", "py-2.5", "px-4"], | ||
}, | ||
@@ -122,18 +144,18 @@ }, | ||
### Problem? | ||
### TypeScript Helpers | ||
### Prior Art | ||
`cva` offers the `VariantProps` helper to extract variant types | ||
- clb | ||
- Stitches | ||
- Vanilla Extract | ||
```ts | ||
// styles/components.ts | ||
import type * as CVA from "class-variance-authority"; | ||
import { cva, cx } from "class-variance-authority"; | ||
## Installation | ||
```sh | ||
npm i @joebell/cva | ||
/** | ||
* YourComponent | ||
*/ | ||
export type YourComponentProps = CVA.VariantProps<typeof yourComponent>; | ||
export const yourComponent = cva(/* ... */); | ||
``` | ||
## Getting Started | ||
### Composing Classes | ||
@@ -147,4 +169,4 @@ | ||
// styles/components.ts | ||
import type * as CVA from "cva"; | ||
import { cva, cx } from "@joebell/cva"; | ||
import type * as CVA from "class-variance-authority"; | ||
import { cva, cx } from "class-variance-authority"; | ||
@@ -187,7 +209,6 @@ /** | ||
- `cva` – build a class variance authority | ||
- `cx` – concatenate class names | ||
### `cva` | ||
Builds a class variance authority | ||
```ts | ||
@@ -197,12 +218,18 @@ const component = cva("base", options); | ||
#### Parameters | ||
1. `base` – the base class name | ||
1. `base`: the base class name (`string`, `string[]` or `null`) | ||
1. `options` _(optional)_ | ||
1. `variants` | ||
1. `compoundVariants` | ||
1. `defaultVariants` | ||
- `variants`: your variants schema | ||
- `compoundVariants`: variants based on a combination of previously defined variants | ||
- `defaultVariants`: set default values for previously defined variants | ||
### `cx` | ||
Concatenates class names | ||
```ts | ||
const className = cx(classes); | ||
``` | ||
- `classes`: array of classes to be concatenated | ||
## Examples | ||
@@ -213,25 +240,25 @@ | ||
<details> | ||
<summary>React (with CSS Modules)</summary> | ||
<summary>BEM</summary> | ||
```css | ||
/* button.css */ | ||
.base { | ||
/* styles.css */ | ||
.button { | ||
/* */ | ||
} | ||
.primary { | ||
.button--primary { | ||
/* */ | ||
} | ||
.secondary { | ||
.button--secondary { | ||
/* */ | ||
} | ||
.small { | ||
.button--small { | ||
/* */ | ||
} | ||
.medium { | ||
.button--medium { | ||
/* */ | ||
} | ||
.primaryMedium { | ||
.button--primary-small { | ||
/* */ | ||
@@ -241,31 +268,18 @@ } | ||
```tsx | ||
// button.tsx | ||
import React from "react"; | ||
import { cva } from "@joebell/cva"; | ||
import * as CVA from "@joebell/cva"; | ||
```ts | ||
import { cva } from "class-variance-authority"; | ||
import { | ||
base, | ||
primary, | ||
secondary, | ||
small, | ||
medium, | ||
primaryMedium, | ||
} from "./button.css"; | ||
// ⚠️ Disclaimer: Use of Tailwind CSS is optional | ||
const button = cva(base, { | ||
const button = cva("button", { | ||
variants: { | ||
intent: { | ||
primary, | ||
secondary, | ||
primary: "button--primary", | ||
secondary: "button--secondary", | ||
}, | ||
size: { | ||
small, | ||
medium, | ||
small: "button--small", | ||
medium: "button--medium", | ||
}, | ||
}, | ||
compoundVariants: [ | ||
{ intent: "primary", size: "medium", class: primaryMedium }, | ||
{ intent: "primary", size: "medium", class: "button--primary-small" }, | ||
], | ||
@@ -278,7 +292,7 @@ defaultVariants: { | ||
export type ButtonProps = CVA.VariantProps<typeof button>; | ||
button(); | ||
// => "button button--primary button--medium" | ||
export const Button: React.FC<ButtonProps> = ({ intent, size, ...props }) => ( | ||
<button className={button({ intent, size })} {...props} /> | ||
); | ||
button({ intent: "secondary", size: "small" }); | ||
// => "button button--secondary button--small" | ||
``` | ||
@@ -289,9 +303,7 @@ | ||
<details> | ||
<summary>React (with Tailwind)</summary> | ||
<summary>11ty (with Tailwind)</summary> | ||
```tsx | ||
// button.tsx | ||
import React from "react"; | ||
import { cva } from "@joebell/cva"; | ||
import * as CVA from "@joebell/cva"; | ||
```js | ||
// button.11ty.js | ||
const { cva } = require("class-variance-authority"); | ||
@@ -327,2 +339,73 @@ // ⚠️ Disclaimer: Use of Tailwind CSS is optional | ||
module.exports = function ({ label, intent, size }) { | ||
return `<button class="${button({ intent, size })}">${label}</button>`; | ||
}; | ||
``` | ||
</details> | ||
<details> | ||
<summary>React (with CSS Modules)</summary> | ||
```css | ||
/* button.css */ | ||
.base { | ||
/* */ | ||
} | ||
.primary { | ||
/* */ | ||
} | ||
.secondary { | ||
/* */ | ||
} | ||
.small { | ||
/* */ | ||
} | ||
.medium { | ||
/* */ | ||
} | ||
.primaryMedium { | ||
/* */ | ||
} | ||
``` | ||
```tsx | ||
// button.tsx | ||
import React from "react"; | ||
import { cva } from "class-variance-authority"; | ||
import * as CVA from "class-variance-authority"; | ||
import { | ||
base, | ||
primary, | ||
secondary, | ||
small, | ||
medium, | ||
primaryMedium, | ||
} from "./button.css"; | ||
// ⚠️ Disclaimer: Use of Tailwind CSS is optional | ||
const button = cva(base, { | ||
variants: { | ||
intent: { | ||
primary, | ||
secondary, | ||
}, | ||
size: { | ||
small, | ||
medium, | ||
}, | ||
}, | ||
compoundVariants: [ | ||
{ intent: "primary", size: "medium", class: primaryMedium }, | ||
], | ||
defaultVariants: { | ||
intent: "primary", | ||
size: "medium", | ||
}, | ||
}); | ||
export type ButtonProps = CVA.VariantProps<typeof button>; | ||
@@ -338,7 +421,9 @@ | ||
<details> | ||
<summary>11ty (with Tailwind)</summary> | ||
<summary>React (with Tailwind)</summary> | ||
```js | ||
// button.11ty.js | ||
const { cva } = require("@joebell/cva"); | ||
```tsx | ||
// button.tsx | ||
import React from "react"; | ||
import { cva } from "class-variance-authority"; | ||
import * as CVA from "class-variance-authority"; | ||
@@ -374,5 +459,7 @@ // ⚠️ Disclaimer: Use of Tailwind CSS is optional | ||
module.exports = function ({ label, intent, size }) { | ||
return `<button class="${button({ intent, size })}">${label}</button>`; | ||
}; | ||
export type ButtonProps = CVA.VariantProps<typeof button>; | ||
export const Button: React.FC<ButtonProps> = ({ intent, size, ...props }) => ( | ||
<button className={button({ intent, size })} {...props} /> | ||
); | ||
``` | ||
@@ -388,4 +475,4 @@ | ||
<script lang="ts"> | ||
import { cva } from "@joebell/cva"; | ||
import type * as CVA from "@joebell/cva"; | ||
import { cva } from "class-variance-authority"; | ||
import type * as CVA from "class-variance-authority"; | ||
@@ -443,4 +530,4 @@ const button = cva("button", { | ||
import { cva } from "@joebell/cva"; | ||
import type * as CVA from "@joebell/cva"; | ||
import { cva } from "class-variance-authority"; | ||
import type * as CVA from "class-variance-authority"; | ||
@@ -509,1 +596,30 @@ const button = cva("button", { | ||
</details> | ||
### Other Use Cases | ||
Although primarily designed for handling class names, at its core, `cva` is really just a fancy way of managing a string… | ||
<details> | ||
<summary>Dynamic Text Content</summary> | ||
```ts | ||
const greeter = cva("Good morning!", { | ||
variants: { | ||
isLoggedIn: { | ||
true: "Here's a secret only logged in users can see", | ||
false: "Log in to find out more…", | ||
}, | ||
}, | ||
defaultVariants: { | ||
isLoggedIn: "false", | ||
}, | ||
}); | ||
greeter(); | ||
// => "Good morning! Log in to find out more…" | ||
greeter({ isLoggedIn: "true" }); | ||
// => "Good morning! Here's a secret only logged in users can see" | ||
``` | ||
</details> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Empty package
Supply chain riskPackage does not contain any code. It may be removed, is name squatting, or the result of a faulty package publish.
Found 1 instance in 1 package
32965
16
8
106
609
0