CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tailwind-variants

First-class variant API for Tailwind CSS that enables developers to create reusable component styles with type-safe variants, slots support, and automatic conflict resolution

Pending
Overview
Eval results
Files

core-api.mddocs/

Core Variant API

The core variant API provides the main functionality for creating styled components with variants, slots, compound variants, and default values. This is the primary interface for building reusable component styles.

Main Functions

tv

The primary function for creating variant-enabled styled components.

const tv: TV;

The tv function accepts a configuration object and returns a styled component function that can be called with variant props to generate class names.

createTV

Creates a custom tv instance with specific configuration.

function createTV(config: TVConfig): TV;

Parameters:

  • config - Configuration object for tailwind-merge integration

Returns: A tv function instance with the specified configuration

TV Configuration Options

The tv function accepts a configuration object with the following options:

interface TVOptions<V, S, B, EV, ES> {
  extend?: TVReturnType;
  base?: B;
  slots?: S;
  variants?: V;
  compoundVariants?: TVCompoundVariants<V, S, B, EV, ES>[];
  compoundSlots?: TVCompoundSlots<V, S, B>[];
  defaultVariants?: TVDefaultVariants<V, S, EV, ES>;
}

base

Base classes applied to all variants of the component.

const button = tv({
  base: "font-medium rounded-lg transition-colors focus:outline-none focus:ring-2"
});

variants

Named variant groups with their corresponding classes.

const button = tv({
  base: "font-medium rounded-lg",
  variants: {
    color: {
      primary: "bg-blue-500 text-white hover:bg-blue-600",
      secondary: "bg-gray-500 text-white hover:bg-gray-600",
      danger: "bg-red-500 text-white hover:bg-red-600",
    },
    size: {
      sm: "px-3 py-1.5 text-sm",
      md: "px-4 py-2 text-base",
      lg: "px-6 py-3 text-lg",
    },
    disabled: {
      true: "opacity-50 cursor-not-allowed",
      false: "cursor-pointer",
    },
  },
});

slots

Named component parts that can each have their own styling variants.

const card = tv({
  slots: {
    base: "rounded-lg border shadow-sm",
    header: "px-6 py-4 border-b",
    body: "px-6 py-4",
    footer: "px-6 py-4 border-t bg-gray-50",
  },
  variants: {
    size: {
      sm: {
        base: "max-w-sm",
        header: "px-4 py-2 text-sm",
        body: "px-4 py-2 text-sm", 
        footer: "px-4 py-2 text-sm",
      },
      lg: {
        base: "max-w-4xl",
        header: "px-8 py-6 text-xl",
        body: "px-8 py-6",
        footer: "px-8 py-6",
      },
    },
  },
});

// Usage with slots
const { base, header, body, footer } = card({ size: "lg" });

compoundVariants

Conditional styles applied when specific variant combinations are active.

const button = tv({
  base: "font-medium rounded-lg",
  variants: {
    color: {
      primary: "bg-blue-500 text-white",
      secondary: "bg-gray-500 text-white",
    },
    size: {
      sm: "px-3 py-1.5 text-sm",
      lg: "px-6 py-3 text-lg",
    },
  },
  compoundVariants: [
    {
      color: "primary",
      size: "lg",
      class: "shadow-lg shadow-blue-500/25",
    },
    {
      color: ["primary", "secondary"],
      size: "sm",
      class: "font-semibold",
    },
  ],
});

compoundSlots

Conditional slot styles applied when specific variant combinations are active.

const card = tv({
  slots: {
    base: "rounded-lg border",
    header: "px-6 py-4 border-b",
  },
  variants: {
    color: {
      primary: { base: "border-blue-200" },
      danger: { base: "border-red-200" },
    },
  },
  compoundSlots: [
    {
      color: "primary",
      slots: ["header"],
      class: "bg-blue-50 text-blue-900",
    },
  ],
});

defaultVariants

Default variant values applied when no specific variants are provided.

const button = tv({
  variants: {
    color: {
      primary: "bg-blue-500",
      secondary: "bg-gray-500",
    },
    size: {
      sm: "text-sm px-3 py-1.5",
      md: "text-base px-4 py-2",
    },
  },
  defaultVariants: {
    color: "primary",
    size: "md",
  },
});

// Uses default variants (primary, md)
const defaultButton = button();

extend

Compose and extend existing tv components.

const baseButton = tv({
  base: "font-medium rounded-lg",
  variants: {
    size: {
      sm: "px-3 py-1.5 text-sm",
      md: "px-4 py-2 text-base",
    },
  },
});

const iconButton = tv({
  extend: baseButton,
  base: "inline-flex items-center gap-2",
  variants: {
    iconPosition: {
      left: "flex-row",
      right: "flex-row-reverse",
    },
  },
});

