Ya es tiempo par hablar de input type time. Hemos visto ya muchos input types, pero este va a ser el primer «picker» , es decir, el primer campo que proporciona una UI de selección, en este caso para seleccionar la hora, pues esa es la función de del type time.
Hay varias razones por las que he elegido un input time para iniciar la serie de «pickers» : para empezar es el más sencillo de todos, pero además es el que tiene mayor compatibilidad con todos los navegadores. Ojo!!, esto no quiere decir que todos lo traten o muestren igual. Nada más lejos de la realidad. Cada navegador dispondrá de un time picker que será más o menos parecido a los otros o puede que incluso ni siquiera disponga de uno y sólo permita la edición «manual». Tampoco serán iguales los disponibles en sistemas operativos de escritorio que los que ofrecen los SO para móvil.
Comparativa input type time por navegador
Como ya hemos dicho el input time tiene compatibilidad total con navegadores, sin embargo, vamos a ver qué UI ofrece cada uno de ellos.

A simple vista no hay grandes diferencias estéticas, Opera tiene una caja sin cantos redondeados y el borde más oscuro, mientras que el reloj de Edge es algo más grande. Más tarde veremos como aplicar los mismos estilos a todos ellos con CSS propio, sobreescribiendo los estilos del navegador. Sin embargo vemos que tanto Safari como Firefox carecen de dicho relojito, pues ninguno de los dos ofrece un selector de hora o timepicker.
Time selector o timepicker
Una de las ventajas de usar un input type time para establecer la hora es que los navegadores implementan UI específicas que por un lado hacen más intuitivo el campo mediante un formato predefinido( – – : – – ); y por otro, en muchos casos, facilitan dicha selección con timepickers de distinta índole proporcionados por los distintos sistemas operativos. Éstos se desplegarán al pulsar sobre el icono del reloj contenido en el propio campo o si son llamados por medio de los métodos JS disponibles para ese fin y que más tarde veremos en detalle, así como otras opciones proporcionadas por el type time.
Time selector para navegadores Windows y MacOS
Así, nos encontramos que para Windows los nevagadores que disponen de time selector usan un time picker a modo de desplegable dividido en culumnas de hora y minutos cada una de las cuales dispone de su propio scroll. Adicionalmente, podrá mostrar una tercera columna para los segundos o, dependiendo de la configuración local y/o idioma de tu navegador y/o SO, para el formato de 12 horas permitiendo elegir entre AM y PM. En ningún caso se podrán ver ambas al mismo tiempo.

En cuanto a las diferencias estéticas, de nuevo son mínimas, pero a diferencia de la interfaz del propio input, a ésta no podremos darle estilos mediante CSS, pues se trata de un elemento de sistema. Aunque curiosamente Chrome, p.ej. usa el mismo selector en MacOS, ya que por lo visto éste no ofrece un timepicker.

Time selector para navegadores Mobile
Aquí cada SO ha desarrollado su propio método de selección, mientras Android a optado por un método de entrada dual permitiendo intercambiar entre un dial y un input para teclado; iOS ha preferido usar el ya clásico método con scroll infinito que permite rotar cada una de las columnas, similares a las usadas en desktop.


