En este tutorial voy a explicar cómo montar un sencillo campo de búsqueda accesible con funcionalidades nativas soportadas en todos los navegadores.
¿Por qué type Search y no type Text?
Type search permite a los navegadores identificar el elemento como un campo de búsqueda, y a través de él al SO y a los lectores de pantalla.
Accesibilidad
Type search se corresponde con el role Searchbox que es una subclase del role textbox según la recomendación del W3C de 2017 (WAI-ARIA). Y así continúa en el borrador de 2019.
El atributo role es el que permite la identificación semántica de los elementos. Y es gracias a esta identificación que los lectores de pantalla pueden anunciar a los usuarios la función de los elementos. Aunque hemos de decir que esto aún no sucede con type search.
Sin embargo sí podemos disfrutar ya de funcionalidades nativas (UI específicas) como veremos a continuación.
UI específicas
Algunos navegadores ya han comenzado a añadir UI específicas como p.e. Chrome, Safari o Android browser añadiendo un botón reset nativo para limpiar el campo. Aunque no lo hagan otros como la familia IE, FF u Opera, podemos considerarlo un progressive enhancement. Una mejora para aquellos navegadores más modernos sin detrimento de los que aún no le dan un tratamiento especial, pero probablemente lo hagan en el futuro.
Actualmente ya es muy útil por ejemplo para que los dispositivos móviles lo identifiquen y muestren el botón «Buscar» dentro del teclado. Algo que, junto la funcionalidad reset integrada en la UI, a la larga se podría convertir en un estándar.
Es por lo tanto una cuestión de accesibilidad pero también de usabilidad.
En este artículo la usaremos además como selector para aplicar nuestros estilos. Otra utilidad añadida a las anteriores.
[type="search"] { /*aquí nuestros estilos*/ }
Aplicación práctica
Vamos a ver pues cómo montar un campo de búsqueda funcional, accesible y muy usable libre de javascript. Puro HTML5 y CSS3.
Estructura semántica del buscador
Definiremos pues tres tipos de input, type search para otorgar semántica a la búsqueda y seguidamente dos botones, type reset para borrar la búsqueda y type submit para convocar el action del formulario en el que deberán estar contenidos.
El form será particular y exclusivo de la búsqueda. De otra forma, si incluyésemos otros campos dentro, estos también se borrarían al invocar el reset.
<form action="miAccion"> <input type="search"/> <button type="reset"></button> <button type="submit">Search</button> </form>
Type reset
Una cuestión importante a considerar es el hecho de que los lectores de pantalla no reconocen la UI específica aportada por el navegador para los type search. Algunos navegadores además del control de borrado del campo soportan ESC para esa misma función, pero es una funcionalidad poco conocida y tampoco es anunciado por los lectores de pantalla.
Funcionalidad multi-navegador
Puesto que, además, no hay homogeneidad de dichas UIs os recomiendo ocultarla y usar vuestro propio botón reset o «clear button», para limpiar el campo de búsqueda.
[type="search"]::-webkit-search-cancel-button { display: none; /*Ocultamos la UI de Webkit*/ } input[type=search]::-ms-clear { display: none; width : 0; height: 0; /*Ocultamos la UI de IE */ }
Su funcionalidad viene predefinida por el navegador y es la misma para todos ellos. Pero queremos que se asemeje a la UI por defecto.
Imitar la función nativa de type search
Para ello, lo primero será determinar si el campo está o no vacío. Aquí podríamos caer en el error de usar el selector ::empty de CSS pero no funcionará ya que el elemento input no puede contener otros elementos.
Por esa razón deberemos ser un poco más creativos y usar el atributo required en nuestro campo. Así podremos validar si está o no cumplimentado.
Le damos los estilos a nuestro «clear button» para los valores válidos.
HTML <input type="search required"/> CSS /*Añadimos unas variables en SCSS que nos ayuden a trabajar*/ $module: 3rem; $spacing: 0.375rem; [type=search] + [type=reset] { /*Lo posicionamos dentro del campo*/ position: absolute; left: calc(16rem - #{$module}); width: $module; /*Aquí añadimos las reglas para perfilar la estética deseada*/ }
Seguidamente deberemos ocultarlo si el valor es inválido. Es decir, vacío.
[type=search]:invalid + [type=reset] { display: none; }
Reservar espacio dentro del input
Puesto que hemos posicionado el botón dentro del input, deberemos dejar un margen interno a la derecha para que no se solape con el texto.
[type="search"] { padding: $spacing $module $spacing $spacing; }
Type Submit
Este botón podría parecer prescindible ya que en desktop podríamos usar la tecla Intro de nuestro teclado físico y en Android e iOS el teclado virtual muestra una tecla de búsqueda gracias a type search.
Sin embargo, es una buena práctica por tres razones:
- El botón ayuda a reconocer el buscador, en especial si se usa la lupa.
- Usuarios menos avanzados pueden sentirse confundidos si no están habituados a usar la tecla Intro. Muchos son muy dependientes del ratón.
- En términos de accesibilidad y en el caso de tener más de un campo, reduce las pulsaciones de teclas necesarias para enviar el formulario (ver nota 2).
Nota 1: la posibilidad de usar Intro para lanzar el evento submit cuando tenemos el foco en un input viene condicionada por el hecho de que ése sea el único campo del formulario.
Nota 2: En caso de tener más de un campo, el botón de búsqueda deberá ser type=’submit’ para que pulsar Intro lance la búsqueda sin necesidad de poner el foco en él.
Atributo name
Sólo si nuestro input search dispone del atributo name pasará su valor al form al enviarlo (submit). Son muy comunes los valores s y q por lo que os recomiendo, si queréis evitar que se mezcle el historial de búsquedas de vuestra página con el de otras páginas, elijáis un name más específico.
Autocomplete Off
Autocomplete es una propiedad estándar de html5. Por defecto parece que va guardando todo el historial de búsqueda y muestra un máximo de seis. Entiendo que debe haber un máximo de registros guardados o, de otra forma, podría suponer un problema de memoria y consecuentemente de rendimiento.
Para deshabilitar el historial basta añadir automplete=’off’ a nuestro type=’search’ o a cualquier otro textbox.
Debido a la falta total de control sobre los estilos y el funcionamiento, he optado por deshabilitarla en el ejemplo.
Placeholder
Finalmente añadiremos un placeholder para indicar al usuario qué puede buscar. En nuestro caso será una búsqueda libre.
Podéis ver el código hasta éste punto.
See the Pen
Input type search by Jesuke (@jesuke)
on CodePen.
Buscador accesible
Qué hacer para cumplir los requisitos WAI (WCAG 2.1)
Deberemos añadir un par de elementos o atributos sencillos y seguir otro par de directrices simples.
Role search
En primer lugar deberíamos añadir el atributo role search a nuestro form. De esa forma el lector de pantalla anunciará la finalidad de dicho formulario.
<form action="miAccion" role="search"> <input type="search" required /> <button type="reset"></button> <button type="submit">Search</button> </form>
Nota: debéis saber que en 2015 hubo algo de controversia a cerca de añadir role=’search’ a un elemento form. Incluso se llegó a pedir que se cambiara un ejemplo del draft de WAI-ARIA así como la frase a la que acompañaba:
«El rol search va normalmente ubicado en el campo/elemento form o en un div que lo contenga.» A lo que el draft respondió que el ejemplo era correcto y que no se cambiaba. Sin embargo lo que hizo fue corregir el validador que devolvía la alerta (no el error) «El elemento form no necesita un atributo role», causante de la controversia junto a la regla «No cambie la semántica nativa, a menos que sea absolutamente necesario.»
Ahora puedes validar el código de este tutorial sin miedo.
Label
Además, como con cualquier otro input deberíamos también añadir un label. Ésta es una de las pocas reglas de accesibilidad que chocan con la usabilidad del componente que, en el caso de los buscadores recomienda no añadir un label y dejar que sean el placeholder y el botón «Buscar» los que permitan intuir su finalidad. Para solucionar este conflicto os puedo recomendar varias soluciones:
- Podéis adoptar el método de Material Design de Google.
- Por otro lado, yo suelo usar placeholder para guiar al usuario cuando los términos de búsqueda comprenden unos campos específicos de la base de datos. Por lo que no es factible usar el Material, pero sí se podría superponer al borde superior del campo y, así, complementarse ambos textos entre sí.
- Otra forma sería usar un label de imagen integrado con el input a modo de lupa.
- Finalmente podemos prescindir del elemento label siempre y cuando tengamos una etiqueta para el lector de pantalla. Y una vez más esto se puede hacer de varias formas.
Método 1. Esconderlo con opacity: 0
HTML <form action="miAccion" role="search"> <label for="mainSearch" class="hidden">Search</label> <input id="mainSearch" type="search" required /> <button type="reset"></button> <button type="submit">Search</button> </form> CSS .hidden { position: absolute; opacity: 0; }
Nota: evita usar display: none; o visibility: hidden; ya que equivalen a aplicar un aria-hidden=’true’ y por lo tanto el elemento sería eliminado del árbol de accesibilidad.
Método 2. Usar aria-label
<input type="search" required aria-label="Search">
Este mismo atributo lo necesitaremos también para nuestros type reset y type submit ya que no contienen texto:
<button type="reset" aria-label="Limpiar campo"></button> <button type="submit" aria-label="Buscar"></button>
De esta forma cumplimos los requerimientos de accesibilidad a la vez que seguimos las recomendaciones de buenas prácticas de usabilidad (simple, intuitivo y claro).
Contraste de placeholders
Asegúrate de que tus placeholders tengan al menos un contraste de 4.5:1 como por ejemplo #767676 sobre fondo #ffffff. De esa forma obtendrás la validación AA según las directrices WCAG 2.0.
Accesibilidad redundante
Sobrecargar los componentes con atributos de accesibilidad redundantes puede ser molesto para los usuarios de lectores de pantalla, por ello conviene evitar algo como esto:
<form action="miAccion" role="search" aria-label="global search">
/*Formulario*/
</form>
Esto sería anunciado por los lectores de pantalla como «global search search».
Si fuese necesaria la inclusión de un label, por ejemplo para diferenciarlo de otro form con idéntico role, deberemos usar sólo palabras que complementen y ayuden a especificar.
<form action="miAccion" role="search" aria-label="global">
/*Formulario*/
</form>
Tecla de acceso o accesskey
Por último pero no menos importante, recomiendo siempre el uso de un atributo accesskey para campos relevantes como la búsqueda. Sin embargo, la recomendación debe ir acompañada de ciertas advertencias:
- El valor del la tecla de acceso debe tener sentido en el idioma actual siempre que sea posible.
- Evitar múltiples valores pues aumenta la posibilidad de duplicidades y conflictos.
- Evitar valores numéricos, ya que son poco intuitivos.
- Controlar las duplicidades accidentales y los conflictos con otras funciones del navegador o sus plugins.
- Se deberían mostrar de alguna forma los comandos que dan acceso a los campos con accesskey para aquellas personas que no usan lectores de pantalla. Para ello habrá que tener en cuenta las distintas implementaciones de cada navegador y sistema operativo. Generalmente son Shift + Alt + [tecla] (Windows) y Control + Option + [tecla] (MacOS)
<input id="mainSearch" type="search" aria-label="Search" accesskey="k" placeholder="Shift + Alt + k" required />
Y aquí tenéis el resultado final accesible.
See the Pen
Input type search by Jesuke (@jesuke)
on CodePen.
La historia del Historial de búsquedas
Algunos navegadores recuerdan los valores introducidos en los campos de tipo texto, como es el caso de type search. Por defecto suelen guardar los valores introducidos y te lo muestran a modo de sugerencia en el mismo orden que fueron buscados. Al comenzar a escribir, esas sugerencias se ordenan alfabéticamente.
Type search dispuso en su momento de dos atributos muy interesantes: results y autosave; que nos permitían mostrar búsquedas recientes de forma controlada al hacer foco en el campo. Fueron implementados por Chrome y Safari y si estaban presentes, el input mostraba una lupa junto a una flecha que supuestamente debía desplegar el historial de búsqueda. Debido al mal funcionamiento de la implementación y al hecho de que no eran estándares, finalmente se eliminaron de ambos navegadores en 2013.
La idea original se la debemos a Apple, quienes inventaron el primer input search incluyendo estos atributos y fue propuesto por el WHATWG en 2008 para ser incluido en HTML5 con la finalidad de controlar el guardado automático de las búsquedas previas. Algo que como hemos visto ya hacen por defecto cualquier textbox en muchos navegadores.
Por lo tanto, si leéis sobre ellos, no os rompáis la cabeza intentado comprobar qué hacen. Todas las referencias de desarrollo de los navegadores han sido eliminadas y no son parte de las especificaciones de HTML5. Si tenéis curiosidad, esto es lo que hacían:
Results
Este atributo nos permitía determinar el número máximo de resultados a mostrar. Al no ser funcional en la actualidad desconozco si reduciendo el valor, se ocultarían los registros ya guardados o si los registros nuevos sustituirían a los viejos.
Entiendo que su comportamiento debe ser similar al que ha adoptado la funcionalidad autocomplete de los navegadores, comentada más arriba.
Autosave
Su función básicamente era activar o, en su ausencia, desactivar el guardado automático gracias a la asignación de una categoría. A su vez, esta categoría podía ser aplicada a múltiples type search, de forma que compartiesen los resultados de búsqueda.Permitía además que los valores de búsqueda se conservasen incluso tras recargar la página.
Es decir, si no se declaraba sería el equivalente a autocomplete actual en modo off.
Sumario
Tenemos pues un buscador que, gracias al los botones reset y submit, resulta muy usable e intuitivo. Y también gracias a ellos funciona en cualquier navegador y tiene un rendimiento optimizado gracias al uso de las funciones nativas de estos.
Además tiene todo lo necesario para pasar un test WAI-ARIA y ser accesible tanto para lectores de pantalla como para dispositivos móviles.
El cssar dice
Cuando trabajemos en un formulario hay un aspecto básico de usabilidad que nunca debemos olvidar: los estados de los elementos (focus, active, hover…). Recuerda aplicar estilos específicos incluyendo cambios de color y de cursor.
En el caso de los buscadores hay un par de cosas que tampoco debemos olvidar:
- El campo debe ser suficiente para al menos 2 palabras largas. Se estimó un mínimo de 27 caracteres, a lo que además hay que sumar el espacio para el reset.
Si no disponéis de ese espacio podéis optar por bajar el botón a una segunda línea o alargar el campo en el momento de poner el foco como hace el ejemplo de este artículo. Para ello el form deberá poder superponerse a otros elementos mediante la propiedad position: absolute; - Puedes elegir texto o lupa para el botón, pero éste debe estar siempre presente a menos que sea un campo typeahead.
- Añadir una lupa ya sea en el botón o integrada con el campo ayudará al usuario a identificar el componente de un vistazo.
- Posiciona la búsqueda preferiblemente en las zonas superior derecha o izquierda. Es donde los usuarios esperan encontrarla.
Nada más, si os ha gustado el artículo, siempre se agradece un comentario 😉