Сравните и усилите; СравнитьЗаголовок

С атрибутом lang может быть связан только один язык. Это означает, что атрибут <html> может иметь только один язык, даже если на странице присутствует несколько языков. Установите lang в качестве основного языка страницы.

Не
<html lang="ar,en,fr,pt">...</html>
Несколько языков не поддерживаются.
Делать
<html lang="ar">...</html>
Устанавливает только основной язык страницы. В данном случае язык арабский.

Подобно кнопкам, ссылки в первую очередь получают свое доступное имя из текстового содержимого. Хороший трюк при создании ссылки — поместить в саму ссылку наиболее значимый фрагмент текста, а не слова-наполнители, такие как «Здесь» или «Читать дальше».

Не достаточно описательно
Check out our guide to web performance <a href="/guide">here</a>.
Полезный контент!
Check out <a href="/guide">our guide to web performance</a>.

Проверьте, вызывает ли анимация макет

Анимация, которая перемещает элемент с помощью чего-то другого, кроме transform , скорее всего, будет медленной. В следующем примере я добился того же визуального результата, анимируя top и left и используя transform .

Не
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

@keyframes move {
  50% {
     top: calc(90vh - 160px);
     left: calc(90vw - 200px);
  }
}
Делать
.box {
  position: absolute;
  top: 10px;
  left: 10px;
  animation: move 3s ease infinite;
}

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

Вы можете проверить это на следующих двух примерах Glitch и изучить производительность с помощью DevTools.

С помощью той же разметки мы можем заменить: padding-top: 56.25% на aspect-ratio: 16 / 9 , установив для aspect-ratio указанное соотношение width / height .

Использование отступа-top
.container {
  width: 100%;
  padding-top: 56.25%;
}
Использование соотношения сторон
.container {
  width: 100%;
  aspect-ratio: 16 / 9;
}

Использование aspect-ratio вместо padding-top гораздо более понятно и не требует изменения свойства отступа, чтобы сделать что-то выходящее за рамки его обычной области действия.

Да, верно, я использую reduce , чтобы связать последовательность обещаний. Я такой умный . Но это немного умного кодирования, без которого вам лучше обойтись.

Однако при преобразовании приведенного выше в асинхронную функцию возникает соблазн пойти слишком последовательно :

Не рекомендуется — слишком последовательно
async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}
Выглядит намного аккуратнее, но моя вторая выборка не начнется, пока моя первая выборка не будет полностью прочитана, и так далее. Это намного медленнее, чем пример с обещаниями, в котором выборка выполняется параллельно. К счастью, есть идеальная золотая середина.
Рекомендуется - красиво и параллельно
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);
  }
}
В этом примере URL-адреса извлекаются и читаются параллельно, но «умный» бит reduce заменяется стандартным, скучным и читаемым циклом for.

Написание пользовательских свойств Houdini

Вот пример установки пользовательского свойства (например, переменной CSS), но теперь с синтаксисом (типом), начальным значением (резервным) и логическим значением наследования (наследует ли оно значение от своего родителя или нет?). Текущий способ сделать это — через CSS.registerProperty() в JavaScript, но в Chromium 85 и более поздних версиях синтаксис @property будет поддерживаться в ваших файлах CSS:

Отдельный файл JavaScript (Chromium 78)
CSS.registerProperty({
  name: '--colorPrimary',
  syntax: '',
  initialValue: 'magenta',
  inherits: false
});
Включено в файл CSS (Chromium 85).
@property --colorPrimary {
  syntax: '';
  initial-value: magenta;
  inherits: false;
}

Теперь вы можете получить доступ к --colorPrimary как и к любому другому пользовательскому свойству CSS, через var(--colorPrimary) . Однако разница здесь в том, что --colorPrimary не читается просто как строка. Есть данные!

CSS backdrop-filter применяет один или несколько эффектов к полупрозрачному или прозрачному элементу. Чтобы понять это, рассмотрите изображения ниже.

Нет прозрачности переднего плана
Треугольник, наложенный на круг. Круг не виден сквозь треугольник.
.frosty-glass-pane {
  backdrop-filter: blur(2px);
}
Прозрачность переднего плана
Треугольник, наложенный на круг. Треугольник полупрозрачен, поэтому сквозь него можно увидеть круг.
.frosty-glass-pane {
  opacity: .9;
  backdrop-filter: blur(2px);
}