Estilos CSS para input type time
<input type="time" />
Comenzaremos con unos estilos básicos para la caja y nos pondremos ya de lleno con el reloj que habrirá el timepicker:
input {
box-sizing: border-box;
height: 48px;
padding: 12px;
border-radius: 6px;
border: solid 1px #999;
outline: none;
}
[type=time] {
/*Aquí cualquier estilo específico para type time*/
}
Personalizar el reloj
Para este paso debemos atender al shadow DOM y sus pseudo-elementos para poder modificar sus componentes.
<div pseudo="-webkit-calendar-picker-indicator" id="picker" tabindex="0" aria-haspopup="menu" role="button" title="Mostrar selector de hora"></div>
Si lo inspeccionamos en el navegador, para lo cual debes tener la opción shadow DOM activada en la configuración del depurador de código (F12), veremos el selector CSS «input[type=’time’ i]::-webkit-calendar-picker-indicator» que contiene un background-image algo complejo, ya que proporciona dos versiones del mismo SVG en función de si el navegador usa el color-scheme light o dark. Nosotros lo vamos a simplificar dándole además más versatilidad.
[type=time]::-webkit-calendar-picker-indicator {
font-size: 20px;
cursor: pointer;
background-color: #66C; /*Aquí podría incluirse la función light-dark()*/
mask-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNCIgaGVpZ2h0PSIxNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMTEuOTkgMkM2LjQ3IDIgMiA2LjQ4IDIgMTJzNC40NyAxMCA5Ljk5IDEwQzE3LjUyIDIyIDIyIDE3LjUyIDIyIDEyUzE3LjUyIDIgMTEuOTkgMnpNMTIgMjBjLTQuNDIgMC04LTMuNTgtOC04czMuNTgtOCA4LTggOCAzLjU4IDggOC0zLjU4IDgtOCA4eiIvPjxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48cGF0aCBmaWxsPSIjRkZGRkZGIiBkPSJNMTIuNSA3SDExdjZsNS4yNSAzLjE1Ljc1LTEuMjMtNC41LTIuNjd6Ii8+PC9zdmc+);
mask-repeat: no-repeat; /*Usamos el SVG del reloj para enmascarar el color de fondo y le indicamos que no se repita*/
mask-size: 24px;
background-image: unset; /*Sobreescribimos el background-image del navegador para que no tenga ningún valor*/
mask-position: center;
}
El CSSar dice: Aunque por defecto el reloj es del color del texto del input, yo recomiendo usar siempre el color que estemos usando en el diseño para los CTA («Call To Action»), ya sea el de los enlaces o el de los botones. De esa forma el usuario lo identificará mejor como lo que es: un elemento interactivo.
Nuestro input type time quedará tal que así:

