Introducción
Este tutorial explica cómo estilizar las variaciones de producto (variations) en WooCommerce/WordPress para que se muestren como botones en lugar de un dropdown, usando únicamente CSS en la parte visual. Incluye ejemplos concretos, opciones cuando tu tema imprime select en lugar de radios y recomendaciones de accesibilidad y compatibilidad. El objetivo es que el usuario vea y seleccione cada opción como un botón independiente (píldora o segmento), manteniendo la funcionalidad nativa de selección de variación.
Requisitos previos y consideraciones
- Este método CSS funciona perfectamente cuando las variaciones se imprimen como inputs radio (o checkboxes). El CSS no puede convertir la semántica de un elemento select en múltiples botones funcionales sin cambiar el HTML o añadir JavaScript por tanto hay dos caminos: (A) si ya tienes radios, aplica sólo CSS (B) si tu tienda usa select, verás opciones (una recomendada implica un pequeño cambio PHP para emitir radios otra es una solución visual limitada con sólo CSS).
- Prioriza la accesibilidad: ocultar el input visualmente debe hacerse de manera que siga siendo accesible por teclado y lectores de pantalla.
- Prueba en dispositivos móviles y con las actualizaciones de variaciones de WooCommerce, para asegurar que la selección sigue funcionando y que el precio/imagen se actualiza según la variación seleccionada.
Paso 1 — Verifica el HTML que imprime tu tema
Ejemplos de markup que te interesan:
ltdiv class=variations>
ltdiv class=variation>
ltlabelgtColorlt/labelgt
ltdiv class=variation-radios data-attribute_name=attribute_pa_colorgt
ltinput type=radio id=pa_color_red name=attribute_pa_color value=redgt
ltlabel for=pa_color_redgtRojolt/labelgt
ltinput type=radio id=pa_color_blue name=attribute_pa_color value=bluegt
ltlabel for=pa_color_bluegtAzullt/labelgt
lt/divgt
lt/divgt
lt/divgt
Si ves un ltselectgt, tu tema imprime dropdowns. En ese caso, sigue lectura para opciones.
Paso 2 — CSS recomendado para radios que se vean como botones
La técnica más limpia: ocultar visualmente los radios pero mantenerlos accesibles y estilizar sus labels como botones. Aquí tienes una hoja de estilo completa, con estados para hover, focus, checked y disabled. Puedes pegarla en el CSS de tu tema o en Personalizar gt CSS adicional.
/ Contenedor base /
.variation-radios {
display: flex
flex-wrap: wrap
gap: 0.5rem / separación entre botones /
margin-top: 0.4rem
}
/ Ocultar el input visualmente pero mantener accesible /
.variation-radios input[type=radio] {
position: absolute
opacity: 0
width: 1px
height: 1px
margin: -1px
overflow: hidden
clip: rect(0 0 0 0)
white-space: nowrap
border: 0
padding: 0
}
/ Estilo de los labels que actúan de botones /
.variation-radios label {
display: inline-flex
align-items: center
justify-content: center
min-width: 56px
padding: 0.45rem 0.9rem
border-radius: 999px / full pill /
border: 1px solid transparent
background: #f3f4f6
color: #111827
cursor: pointer
font-size: 0.95rem
line-height: 1
transition: background .16s ease, transform .06s ease, box-shadow .12s ease
box-shadow: none
user-select: none
}
/ Hover y focus /
.variation-radios label:hover,
.variation-radios input[type=radio]:focus label {
background: #e6eef8
transform: translateY(-1px)
box-shadow: 0 1px 2px rgba(16,24,40,0.04)
outline: none
}
/ Estado seleccionado (usando el combinador cuando el input viene antes) /
.variation-radios input[type=radio]:checked label {
background: linear-gradient(180deg,#1f6feb,#1658c0)
color: #fff
border-color: rgba(0,0,0,0.06)
box-shadow: 0 4px 12px rgba(23,70,161,0.18)
transform: none
}
/ Estado disabled /
.variation-radios input[type=radio]:disabled label,
.variation-radios label[aria-disabled=true] {
opacity: 0.5
cursor: not-allowed
filter: grayscale(0.02)
}
/ Responsivo: en pantallas muy pequeñas ajustar tamaños /
@media (max-width: 420px) {
.variation-radios label {
min-width: 44px
padding: 0.36rem 0.6rem
font-size: 0.88rem
}
}
Explicaciones clave:
- El input está oculto con técnica accesible (no display:none) para preservar el foco y compatibilidad con keyboard/lectores de pantalla.
- El selector input:checked label asume que el input está justo antes del label en el DOM (estructura común). Si tu markup tiene el label antes del input, deberás ajustar el HTML o usar una estructura donde el label contenga el input.
- Para animaciones suaves, se usan transiciones cortas y colores personalizados.
Ejemplo avanzado: swatches de color (iconos dentro del label)
/ Swatch circular con color de fondo /
.variation-radios label.swatch {
padding: 6px
min-width: 36px
min-height: 36px
border-radius: 6px
background: transparent
border: 1px solid #e5e7eb
}
.variation-radios label.swatch .swatch-dot {
display: block
width: 22px
height: 22px
border-radius: 50%
border: 1px solid rgba(0,0,0,0.05)
box-shadow: inset 0 1px 0 rgba(255,255,255,0.3)
}
/ Checked style for swatch /
.variation-radios input[type=radio]:checked label.swatch {
border-color: #111827
box-shadow: 0 0 0 3px rgba(30,64,175,0.12)
}
Paso 3 — Si tu tienda imprime ltselectgt (opciones)
Si ves un ltselectgt, estas son las alternativas:
- Mejor opción (recomendada): Cambiar la salida HTML a radios en el servidor (un pequeño fragmento PHP que convierte la generación del dropdown en radios). Esto no es sólo visual: facilita la interacción, accesibilidad y control por CSS como hemos mostrado.
- Solución sólo CSS (limitada): Puedes estilizar el ltselectgt para que parezca un botón o grupo de botones, pero seguirá siendo un único control que abre un menú desplegable. No podrás mostrar varias opciones simultáneamente como botones individuales y obtener el comportamiento nativo de elegir uno sin JS/HTML adicional.
Snippet PHP opcional: imprimir radios en lugar de dropdown
Si decides emitir radios (recomendado), este snippet reemplaza el HTML del dropdown de variación por un grupo de input radios. Añádelo al functions.php de tu child theme o en un plugin de snippets.
add_filter(woocommerce_dropdown_variation_attribute_options_html, wc_variation_radio_buttons, 10, 2)
function wc_variation_radio_buttons(html, args) {
args = wp_parse_args(args, array(
options => false,
product => false,
attribute => false,
selected => false,
name => ,
))
if (empty(args[options]) empty(args[product]) empty(args[attribute])) {
return html
}
options = args[options]
product = args[product]
attribute = args[attribute]
name = args[name]
// Obtener términos del producto/atributo
terms = wc_get_product_terms(product->get_id(), attribute, array(fields => all))
if (empty(terms)) {
return html
}
container =
foreach (terms as term) {
value = esc_attr(term->slug)
id = esc_attr(attribute . _ . value)
label = esc_html(term->name)
checked = (isset(_REQUEST[attribute_ . attribute]) _REQUEST[attribute_ . attribute] === term->slug) ? checked :
container .=
container .=
}
container .=
return container
}
Este código es opcional pero transforma la estructura HTML para que el CSS anterior funcione sin problemas.
Solución CSS sólo visual para ltselectgt (limitada)
Si no puedes cambiar HTML pero quieres mejorar la apariencia del dropdown, puedes skinnear el select para que luzca más integrado y menos nativo. No da botones separados ni selección múltiple sin JS.
/ Hacer que el select parezca una píldora /
.variations select {
-webkit-appearance: none
-moz-appearance: none
appearance: none
background: #f3f4f6 url(data:image/svg xmlutf8,
Ventaja: estética mejorada. Inconveniente: sigue siendo un dropdown con el comportamiento nativo.
Integración con la lógica de variaciones de WooCommerce
- Asegúrate de que los radios usen el mismo nombre que espera WooCommerce para cada atributo (por ejemplo name=attribute_pa_color).
- WooCommerce depende de eventos JS para actualizar precios/imagen cuando cambia la variación. Si sustituyes el HTML por radios, los scripts nativos deberían seguir detectando cambios (input change), pero comprueba que no haya conflicto con temas/plugins que esperen selects. En varios casos los scripts escuchan cambios en los inputs dentro del formulario de variaciones.
Accesibilidad y pruebas
- Verifica la navegación por teclado (tab flechas según implementación) y el foco visible. No uses display:none para el input.
- Prueba con lectores de pantalla (NVDA/VoiceOver) para confirmar que las opciones se anuncian correctamente.
- Verifica comportamientos de disabled o out-of-stock (estas opciones deben mostrarse como deshabilitadas y no seleccionables).
Problemas comunes y soluciones rápidas
- Los labels no reflejan el estado seleccionado: Asegúrate de que el input aparezca antes del label en el DOM (input label) o modifica la estructura para que el label envuelva al input.
- La página no actualiza precio/imágenes: Confirma que el cambio de input dispara eventos change que WooCommerce escucha algunos temas usan selects exclusivamente y pueden necesitar ajustes JS menores. Antes de añadir JS, prueba si WooCommerce nativo responde al evento change de los radios.
- Estilos conflictivos del tema: Usa selectores lo suficientemente específicos (.variation-radios label) o !important con moderación si algo se sobrescribe por CSS del tema.
Conclusión
La manera más robusta y elegante de mostrar variaciones como botones sin JavaScript es asegurar que el HTML genere inputs radio y aplicar el CSS explicado aquí para convertir los labels en botones/píldoras visuales. Si no puedes cambiar el HTML, puedes mejorar el aspecto del dropdown con CSS, aunque con limitaciones funcionales. Siempre prueba accesibilidad y la integración con los scripts de WooCommerce para mantener la experiencia de compra completa.
Leave a Reply