How To Master Component Variants With This Simple Hack

I once took over a WooCommerce project for a high-traffic luxury brand that was a total nightmare. The previous dev and designer had no shared language. In the design files, there weren’t just buttons—there were about 20 different “rectangles with text” that looked identical but had slightly different padding. Every time the client wanted a small tweak, like changing a border-radius, it turned into a three-hour hunt through CSS selectors. We needed a way to enforce consistency, and that’s where Component Variants come into play.

My first instinct was to just throw a bunch of CSS variables at the problem and call it a day. I thought if I just defined --primary-button-bg, everything would magically align. And it worked… for about five minutes. Then we needed a “disabled” state that used the brand’s muted purple, and suddenly my simple variable system started looking like a bowl of spaghetti. The real fix wasn’t just tokens; it was grouping the logic using Component Variants in a tool like Penpot.

Why Component Variants Are The Secret To Robust Design

In Penpot, component variants allow you to take one base object and define multiple “states” or “types” without creating entirely new components. If you’ve ever built a sustainable design system, you know that the goal is to update in one place and see it everywhere. Penpot’s implementation is particularly sweet because it’s open-source and uses code-friendly structures like the W3C DTCG format for design tokens.

This is exactly how Brad Frost’s Atomic Design was meant to work. You don’t just have an “Atom”; you have an Atom that understands its context. When you build these in Penpot, you’re essentially writing the blueprint for your theme.json or your CSS architecture. Trust me on this: spending twenty minutes setting up your variants now saves you twenty hours of debugging later.

Step 1: Get Your Tokens In Order

Before you even touch a variant, you need tokens. In Penpot, you create a “Main” component, but the magic happens when you alias your colors. Instead of using a raw hex code, you use a token like color.brand.default. I’ve seen teams skip this and try to manually style every variant—total mess. Here’s a quick look at how you should structure your naming convention to keep things clean for dev handoff.

/* 
 * Example token structure for a bbioon_system setup.
 * This mirrors how Penpot exports tokens to JSON.
 */

{
  "button": {
    "primary": {
      "default": { "value": "{color.brand.main}" },
      "hover": { "value": "{color.brand.lighter}" },
      "disabled": { "value": "{color.brand.muted}" }
    }
  }
}

Step 2: Building and Scaling Component Variants

Once your tokens are set, you select your main component and click “Create Variant.” This creates a linked copy. Now, here’s the kicker: you don’t just change the color; you change the property value. I always name my properties logically—”State,” “Size,” or “Icon Position.” If you’re familiar with taming border radii with Theme.json, this workflow will feel like second nature.

In Penpot, you can even use a forward slash naming convention (e.g., Button/Primary/Default) to auto-group things. It’s incredibly intuitive. When you drag an instance of that button onto your canvas later, you get a simple dropdown menu to switch between “Default,” “Hover,” and “Disabled.” No more hunting through a list of 50 separate components.

The Bottom Line

Component variants aren’t just a “design feature”—they are a communication tool. They bridge the gap between what the designer sees and what we, as developers, actually have to code. By grouping variations under a single main component, you reduce technical debt and ensure that your UI doesn’t fall apart the next time the brand guidelines change. And that was it. Once we moved the luxury brand’s site to this system, our maintenance time dropped by nearly 60%.

Look, design systems get complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work with a clean, scalable architecture, drop me a line. I’ve probably seen your exact problem before.

How are you handling your component states? Still doing it the manual way, or have you made the switch to a variant-first workflow?

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.

Leave a Reply

Your email address will not be published. Required fields are marked *