Estilos para la hora
El resto de atributos CSS del campo son configurables igual que cualquier otro campo: color del texto y del fondo, atributos del borde…; y además, dispones de algunos pseudo-elementos adicionales que se corresponden con el preformateado de la hora, a los que les puedes aplicar su propios atributos de background, border y/o color. Eso nos abre bastantes posibilidades en cuanto a diseño se refiere, aunque obviamente tiene sus limitaciones. P.ej. admite la propiedad filter, pero no transform. Tampoco admiten pseudo-clases como :focus o :active directamente, aunque el navegador sí las use. Las reglas que implementemos para esas pseudoclases de estado serán ignoradas, aunque sí podemos usar los estados del input. Sin embargo no recomiendo sobreescribir la implementación nativa ya que funciona muy bien, resaltando cada uno de los spinbuttons cuando reciben el foco.
Este es el shadow DOM completo en Chrome:
/*Shadow DOM root*/ <div pseudo="-internal-datetime-container" style="unicode-bidi: normal;"> <div pseudo="-webkit-datetime-edit" id="date-time-edit" datetimeformat="H:mm" style="unicode-bidi: normal;"> <div pseudo="-webkit-datetime-edit-fields-wrapper" style="unicode-bidi: normal;"> <span role="spinbutton" aria-placeholder="--" aria-valuemin="0" aria-valuemax="23" aria-label="Horas" pseudo="-webkit-datetime-edit-hour-field">--</span> <div pseudo="-webkit-datetime-edit-text" style="unicode-bidi: normal;">:</div> <span role="spinbutton" aria-placeholder="--" aria-valuemin="0" aria-valuemax="59" aria-label="Minutos" pseudo="-webkit-datetime-edit-minute-field">--</span> </div> </div> <div pseudo="-webkit-calendar-picker-indicator" id="picker" tabindex="0" aria-haspopup="menu" role="button" title="Mostrar selector de hora"></div> </div>
¿Por qué Firefox y Safari no muestran el reloj?
Básicamente porque no han implementado un timepicker, por lo que no hay nada que mostrar. Sin embargo, en el shadow DOM de Firefox sí podemos encontrar un botón, el del calendario, que comparte con el resto de input types de tipo tiempo (date, datetime-local, etc.). Firefox lo oculta de la siguiente manera:
HTML <button data-l10n-id="datetime-calendar" class="datetime-calendar-button" id="calendar-button" aria-expanded="false" aria-label="Calendario"> <svg role="none" class="datetime-calendar-button-svg" xmlns="http://www.w3.org/2000/svg" id="calendar-16" viewBox="0 0 16 16" width="16" height="16"> <path d="M13.5 2H13V1c0-.6-.4-1-1-1s-1 .4-1 1v1H5V1c0-.6-.4-1-1-1S3 .4 3 1v1h-.5C1.1 2 0 3.1 0 4.5v9C0 14.9 1.1 16 2.5 16h11c1.4 0 2.5-1.1 2.5-2.5v-9C16 3.1 14.9 2 13.5 2zm0 12.5h-11c-.6 0-1-.4-1-1V6h13v7.5c0 .6-.4 1-1 1z"></path> </svg> </button> CSS :host(:is(:disabled, :read-only, [type="time"])) .datetime-calendar-button { display: none; }
Esa regla sólo está disponible en el contexto del propio shadow DOM, no desde nuestra hoja de estilos. Aunque si, por pura curiosidad, la comentas en el inspector, verás aparecer el icono del calendario.
Shadow DOM de type time en mobile
Los navegadores móviles, a pesar de ofrecer ambos buenos selectores de hora, no disponen de shadow DOM y por lo tanto es imposible manipular su aspecto, aunque estoy seguro que muchos lo desearíais pues son feos, poco intuitivos y se confunden con un simple <select>.
Tampoco es posible ocultar la flecha con un appearance: none;
A pesar de ello, la usabilidad y accesibilidad del timepicker nativo compensa con creces esa falta de opciones de personalización del input.
Atributos y funciones de type time
Los atributos de type time son comunes a muchos otros input types. Sin embargo sus valores lo hacen único.
Atributos principales
Min Max
Determinan los límites entre los que deberá estar el valor introducido, debiéndo estar todos ellos en formato horario de 24 horas, independientemente de los mostrados por la UI, que dependerán del navegador y el SO, tal como hemos explicado.
A diferencia del resto de data types, los valores de type time, tienen un dominio periódico, volviendo al inicio al superar el máximo si min es superior a max, es decir, saltando al día siguiente.
Step
Es el número de segundos que especifica la granularidad a la que debe adherirse el valor del input, siendo su valor base el indicado por min si es especificado, value si no, o 0 en su defecto. Entendiéndose la base como el punto de partida y no como su valor por defecto que son 60 segundos (valor que por detrás es tratado en milisegundos).
Esa es la razón de que por defecto no veamos los segundos. Para mostrar la columna de los segundos es tan fácil como indicar un step inferior a 60. También se mostrará si los atributos value o min contienen segundos (p.ej.: 00:00:01). Cuando los segundos están visibles, la UI de 12h pasa a 24h, desapareciendo el indicador de AM/PM tanto del input como del timepicker.

