Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request] Complex/Advanced Compound Variants #94

Open
randallmlough opened this issue Feb 6, 2023 · 6 comments
Open

[Feature Request] Complex/Advanced Compound Variants #94

randallmlough opened this issue Feb 6, 2023 · 6 comments
Labels
enhancement New feature or request

Comments

@randallmlough
Copy link

randallmlough commented Feb 6, 2023

Feature Request

I'm looking for more functionality around compound variants. I would love a way to use a keyed value from a component as the key for a variant in compound variants.

If the above doesn't make a lot of sense, here's an example of a workaround that I have that achieves what I am after. Hopefully, this will illustrate the problem better.

Below is a button that can have multiple states: plain text, a solid color, or outlined

<Button>Text Button</Button>
<Button solid>Solid Primary Button</Button>
<Button color="secondary" solid>Solid Secondary Button</Button>
<Button outline>Outlined Primary Button</Button>
<Button color="secondary" outline>Outlined Secondary Button</Button>

To have the above functionality my button component needs to look like this

// Note: in my project I have customized classed to leverage tailwind merge: https://tw-classed.vercel.app/docs/guide/configuring-classed#using-tailwind-merge
// import { classed } from "./ui";
import { classed } from "@tw-classed/react"

const solidVariants = {
  true: "",
  primary: "bg-indigo-600 hover:bg-indigo-700 text-white",
  secondary: "bg-gray-600 hover:bg-gray-700 text-white",
};

const Button = classed(
  "button",
  "inline-flex items-center border border-transparent font-medium",
  {
    variants: {
      color: {
        primary: "text-indigo-600",
        secondary: "text-gray-600",
      },
      solid: solidVariants,
      outline: {
        true: "",
      },
    },
    compoundVariants: [
      {
        solid: true,
        color: "primary",
        className: solidVariants["primary"],
      },
      {
        solid: true,
        color: "secondary",
        className: solidVariants["secondary"],
      },
      {
        outline: true,
        color: "primary",
        className:
          "border border-indigo-600 hover:border-indigo-700 text-indigo-600",
      },
      {
        outline: true,
        color: "secondary",
        className: "border border-gray-600 hover:border-gray-700 text-gray-600",
      },
    ],
    defaultVariants: {
      color: "primary",
    },
  }
);

As you can see, I'm abusing the compound variant array and the boolean variant attribute. In a perfect world, condensing the "solid" and "outline" variants into one would be awesome.

An idea that could solve this is to have class/className accept either a string or a function that passes a variants object and returns a string.

Example:

compoundVariants: [
      {
        solid: true,
        className: (variants) => solidVariants[variants.color],
        // variants is a key/value object
        // <Button solid> -> variants: {color: primary, solid: true}
        // <Button color="secondary" solid> -> variants: {color: secondary, solid: true}
      },
      {
        outline: true,
        className: (variants) => outlineVariants[variants.color], 
      },
]
@randallmlough randallmlough changed the title Complex/Advanced Compound Variants [Feature Request] Complex/Advanced Compound Variants Feb 6, 2023
@sannajammeh sannajammeh added the enhancement New feature or request label Feb 7, 2023
@sannajammeh
Copy link
Owner

sannajammeh commented Feb 7, 2023

I've thought about something similar. Essentially introducing Stitches.js variant serialization. It would be make your "abuse" much simpler, but I will consider this too. I like the callback idea :)

I have an advanced button component if you're looking for something similar here: https://github.com/sannajammeh/opendash/blob/main/packages/ui/src/Button.tsx

This uses plenty of compound variants. I wouldn't classify it as abuse as Stitches.js recommends the same thing. Keep in mind the template literal class names require tailwind-merge or a custom merger in classed.config.ts, otherwise the DOM will look very ugly.

@randallmlough
Copy link
Author

Cool. I looked at your example and yeah, very similar. The current process works for a couple of items, but the cracks begin to show when have a much larger variant object like the real one I have below.

const outlineCompoundVariants = [
  {
    outline: true,
    color: "primary",
    className: outlineVariants["primary"],
  },
  {
    outline: true,
    color: "primary-light",
    className: outlineVariants["primary-light"],
  },
  {
    outline: true,
    color: "primary-dark",
    className: outlineVariants["primary-dark"],
  },
  {
    outline: true,
    color: "secondary",
    className: outlineVariants["secondary"],
  },
  {
    outline: true,
    color: "secondary-light",
    className: outlineVariants["secondary-light"],
  },
  {
    outline: true,
    color: "secondary-dark",
    className: outlineVariants["secondary-dark"],
  },
  {
    outline: true,
    color: "light",
    className: outlineVariants["light"],
  },
  {
    outline: true,
    color: "dark",
    className: outlineVariants["dark"],
  },
  {
    outline: true,
    color: "success",
    className: outlineVariants["success"],
  },
  {
    outline: true,
    color: "danger",
    className: outlineVariants["danger"],
  },
];

This is just for outline styling. I have the same object size for solid colors and focus too. That said, I wouldn't recommend what I'm doing for a one-off project, but this is part of a UI package that I use across all my projects and with other people, so the need to have uniformity (along with leveraging stories) outweighs the decrease in readability.

Regardless, I acknowledge it is a monster of my own creation

@sannajammeh
Copy link
Owner

Going to start the work on this feature! Thanks for the request

@randallmlough
Copy link
Author

Hey @sannajammeh, any progress on this feature? I have some time in the next couple of weeks if I can be of any help.

@sannajammeh
Copy link
Owner

Hey @randallmlough. Apologies for the long wait time on this issue, I've been pretty swamped with work. Still going to be a bit swamped the coming weeks I'm afraid. Feel free to fork and submit a PR.

@ioss
Copy link

ioss commented Nov 16, 2023

@randallmlough couldn't you do something like:

const outlineCompoundVariants = Object.keys(outlineVariants).map((color) => ({
  outline: true,
  color,
  className: outlineVariants[color],
}));

?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

When branches are created from issues, their pull requests are automatically linked.

3 participants