¡Resuelto con CSS! Menús desplegables | Programar Plus

CSS se está volviendo cada vez más poderoso, y con características como la cuadrícula CSS y las propiedades personalizadas (también conocidas como variables CSS), estamos viendo que surgen algunas soluciones realmente creativas. Algunas de esas soluciones se centran no solo en hacer la web más bonita, sino en hacerla más accesible y en mejorar el estilo de las experiencias accesibles. ¡Definitivamente estoy aquí para eso!

Serie de artículos:

  1. Colorear fondos SVG
  2. Menús desplegables (esta publicación)
  3. Estilo lógico basado en el número de elementos dados

Un patrón de interfaz de usuario común que vemos en la web son los menús desplegables. Se utilizan para mostrar información relacionada por partes, sin abrumar al usuario con botones, texto y opciones. En algún lugar donde vemos estos muchos, es dentro de los encabezados o áreas de navegación en los sitios web.

Un collage de capturas de pantalla que muestran diferentes ejemplos de menús desplegables.Una búsqueda en Google de “menú desplegable” arroja muchos ejemplos

Veamos si podemos hacer uno de estos menús solo con CSS. Crearemos una lista de enlaces dentro de un componente de navegación así:

<nav role="navigation">
  <ul>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">One</a></li>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Two</a></li>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Three</a></li>
  </ul>
</nav>

Ahora, digamos que queremos un submenú desplegable en el segundo elemento de navegación. Podemos hacer lo mismo allí e incluir una lista de enlaces dentro de ese elemento de la lista:

<nav role="navigation">
  <ul>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">One</a></li>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Two</a>
      <ul class="dropdown">
        <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Sub-1</a></li>
        <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Sub-2</a></li>
        <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Sub-3</a></li>
      </ul>
    </li>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Three</a></li>
  </ul>
</nav>

Ahora tenemos nuestro sistema de navegación de dos niveles. Para tener el contenido oculto y mostrado cuando queremos que sea visible, necesitaremos aplicar algo de CSS. Todas las propiedades de estilo se han eliminado del siguiente ejemplo para mayor claridad en la interacción:

li {
 display: block;
 transition-duration: 0.5s;
}

li:hover {
  cursor: pointer;
}

ul li ul {
  visibility: hidden;
  opacity: 0;
  position: absolute;
  transition: all 0.5s ease;
  margin-top: 1rem;
  left: 0;
  display: none;
}

ul li:hover > ul,
ul li ul:hover {
  visibility: visible;
  opacity: 1;
  display: block;
}

ul li ul li {
  clear: both;
  width: 100%;
}

Ahora, el menú desplegable del submenú está oculto, pero estará expuesto y se volverá visible cuando pasemos el cursor sobre su padre correlativo en la barra de navegación. Por el estilo ul li ul, tenemos acceso a ese submenú, y aplicando estilo ul li ul li, tenemos acceso a los elementos individuales de la lista que contiene.

El problema

Esto está empezando a parecerse a lo que queremos, pero todavía estamos lejos de terminar en este momento. La accesibilidad web es una parte fundamental del desarrollo de su producto, y ahora mismo sería la oportunidad perfecta para sacar esto a relucir. Añadiendo role="navigation" es un buen comienzo, pero para que una barra de navegación sea accesible, uno debe poder navegar a través de ella (y enfocarse en el elemento apropiado en un orden sensato), y también tener un lector de pantalla que lea con precisión en voz alta lo que se está leyendo. centrado en.

Puede pasar el cursor sobre cualquiera de los elementos de la lista y ver claramente sobre qué se desplaza, pero esto no es cierto para la navegación por pestañas. Continúe e intente desplazarse por el ejemplo anterior. Pierde la noción de dónde está el enfoque visualmente cuando se desplaza hacia Dos en el menú principal, verá un anillo indicador de enfoque, pero cuando pase al siguiente elemento (uno de sus elementos del submenú), ese enfoque desaparece.

Una captura de pantalla animada que muestra anillos de enfoque en los elementos del menú a medida que se tabulan.

Ahora bien, es importante tener en cuenta que, en teoría, se centra en este otro elemento y que un lector de pantalla podría analizarlo leyendo Sub-uno, pero los usuarios del teclado no podrán ver lo que está sucediendo y perderán la pista.

La razón por la que esto sucede es porque, mientras aplicamos estilo al elemento flotante del elemento principal, tan pronto como cambiamos el enfoque del elemento principal a uno de los elementos de la lista dentro de ese elemento principal, perdemos ese estilo. Esto tiene sentido desde el punto de vista de CSS, pero no es lo que queremos.

Afortunadamente, hay una nueva pseudoclase CSS que nos dará exactamente lo que queremos en este caso, y se llama :focus-within.

La solución: :focus-within

El :focus-within El pseudo selector es parte de la especificación de nivel 4 de selectores CSS y le dice al navegador que aplique un estilo a un padre cuando cualquiera de sus hijos esté enfocado. En nuestro caso, esto significa que podemos tabular para Sub-uno y aplicar un :focus-within estilo junto con el :hover estilo del padre y ver exactamente dónde estamos en el menú desplegable de navegación. En nuestro caso sería ul li:focus-within > ul:

ul li:hover > ul,
ul li:focus-within > ul,
ul li ul:hover {
  visibility: visible;
  opacity: 1;
  display: block;
}

¡Dulce! ¡Funciona!

¡Desvío rápido! Si solo admite navegadores modernos, el CSS que hemos visto hasta ahora está bien. Pero debe saber que cuando un navegador no comprende parte de un selector, arroja todo el selector. Entonces, si desea admitir IE 11, no puede mezclar el :focus-within parte.