Изображение слева показывает, как будут отображаться перекрывающиеся элементы, если backdrop-filter не будет использоваться или поддерживаться. На изображении справа применяется эффект размытия с помощью backdrop-filter . Обратите внимание, что в дополнение к backdrop-filter он использует opacity . Без opacity не к чему было бы применять размытие. Само собой разумеется, что если для opacity установлено 1 (полностью непрозрачный), фон не будет затронут.

Однако, в отличие от события unload , у beforeunload есть законные варианты использования. Например, если вы хотите предупредить пользователя о том, что у него есть несохраненные изменения, которые они потеряют, если покинут страницу. В этом случае рекомендуется добавлять прослушиватели beforeunload только в том случае, если у пользователя есть несохраненные изменения, а затем удалять их сразу после сохранения несохраненных изменений.

Не
window.addEventListener('beforeunload', (event) => {
  if (pageHasUnsavedChanges()) {
    event.preventDefault();
    return event.returnValue = 'Are you sure you want to exit?';
  }
});
Приведенный выше код безоговорочно добавляет прослушиватель beforeunload .
Делать
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);
});
Приведенный выше код добавляет прослушиватель beforeunload только тогда, когда это необходимо (и удаляет его, когда это не так).

Минимизируйте использование Cache-Control: no-store

Cache-Control: no-store — это HTTP-заголовок, который веб-серверы могут устанавливать для ответов, который указывает браузеру не сохранять ответ в каком-либо HTTP-кэше. Это следует использовать для ресурсов, содержащих конфиденциальную информацию пользователя, например страниц после входа в систему.

Элемент fieldset , который содержит каждую входную группу ( .fieldset-item ), использует gap: 1px для создания тонких границ между элементами. Никакого сложного решения границ!

Заполненный пробел
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
Пограничный трюк
.grid {
  display: grid;

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

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

Обёртка натуральной сеткой

Самым сложным макетом оказался макет макроса, логическая система макета между <main> и <form> .

вход
<input
  type="checkbox"
  id="text-notifications"
  name="text-notifications"
>
этикетка
<label for="text-notifications">
  <h3>Text Messages</h3>
  <small>Get notified about all text messages sent to your device</small>
</label>

Элемент fieldset , который содержит каждую входную группу ( .fieldset-item ), использует gap: 1px для создания тонких границ между элементами. Никакого сложного решения границ!

Заполненный пробел
.grid {
  display: grid;
  gap: 1px;
  background: var(--bg-surface-1);

  & > .fieldset-item {
    background: var(--bg-surface-2);
  }
}
Пограничный трюк
.grid {
  display: grid;

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

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

Макет вкладок <header>

Следующий макет почти такой же: я использую flex для создания вертикального порядка.

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

.snap-indicator должен перемещаться горизонтально вместе с группой ссылок, и этот макет заголовка помогает подготовить этот этап. Здесь нет абсолютно позиционированных элементов!

Gentle Flex — это более верная стратегия, основанная только на центрировании. Это мягко и нежно, потому что в отличие от place-content: center размеры дочерних блоков во время центрирования не изменяются. Как можно аккуратнее все предметы укладываются, центрируются и располагаются на расстоянии.

.gentle-flex {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1ch;
}
Плюсы
  • Управляет только выравниванием, направлением и распределением.
  • Редактирование и обслуживание — все в одном месте
  • Gap гарантирует равное расстояние между n детьми
Минусы
  • Большинство строк кода

Отлично подходит как для макро, так и для микро макетов.

Применение

gap принимает в качестве значения любую длину или процент CSS.

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


В пробел можно передать 1 длину, которая будет использоваться как для строки, так и для столбца.

стенография
.grid {
  display: grid;
  gap: 10px;
}
Объединение строк и столбцов одновременно
Расширенный
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 10px;
}


В пробел можно передать две длины, которые будут использоваться для строки и столбца.

стенография
.grid {
  display: grid;
  gap: 10px 5%;
}
Установить одновременно строки и столбцы отдельно
Расширенный
.grid {
  display: grid;
  row-gap: 10px;
  column-gap: 5%;
}