Comparar y CompareCaption

El atributo lang solo puede tener un idioma asociado. Esto significa que el atributo <html> solo puede tener un idioma, incluso si hay varios idiomas en la página. Establece lang en el idioma principal de la página.

Qué no debes hacer
<html lang="ar,en,fr,pt">...</html>
No se admiten varios idiomas.
Qué debes hacer
<html lang="ar">...</html>
Establece solo el idioma principal de la página. En este caso, el idioma es árabe.

Al igual que los botones, los vínculos obtienen su nombre accesible principalmente de su contenido de texto. Un buen truco cuando creas un vínculo es colocar el texto más significativo en el vínculo en sí, en lugar de palabras de relleno como "Aquí" o "Más información".

No es lo suficientemente descriptivo
Check out our guide to web performance <a href="/guide">here</a>.
Contenido útil
Check out <a href="/guide">our guide to web performance</a>.

Verifica si una animación activa el diseño

Es probable que una animación que mueva un elemento con algo que no sea transform sea lenta. En el siguiente ejemplo, obtuve el mismo resultado visual animando top y left, y usando transform.

Qué no debes hacer
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     top: calc(90vh - 160px);
     left: calc(90vw - 200px);
  }
}
Qué debes hacer
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     transform: translate(calc(90vw - 200px), calc(90vh - 160px));
  }
}

Puedes probar esto en los siguientes dos ejemplos de Glitch y explorar el rendimiento con DevTools.

Con el mismo lenguaje de marcado, podemos reemplazar padding-top: 56.25% por aspect-ratio: 16 / 9 y configurar aspect-ratio en una proporción especificada de width / height.

Usa padding-top
.container {
  width: 100%;
  padding-top: 56.25%;
}
Usa aspect-ratio
.container {
  width: 100%;
  aspect-ratio: 16 / 9;
}

Usar aspect-ratio en lugar de padding-top es mucho más claro y no revisa la propiedad de padding para hacer algo fuera de su alcance habitual.

Sí, así es, estoy usando reduce para encadenar una secuencia de promesas. Soy muy inteligente. Pero esta es una codificación tan inteligente que es mejor no usarla.

Sin embargo, cuando convertimos lo anterior en una función asíncrona, es tentador generar demasiadas secuencias:

No se recomienda: Demasiado secuencial
async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}
Luce más prolijo, pero mi segundo fetch no comienza hasta que mi primer fetch se haya leído por completo, y así sucesivamente. Esto es mucho más lento que el ejemplo de promesas que realiza los fetch en paralelo. Afortunadamente, hay un punto intermedio ideal.
Recomendado: agradable y paralelo
function markHandled(...promises) {
  Promise.allSettled(promises);
}

async function logInOrder(urls) {
  // fetch all the URLs in parallel
  const textPromises = urls.map(async (url) => {
    const response = await fetch(url);
    return response.text();
  });

  markHandled(...textPromises);

  // log them in sequence
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}
En este ejemplo, las URLs se obtienen y se leen en paralelo, pero el bit reduce “inteligente” se reemplaza por un bucle for estándar, aburrido y legible.

Cómo escribir propiedades personalizadas de Houdini

Este es un ejemplo de cómo establecer una propiedad personalizada (piense en una variable de CSS), pero ahora con una sintaxis (tipo), un valor inicial (reemplazo) y un valor booleano de herencia (¿hereda el valor de su elemento superior o no?). La forma actual de hacerlo es a través de CSS.registerProperty() en JavaScript, pero en Chromium 85 y versiones posteriores, la sintaxis @property será compatible con tus archivos CSS:

Archivo JavaScript independiente (Chromium 78)
CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '',
  initialValue: 'magenta',
  inherits: false
});
Se incluye en el archivo CSS (Chromium 85)
@property --colorPrimary {
  syntax: '';
  initial-value: magenta;
  inherits: false;
}

Ahora puedes acceder a --colorPrimary como a cualquier otra propiedad personalizada de CSS a través de var(--colorPrimary). Sin embargo, la diferencia aquí es que --colorPrimary no solo se lee como una cadena. Tiene datos.

CSS backdrop-filter aplica uno o más efectos a un elemento que es translúcido o transparente. Para comprenderlo, observa las siguientes imágenes.

