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: nonenivisibility: hiddenen el input: rompen el foco y el lector de pantalla. Usa la técnica de ocultación visual del ejemplo. - Asocia siempre el
labelconforeid. Es lo que permite abrir el selector con teclado. - Mantén un foco visible claro con
:focus-visible. Nunca elimines eloutlinesin 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:
- Microinteracciones web con ejemplos y código: pequeños detalles que hacen que tu botón de subida se sienta vivo.
- Menús animados con CSS3: la misma lógica de transiciones aplicada a la navegación.
- Cómo hacer un slider en HTML5 y CSS3: otro control clásico que puedes estilizar a tu gusto.
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.


