joe-bell / cva

Class Variance Authority
https://cva.style
Apache License 2.0
5.46k stars 107 forks source link

Issue with `stroke-current` Class Not Applying Correctly in `cva` Function #293

Closed JustScriptin closed 1 month ago

JustScriptin commented 1 month ago

Describe the bug The stroke-current class from Tailwind CSS is not being applied correctly when used within the cva function from the class-variance-authority library. Despite being included in the base styles of cva, the stroke-current class does not appear in the final class string.

To Reproduce Steps to reproduce the behavior:

  1. Create a component using the cva function to define class variants.
  2. Include the stroke-current class in the base styles of the cva function (first argument to the cva function).
  3. Use the component in your application and inspect the rendered SVG element.
  4. Observe that the stroke-current class is missing from the final class string.

note: notice the order in my code snippet below. you will not be able to replicate the issue if you change the order. because putting stroke-current at the end fixes the issue

Expected behavior The stroke-current class should be included in the final class string generated by the cva function, allowing the SVG stroke color to inherit the current text color.

Desktop:

Additional context Here is the relevant configuration and code:

package.json:

{
  "name": "custom-knowledge",
  "version": "0.1.0",
  "engines": {
    "node": "20.10.0"
  },
  "private": true,
  "scripts": {
    "dev": "next dev --turbo",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "test": "jest",
    "lint:fix": "next lint --fix",
    "type-check": "tsc --noEmit",
    "clean": "rm -rf .next node_modules package-lock.json && npm cache clean --force && npm install"
  },
  "dependencies": {
    "@radix-ui/react-dropdown-menu": "^2.0.6",
    "@radix-ui/react-slot": "^1.0.2",
    "class-variance-authority": "^0.7.0",
    "clsx": "^2.1.1",
    "lucide-react": "^0.378.0",
    "next": "14.2.3",
    "next-themes": "^0.3.0",
    "react": "^18",
    "react-dom": "^18",
    "tailwind-merge": "^2.3.0",
    "tailwindcss-animate": "^1.0.7",
    "zod": "^3.23.8"
  },
  "devDependencies": {
    "@types/jest": "^29.5.12",
    "@types/node": "^20",
    "@types/react": "^18",
    "@types/react-dom": "^18",
    "@typescript-eslint/eslint-plugin": "^7.10.0",
    "eslint": "^8",
    "eslint-config-next": "14.2.3",
    "jest": "^29.7.0",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "ts-jest": "^29.1.3",
    "typescript": "^5"
  }
}

Component Code:

import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";

// Define the cva with stroke-current class included
const rocketIconVariants = cva(
  "fill-none stroke-current stroke-2 stroke-round stroke-linejoin-round",
  {
    variants: {
      size: {
        sm: "w-4 h-4",
        md: "w-6 h-6",
        lg: "w-8 h-8",
      },
    },
    defaultVariants: {
      size: "md",
    },
  }
);

type RocketIconProps = React.SVGProps<SVGSVGElement> &
  VariantProps<typeof rocketIconVariants>;

// Wrapper function to ensure stroke-current is always applied
const RocketIcon = ({ size, className, ...props }: RocketIconProps) => {
  const finalClassName = cn(rocketIconVariants({ size }), className);
  console.log(finalClassName); // Log the final class string for debugging
  return (
    <svg
      {...props}
      xmlns="http://www.w3.org/2000/svg"
      viewBox="0 0 24 24"
      className={finalClassName}
    >
      <path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z" />
      <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" />
      <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" />
      <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" />
    </svg>
  );
};

export default RocketIcon;

Usage Example:

<RocketIcon className="text-foreground dark:text-accent-foreground" />

Observed Behavior: The final class string output in the console:

fill-none stroke-2 stroke-linejoin-round text-foreground dark:text-accent-foreground

stroke-current is not present in the output.

Expected Behavior: The final class string should include stroke-current:

fill-none stroke-current stroke-2 stroke-linejoin-round text-foreground dark:text-accent-foreground

Additional Information:

Please help investigate why stroke-current is not being included in the final class string when using the cva function, and provide a solution.

workarounds for this issue:

  1. add the stroke="currentColor" attribute directly on the svg
  2. add it to the className on the component like this <RocketIcon className="stroke-current text-foreground dark:text-accent-foreground" />
  3. add the stroke-current tailwindcss class to the end of cva's first argument

Thank you! ps. this might be a tailwind-merge issue but I'm not entirely sure.

joe-bell commented 1 month ago

this might be a tailwind-merge issue but I'm not entirely sure.

I think it is in this case! I don't see why cva would be stripping your default classes away here; it's just joining them together

I'd reach out over there 🙏