Cómo compilar un componente de configuración

Descripción general de los conceptos básicos para crear un componente de configuración con controles deslizantes y casillas de verificación

En esta publicación, compartiré ideas sobre cómo crear un componente de configuración para el web que sea responsiva, admita múltiples entradas de dispositivos y funcione en navegadores. Prueba la demostración.

. Demostración

Si prefieres ver videos o quieres obtener una vista previa de la IU/UX de lo que estamos creando, aquí tienes explicación más breve en YouTube:

Descripción general

He desglosado los aspectos de este componente en las siguientes secciones:

  1. Diseños
  2. Color
  3. Entrada de rango personalizado
  4. Entrada de casilla de verificación personalizada
  5. Consideraciones de accesibilidad
  6. JavaScript

Diseños

Esta es la primera demostración del desafío de la GUI de Cuadrícula de CSS. Esta es cada cuadrícula destacado con Herramientas para desarrolladores de Chrome para cuadrícula:

Contornos coloridos y superposiciones de espaciado entre espacios que ayudan a mostrar todos los cuadros que conforman el diseño de la configuración

Solo para espacios

El diseño más común:

foo {
  display: grid;
  gap: var(--something);
}

A este diseño lo llamo "solo para espacios". porque solo usa una cuadrícula para agregar espacios entre los bloques.

Cinco diseños usan esta estrategia. A continuación, se muestran todos:

Diseños de cuadrícula vertical destacados con contornos y rellenos con espacios vacíos

El elemento fieldset, que contiene cada grupo de entrada (.fieldset-item), usa gap: 1px para crear los bordes finos entre los elementos. No hay una solución de frontera complicada.

Brecha llena
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
Truco de borde
.grid {
  display: grid;

  & > .fieldset-item {
    background: var(--bg-surface-2);

    &:not(:last-child) {
      border-bottom: 1px solid var(--bg-surface-1);
    }
  }
}

Envoltura de cuadrícula natural

El diseño más complejo terminó siendo el diseño macro, el diseño lógico sistema entre <main> y <form>.

Centrar contenido de unión

El Flexbox y la cuadrícula proporcionan capacidades a align-items o align-content y, cuando se trata de unir elementos, el diseño de content los alineamientos distribuirán el espacio entre los elementos secundarios como grupo.

main {
  display: grid;
  gap: var(--space-xl);
  place-content: center;
}

El elemento principal usa la alineación place-content: center. abreviatura para que los elementos secundarios estén centrados vertical y horizontalmente en los diseños de una y dos columnas.

Mira en el video anterior cómo el "contenido" se mantenga centrado, aunque encaje el problema.

Repetir máx. mínimo de ajuste automático

<form> usa un diseño de cuadrícula adaptable para cada sección. Este diseño cambia de una a dos columnas en función del espacio disponible.

form {
  display: grid;
  gap: var(--space-xl) var(--space-xxl);
  grid-template-columns: repeat(auto-fit, minmax(min(10ch, 100%), 35ch));
  align-items: flex-start;
  max-width: 89vw;
}

Esta cuadrícula tiene un valor diferente para row-gap (--space-xl) que para column-gap (--space-xxl) para darle un toque personalizado al diseño adaptable. Cuando se apilan las columnas, un espacio grande, pero no tan grande como si estuviéramos en una pantalla panorámica.

La propiedad grid-template-columns usa 3 funciones de CSS: repeat(), minmax() y min() Una Kravets posee un excelente blog de diseño. publicación sobre esto, llamándolo RAM

Hay 3 adiciones especiales en nuestro diseño, si lo comparas con el de Una:

  • Pasamos una función min() adicional.
  • Especificamos align-items: flex-start.
  • Hay un estilo max-width: 89vw.

La función adicional min() está bien descrita por Evan Minto en su blog en el post Cuadrícula de CSS intrínsecamente responsiva con minmax() y min(). Te recomiendo que lo leas. La corrección de la alineación flex-start consiste en eliminar el efecto de estiramiento predeterminado, de modo que los elementos secundarios de este diseño no deben tener alturas iguales, pueden tener alturas intrínsecas y naturales. El En el video de YouTube, se incluye un desglose rápido de esta incorporación de alineación.