Sin transparencia en primer plano
Un triángulo superpuesto en un círculo. No se puede ver el círculo a través del triángulo.
.frosty-glass-pane {
  backdrop-filter: blur(2px);
}
Transparencia en primer plano
Un triángulo superpuesto en un círculo. El triángulo es translúcido, lo que permite ver el círculo a través de él.
.frosty-glass-pane {
  opacity: .9;
  backdrop-filter: blur(2px);
}

En la imagen de la izquierda, se muestra cómo se renderizarían los elementos superpuestos si no se usara o no se admitiera backdrop-filter. La imagen de la derecha aplica un efecto de desenfoque con backdrop-filter. Observa que usa opacity además de backdrop-filter. Sin opacity, no habría nada a lo que aplicar el desenfoque. No hace falta decir que, si opacity se establece como 1 (totalmente opaco), no habrá efecto en el fondo.

Sin embargo, a diferencia del evento unload, existen usos legítimos para beforeunload. Por ejemplo, cuando quieras advertirle al usuario que tiene cambios no guardados que perderá si sale de la página. En este caso, se recomienda que solo agregues objetos de escucha beforeunload cuando un usuario tenga cambios no guardados y, luego, quítalos inmediatamente después de que se guarden.

Qué no debes hacer
window.addEventListener('beforeunload', (event) => {
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
});
El código anterior agrega un objeto de escucha beforeunload de forma incondicional.
Qué debes hacer
function beforeUnloadListener(event) {
  event.preventDefault();
  return event.returnValue = 'Are you sure you want to exit?';
};

// A function that invokes a callback when the page has unsaved changes.
onPageHasUnsavedChanges(() => {
  window.addEventListener('beforeunload', beforeUnloadListener);
});

// A function that invokes a callback when the page's unsaved changes are resolved.
onAllChangesSaved(() => {
  window.removeEventListener('beforeunload', beforeUnloadListener);
});
El código anterior solo agrega el objeto de escucha beforeunload cuando es necesario (y lo quita cuando no lo es).

Minimiza el uso de Cache-Control: no-store

Cache-Control: no-store es un encabezado HTTP que los servidores web pueden configurar en las respuestas para indicarle al navegador que no almacene la respuesta en ninguna caché HTTP. Se debe usar para recursos que contengan información sensible del usuario, por ejemplo, páginas protegidas por un acceso.

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

Brecha rellena
.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);
    }
  }
}

Ajuste de cuadrícula natural

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

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>

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

Brecha rellena
.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);
    }
  }
}

Diseño <header> de pestañas

El siguiente diseño es casi el mismo: uso flex para crear un orden vertical.

HTML
<snap-tabs>
  <header>
    <nav></nav>
    <span class="snap-indicator"></span>
  </header>
  <section></section>
</snap-tabs>
CSS
header {
  display: flex;
  flex-direction: column;
}

El .snap-indicator debe desplazarse horizontalmente con el grupo de vínculos, y este diseño de encabezado ayuda a establecer esa etapa. No hay elementos con posicionamiento absoluto aquí.

La Flexibilidad suave es una estrategia más auténtica de solo centrado. Es suave y gentil, ya que, a diferencia de place-content: center, no se cambian los tamaños de los cuadros de los elementos secundarios durante el centrado. Todos los elementos se apilan, centran y espacian de la manera más cuidadosa posible.

.gentle-flex {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1ch;
}
Ventajas
  • Solo controla la alineación, la dirección y la distribución.
  • Las ediciones y el mantenimiento se encuentran en un solo lugar
  • El espacio garantiza un espaciado igual entre n elementos secundarios.
Desventajas
  • Mayor cantidad de líneas de código

Excelente para diseños macro y micro.

Uso

gap acepta cualquier longitud o porcentaje de CSS como valor.

.gap-example {
  display: grid;
  gap: 10px;
  gap: 2ch;
  gap: 5%;
  gap: 1em;
  gap: 3vmax;
}


Se puede pasar 1 longitud para el espacio, que se usará para la fila y la columna.

Abreviatura
.grid {
  display: grid;
  gap: 10px;
}
Configurar filas y columnas juntos a la vez
Expandido
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 10px;
}


Se pueden pasar 2 longitudes para el espacio, que se usarán para la fila y la columna.

Abreviatura
.grid {
  display: grid;
  gap: 10px 5%;
}
Establece filas y columnas por separado a la vez
Expandido
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 5%;
}