CSS input file: personaliza el botón de subir archivos

El input type="file" por defecto es feo y cada navegador lo pinta a su manera. Y cuando intentas estilizarlo directo, no responde. Es uno de los controles más rebeldes del formulario. Aquí tienes el código listo para pegar para dejarlo a tu gusto.

Te enseño dos técnicas para trabajar el css input file: la moderna con ::file-selector-button y la clásica con label más input oculto, que es la más flexible y accesible. Al final añado un poco de JavaScript para mostrar el nombre del archivo elegido. Copia, pega y adapta los colores.

Por qué no puedes estilizar el input file directo

El navegador dibuja el input type="file" con un botón interno propio del sistema. Ese botón vive fuera de tu alcance con CSS normal. Puedes darle margen o borde al contenedor, pero el botón sigue con su aspecto gris de siempre.

Hay dos caminos para personalizar el input file. El primero apunta directo al botón interno con un pseudo-elemento. El segundo lo esconde y usa un label como botón visible. Los dos funcionan; elige según lo que necesites.

Solución 1: CSS para input file con ::file-selector-button

Esta es la vía moderna. El pseudo-elemento ::file-selector-button te da acceso al botón interno del control. Lo soportan todos los navegadores actuales (Chrome, Edge, Firefox y Safari). Antes cada uno usaba su prefijo; hoy el estándar ya está unificado.

Tu HTML no cambia. Sigue siendo un input normal:

<input type="file" id="archivo" name="archivo">

Y este es el CSS que estiliza el botón interno:

/* Estiliza el botón interno del input file */
input[type="file"]::file-selector-button {
  padding: 0.6rem 1.2rem;
  margin-right: 1rem;
  border: none;
  border-radius: 8px;
  background-color: #2563eb;
  color: #fff;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

input[type="file"]::file-selector-button:hover {
  background-color: #1d4ed8;
}

/* Opcional: oculta el texto "Ningún archivo seleccionado" del navegador */
input[type="file"] {
  color: #6b7280;
  font-size: 0.95rem;
}

Con esto ya tienes un botón azul con hover y esquinas redondeadas. La ventaja es que el control sigue siendo nativo: accesible, con teclado y lector de pantalla sin tocar nada más. La limitación es que solo estilizas el botón, no el texto del archivo que pinta el navegador.

Si buscas lo más rápido y no necesitas rediseñar el layout entero, usa ::file-selector-button. Es la opción con menos código.

Solución 2: label más input oculto (accesible y flexible)

Esta técnica clásica te da control total. Escondes el input real y conviertes un label en tu botón. El truco está en no usar display: none, porque eso lo saca del árbol de accesibilidad y rompe el foco por teclado.

El HTML asocia el label al input con for e id. Al hacer clic en el label, se abre el selector de archivos:

<input type="file" id="archivo" name="archivo" class="input-oculto">
<label for="archivo" class="boton-subida">Selecciona un archivo</label>

El CSS oculta el input sin romper la accesibilidad y estiliza el label como botón:

/* Oculta el input sin sacarlo del árbol de accesibilidad */
.input-oculto {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* El label se convierte en tu botón */
.boton-subida {
  display: inline-block;
  padding: 0.7rem 1.4rem;
  background-color: #2563eb;
  color: #fff;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.2s ease;
}

.boton-subida:hover {
  background-color: #1d4ed8;
}

/* Foco visible cuando el usuario navega con teclado */
.input-oculto:focus-visible + .boton-subida,
.input-oculto:focus + .boton-subida {
  outline: 3px solid #93c5fd;
  outline-offset: 2px;
}

Fíjate en el último selector. El input va antes del label en el HTML para que el combinador + aplique el foco al botón cuando el usuario llega con el tabulador. Sin ese anillo de foco, quien navega con teclado no sabe dónde está.

Con este método puedes poner iconos, cambiar tipografías o montar cualquier diseño. Es lo que uso cuando el formulario tiene que encajar con un sistema visual concreto.

Mostrar el nombre del archivo con un poco de JavaScript

Al ocultar el input con la solución 2, pierdes el texto que indica qué archivo eligió el usuario. Se recupera con unas pocas líneas de JavaScript. Es opcional, pero mejora mucho la experiencia.

Añade un span donde mostrar el nombre:

<input type="file" id="archivo" name="archivo" class="input-oculto">
<label for="archivo" class="boton-subida">Selecciona un archivo</label>
<span id="nombre-archivo" class="nombre-archivo">Ningún archivo seleccionado</span>

Y escucha el evento change para actualizar el texto:

const input = document.getElementById('archivo');
const salida = document.getElementById('nombre-archivo');

input.addEventListener('change', () => {
  if (input.files.length > 0) {
    salida.textContent = input.files[0].name;
  } else {
    salida.textContent = 'Ningún archivo seleccionado';
  }
});

Ahora, al elegir un archivo, el usuario ve su nombre junto al botón. Si quieres avisar a los lectores de pantalla del cambio, añade aria-live="polite" al span.

Accesibilidad: lo que no puedes saltarte

Personalizar el input file está bien, pero no a costa de dejar fuera a nadie. Estos son los puntos que reviso siempre:

  • No uses display: none ni visibility: hidden en el input: rompen el foco y el lector de pantalla. Usa la técnica de ocultación visual del ejemplo.
  • Asocia siempre el label con for e id. Es lo que permite abrir el selector con teclado.
  • Mantén un foco visible claro con :focus-visible. Nunca elimines el outline sin poner otro.
  • Cuida el contraste del botón. El azul del ejemplo pasa el mínimo AA sobre texto blanco.

Si quieres formularios que además conviertan, echa un vistazo a mi guía de ejemplos de formularios que enganchan al visitante. El diseño del campo de subida es solo una pieza del conjunto.

Más snippets de CSS para tus formularios

Si te gusta pulir la interfaz al detalle, estos tutoriales del blog te van a venir bien:

La referencia oficial del pseudo-elemento está en MDN: ::file-selector-button, por si quieres ver todas las propiedades que admite.

Preguntas frecuentes sobre el css input file

¿Se puede cambiar el texto del botón del input file?

Con CSS puro no. El texto del botón nativo («Elegir archivo» o «Examinar») lo controla el navegador y no es editable. Si necesitas tu propio texto, usa la solución 2 con un label, que sí escribes tú.

¿::file-selector-button funciona en todos los navegadores?

Sí, en todas las versiones modernas de Chrome, Edge, Firefox y Safari. Es el estándar que reemplazó a los antiguos prefijos como ::-webkit-file-upload-button. Para navegadores muy viejos, la técnica del label es la opción segura.

¿Por qué no debo usar display: none en el input?

Porque display: none saca el input del árbol de accesibilidad. El lector de pantalla deja de verlo y el foco por teclado se pierde. Usa la ocultación visual con clip y posición absoluta, que mantiene el control operativo.

¿Cómo muestro el nombre del archivo seleccionado?

Con CSS no se puede, necesitas JavaScript. Escucha el evento change del input y lee input.files[0].name para pintarlo en un span. Tienes el snippet completo más arriba, listo para copiar.

¿Qué técnica es mejor para personalizar el input file?

Depende. Para un retoque rápido manteniendo el control nativo, usa ::file-selector-button. Para diseño total con iconos y textos propios, usa el label con input oculto. Yo combino: label para el aspecto y JS para el nombre del archivo.

Ya tienes las dos técnicas con su código. Empieza por la que encaje con tu proyecto, prueba el foco con el tabulador y ajusta los colores. Si te ha servido, compártelo con quien esté peleándose con el mismo input.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio