Skip to content

jonambas/panda-plugin-crv

Repository files navigation

panda-plugin-crv

A Panda CSS plugin for responsive cva variants. This plugin allows you to to use Panda's responsive syntax, without config recipes, such as:

<Component variant={{ base: 'secondary', lg: 'primary' }} />

Installation

npm i panda-plugin-crv
// panda.config.ts

import { defineConfig } from '@pandacss/dev';
import { pluginResponsiveVariants } from 'panda-plugin-crv';

export default defineConfig({
  plugins: [pluginResponsiveVariants()],
});

Usage

This plugin removes the boilerplate required to build responsive variants within cva. This allows you to (1) co-locate variants near your components instead of in Panda's config, and (2) generate atomic class names.

Example component styles:

import { crv, cva } from '@/styled-system/css';

const styles = cva({
  variants: {
    // Create responsive variants
    ...crv('variant', {
      primary: { bg: 'blue.500' },
      secondary: { bg: 'gray.500' },
      destructive: { bg: 'red.500' },
    }),
  },
});

This plugins ships two helpers to parse responsive variants in your components.

  • ResponsiveVariant – creates appropriate types, with your theme's breakpoints
  • splitResponsiveVariant – translates the incoming responsive object into variants generated by crv
import {
  splitResponsiveVariant,
  type ResponsiveVariant,
} from '@/styled-system/css';

type ComponentProps = PropsWithChildren<{
  variant?: ResponsiveVariant<'primary' | 'secondary' | 'destructive'>;
}>;

const Component: FC<ComponentProps> = (props) => {
  const { children, variant = 'secondary' } = props;
  const variants = splitResponsiveVariant('variant', variant);

  return <div className={styles({ ...variants })}>{children}</div>;
};

Using your component will look like this:

<Component variant="primary" />
<Component variant={{ base: 'secondary', lg: 'primary' }} />

Compound Variants (experimental)

This plugin supports responsive compound variants, through the ccv function.

import { ccv, crv, cva } from '@/styled-system/css';

const styles = cva({
  variants: {
    ...crv('variant1', {
      primary: { bg: 'blue.500' },
      secondary: { bg: 'gray.500' },
      destructive: { bg: 'red.500' },
    }),
    ...crv('variant2', {
      true: { opacity: 1 },
      false: { opacity: 0 },
    }),
  },
  compoundVariants: [
    // Create compound variants
    ...ccv({
      variant1: 'primary',
      variant2: true,
      css: {
        color: 'green.500',
      },
    }),
  ],
});

The above code will render "green.500" if variant1 is "primary" and if variant2 is true

<Component variant1="primary" variant2>
// -> opacity_1 bg_blue.500 text_green.500

<Component
  variant1={{ base: 'secondary', lg: 'primary' }}
  variant2={{ base: false, lg: true }}
/>
// -> opacity_0 lg:opacity_1 lg:bg_blue.500 bg_gray.500 lg:text_green.500

Note: Compound variants with ccv don't infer a variants' value unless the same keys are specified for all variants used in the compound variant. For example:

// βœ… `md` is specified on both
<Component variant1={{ base: 'secondary', md: 'primary' }} variant2={{ base: 'false', md: true  }}>

// ⛔️ `variant1` at `md` is inferred, this won't work
<Component variant1="primary" variant2={{ base: 'false', md: true  }}>

Should I use this plugin?

This plugin solves a specific use case that Panda's config recipes and atomic (cva) recipes do not cover. Generally, if you maintain a component library, and need to support responsive variants, with responsive compound variants, this plugin is for you.

See Panda's documenation on config recipes vs. atomic recipes (cva).

config cva cva + crv
Theme tokens, utilities, conditions βœ… βœ… βœ…
Generated JIT βœ… ❌ ❌
Preset sharing βœ… ❌ ❌
Colocation ❌ βœ… βœ…
Atomic classes ❌ βœ… βœ…
Can be composed at runtime ❌ βœ… βœ…
Responsive variants βœ… ❌ βœ…
Responsive compound variants ❌ ❌ βœ…

Current Limitations

  • The plugin generates variants for all breakpoints defined in your theme, and does not include Panda's generated breakpoints, such as mdDown, mdOnly, mdToLg.
  • There is currently no way to limit or pick which breakpoints you wish to generate.