Otro valor admitido por step es any que a efectos prácticos equivale a no indicar nada pues por defecto, tanto si usamos el time selector como si aumentamos o disminuímos el valor mediante las flechas, lo hará de 1 en 1 minuto. La diferencia radica en que any elimina cualquier restricción impuesta por min y max .
Funciones avanzadas del el campo time
A continuación vamos a ver una serie de casos prácticos exprimiendo al máximo las posibilidades que los atributos y funciones de type time nos brindan.
Sustituir el timepicker por defecto por una lista de valores
Comenzaremos por el atributo list, que nos permite asociar el <input> a un <datalist> vía su Id. Éste contendrá elementos <option>, tal como tiene un <select> pero conservando todas las propiedades, funciones y formato de un input type time. El atributo list lo convierte en una gran alternativa a un <select> con valores de hora. El único inconveniente es que dicho datalist es el sustituto del timepicker en cualquier nevegador que lo soporte, y por tanto será totalmente inútil para el resto… a menos que lo usemos sin el atributo list 😉
Seguramente ahora estarás pensando «pero sin el list no funciona». No es del todo exacto, sin el list lo que se pierde es la capacidad del navegador de asociar el input con el datalist vía Id, y poder generar su propia UI con el listado de valores para crear la interacción. Sin emargo dicha interacción la podemos simular por Javascript, ganando además compatibilidad con todos los navegadores y la capacidad de personalizar los estilos de nuestra lista. Explico cómo en el artículo HTML Option – experimentos CSS.
Pero en este artículo nos vamos a quedar con el caso estándar que nos será más útil para explicar también una de sus funciones. Dejo el código a continuación y pasámos al siguiente caso.
<input id="timepicker2" type="time" list="times"> <datalist id="times"> <option>10:00</option> <option>10:30</option> <option>11:00</option> <option>11:30</option> <option>12:00</option> <option>12:30</option> </datalist>
Botón personalizado para abir el time selector
A veces los requerimientos de diseño nos obligan a prescindir de la UI proporcionada por el navegador. Es en esos casos que la función showpicker() nos será de gran utilidad pues nos permite abrir el timepicker nativo desde fuera del input.
Veamos los pasos a seguir:
Paso 1. Esconder la UI del navegador
.customPicker ::-webkit-calendar-picker-indicator {
display: none;
}
Paso 2. Crear nuestro nuevo botón
HTML /*OnClick llamamos a la función dropPicker y le pasamos el id del input cuyo timepicker queremos abrir*/ <button type="button" onclick="dropPicker('timepicker')" title="show picker"><span class="buttonICN"></span></button> CSS .customPicker :is(input, select) { width: 126px; } .buttonICN { display: inline-block; width: 100%; height: 100%; } /*Podemos aprovechar el mismo icono que usamos para la UI nativa o usar uno propio o de alguna librería tipo gliphicons o fontawsome*/ .datalistPicker ::-webkit-calendar-picker-indicator, .customPicker .buttonICN {...} Javascript function dropPicker(id){document.getElementById(id).showPicker();}
El siguiente PEN incluye ambos casos, el datalist y el botón personalizado. El segundo caso no funciona en el PEN por estar contenido en un iframe, a menos que se ejecute en modo depuración. Lo más fácil es cópiar, pegar y probar fuera del PEN.
See the Pen
input type time – showpicker and datalist by Daniel Abril (@elcssar)
on CodePen.
La función ShowPicker() también podría usarse para mostrar el datalist. En el PEN he creado una función algo más compleja que controla la compatibilidad del navegador con la función. Para FF y Safari en concreto, que sí disponen de esa función en el prototipo de type time(aunque no la usen), he comprobado el userAgent para mostrar un alert, con la idea de poder ser sustituido por un timepicker personalizado. Pero en realidad se podría usar esa misma condición para mostrar una interfaz distinta, ocultando el botón personalizado y evitando problemas de accesibilidad y mucho trabajo. El input time aún en los casos que no dispone de timepicker dispone de una accesibiliad escelente por sí solo.
Yo considero que el selector de hora ofrecido por algunos navegadores es más un progressive enhancement , una funcionalidad de la que no todos los navegadores disponen actualmente pero que apunta a ser el estándar en el futuro, aportando una mejora, pero no suponiendo un inconveniente el hecho de que algunos no la ofrezcan.
Control secuencial con las funciones stepUp y stepDown
La interfaz de input type time permite poner el foco en cada uno de las unidades (horas, minutos y segundos) y, o bien introducir un valor mediante el teclado, o bien incrementar o disminuir cada valor mediante las flechas de éste. Esta misma interacción la podemos crear fácilmente gracias a las funciones stepUp y stepDown. Como probablemente se intuya de sus nombres, estas funciones hacen uso del atributo step que, por defecto, sabemos que equivale a 1 minuto (60s).
La diferencia con la interacción estándar estriba en que no disponemos del foco sobre cada uno de los spinbuttons que representas las distintas unidades. Por lo tanto, si queremos movernos por las horas, step deberá tener un valor superior o igual a una hora, p.ej. 3600 segundos. Si necesitamos que step mantenga un valor inferior, podemos alternativamente multiplicar su valor. Esto es posible ya que ambas funciones admiten un parámetro que hace las veces de multiplicador del valor de step.
El siguiente código incrementa los minutos de 15 en 15.
<input id="timepicker" type="time" step="900"/>
<button type="button" onclick="timepicker.stepDown();" title="menos 15 minutos">-</button>
<button type="button" onclick="timepicker.stepUp()" title="más 15 minutos">+</button>
El siguiente código también.
<input id="timepicker" type="time"/> <button type="button" onclick="timepicker.stepDown(15);" title="menos 15 minutos">-</button> <button type="button" onclick="timepicker.stepUp(15)" title="más 15 minutos">+</button>
El multiplicador deberá ser un número entero, por lo que p.ej. «0.5» se redondearía a 1. Si queremos trabajar con segundos, el valor de step deberá ser inferior a 60.
Readonly – ocultar el reloj
Para terminar vamos a aplicar el último de los atributos: readonly. No es imprescindible pero puede ser una opción interesante si quieremos ofrecer una interfaz consistente para todos los navegadores. Cuando aplicamos readonly la UI del navegador (el relojito) se oculta, haciendo que el timepicker sólo esté accesible vía Javascript o presionando la barra de espaciado cuando tenemos puesto el foco en el campo. Tampoco es posible modificar los valores manualmente desde el teclado.
Para ocultar su UI, el agente de usuario (el navegador) lo único que ha hecho es aplicar un visibility: hidden y por tanto el pseudo-elemento sigue ocupando espacio y existiendo en el arbol del shadow DOM. Tendremos que aplicar, pues, un display: none si queremos hacerlo desparecer por completo.
HTML
<input id="timepicker" type="time" step="900" readonly />
CSS
[type=time]:read-only {
width: 90px;
}
[type=time]:read-only::-webkit-calendar-picker-indicator {
display: none;
}
He preparado un PEN en el que onClick incrementa/reduce la hora en 15 min y onDoubleClick lo hace en 1 hora.
See the Pen
Type time custom controlers v2 by Daniel Abril (@elcssar)
on CodePen.
Validación formato hora
Lo cierto es que este data type a penas te da opción a introducir ningún valor erróneo, ni siquiera pemite pegar del portapapeles. Habría que pasarle un valor por javascript para forzar el error. Eso sí, una vez que comiences a introducir valores manualmente, el campo devolverá :invalid hasta que completes todos sus valores.
Quizás te interese este artículo sobre validación con CSS
Accesibilidad de type time
De nuevo, el estándar proporciona una accesibilidad bastante digna. Si bien es cierto que la navegación por el timepicker puede ser un poco torpe cuando somos guiados por los voice readers, estos informan de cada uno de los elementos en los que se pone el foco tanto en los spinbuttons del input como en las opciones del selector desplegado.
Tendremos que tener especial cuidado al sustituir alguno de ellos por opciones personalizadas, como hemos hecho en los dos últimos casos. Para los botones +/- y el trigger del timepicker yo recomendaría sacarlos del árbol de accesibilidad aplicando aria-hidden= «true». Podéis usar además tabindex= «-1» si queréis evitar también la duplicidad funcional del +/- para aquellos que naveguen por teclado.
Conclusiones
El input type time aporta una solución semántica, acccesible y exténsamente soportada por los navegadores, a un campo cuyo formato es por definición complejo por las distintas formas en las que puede representarse la hora, devolviendo un valor legible por cualquier sistema informático mientras facilita al usuario herramientas para entrar la hora de forma sencilla y adaptada a cada dispositivo. Además toma la configuración del usuario del navegador/sistema operativo de forma que la información visualizada sea los más familiar posible al usuario.
Si te ha gustado podrás encontrar muchos más input types de HTML5 en el blog con aplicaciones prácticas, validacón y cuestiones de accesibilidad y usabilidad explicadas al detalle. Y si te sabe a poco, subscríbete y recibirás actualizaciones con próximas publicaciones (sin publicidad). Nos leemos 😉