Vale la pena hacer un pequeño desglose de max-width: 89vw en esta publicación. Permíteme mostrarte el diseño con y sin el estilo aplicado:

¿Qué sucede? Cuando se especifica max-width, proporciona contexto. tamaño explícito o definitivo tamaño de auto-fit de diseño para saber cómo muchas repeticiones puede caber en el espacio. Si bien parece obvio que el sea de “ancho completo”, según las especificaciones de la cuadrícula de CSS, se debe especificar un tamaño definido o un tamaño máximo. que se proporcione. Proporcioné un tamaño máximo.

Entonces, ¿por qué elegir 89vw? Porque "funcionó" para mi diseño. Yo y otros usuarios de Chrome investigan por qué un valor más razonable, como 100vw no es suficiente y si en realidad se trata de un error.

Espaciado

La mayor parte de la armonía de este diseño proviene de una paleta limitada de espaciado, 7 para ser exactos.

:root {
  --space-xxs: .25rem;
  --space-xs:  .5rem;
  --space-sm:  1rem;
  --space-md:  1.5rem;
  --space-lg:  2rem;
  --space-xl:  3rem;
  --space-xxl: 6rem;
}

El uso de estos flujos funciona muy bien con cuadrícula, CSS @nest y sintaxis de nivel 5 de @media. Este es un ejemplo del conjunto de estilos de diseño completamente <main>.

main {
  display: grid;
  gap: var(--space-xl);
  place-content: center;
  padding: var(--space-sm);

  @media (width >= 540px) {
    & {
      padding: var(--space-lg);
    }
  }

  @media (width >= 800px) {
    & {
      padding: var(--space-xl);
    }
  }
}

Una cuadrícula con contenido centrado, con relleno moderado de forma predeterminada (como en los dispositivos móviles) Sin embargo, a medida que hay más espacio de viewport disponible, se extiende aumentando el relleno. ¡El CSS de 2021 se ve bastante bien!

¿Recuerdas el diseño anterior, "solo para la brecha"? Aquí hay una versión más completa de cómo se ven en este componente:

header {
  display: grid;
  gap: var(--space-xxs);
}

section {
  display: grid;
  gap: var(--space-md);
}

Color

Un uso controlado del color ayudó a que este diseño se destacara como expresivo pero, a la vez, mínimo. Lo hago de la siguiente manera:

:root {
  --surface1: lch(10 0 0);
  --surface2: lch(15 0 0);
  --surface3: lch(20 0 0);
  --surface4: lch(25 0 0);

  --text1: lch(95 0 0);
  --text2: lch(75 0 0);
}

Nombraré los colores de la superficie y del texto con números en lugar de nombres como surface-dark y surface-darker porque, en una consulta de medios, invertiré la información por lo que la luz y la oscuridad no serán significativas.

Los hago girar en una consulta de medios de preferencia de la siguiente manera:

:root {
  ...

  @media (prefers-color-scheme: light) {
    & {
      --surface1: lch(90 0 0);
      --surface2: lch(100 0 0);
      --surface3: lch(98 0 0);
      --surface4: lch(85 0 0);

      --text1: lch(20 0 0);
      --text2: lch(40 0 0);
    }
  }
}

Es importante echar un vistazo rápido al panorama y la estrategia generales antes profundizaremos en los detalles de la sintaxis de colores. Pero, como me adelantaba un poco, déjame retroceder un poco.

¿LCH?

Sin profundizar demasiado en la teoría del color, la LCH tiene una sintaxis orientada al ser humano, se adapta a cómo percibimos el color, no a cómo lo medimos con matemáticas (como 255). Esto le da una ventaja distintiva, ya que las personas pueden escribirlo más fácilmente y otras humanos estarán en sintonía con estos ajustes.

Una captura de pantalla de la página web pod.link/csspodcast, en la que se muestra Color 2: Episodio de percepción
Obtén más información sobre el color perceptual (y mucho más) en el podcast de CSS
.

Hoy, en esta demostración, enfoquémonos en la sintaxis y en los valores para aclarar y oscurecer. Veamos 1 superficie y 1 color del texto:

