WooCommerce's default dropdown selectors for variable products work, but they're not exactly inspiring. For stores selling items like body jewelry with multiple attributes (gauge, size, material), dropdowns create friction. We built Variably Pretty to solve this with an elegant, modern approach.

The Problem: Dropdown Fatigue

Traditional WooCommerce variable product selection looks like this:

  • Hidden Options: Customers must click to see what's available
  • Multiple Clicks: Each attribute requires opening a dropdown
  • Poor Mobile UX: Dropdowns are clunky on touchscreens
  • No Visual Feedback: Can't see all options at once
  • Confusing Availability: Customers don't know which combinations work until they try

For a body jewelry store, imagine someone trying to buy a hinge ring. They need to select gauge AND size. With dropdowns, they might pick 18g, then open the size dropdown only to find their preferred 8mm isn't available for that gauge. Frustrating!

Our Solution: Pill-Style Selectors

Variably Pretty replaces dropdowns with clickable "pills" - rounded buttons that display all options at once. When you select one attribute, incompatible options in other attributes automatically gray out.

Visual Clarity

All options visible at once - no hidden menus

Smart Availability

Incompatible options gray out automatically

Touch-Friendly

Large, tappable targets for mobile users

Native Integration

Works seamlessly with WooCommerce's variation system

Core Architecture

The plugin consists of three main components:

1. PHP Backend (Plugin Core)

The main plugin file hooks into WooCommerce's variation rendering system:

class Variably_Pretty {
    public function __construct() {
        // Replace dropdown HTML with pill HTML
        add_filter(
            'woocommerce_dropdown_variation_attribute_options_html',
            [$this, 'render_pill_variations'],
            10,
            2
        );
        
        // Enqueue frontend assets
        add_action('wp_enqueue_scripts', [$this, 'enqueue_scripts']);
        
        // Admin settings
        add_action('admin_menu', [$this, 'add_admin_menu']);
    }
}

2. Pill Rendering

Instead of returning a <select> element, we generate pill buttons:

public function render_pill_variations($html, $args) {
    if (!$this->is_enabled_for_product($product->get_id())) {
        return $html; // Use default dropdowns if not enabled
    }
    
    $pill_html = '<div class="variably-pretty-wrapper">';
    $pill_html .= '<div class="variably-pretty-label">' . $attribute_label . '</div>';
    
    foreach ($options as $option) {
        $pill_html .= sprintf(
            '<button class="variably-pretty-pill" data-value="%s">%s</button>',
            esc_attr($option),
            esc_html($option)
        );
    }
    
    // Hidden select for WooCommerce compatibility
    $pill_html .= '<select class="variably-pretty-hidden-select" style="display:none;">';
    // ... options ...
    $pill_html .= '</select>';
    
    return $pill_html;
}

Key insight: We keep a hidden <select> element to maintain compatibility with WooCommerce's JavaScript. When a pill is clicked, we update the hidden select and trigger WooCommerce's change event.

3. JavaScript Interaction Logic

The JavaScript handles pill clicks and availability updates:

class VariablyPretty {
    setupPillClicks() {
        $('.variably-pretty-pill').on('click', function() {
            const $pill = $(this);
            const $wrapper = $pill.closest('.variably-pretty-wrapper');
            const $hiddenSelect = $wrapper.find('.variably-pretty-hidden-select');
            
            // Deselect others in same group
            $wrapper.find('.variably-pretty-pill').removeClass('selected');
            
            // Select this pill
            $pill.addClass('selected');
            $hiddenSelect.val($pill.data('value')).trigger('change');
            
            // Update availability
            this.updateAvailability();
        });
    }
}

Smart Availability Logic

The most complex part is determining which options should be grayed out. Here's how it works:

Getting Variation Data

WooCommerce stores variation data in JavaScript:

const variationData = this.form.data('product_variations');
// Example data structure:
// [
//   {
//     attributes: { attribute_gauge: "18g", attribute_size: "6mm" },
//     is_in_stock: true,
//     variation_id: 123
//   },
//   ...
// ]

Checking Option Availability

For each pill, we check if any variation exists that matches the current selections plus this option:

function isOptionAvailable(attrName, optionValue, currentSelections, variations) {
    // Build test selections with this option
    const testSelections = {...currentSelections, [attrName]: optionValue};
    
    // Check if any variation matches
    for (const variation of variations) {
        if (!variation.is_in_stock) continue;
        
        let matches = true;
        for (const [key, value] of Object.entries(testSelections)) {
            const varAttrKey = 'attribute_' + key;
            const varAttrValue = variation.attributes[varAttrKey];
            
            // Variation attribute is "" (any) or matches our value
            if (varAttrValue !== '' && varAttrValue !== value) {
                matches = false;
                break;
            }
        }
        
        if (matches) return true;
    }
    
    return false; // No matching variations found
}