Usage Examples

Basic Component

const alert = tv({
  base: "p-4 rounded-md border",
  variants: {
    variant: {
      info: "bg-blue-50 border-blue-200 text-blue-800",
      success: "bg-green-50 border-green-200 text-green-800",
      warning: "bg-yellow-50 border-yellow-200 text-yellow-800",
      error: "bg-red-50 border-red-200 text-red-800",
    },
  },
  defaultVariants: {
    variant: "info",
  },
});

// Usage
const infoAlert = alert(); // Uses default 'info' variant
const errorAlert = alert({ variant: "error" });

Multi-Slot Component

const modal = tv({
  slots: {
    overlay: "fixed inset-0 bg-black/50 flex items-center justify-center",
    content: "bg-white rounded-lg shadow-xl max-w-md w-full mx-4",
    header: "px-6 py-4 border-b border-gray-200",
    body: "px-6 py-4",
    footer: "px-6 py-4 border-t border-gray-200 flex justify-end gap-3",
  },
  variants: {
    size: {
      sm: {
        content: "max-w-sm",
        header: "px-4 py-3 text-sm",
        body: "px-4 py-3 text-sm",
        footer: "px-4 py-3",
      },
      lg: {
        content: "max-w-2xl",
        header: "px-8 py-6 text-xl",
        body: "px-8 py-6",
        footer: "px-8 py-6",
      },
    },
  },
});

// Usage
const { overlay, content, header, body, footer } = modal({ size: "lg" });

Type Definitions

type TVReturnType<V extends TVVariants<S>, S extends TVSlots, B extends ClassValue, EV extends TVVariants<ES>, ES extends TVSlots, E extends TVReturnType = undefined> = {
  (props?: TVProps<V, S, EV, ES>): HasSlots<S, ES> extends true
    ? {
        [K in keyof (ES extends undefined ? {} : ES)]: (slotProps?: TVProps<V, S, EV, ES>) => string;
      } & {
        [K in keyof (S extends undefined ? {} : S)]: (slotProps?: TVProps<V, S, EV, ES>) => string;
      } & {
        [K in TVSlotsWithBase<{}, B>]: (slotProps?: TVProps<V, S, EV, ES>) => string;
      }
    : string;
} & TVReturnProps<V, S, B, EV, ES, E>;

type TVProps<V extends TVVariants<S>, S extends TVSlots, EV extends TVVariants<ES>, ES extends TVSlots> = 
  EV extends undefined
    ? V extends undefined
      ? ClassProp<ClassValue>
      : {
          [K in keyof V]?: StringToBoolean<keyof V[K]> | undefined;
        } & ClassProp<ClassValue>
    : V extends undefined
      ? {
          [K in keyof EV]?: StringToBoolean<keyof EV[K]> | undefined;
        } & ClassProp<ClassValue>
      : {
          [K in keyof V | keyof EV]?:
            | (K extends keyof V ? StringToBoolean<keyof V[K]> : never)
            | (K extends keyof EV ? StringToBoolean<keyof EV[K]> : never)
            | undefined;
        } & ClassProp<ClassValue>;

type TVCompoundVariants<V extends TVVariants<S>, S extends TVSlots, B extends ClassValue, EV extends TVVariants<ES>, ES extends TVSlots> = Array<
  {
    [K in keyof V | keyof EV]?:
      | (K extends keyof V ? StringToBoolean<keyof V[K]> : never)
      | (K extends keyof EV ? StringToBoolean<keyof EV[K]> : never)
      | (K extends keyof V ? StringToBoolean<keyof V[K]>[] : never);
  } & ClassProp<SlotsClassValue<S, B> | ClassValue>
>;

type TVCompoundSlots<V extends TVVariants<S>, S extends TVSlots, B extends ClassValue> = Array<
  V extends undefined
    ? {
        slots: Array<TVSlotsWithBase<S, B>>;
      } & ClassProp
    : {
        slots: Array<TVSlotsWithBase<S, B>>;
      } & {
        [K in keyof V]?: StringToBoolean<keyof V[K]> | StringToBoolean<keyof V[K]>[];
      } & ClassProp
>;

type TVDefaultVariants<V extends TVVariants<S>, S extends TVSlots, EV extends TVVariants<ES>, ES extends TVSlots> = {
  [K in keyof V | keyof EV]?:
    | (K extends keyof V ? StringToBoolean<keyof V[K]> : never)
    | (K extends keyof EV ? StringToBoolean<keyof EV[K]> : never);
};

Install with Tessl CLI

npx tessl i tessl/npm-tailwind-variants

docs

core-api.md

index.md

lite-mode.md

utilities.md

tile.json