:root {
  --surface1: lch(10 0 0);
  --text1:    lch(95 0 0);

  @media (prefers-color-scheme: light) {
    & {
      --surface1: lch(90 0 0);
      --text1:    lch(40 0 0);
    }
  }
}

--surface1: lch(10 0 0) se traduce en 10% de luminosidad, 0 croma y 0 matiz: un gris muy oscuro sin color. En la consulta de medios del modo claro, se cambió a 90% con --surface1: lch(90 0 0);. Y esa es la esencia de la de administración de amenazas. Comienza cambiando la luminosidad entre los 2 temas, manteniendo el las relaciones de contraste que requiere el diseño o lo que puede mantener la accesibilidad.

La ventaja con lch() es que la ligereza está orientada al ser humano, y podemos sentir bueno acerca de un cambio de %, que será perceptual y coherente que % es diferente. hsl(), por ejemplo, no es tan confiables.

Hay más que más información sobre espacios de color y lch() si te interesa. ¡Próximamente!

En este momento, el CSS no puede acceder a estos colores. Permítanme repetir: No tenemos acceso a un tercio de los colores en los modelos más modernos. monitores. Y no son todos los colores, sino los colores más vívidos que puede mostrar la pantalla. Nuestros sitios web están desgastados porque el hardware de los monitores evolucionó más rápido que las especificaciones de CSS y las implementaciones del navegador.

Lea Verou

Controles de forma adaptables con esquema de colores

Muchos navegadores incluyen controles de tema oscuro, actualmente Safari y Chromium, debes especificar en CSS o HTML que tu diseño los usa.

Lo anterior demuestra el efecto de la propiedad desde el panel Estilos de Herramientas para desarrolladores. La demostración usa la etiqueta HTML, que en mi opinión es, por lo general, una mejor ubicación:

<meta name="color-scheme" content="dark light">

Obtén toda la información al respecto en este color-scheme artículo de Thomas Steiner Hay mucho más por ganar que las entradas de casillas de verificación oscuras.

CSS accent-color

Hubo recientes actividad alrededor de accent-color para los elementos de formulario, por lo que es un estilo de CSS único que puede cambiar la el color de tono que se usa en el elemento de entrada del navegador. Obtenga más información aquí en GitHub. Lo incluí en mi estilos de este componente. Como los navegadores lo admiten, mis casillas de verificación aparecerán más el tema con los que se destacan rosa y púrpura.

input[type="checkbox"] {
  accent-color: var(--brand);
}

Captura de pantalla de Chromium en Linux de casillas de verificación rosas

Resalta los colores con degradados fijos y enfoque dentro

El color se destaca más cuando se usa con moderación, y es una de las formas es mediante interacciones coloridas de la IU.

Hay muchas capas de comentarios e interacción sobre la IU en el video anterior, que ayudan a darle personalidad a la interacción al:

  • Resaltar el contexto
  • Cómo proporcionar comentarios en la IU sobre "qué tan lleno" el valor está en el rango.
  • Proporcionar comentarios a la IU en los que se indique que un campo acepta entradas

Para proporcionar comentarios cuando se interactúa con un elemento, CSS utiliza el :focus-within seudoclase para cambiar el aspecto de varios elementos, .fieldset-item, es muy interesante:

.fieldset-item {
  ...

  &:focus-within {
    background: var(--surface2);

    & svg {
      fill: white;
    }

    & picture {
      clip-path: circle(50%);
      background: var(--brand-bg-gradient) fixed;
    }
  }
}

Cuando uno de los elementos secundarios de este elemento tiene foco adentro:

  1. Al fondo .fieldset-item se le asigna un color de superficie de mayor contraste.
  2. El elemento svg anidado se rellena de color blanco para ofrecer un mayor contraste.
  3. El <picture> clip-path anidado se expande a un círculo completo y el el fondo se rellena con el gradiente fijo brillante.

Período personalizado

Con el siguiente elemento de entrada HTML, te mostraré cómo personalicé su apariencia:

<input type="range">

Este elemento tiene 3 partes que debemos personalizar:

  1. Elemento de rango / contenedor
  2. Realice un seguimiento
  3. Pulgar

Estilos de elementos de rango