Example Scenario

Let's say we have these variations available:

  • 18g + 6mm
  • 18g + 7mm
  • 16g + 6mm
  • 16g + 8mm

When the customer selects 18g:

  1. Plugin checks which sizes work with 18g
  2. Finds 6mm and 7mm are available
  3. Grays out 8mm (only works with 16g)

CSS Styling

The visual design uses modern CSS with smooth transitions:

.variably-pretty-pill {
    padding: 10px 20px;
    border: 2px solid #ddd;
    border-radius: 25px;
    background: #fff;
    cursor: pointer;
    transition: all 0.2s ease;
}

.variably-pretty-pill:hover:not(.disabled) {
    border-color: #666;
    transform: translateY(-2px);
    box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}

.variably-pretty-pill.selected {
    background: #000;
    color: #fff;
    border-color: #000;
}

.variably-pretty-pill.disabled {
    opacity: 0.3;
    cursor: not-allowed;
    pointer-events: none;
}

Admin Configuration

Store owners need control over which products use pill-style variations. We added a settings page under WooCommerce:

public function settings_page() {
    $variable_products = wc_get_products([
        'type' => 'variable',
        'limit' => -1
    ]);
    
    echo '<form method="post">';
    echo '<label>';
    echo '<input type="checkbox" name="enable_all" />';
    echo 'Enable for all variable products';
    echo '</label>';
    
    foreach ($variable_products as $product) {
        echo '<label>';
        echo '<input type="checkbox" name="enabled_products[]" value="' . $product->get_id() . '" />';
        echo $product->get_name();
        echo '</label>';
    }
    
    echo '</form>';
}

Mobile Responsiveness

Pills automatically wrap and adjust size for smaller screens:

.variably-pretty-pills {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
}

@media (max-width: 768px) {
    .variably-pretty-pill {
        padding: 8px 16px;
        font-size: 13px;
    }
}

Technical Challenges

Challenge 1: WooCommerce Integration

Problem: WooCommerce expects <select> elements for its variation logic.

Solution: Keep hidden selects and sync them with pill selections. This maintains full compatibility with WooCommerce's cart, pricing, and stock systems.

Challenge 2: Performance with Many Variations

Problem: Products with 5+ attributes and 100+ variations could slow down availability checks.

Solution: Cache variation checks and only recalculate when selections change. Use efficient JavaScript with early returns.

Challenge 3: Theme Compatibility

Problem: Different themes structure variation forms differently.

Solution: Hook into WooCommerce's official filter instead of manipulating DOM. This works with any theme that follows WooCommerce standards.

Real-World Impact

For a body jewelry store selling hinge rings:

  • Reduced Selection Time: Customers see all options immediately
  • Fewer Cart Abandonments: No confusion about which combinations work
  • Better Mobile Experience: Touch-friendly pills vs tiny dropdowns
  • Increased Conversions: Clearer path to purchase

๐Ÿ’ก Pro Tip

Use clear, concise attribute names. "18g" is better than "18 gauge (1.0mm)" in pills. Put technical details in product descriptions instead.

Future Enhancements

Potential improvements for v2.0:

  • Image Swatches: Show color/pattern pills with actual images
  • Price Display: Show price changes on hover
  • Stock Indicators: Visual badges for low stock
  • Keyboard Navigation: Arrow keys to select options
  • Animation Options: Configurable transition effects

Conclusion

Variably Pretty proves that small UX improvements can have big impacts. By replacing dropdowns with pills and adding smart availability logic, we created a more intuitive shopping experience that works seamlessly with WooCommerce.

The plugin is free, open-source, and ready to use on any WooCommerce store. Whether you're selling body jewelry, fashion, electronics, or any product with variations, Variably Pretty makes selection beautiful and effortless.

๐Ÿ“ฆ Download Variably Pretty

Ready to transform your variable products? Download the plugin and try it on your store!

Complete Code Structure

variably-pretty/
โ”œโ”€โ”€ variably-pretty-plugin.php    # Main plugin file (280 lines)
โ”œโ”€โ”€ assets/
โ”‚   โ”œโ”€โ”€ style.css                 # Pill styling
โ”‚   โ””โ”€โ”€ script.js                 # Interaction logic
โ””โ”€โ”€ README.md                     # Documentation

The entire plugin is just ~280 lines of well-structured PHP and JavaScript. Sometimes the best solutions are the simplest ones.