Introducción
Contact Form 7 (CF7) es uno de los formularios más usados en WordPress. Por defecto su estética depende del tema y de estilos básicos del plugin, por lo que casi siempre conviene personalizarlo con CSS para integrarlo visualmente con el resto del sitio. Este artículo explica, con todo lujo de detalles, cómo estilizar formularios creados con Contact Form 7 solo con CSS. No se requieren modificaciones en PHP ni plugins adicionales: solo CSS y conocimiento de la estructura HTML que CF7 genera.
Requisitos y buenas prácticas previas
- Identificar el formulario que quieres estilizar: añade una clase personalizada al shortcode del formulario si quieres limitar los estilos a ese formulario concreto. Ejemplo: [contact-form-7 id=123 html_class=mi-formulario]. Esto evita conflictos con otros formularios.
- Dónde colocar el CSS: en Apariencia → Personalizar → CSS adicional o en el style.css de un child theme o mediante un plugin de CSS personalizado. Evita modificar archivos del tema padre.
- Usa la mínima especificidad necesaria y procura no abusar de !important. Si necesitas mayor especificidad, añade una clase contenedora (por ejemplo .mi-formulario) y prefíjala a los selectores.
- Comprueba la accesibilidad: mantén contraste suficiente, estilos de foco visibles y etiquetas legibles.
Entendiendo la estructura HTML que genera CF7
CF7 genera estructuras como estas (simplificado):
Las clases principales a recordar: .wpcf7-form, .wpcf7-form-control, .wpcf7-text, .wpcf7-textarea, .wpcf7-submit, .wpcf7-form-control-wrap, .wpcf7-list-item y .wpcf7-response-output. Además hay clases que CF7 añade cuando hay errores: .wpcf7-not-valid, y para los mensajes .wpcf7-mail-sent-ok, .wpcf7-validation-errors.
Variables CSS y reset base
Empezar con variables facilita cambiar rápidamente colores y radios. Incluye un pequeño reset para inputs y textareas.
:root{
--cf7-bg:#ffffff
--cf7-border:#d0d5da
--cf7-accent:#1366d6
--cf7-accent-600:#0f53b0
--cf7-text:#1f2630
--cf7-radius:8px
--cf7-transition:.18s ease
}
/ Scope recommended: si usas la clase html_class en el shortcode, reemplaza .mi-formulario por tu clase /
.mi-formulario .wpcf7-form,
.wpcf7-form {
font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial
color:var(--cf7-text)
}
/ Reset visual básico /
.mi-formulario .wpcf7-form-control,
.wpcf7-form .wpcf7-form-control {
box-sizing:border-box
width:100%
margin:0
padding:0
border:0
background:transparent
}
Estilizar inputs, textarea y select
Crear inputs coherentes: fondo, borde, radio, padding y focus. Controla placeholder y estados deshabilitados.
/ Inputs y textarea /
.mi-formulario .wpcf7-form-control-wrap input[type=text],
.mi-formulario .wpcf7-form-control-wrap input[type=email],
.mi-formulario .wpcf7-form-control-wrap input[type=tel],
.mi-formulario .wpcf7-form-control-wrap input[type=url],
.mi-formulario .wpcf7-form-control-wrap textarea,
.mi-formulario .wpcf7-form-control-wrap select {
background:var(--cf7-bg)
border:1px solid var(--cf7-border)
border-radius:var(--cf7-radius)
padding:12px 14px
font-size:15px
color:var(--cf7-text)
transition:border-color var(--cf7-transition), box-shadow var(--cf7-transition), transform var(--cf7-transition)
min-height:44px
}
/ Textarea específico /
.mi-formulario .wpcf7-textarea {
min-height:120px
resize:vertical
padding-top:12px
}
/ Placeholder /
.mi-formulario .wpcf7-form-control::placeholder {
color: #9aa3b2
}
/ Disabled /
.mi-formulario .wpcf7-form-control[disabled],
.mi-formulario .wpcf7-form-control[readonly] {
opacity:.6
cursor:not-allowed
}
Focus, validación visual y mensajes de error
Señala el foco para accesibilidad y mejora las validaciones que CF7 expone con clases.
/ Focus accesible /
.mi-formulario .wpcf7-form-control:focus {
outline:none
border-color:var(--cf7-accent)
box-shadow:0 0 0 4px rgba(19,102,214,0.12)
}
/ Estado inválido marcado por CF7 /
.mi-formulario .wpcf7-not-valid,
.mi-formulario .wpcf7-not-valid .wpcf7-form-control,
.mi-formulario .wpcf7-form-control.wpcf7-not-valid {
border-color:#e24b4b !important
box-shadow:0 0 0 4px rgba(226,75,75,0.10)
}
/ Mensajes de respuesta (éxito / error) /
.mi-formulario .wpcf7-response-output {
margin-top:12px
padding:12px 14px
border-radius:6px
font-size:14px
display:none / CF7 muestra/oculta con inline styles esto asegura el estilo base /
}
.mi-formulario .wpcf7-mail-sent-ok.wpcf7-response-output {
background:#e6f4ea
border:1px solid #c6ecd3
color:#116629
display:block
}
.mi-formulario .wpcf7-validation-errors.wpcf7-response-output {
background:#fff0f0
border:1px solid #f3c2c2
color:#8a1f1f
display:block
}
Botón de envío (submit)
Diseña un botón coherente con microinteracciones (hover, active) y variaciones con icono.
.mi-formulario .wpcf7-submit {
display:inline-block
background:var(--cf7-accent)
color:#fff
border:0
padding:12px 20px
border-radius:999px
font-weight:600
cursor:pointer
transition:transform var(--cf7-transition), background-color var(--cf7-transition), box-shadow var(--cf7-transition)
box-shadow:0 6px 18px rgba(13,40,90,0.08)
}
/ Hover / Active /
.mi-formulario .wpcf7-submit:hover {
background:var(--cf7-accent-600)
transform:translateY(-2px)
}
.mi-formulario .wpcf7-submit:active {
transform:translateY(0)
box-shadow:none
}
/ Envío deshabilitado /
.mi-formulario .wpcf7-submit[disabled] {
opacity:.6
cursor:not-allowed
transform:none
box-shadow:none
}
Checkboxes y radios personalizados
CF7 suele envolver checkboxes/radios en etiquetas. La técnica consiste en ocultar el input y dibujar un indicador con pseudo-elementos.
/ Estilos base para la lista /
.mi-formulario .wpcf7-list-item {
display:flex
align-items:center
margin-bottom:8px
}
/ Ocultar control nativo pero mantener accesibilidad /
.mi-formulario .wpcf7-list-item input[type=checkbox],
.mi-formulario .wpcf7-list-item input[type=radio] {
position:absolute
opacity:0
width:1px
height:1px
margin:-1px
clip:rect(0 0 0 0)
overflow:hidden
}
/ Indicador visual /
.mi-formulario .wpcf7-list-item .wpcf7-list-item-label {
position:relative
padding-left:34px
cursor:pointer
display:inline-block
line-height:1.2
}
/ Caja visible para checkbox/radio /
.mi-formulario .wpcf7-list-item .wpcf7-list-item-label::before{
content:
position:absolute
left:0
top:50%
transform:translateY(-50%)
width:20px
height:20px
border:1px solid var(--cf7-border)
border-radius:6px
background:#fff
box-sizing:border-box
}
/ Marca interna para checked /
.mi-formulario .wpcf7-list-item input[type=checkbox]:checked .wpcf7-list-item-label::after {
content:
position:absolute
left:5px
top:50%
transform:translateY(-50%) rotate(45deg)
width:6px
height:10px
border:solid var(--cf7-accent)
border-width:0 2px 2px 0
border-radius:1px
}
/ Radio: forma circular /
.mi-formulario .wpcf7-list-item input[type=radio] .wpcf7-list-item-label::before{
border-radius:50%
}
.mi-formulario .wpcf7-list-item input[type=radio]:checked .wpcf7-list-item-label::after{
content:
position:absolute
left:6px
top:50%
transform:translateY(-50%)
width:8px
height:8px
background:var(--cf7-accent)
border-radius:50%
}
Iconos dentro del input (sin JS)
Usa pseudo-elementos en el contenedor para añadir iconos (fonts o SVG en background). Ejemplo: icono de correo en campo email.
.mi-formulario .wpcf7-form-control-wrap.-icon {
position:relative
}
.mi-formulario .wpcf7-form-control-wrap.-icon input {
padding-left:44px
}
.mi-formulario .wpcf7-form-control-wrap.-icon::before{
content:
position:absolute
left:14px
top:50%
transform:translateY(-50%)
width:18px
height:18px
background-image:url(/wp-content/uploads/mail-icon.svg)
background-size:18px 18px
background-repeat:no-repeat
opacity:.9
pointer-events:none
}
Layout y campos lado a lado
Para formularios con varias columnas, usa flexbox o grid. Mantén la adaptabilidad para móviles.
/ Contenedor de filas (puedes envolver campos en .form-row en el editor del formulario) /
.mi-formulario .form-row{
display:flex
gap:12px
flex-wrap:wrap
}
.mi-formulario .form-row .wpcf7-form-control-wrap{
flex:1 1 220px / flexible mín. 220px /
min-width:0
}
/ Media query para ajustar en móviles /
@media (max-width:720px){
.mi-formulario .form-row{
flex-direction:column
}
}
Estilizar el cargador y estados AJAX
CF7 puede mostrar un loader (ajax-loader.gif) y clases en envío personalízalo así:
/ Ocultar loader original y crear uno con CSS si se desea /
.mi-formulario .wpcf7-spinner {
display:inline-block
width:18px
height:18px
border-radius:50%
border:2px solid rgba(0,0,0,0.08)
border-top-color:var(--cf7-accent)
animation:spin 1s linear infinite
vertical-align:middle
}
@keyframes spin{to{transform:rotate(360deg)}}
/ Durante envío se añade clase .wpcf7-mail-sent-ng o similar el botón puede cambiar /
.mi-formulario .wpcf7-form.is-submitting .wpcf7-submit {
pointer-events:none
opacity:.8
}
Técnica avanzada: etiquetas flotantes (floating labels) solo CSS
Si tu marcado coloca la etiqueta antes o después del control puedes usar la técnica con :placeholder-shown. Requiere placeholder vacío en inputs para activarse correctamente en algunos navegadores.
/ Estructura esperada: label input dentro de .floating /
.mi-formulario .floating {
position:relative
}
.mi-formulario .floating input,
.mi-formulario .floating textarea {
padding-top:22px
}
.mi-formulario .floating label{
position:absolute
left:12px
top:14px
font-size:13px
color:#6b7280
transition:transform .18s ease, font-size .18s ease, top .18s ease
pointer-events:none
}
/ Si el input tiene contenido o focus, mueve la etiqueta arriba /
.mi-formulario .floating input:focus label,
.mi-formulario .floating input:not(:placeholder-shown) label,
.mi-formulario .floating textarea:focus label,
.mi-formulario .floating textarea:not(:placeholder-shown) label {
top:-8px
font-size:12px
transform:translateY(0)
color:var(--cf7-accent)
}
Depuración y problemas comunes
- Si los estilos no se aplican, comprueba la especificidad: prueba prefijar con la clase de wrapper (.mi-formulario .wpcf7-form …).
- CF7 a veces añade estilos en línea al mostrar mensajes tu CSS debería apuntar a las clases (.wpcf7-response-output) y no confiar en estilos inline inesperados.
- Si tu tema ya aplica estilos agresivos, inspecciona con las herramientas de desarrollador y utiliza selectores más concretos en lugar de !important.
- Comprueba la salida HTML real del formulario (puede variar según campos). Adaptar selectores a la estructura real es esencial.
Checklist de accesibilidad (imprescindible)
- Los campos deben tener labels asociados o aria-labels legibles por screen readers.
- Mantén contraste suficiente entre texto y fondo.
- Proporciona un indicador visual claro para el foco (no eliminar outline sin reemplazarlo por otro visible).
- Asegura que los elementos ocultos por CSS sigan accesibles si son necesarios (ej., no usar display:none en inputs que deben ser leídos por el lector).
- Las validaciones deben indicar el campo con error y ofrecer mensaje claro de corrección.
Ejemplo completo de CSS listo para pegar
Este bloque es un ejemplo funcional y completo que combina muchas de las técnicas anteriores. Pégalo en el CSS adicional y cambia .mi-formulario por la clase de tu formulario si aplicaste html_class en el shortcode.
:root{
--cf7-bg:#ffffff
--cf7-border:#e1e6ec
--cf7-accent:#0066cc
--cf7-accent-600:#0055aa
--cf7-text:#0f1720
--cf7-radius:8px
--cf7-transition:.18s ease
}
.mi-formulario .wpcf7-form{
font-family:system-ui, -apple-system, Segoe UI, Roboto, Helvetica Neue, Arial
color:var(--cf7-text)
font-size:15px
}
/ Base inputs /
.mi-formulario .wpcf7-form-control {
box-sizing:border-box
width:100%
background:var(--cf7-bg)
border:1px solid var(--cf7-border)
border-radius:var(--cf7-radius)
padding:12px 14px
transition:border-color var(--cf7-transition), box-shadow var(--cf7-transition), transform var(--cf7-transition)
}
/ Textarea /
.mi-formulario .wpcf7-textarea { min-height:120px resize:vertical }
/ Focus /
.mi-formulario .wpcf7-form-control:focus{
outline:none
border-color:var(--cf7-accent)
box-shadow:0 0 0 6px rgba(0,102,204,0.08)
}
/ Submit /
.mi-formulario .wpcf7-submit{
background:var(--cf7-accent)
color:#fff
border:0
padding:12px 20px
border-radius:999px
cursor:pointer
font-weight:600
transition:transform var(--cf7-transition), background-color var(--cf7-transition)
}
.mi-formulario .wpcf7-submit:hover{ background:var(--cf7-accent-600) transform:translateY(-2px) }
/ Validation states /
.mi-formulario .wpcf7-not-valid .wpcf7-form-control,
.mi-formulario .wpcf7-form-control.wpcf7-not-valid {
border-color:#d9534f !important
box-shadow:0 0 0 6px rgba(217,83,79,0.06)
}
/ Response output /
.mi-formulario .wpcf7-response-output{ display:none margin-top:12px padding:12px border-radius:6px font-size:14px }
.mi-formulario .wpcf7-mail-sent-ok.wpcf7-response-output{ display:block background:#eaf7ee border:1px solid #c7eed3 color:#0b5b2f }
.mi-formulario .wpcf7-validation-errors.wpcf7-response-output{ display:block background:#fff2f2 border:1px solid #f3cfcf color:#8a1f1f }
/ Custom checkboxes/radios /
.mi-formulario .wpcf7-list-item{ display:flex align-items:flex-start gap:8px margin-bottom:8px }
.mi-formulario .wpcf7-list-item input{ position:absolute opacity:0 width:1px height:1px margin:-1px clip:rect(0 0 0 0) overflow:hidden }
.mi-formulario .wpcf7-list-item .wpcf7-list-item-label{ position:relative padding-left:34px cursor:pointer }
.mi-formulario .wpcf7-list-item .wpcf7-list-item-label::before{ content: position:absolute left:0 top:50% transform:translateY(-50%) width:20px height:20px border:1px solid var(--cf7-border) border-radius:6px background:#fff }
.mi-formulario .wpcf7-list-item input[type=checkbox]:checked .wpcf7-list-item-label::after{ content: position:absolute left:5px top:50% transform:translateY(-50%) rotate(45deg) width:6px height:10px border:solid var(--cf7-accent) border-width:0 2px 2px 0 }
/ Responsive rows /
.mi-formulario .form-row{ display:flex gap:12px flex-wrap:wrap }
.mi-formulario .form-row .wpcf7-form-control-wrap{ flex:1 1 220px min-width:0 }
@media(max-width:720px){ .mi-formulario .form-row{ flex-direction:column } }
/ Spinner /
.mi-formulario .wpcf7-spinner{ width:18px height:18px border-radius:50% border:2px solid rgba(0,0,0,0.08) border-top-color:var(--cf7-accent) animation:spin 1s linear infinite }
@keyframes spin{ to{ transform:rotate(360deg) } }
Conclusión
Con CSS puedes transformar por completo el aspecto de un formulario de Contact Form 7 y adaptar su experiencia a tu diseño sin tocar PHP. Identifica la estructura del formulario, usa una clase contenedora para scope si es necesario, aplica variables CSS para mantener la coherencia y no olvides las consideraciones de accesibilidad. El ejemplo completo incluido es un buen punto de partida que puedes copiar y ajustar a tu paleta y tipografías.
Leave a Reply