input[type="range"] {
  /* style setting variables */
  --track-height: .5ex;
  --track-fill: 0%;
  --thumb-size: 3ex;
  --thumb-offset: -1.25ex;
  --thumb-highlight-size: 0px;

  appearance: none;         /* clear styles, make way for mine */
  display: block;
  inline-size: 100%;        /* fill container */
  margin: 1ex 0;            /* ensure thumb isn't colliding with sibling content */
  background: transparent;  /* bg is in the track */
  outline-offset: 5px;      /* focus styles have space */
}

Las primeras líneas de CSS son las partes personalizadas de los estilos etiquetarlos claramente ayuda. El resto de los estilos son principalmente estilos restablecidos, para proporcionarán una base consistente para crear las partes complicadas del componente.

Estilos de pista

input[type="range"]::-webkit-slider-runnable-track {
  appearance: none; /* clear styles, make way for mine */
  block-size: var(--track-height);
  border-radius: 5ex;
  background:
    /* hard stop gradient:
        - half transparent (where colorful fill we be)
        - half dark track fill
        - 1st background image is on top
    */
    linear-gradient(
      to right,
      transparent var(--track-fill),
      var(--surface1) 0%
    ),
    /* colorful fill effect, behind track surface fill */
    var(--brand-bg-gradient) fixed;
}

El truco para esto es "revelar" el color de relleno vibrante. Esto se hace con el con gradiente de parada fija en la parte superior. El gradiente es transparente hasta el porcentaje de relleno y, luego, que usa el color de superficie del recorrido sin rellenar. Detrás de esa superficie vacía, hay una a todo color, esperando que lo revele la transparencia.

Estilo de relleno del segmento

Mi diseño requiere JavaScript para mantener el estilo de relleno. Hay son estrategias exclusivas de CSS, pero requieren que el elemento miniatura tenga la misma altura. la canción, y no pude encontrar la armonía dentro de esos límites.

/* grab sliders on page */
const sliders = document.querySelectorAll('input[type="range"]')

/* take a slider element, return a percentage string for use in CSS */
const rangeToPercent = slider => {
  const max = slider.getAttribute('max') || 10;
  const percent = slider.value / max * 100;

  return `${parseInt(percent)}%`;
};

/* on page load, set the fill amount */
sliders.forEach(slider => {
  slider.style.setProperty('--track-fill', rangeToPercent(slider));

  /* when a slider changes, update the fill prop */
  slider.addEventListener('input', e => {
    e.target.style.setProperty('--track-fill', rangeToPercent(e.target));
  })
})

Creo que esto representa una buena mejora visual. El control deslizante funciona muy bien sin En JavaScript, no se requiere el prop --track-fill; simplemente no tendrá un relleno si no está presente. Si JavaScript está disponible, propaga el archivo del usuario y, al mismo tiempo, se observan los cambios del usuario, y se sincroniza la propiedad personalizada con el valor.

Este es un gran publicación en Trucos de CSS de Ana Tudor, que muestra una solución de CSS únicamente para relleno de pista. También encontré esto El elemento range es muy inspirador.

Estilos de miniatura

input[type="range"]::-webkit-slider-thumb {
  appearance: none; /* clear styles, make way for mine */
  cursor: ew-resize; /* cursor style to support drag direction */
  border: 3px solid var(--surface3);
  block-size: var(--thumb-size);
  inline-size: var(--thumb-size);
  margin-top: var(--thumb-offset);
  border-radius: 50%;
  background: var(--brand-bg-gradient) fixed;
}

La mayoría de estos estilos están diseñados para crear un bonito círculo. De nuevo, se ve el gradiente de fondo fijo que unifica colores dinámicos de las miniaturas, los seguimientos y los elementos SVG asociados. Separé los estilos de la interacción para aislar el box-shadow. técnica que se utiliza para destacar al colocar el cursor sobre un elemento:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

::-webkit-slider-thumb {
  

  /* shadow spread is initally 0 */
  box-shadow: 0 0 0 var(--thumb-highlight-size) var(--thumb-highlight-color);

  /* if motion is OK, transition the box-shadow change */
  @media (--motionOK) {
    & {
      transition: box-shadow .1s ease;
    }
  }

  /* on hover/active state of parent, increase size prop */
  @nest input[type="range"]:is(:hover,:active) & {
    --thumb-highlight-size: 10px;
  }
}

