An open API service indexing awesome lists of open source software.

https://github.com/jackardios/react-tailwind-variants

React Stitches-like variants API for tailwindcss classes
https://github.com/jackardios/react-tailwind-variants

classnames react stitches tailwind tailwindcss variants

Last synced: 2 months ago
JSON representation

React Stitches-like variants API for tailwindcss classes

Awesome Lists containing this project

README

          

# React Tailwind Variants

[![npm package][npm-img]][npm-url]
[![npm bundle size][bundle-size-img]][bundle-size-url]
[![Downloads][downloads-img]][downloads-url]

`Stitches.js`-like API for creating composable components. You can define a single variant, multiple variants, and even compound variants which allow you to define classes based on a combination of variants.

This is a modified version of the [`classname-variants`](https://github.com/fgnass/classname-variants/)

## Features

- 📦 Lightweight
- 📜 Fully type-safe
- 💅🏼 Elegant [Stitches-like](https://github.com/stitchesjs/stitches) variants API
- 🗑️ Automatic tailwindcss classes conflict resolution via [tailwind-merge](https://github.com/dcastil/tailwind-merge)
- ♻️ Polymorphic components via [@radix-ui/slot](https://www.radix-ui.com/docs/primitives/utilities/slot)

## Installation

```bash
npm install tailwind-merge react-tailwind-variants
```

## Usage

- [Basics](#basics)
- [Boolean variants](#boolean-variants)
- [Compound variants](#compound-variants)
- [Default variants](#default-variants)
- [Polymorphic components](#polymorphic-components)
- [Composing components](#composing-components)
- [Utilities](#utilities)
- [variants(config)](#variantsconfig)
- [variantProps(config)](#variantpropsconfig)
- [extractVariantsConfig(component)](#extractvariantsconfigcomponent)
- [Typescript utilities](#typescript-utilities)
- [VariantsConfigOf\](#variantsconfigofcomponent)
- [VariantPropsOf\](#variantpropsofcomponent)
- [Tailwind CSS IntelliSense](#tailwind-css-intellisense)

### Basics

Let's assume we want to build a button component with Tailwind CSS that comes in different sizes and colors.

It consists of some _base classes_ that are always present as well as some optional classes that need to be added depending on the desired _variants_.

```tsx
import { styled } from 'react-tailwind-variants';

const Button = styled('button', {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
```

The result is a react component:

```tsx

Click me!

```

Component will be rendered as:

```html

Click me!

```

As a value for classes, you can use a `"string"`, an `"array"` of strings, or `"null"`:

```tsx
import { styled } from 'react-tailwind-variants';

const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
```

---

### Boolean variants

Variants can be of type `boolean` by using `"true"` or/and `"false"` as the key:

```tsx
import { styled } from 'react-tailwind-variants';

const Button = styled('button', {
base: 'text-white',
variants: {
rounded: {
true: 'rounded-full',
},
},
});
```

---

### Compound variants

The `compoundVariants` option can be used to apply class names based on a combination of other variants.

```tsx
import { styled } from 'react-tailwind-variants';

const Button = styled('button', {
base: 'text-base'
variants: {
variant: {
none: null,
filled: 'bg-blue-500 text-white',
outlined: 'border border-blue-500 text-blue-500',
plain: 'bg-transparent text-blue-500',
},
size: {
sm: 'px-3 py-1.5'
md: 'px-4 py-2'
lg: 'px-6 py-3'
},
},
compoundVariants: [
{
variants: {
variant: ['filled', 'outlined'],
size: 'sm'
},
className: 'text-sm'
},
{
// `compoundVariants` className takes
// precedence over `variants`,
// so in this case the class `p-0`
// will override `padding` classes
variants: {
variant: 'none'
},
className: 'p-0'
},
],
});
```

```tsx
Outlined button
Unstyled button
```

will be rendered as:

```html

Outlined button

Unstyled button
```

---

### Default variants

The `defaultVariants` option can be used to select a variant by default.
All non-boolean variants for which no default values are specified are required.
If no default value is specified for boolean options, it evaluates to "false"

Below is an example with a component that has a required `size` and an optional `color` variants

```tsx
import { styled } from 'react-tailwind-variants';

const Button = styled('button', {
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
elevated: {
true: 'shadow',
},
},
defaultVariants: {
color: 'neutral',
},
});
```

---

### Polymorphic components

If you want to keep all the variants you have defined for a component but want to render a different HTML tag or a different custom component, you can use the `"asChild"` prop to do so:

```tsx
import { styled } from 'react-tailwind-variants';

const Button = styled('button', {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});
```

```tsx


Button as link

```

will be rendered as:

```html

Button as link

```

---

### Composing components

Composing one styled component into another.

1. Components can be composed via the `styled` function.

```tsx
import { styled } from 'react-tailwind-variants';

const BaseButton = styled('button', {
base: 'text-center bg-blue-500 text-white',
variants: {
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});

const Button = styled(BaseButton, {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
```

```tsx

Click me!

```

will be rendered as:

```html

Click me!

```

2. You can also achieve the same result using `"asChild"` prop:

```tsx
import { styled } from 'react-tailwind-variants';

const BaseButton = styled('button', {
base: 'text-center bg-blue-500 text-white',
variants: {
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});

const Button = styled('button', {
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});
```

```tsx


Click me!

```

will be rendered as:

```html

Click me!

```

---

### Utilities

#### `variants(config)`

The function accepts variants config as argument and returns a `className` builder function

```ts
import { variants } from 'react-tailwind-variants';

const buttonVariants = variants({
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});

console.log(
buttonVariants({
color: 'brand',
size: 'small',
className: 'text-sky-900 px-8',
})
);
// Console output:
// 'rounded bg-sky-500 py-3 text-xs text-sky-900 px-8'
```

#### `variantProps(config)`

The function accepts variants config as argument and returns props builder function

```ts
import { variantProps } from 'react-tailwind-variants';

const buttonVariantProps = variantProps({
base: 'rounded text-white',
variants: {
color: {
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
size: {
small: 'px-5 py-3 text-xs',
large: 'px-6 py-4 text-base',
},
},
});

console.log(
buttonVariantProps({
color: 'brand',
size: 'small',
className: 'text-sky-900 px-8',
type: 'button',
onClick: e => {
// ...
},
})
);
// Console output:
// {
// className: 'rounded bg-sky-500 py-3 text-xs text-sky-900 px-8'
// type: "button",
// onClick: ...
// }
```

#### `extractVariantsConfig(component)`

The function accepts a component from which it extracts the configuration of variants

```ts
import { styled, extractVariantsConfig } from 'react-tailwind-variants';

const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});

console.log(extractVariantsConfig(Button));
// Console output:
// {
// base: ['rounded', 'text-white'],
// variants: {
// color: {
// none: null,
// brand: 'bg-sky-500',
// accent: 'bg-teal-500',
// },
// },
// }
```

### Typescript utilities

#### `VariantsConfigOf`

A utility that allows you to extract the configuration type from the component type

```ts
import { type VariantsConfigOf, styled } from 'react-tailwind-variants';

const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});

type ButtonVariantsConfig = VariantsConfigOf;
```

#### `VariantPropsOf`

A utility that allows you to extract the variant props type from the component type

```ts
import { type VariantPropsOf, styled } from 'react-tailwind-variants';

const Button = styled('button', {
base: ['rounded', 'text-white'],
variants: {
color: {
none: null,
brand: 'bg-sky-500',
accent: 'bg-teal-500',
},
},
});

type ButtonVariantProps = VariantPropsOf;
```

### Tailwind CSS IntelliSense

In order to get auto-completion for the CSS classes themselves, you can use the [Tailwind CSS IntelliSense](https://github.com/tailwindlabs/tailwindcss-intellisense) plugin for VS Code. In order to make it recognize the strings inside your variants-config, you have to somehow mark them and configure the plugin accordingly.

One way of doing so is by using tagged template literals:

```ts
import { styled, tw } from 'react-tailwind-variants';

const Button = styled('button', {
base: tw`px-5 py-2 text-white`,
variants: {
color: {
neutral: tw`bg-slate-500 hover:bg-slate-400`,
accent: tw`bg-teal-500 hover:bg-teal-400`,
},
},
});
```

You can then add the following line to your `settings.json`:

```
"tailwindCSS.experimental.classRegex": ["tw`(\\`|[^`]+?)`"]
```

> **Note**
> The `tw` helper function is just an alias for [`String.raw`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/raw).

[npm-img]: https://img.shields.io/npm/v/react-tailwind-variants
[npm-url]: https://www.npmjs.com/package/react-tailwind-variants
[bundle-size-img]: https://img.shields.io/bundlephobia/minzip/react-tailwind-variants
[bundle-size-url]: https://bundlephobia.com/package/react-tailwind-variants
[downloads-img]: https://img.shields.io/npm/dt/react-tailwind-variants
[downloads-url]: https://www.npmtrends.com/react-tailwind-variants