С атрибутом 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
.
.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:
CSS.registerProperty({ name: '--colorPrimary', syntax: '', initialValue: 'magenta', inherits: false });
@property --colorPrimary { syntax: ''; initial-value: magenta; inherits: false; }
Теперь вы можете получить доступ к --colorPrimary
как и к любому другому пользовательскому свойству CSS, через var(--colorPrimary)
. Однако разница здесь в том, что --colorPrimary
не читается просто как строка. Есть данные!
CSS backdrop-filter
применяет один или несколько эффектов к полупрозрачному или прозрачному элементу. Чтобы понять это, рассмотрите изображения ниже.
![Треугольник, наложенный на круг. Круг не виден сквозь треугольник.](https://web.dev/static/examples/image/admin/LOqxvB3qqVkbZBmxMmKS.png?hl=ru)
.frosty-glass-pane { backdrop-filter: blur(2px); }
![Треугольник, наложенный на круг. Треугольник полупрозрачен, поэтому сквозь него можно увидеть круг.](https://web.dev/static/examples/image/admin/VbyjpS6Td39E4FudeiVg.png?hl=ru)
.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 для создания вертикального порядка.
<snap-tabs> <header> <nav></nav> <span class="snap-indicator"></span> </header> <section></section> </snap-tabs>
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%; }