El objetivo era mostrar una representación visual animada y fácil de administrar para los comentarios de los usuarios. Al usar una sombra de cuadro, puedo evitar activar diseño con el efecto. Hago esto creando una sombra que no esté desenfocada y coincida con la forma circular de la pulgar. Luego, cambio y cambio el tamaño de dispersión al colocar el cursor sobre ellos.

Si solo el efecto de destacar fuera tan fácil en las casillas de verificación...

Selectores entre navegadores

Descubrí que necesitaba estos selectores -webkit- y -moz- para lograr varios navegadores coherencia:

input[type="range"] {
  &::-webkit-slider-runnable-track {}
  &::-moz-range-track {}
  &::-webkit-slider-thumb {}
  &::-moz-range-thumb {}
}

Casilla de verificación personalizada

Con el siguiente elemento de entrada HTML, te mostraré cómo personalicé su apariencia:

<input type="checkbox">

Este elemento tiene 3 partes que debemos personalizar:

  1. Elemento de casilla de verificación
  2. Etiquetas asociadas
  3. Efecto de resaltado

Elemento de casilla de verificación

input[type="checkbox"] {
  inline-size: var(--space-sm);   /* increase width */
  block-size: var(--space-sm);    /* increase height */
  outline-offset: 5px;            /* focus style enhancement */
  accent-color: var(--brand);     /* tint the input */
  position: relative;             /* prepare for an absolute pseudo element */
  transform-style: preserve-3d;   /* create a 3d z-space stacking context */
  margin: 0;
  cursor: pointer;
}

Los estilos transform-style y position se preparan para el seudoelemento que presentaremos más adelante. para darle estilo al resaltado. De lo contrario, es principalmente y cosas insignificantes. Me gusta que el cursor sea puntero, me gusta de contorno, las casillas de verificación predeterminadas son demasiado pequeñas y, si accent-color es compatible, agrega en el esquema de colores de la marca.

Etiquetas de casillas de verificación

Es importante proporcionar etiquetas para las casillas de verificación por 2 motivos. La primera es representan para qué se usa el valor de la casilla de verificación, para responder "¿para qué?" En segundo lugar, para la UX, los usuarios web están acostumbrados a interactuar con casillas de verificación a través de sus etiquetas asociadas.

entrada
<input
  type="checkbox"
  id="text-notifications"
  name="text-notifications"
>
etiqueta
<label for="text-notifications">
  <h3>Text Messages</h3>
  <small>Get notified about all text messages sent to your device</small>
</label>

En la etiqueta, coloca un atributo for que dirija a una casilla de verificación por ID: <label for="text-notifications">. En la casilla de verificación, duplica el nombre y el ID para asegúrate de que se encuentre con varias herramientas y tecnología, como un mouse o un lector de pantalla: <input type="checkbox" id="text-notifications" name="text-notifications"> :hover, :active y otros servicios vienen gratis con la conexión, lo que aumenta el formas en que se puede interactuar con tu formulario.

Casilla de verificación destacada

quiero mantener la coherencia de mis interfaces, y el elemento deslizante tiene un buen miniatura destacada que me gustaría usar con la casilla de verificación. La miniatura era usar box-shadow y su propiedad spread para escalar una sombra y fuera de servicio. Sin embargo, ese efecto no funciona aquí porque las casillas de verificación son, y deben, ser, cuadrado.

pude lograr el mismo efecto visual con un seudoelemento, y una lamentable cantidad de CSS complejo:

@custom-media --motionOK (prefers-reduced-motion: no-preference);

input[type="checkbox"]::before {
  --thumb-scale: .01;                        /* initial scale of highlight */
  --thumb-highlight-size: var(--space-xl);

  content: "";
  inline-size: var(--thumb-highlight-size);
  block-size: var(--thumb-highlight-size);
  clip-path: circle(50%);                     /* circle shape */
  position: absolute;                         /* this is why position relative on parent */
  top: 50%;                                   /* pop and plop technique (https://web.dev/centering-in-css#5-pop-and-plop) */
  left: 50%;
  background: var(--thumb-highlight-color);
  transform-origin: center center;            /* goal is a centered scaling circle */
  transform:                                  /* order here matters!! */
    translateX(-50%)                          /* counter balances left: 50% */
    translateY(-50%)                          /* counter balances top: 50% */
    translateZ(-1px)                          /* PUTS IT BEHIND THE CHECKBOX */
    scale(var(--thumb-scale))                 /* value we toggle for animation */
  ;
  will-change: transform;

  @media (--motionOK) {                       /* transition only if motion is OK */
    & {
      transition: transform .2s ease;
    }
  }
}

