Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pages/docs/api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ type TVOptions = {
slots?: Record<string, ClassValue>;
variants?: Record<string, Record<string, ClassValue>>;
defaultVariants?: Record<string, ClassValue>;
requiredVariants?: string[];
compoundVariants?: Array<Record<string, string> & ClassProp>;
compoundSlots?: Array<Record<string, string> & ClassProp>;
};
Expand Down Expand Up @@ -205,6 +206,16 @@ To learn more about variants and how to use them, check out the [Variants](/docs

To learn more about default variants and how to use them, check out the [Default Variants](/docs/variants#default-variants) page.

#### `requiredVariants`

**description:** This property allows you to define which variants must be provided at both runtime and TypeScript level.

**type:** `string[]` | `undefined`

**default**: `undefined`

To learn more about required variants and how to use them, check out the [Required Variants](/docs/variants#required-variants) page.

#### `compoundVariants`

**description:** This property allows you to define the compound variants for the component.
Expand Down
55 changes: 0 additions & 55 deletions pages/docs/typescript.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -55,58 +55,3 @@ export const Button = (props: ButtonProps) => {
return <button className={button(props)}>{props.children}</button>;
};
```

### Required variants

**Tailwind Variants** doesn't offer a way to make variants required yet, but you can use TypeScript's [Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html) to achieve this.

```tsx copy /VariantProps/
import { tv, type VariantProps } from 'tailwind-variants'

const buttonStyles = tv({
base: "px-4 py-1.5 rounded-full hover:opacity-80",
variants: {
color: {
primary: "bg-blue-500 text-white",
neutral: "bg-zinc-500 text-black dark:text-white",
},
requiredFlat: {
true: "bg-transparent",
false: "bg-white",
},
},
defaultVariants: {
color: "primary",
},
compoundVariants: [...],
});

/**
* Result:
* color?: "primary" | "neutral"
* flat?: boolean
*/
type ButtonVariants = VariantProps<typeof buttonStyles>

export interface ButtonProps
extends Omit<ButtonVariants, "requiredFlat">,
Required<Pick<ButtonVariants, "requiredFlat">> {}

export const button = (props: ButtonProps) => buttonStyles(props);

// ❌ TypeScript Error:
// Argument of type "{}": is not assignable to parameter of type "ButtonProps".
// Property "requiredFlat" is missing in type "{}" but required in type "ButtonProps".
button({});

// ✅
button({ requiredFlat: true });
```

<Callout type="info">
<div className="text-sm">
This sample is taken from the "Required Variants" section of the
[CVA](https://github.com/joe-bell/cva) project on Github, which was
developed by [Joe Bell](https://github.com/joe-bell).
</div>
</Callout>
47 changes: 47 additions & 0 deletions pages/docs/variants.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,53 @@ button({ color: 'success', size: 'lg' });
*/
```

### Required variants

You can enforce that certain variants must be provided at both runtime and TypeScript level by using the `requiredVariants` property. This helps prevent missing critical variant configurations.

```js copy /requiredVariants/
import { tv } from 'tailwind-variants';

const button = tv({
base: 'font-semibold text-white py-2 px-4 rounded-full active:opacity-80',
variants: {
intent: {
primary: 'bg-blue-500 hover:bg-blue-700',
secondary: 'bg-gray-500 hover:bg-gray-700',
danger: 'bg-red-500 hover:bg-red-700'
},
size: {
sm: 'text-sm py-1 px-3',
md: 'text-md py-2 px-4',
lg: 'text-lg py-3 px-6'
}
},
requiredVariants: ['intent']
});

// ✅ Valid - intent is provided
button({ intent: 'primary', size: 'md' });
/**
* Result:
* font-semibold text-white py-2 px-4 rounded-full active:opacity-80 bg-blue-500 hover:bg-blue-700
*/

// ❌ TypeScript Error & Runtime Error - intent is missing
button({ size: 'md' });
/**
* Runtime Error:
* Tailwind Variants: Missing required variant(s): intent
*/
```

<Callout type="warning" emoji="⚠️">
<div className="text-sm">
Required variants provide both TypeScript type checking and runtime
validation. If a required variant is not provided, an error will be thrown
at runtime.
</div>
</Callout>

### Boolean variants

You can also add boolean variants to a component. This is useful when you want to add a state variant e.g. `disabled`.
Expand Down