/* This compound selector will still work in IE 11 because :focus-within isn't mixed in */
ul li:hover > ul,
ul li ul:hover,
ul li ul:focus {
  visibility: visible;
  opacity: 1;
  display: block;
}

/* IE 11 won't get this, but at least the top-level menus will work */
ul li:focus-within > ul {
  visibility: visible;
  opacity: 1;
  display: block;
}

Ahora, cuando pasamos al segundo elemento, aparece nuestro submenú y, a medida que pasamos por el submenú, ¡la visibilidad permanece! Ahora, podemos agregar nuestro código para incluir :focus estados al lado :hover para brindarles a los usuarios del teclado la misma experiencia que a los usuarios de nuestro mouse.

Una captura de pantalla animada de un menú que muestra el menú de símbolos que se revela cuando está activo con pestañas.

En la mayoría de los casos, como en los enlaces directos, normalmente podemos escribir algo como:

a:hover,
a:focus {
  ...
}

Pero en este caso, dado que estamos aplicando estilos de desplazamiento basados ​​en el elemento principal li, podemos utilizar de nuevo :focus-within para obtener la misma apariencia al pasar las pestañas. Esto se debe a que en realidad no podemos concentrarnos en el li (a menos que agreguemos un tabindex="0"). En realidad, nos estamos centrando en el enlace (a) dentro de ella. :focus-within nos permite seguir aplicando estilos al padre li al enfocarse en el enlace (¡bastante genial!):

li:hover,
li:focus-within {
  ...
}

Una captura de pantalla animada que muestra un menú con pestañas donde el submenú se revela cuando se activa con pestañas, los elementos del submenú muestran el anillo de enfoque cuando está activo y los estilos de desplazamiento también se aplican cuando está activo.

En este punto, dado que estamos aplicando un estilo de enfoque, podemos hacer algo que normalmente no se recomienda (eliminar el estilo de ese anillo de enfoque de contorno azul). Podemos hacer esto de la siguiente manera:

li:focus-within a {
  outline: none;
}

El código anterior especifica que cuando nos enfocamos dentro de los elementos de la lista a través del enlace (a), no aplique un esquema al elemento del enlace (a). Es bastante seguro escribirlo de esta manera, porque estamos diseñando exclusivamente el estado de desplazamiento y con navegadores que no admiten :focus-within, el enlace seguirá recibiendo un anillo de enfoque. Ahora nuestro menú se ve así:

Una captura de pantalla animada que muestra el resultado final del menú con pestañas donde el anillo de enfoque se ha eliminado y reemplazado por el estado de desplazamiento cuando los elementos del menú tienen pestañas activamente.Menú final usando :focus-within, :hover estados y personalizar el anillo de enfoque para que desaparezca

¿Y ARIA?

Si está familiarizado con la accesibilidad, es posible que haya oído hablar de las etiquetas y los estados de ARIA. ¡Puede usarlos a su favor para crear también este tipo de menús desplegables con accesibilidad incorporada al mismo tiempo! Puede encontrar un excelente ejemplo aquí por Heydon Pickering. Al incluir el marcado ARIA, su código se parecería un poco más a esto:

<nav role="navigation">
  <ul>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">One</a></li>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#" aria-haspopup="true">Two</a>
      <ul class="dropdown" aria-label="submenu">
        <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Sub-1</a></li>
        <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Sub-2</a></li>
        <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Sub-3</a></li>
      </ul>
    </li>
    <li><a href="https://css-tricks.com/solved-with-css-dropdown-menus/#">Three</a></li>
  </ul>
</nav>

Estas agregando aria-haspopup="true" al padre del menú desplegable para indicar un estado alternativo, e incluyendo aria-label="submenu" en el propio menú desplegable (en este caso, nuestra lista con class="dropdown".

Estas propiedades en sí mismas le brindarán la funcionalidad que necesita para mostrar el menú desplegable, pero la desventaja es que solo funcionan con JavaScript habilitado.

Advertencia sobre compatibilidad con el navegador

Hablando de advertencias, hablemos de la compatibilidad con el navegador. Mientras :focus-within tiene bastante buena compatibilidad con el navegador, es importante tener en cuenta que Internet Explorer y Edge no son compatibles, por lo que sus usuarios en esas plataformas no podrán ver la navegación.

Estos datos de soporte del navegador son de Caniuse, que tiene más detalles. Un número indica que el navegador admite la función en esa versión y posteriores.

Escritorio

Cromo Firefox ES DECIR Borde Safari
60 52 No 79 10.1

Móvil / Tableta

Android Chrome Android Firefox Androide Safari de iOS
96 94 96 10,3

La solución definitiva aquí sería utilizar tanto el marcado ARIA como CSS :focus-within para garantizar una sólida experiencia desplegable para sus usuarios.

Si desea poder usar esta función en el futuro, ¡vote a favor de Edge User Voice! Y votar :focus-ring mientras lo hace, para que podamos diseñar ese anillo de enfoque y crear una hermosa experiencia web interactiva para todos 😀

Más en :focus-within y A11Y

  • Scott O’Hara escribió sobre :focus-within, destacando demostraciones como destacadas <table> filas y menús desplegables
  • Kushagra Gour sobre la creación de un modal atrapado en el enfoque
  • Eric Bailey sobre los estilos de enfoque en general
  • Chris sobre mantener visible un elemento principal cuando el niño está enfocado
  • Todos los artículos sobre Programar Plusrelacionados con :focus-within

(Visited 24 times, 1 visits today)