/* on hover, set scale custom property to "in" state */
input[type="checkbox"]:hover::before {
  --thumb-scale: 1;
}

Crear un seudoelemento circular es una tarea sencilla, pero colocarlo detrás del elemento al que se adjuntó fue más difícil. Esto es antes y después de solucionarlo:

Definitivamente es una microinteracción, pero para mí es importante y mantener la coherencia. La técnica de escalamiento de animación es la misma que usamos en en otros lugares. Configuramos una propiedad personalizada con un valor nuevo y permitimos que CSS realice la transición según las preferencias de movimiento. La función clave aquí es translateZ(-1px). El el elemento superior creó un espacio 3D y este seudoelemento secundario lo aprovechó y se vuelve ligeramente atrás en el espacio z.

Accesibilidad

El video de YouTube ofrece una gran demostración del uso del mouse, el teclado y interacciones de los lectores de pantalla para este componente de configuración. Mencionaré algunos de los haz clic aquí para conocer los detalles.

Opciones del elemento HTML

<form>
<header>
<fieldset>
<picture>
<label>
<input>

Cada uno de ellos contiene sugerencias para la herramienta de navegación del usuario. Algunos elementos ofrecen sugerencias de interacción, algo de interactividad de conexión y otras ayudan a dar forma árbol de accesibilidad por el que navega un lector de pantalla.

Atributos HTML

Podemos ocultar los elementos que los lectores de pantalla no necesitan en este caso, el icono al lado del deslizador:

<picture aria-hidden="true">

El video anterior demuestra el flujo del lector de pantalla en Mac OS. ¿Observas cómo la entrada el enfoque se mueve directamente de un control deslizante al siguiente. Esto se debe a que hemos ocultado el icono que puede haber sido una parada en el camino hacia el siguiente control deslizante. Sin este , un usuario tendría que detenerse, escuchar y avanzar más allá de la imagen que que tal vez no puedan ver.

El SVG es un montón de matemáticas, por ejemplo, agreguemos un elemento <title> para colocar el cursor sobre el elemento. título y un comentario legible sobre la creación de los cálculos:

<svg viewBox="0 0 24 24">
  <title>A note icon</title>
  <path d="M12 3v10.55c-.59-.34-1.27-.55-2-.55-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4V7h4V3h-6z"/>
</svg>

Aparte de eso, usamos HTML claramente marcado para que el formulario no tenga errores muy bien en mouse, teclado, controles de videojuegos y lectores de pantalla.

JavaScript

Ya expliqué cómo se administraba el color de relleno de los segmentos desde JavaScript. Ahora, veamos el JavaScript relacionado con <form>:

const form = document.querySelector('form');

form.addEventListener('input', event => {
  const formData = Object.fromEntries(new FormData(form));
  console.table(formData);
})

Cada vez que se interactúa con el formulario y se lo cambia, la consola lo registra como un objeto en una tabla para facilitar su revisión antes de enviarlo a un servidor.

Captura de pantalla de los resultados de console.table(), en la que los datos del formulario se muestran en una tabla

Conclusión

Ahora que sabes cómo lo hice, ¿cómo lo harías? Esto hace que sea divertido de la arquitectura de componentes de componentes en la nube. ¿Quién hará la 1a versión con ranuras en su framework favorito? 🙂

Diversifiquemos nuestros enfoques y aprendamos todas las formas de desarrollar en la Web. Crear una demostración, twittearme vínculos y la agregaré. a la sección Remixes de la comunidad que está más abajo.

Remixes de la comunidad

  • @tomayac por su estilo en relación con Coloca el cursor sobre el área para las etiquetas de la casilla de verificación. Esta versión no tiene espacios de desplazamiento entre elementos: demo y fuente.