🌈 Свечение внутри элемента, но из любого фона
Подсветить элемент изнутри проще всего через box-shadow с inset. Но такое свечение одноцветное. А если хочется радужный conic-gradient() или картинку?
Решение из статьи: накрыть элемент псевдоэлементом ::after с нужным фоном и маской оставить видимыми только края. Середина прозрачна, и сам элемент под ней виден.
Маска для левого и правого краёв (black в маске значит «видно»):
Вторая такая же маска с to bottom берёт верх и низ, а mask-composite: add объединяет обе полосы в рамку.
Накладка стоит на position: absolute и inset: 0, поэтому самому элементу нужен position: relative.
В статье есть демо, где оба вида свечения вспыхивают на случайных элементах.
👉 @seniorFront
Подсветить элемент изнутри проще всего через box-shadow с inset. Но такое свечение одноцветное. А если хочется радужный conic-gradient() или картинку?
Решение из статьи: накрыть элемент псевдоэлементом ::after с нужным фоном и маской оставить видимыми только края. Середина прозрачна, и сам элемент под ней виден.
Маска для левого и правого краёв (black в маске значит «видно»):
--inset: 30px;
mask-image: linear-gradient(
to right,
black 0%,
transparent var(--inset),
transparent calc(100% - var(--inset)),
black 100%
);
Вторая такая же маска с to bottom берёт верх и низ, а mask-composite: add объединяет обе полосы в рамку.
Накладка стоит на position: absolute и inset: 0, поэтому самому элементу нужен position: relative.
В статье есть демо, где оба вида свечения вспыхивают на случайных элементах.
👉 @seniorFront
❤2
🔍 Убрал повтор в скринридере и сломал кнопку
Джейк Арчибальд делал кнопку-иконку «жирный» с подсказкой «Bold (⌘B)» на popover="hint". На кнопке стояли aria-label="Bold" и aria-describedby со ссылкой на подсказку.
VoiceOver читал «Bold. Bold. Command B». Чтобы убрать повтор, Джейк удалил aria-label. И кнопка осталась без доступного имени, то есть без названия для скринридера. aria-describedby добавляет только описание, имя он не заменяет.
JAWS в итоге собрал текст из соседних элементов и объявил кнопку сразу жирной и курсивной.
Чинится заменой одного атрибута: aria-labelledby вместо aria-describedby. Он тоже ссылается на подсказку по id, но берёт из неё именно имя. Теперь VoiceOver говорит «Bold. Command B. Button».
Сам Джейк советует проверять в нескольких скринридерах: один, как и один браузер, полной картины не даёт.
В статье есть и вариант, где имя и сочетание клавиш разведены по разным span.
👉 @seniorFront
Джейк Арчибальд делал кнопку-иконку «жирный» с подсказкой «Bold (⌘B)» на popover="hint". На кнопке стояли aria-label="Bold" и aria-describedby со ссылкой на подсказку.
VoiceOver читал «Bold. Bold. Command B». Чтобы убрать повтор, Джейк удалил aria-label. И кнопка осталась без доступного имени, то есть без названия для скринридера. aria-describedby добавляет только описание, имя он не заменяет.
JAWS в итоге собрал текст из соседних элементов и объявил кнопку сразу жирной и курсивной.
Чинится заменой одного атрибута: aria-labelledby вместо aria-describedby. Он тоже ссылается на подсказку по id, но берёт из неё именно имя. Теперь VoiceOver говорит «Bold. Command B. Button».
Сам Джейк советует проверять в нескольких скринридерах: один, как и один браузер, полной картины не даёт.
В статье есть и вариант, где имя и сочетание клавиш разведены по разным span.
👉 @seniorFront
🔥1
🎨 Текст, который сам подбирает цвет под фон
Если фон карточки задаётся фирменным цветом и может быть любым, подбирать к нему цвет текста вручную неудобно.
CSS-функция contrast-color() берёт цвет и возвращает black или white, смотря что контрастнее:
Можно пойти дальше. Сохраняем результат в кастомное свойство, зарегистрированное через @property, и проверяем его запросом @container style(). Вышло white, значит фон тёмный, и текст красим в светлый оттенок цвета карточки. Вышло black, берём глубокий тон того же оттенка.
Если цвета палитры задать вручную, а не выводить из фона, на результат contrast-color() уже не опереться: такие пары автор советует тщательно тестировать.
В статье этот приём вместе с light-dark() собран в целую систему светлой и тёмной темы.
👉 @seniorFront
Если фон карточки задаётся фирменным цветом и может быть любым, подбирать к нему цвет текста вручную неудобно.
CSS-функция contrast-color() берёт цвет и возвращает black или white, смотря что контрастнее:
.card {
--bg: var(--brand-color);
background: var(--bg);
color: contrast-color(var(--bg));
}Можно пойти дальше. Сохраняем результат в кастомное свойство, зарегистрированное через @property, и проверяем его запросом @container style(). Вышло white, значит фон тёмный, и текст красим в светлый оттенок цвета карточки. Вышло black, берём глубокий тон того же оттенка.
Если цвета палитры задать вручную, а не выводить из фона, на результат contrast-color() уже не опереться: такие пары автор советует тщательно тестировать.
В статье этот приём вместе с light-dark() собран в целую систему светлой и тёмной темы.
👉 @seniorFront
🧮 Поле ввода, в которое можно вписать формулу
Часто число для формы сначала надо посчитать. С обычным <input> приходится бегать в калькулятор и обратно. А позже в поле виден только итог, и непонятно, откуда он взялся.
Bramus сделал для этого веб-компонент
Считает компонент своим парсером, без eval(). Он понимает обычные операторы, скобки и функции вроде sqrt, round, min, max. Внутри лежат обычные <input>, поэтому отправка формы и валидация работают как есть. С недописанной формулой вроде
Учтите, что по умолчанию в поле с вашим name уходит сама формула «2 + 3», а не 5. Чтобы сервер получил число, нужен атрибут
Как устроены три поля внутри и почему стили на самом теге сразу применяются к ним, Bramus показывает в статье.
👉 @seniorFront
Часто число для формы сначала надо посчитать. С обычным <input> приходится бегать в калькулятор и обратно. А позже в поле виден только итог, и непонятно, откуда он взялся.
Bramus сделал для этого веб-компонент
<calc-input>. Пишешь (2 + 3) * 4, уходишь из поля, и там появляется 20. Возвращаешь фокус, и снова видна формула, её можно проверить или поправить.Считает компонент своим парсером, без eval(). Он понимает обычные операторы, скобки и функции вроде sqrt, round, min, max. Внутри лежат обычные <input>, поэтому отправка формы и валидация работают как есть. С недописанной формулой вроде
2 + форма не отправится.Учтите, что по умолчанию в поле с вашим name уходит сама формула «2 + 3», а не 5. Чтобы сервер получил число, нужен атрибут
submit="result".Как устроены три поля внутри и почему стили на самом теге сразу применяются к ним, Bramus показывает в статье.
👉 @seniorFront
❤1
✨ CSS может заметить, что элементы вот-вот столкнутся
Сбоку от секции с карточками висит декоративный элемент, и его размер и положение меняются вместе с экраном. Медиазапросом момент наезда не поймать, а спрятать декор нужно, как только зазор станет меньше 20px.
Между декором и секцией через anchor positioning растягивается невидимый элемент
Само переполнение запускает scroll-driven анимацию, прокручивать ничего не нужно. Анимация лишь ставит
Работает только в браузерах, где есть и scroll-driven анимации, и anchor positioning, так что это улучшение поверх обычной вёрстки.
В статье есть интерактивные демо, где можно потянуть границу и поймать момент срабатывания.
👉 @seniorFront
Сбоку от секции с карточками висит декоративный элемент, и его размер и положение меняются вместе с экраном. Медиазапросом момент наезда не поймать, а спрятать декор нужно, как только зазор станет меньше 20px.
Между декором и секцией через anchor positioning растягивается невидимый элемент
.measure. Внутри у него псевдоэлемент шириной 20px и overflow-x: auto. Когда зазор меньше 20px, .measure переполняется.Само переполнение запускает scroll-driven анимацию, прокручивать ничего не нужно. Анимация лишь ставит
--touching: 1, а style container query прячет декор:.decor {
@container style(--touching: 1) {
opacity: 0;
}
}Работает только в браузерах, где есть и scroll-driven анимации, и anchor positioning, так что это улучшение поверх обычной вёрстки.
В статье есть интерактивные демо, где можно потянуть границу и поймать момент срабатывания.
👉 @seniorFront
❤1
✨ Скроллбар, который следит за прокруткой без JavaScript
Свой скроллбар обычно рисуют так: нативный прячут, а размер и положение ползунка считают в JS на каждое событие scroll. Это дорого: scroll летит много раз в секунду, а чтение scrollHeight может вызвать reflow.
Разработчик библиотеки Taiga UI отдал эту работу CSS через scroll-driven animations. Их прогресс зависит не от времени, а от прокрутки контейнера.
Хитрее всего с высотой ползунка. Анимации его краёв растягивают на высоту контейнера (100cqb) за конец прокрутки, и в крайней точке они замирают ровно там, где нужно:
JS остаётся только для перетаскивания ползунка мышью.
Учтите, что на момент статьи Firefox умел это лишь в Nightly, поэтому в Taiga UI новая схема включается только там, где браузер её поддерживает.
Отдельно красиво решено, как прибить ползунки к краю контейнера: через anchor positioning.
👉 @seniorFront
Свой скроллбар обычно рисуют так: нативный прячут, а размер и положение ползунка считают в JS на каждое событие scroll. Это дорого: scroll летит много раз в секунду, а чтение scrollHeight может вызвать reflow.
Разработчик библиотеки Taiga UI отдал эту работу CSS через scroll-driven animations. Их прогресс зависит не от времени, а от прокрутки контейнера.
Хитрее всего с высотой ползунка. Анимации его краёв растягивают на высоту контейнера (100cqb) за конец прокрутки, и в крайней точке они замирают ровно там, где нужно:
.bar {
animation-range: 0% calc(100% + 100cqb), calc(0% - 100cqb) 100%;
animation-fill-mode: both;
}JS остаётся только для перетаскивания ползунка мышью.
Учтите, что на момент статьи Firefox умел это лишь в Nightly, поэтому в Taiga UI новая схема включается только там, где браузер её поддерживает.
Отдельно красиво решено, как прибить ползунки к краю контейнера: через anchor positioning.
👉 @seniorFront
🧩 Как сделать, чтобы фоновый узор не обрезался снизу
У секции повторяющийся фоновый узор, плитка 52×44px. Высота блока зависит от текста, поэтому нижний ряд плиток часто обрезается на середине.
Можно поставить background-repeat: round. Но тогда браузер сжимает или растягивает саму плитку, чтобы она уместилась целиком.
Решение аккуратнее: взять автоматическую высоту через calc-size() и округлить её функцией round() до кратного высоте плитки. Высота плитки лежит в переменной --tile-height.
Выглядит как фиксированная высота, но работает скорее как min-height. Блок по-прежнему подстраивается под контент, просто его высота всегда кратна высоте плитки, и узор не режется.
Оговорка: на момент написания статьи это работает только в Chrome.
В оригинале есть живые демо, разбор округления вверх и вниз и то, как подогнать под узор ещё и padding.
👉 @seniorFront
У секции повторяющийся фоновый узор, плитка 52×44px. Высота блока зависит от текста, поэтому нижний ряд плиток часто обрезается на середине.
Можно поставить background-repeat: round. Но тогда браузер сжимает или растягивает саму плитку, чтобы она уместилась целиком.
Решение аккуратнее: взять автоматическую высоту через calc-size() и округлить её функцией round() до кратного высоте плитки. Высота плитки лежит в переменной --tile-height.
.section {
height: calc-size(auto, round(up, size, var(--tile-height)));
}Выглядит как фиксированная высота, но работает скорее как min-height. Блок по-прежнему подстраивается под контент, просто его высота всегда кратна высоте плитки, и узор не режется.
Оговорка: на момент написания статьи это работает только в Chrome.
В оригинале есть живые демо, разбор округления вверх и вниз и то, как подогнать под узор ещё и padding.
👉 @seniorFront
❤1
✨ Рамка, которая рисует себя сама, на чистом CSS
Свойство border-image позволяет сделать рамкой элемента градиент вместо обычной линии. Но анимировать градиент напрямую не выйдет: браузер не умеет плавно двигать проценты внутри него.
Это обходится так: регистрируем свою переменную через @property и указываем её тип. Такую переменную браузер уже плавно меняет:
Дальше она встаёт в градиент рамки:
Есть подвох: border-image не изгибается по форме элемента, так что скруглённые углы он не повторит.
В статье тот же приём крутят дальше: conic-gradient, анимация border-image-slice и ещё куча вариаций.
👍 - если было полезно
👉 @frontendInterview
Свойство border-image позволяет сделать рамкой элемента градиент вместо обычной линии. Но анимировать градиент напрямую не выйдет: браузер не умеет плавно двигать проценты внутри него.
Это обходится так: регистрируем свою переменную через @property и указываем её тип. Такую переменную браузер уже плавно меняет:
@property --p {
syntax: "<percentage>";
initial-value: 0%;
inherits: false;
}Дальше она встаёт в градиент рамки:
linear-gradient(-45deg, red var(--p), transparent 0%). Пока --p равна 0%, рамка прозрачная. При наведении --p становится 100%, красный заполняет рамку, и кажется, что её рисуют на глазах.Есть подвох: border-image не изгибается по форме элемента, так что скруглённые углы он не повторит.
В статье тот же приём крутят дальше: conic-gradient, анимация border-image-slice и ещё куча вариаций.
👍 - если было полезно
👉 @frontendInterview
❤1👍1
🚀 Предзагрузка, которая грузит не те данные
В TanStack Router у маршрута есть loader: он запускает запрос ещё до рендера, а компонент забирает готовые данные из кеша TanStack Query. Но параметры запроса тогда описаны дважды, и если они разойдутся, ошибки никто не покажет.
Пример из статьи. В дашборд добавили ?asOf=дата, но только в компоненте. По ссылке loader грузит данные «на сегодня», а компонент потом запрашивает снимок заново. Лишний запрос и лишнее ожидание.
Автор предлагает класть
Учтите, что функция context на момент статьи была новой и не описанной в документации.
В статье ещё о том, как контекст наследуют дочерние маршруты и почему лишних ререндеров не будет.
👍 - если было полезно
👉 @frontendInterview
В TanStack Router у маршрута есть loader: он запускает запрос ещё до рендера, а компонент забирает готовые данные из кеша TanStack Query. Но параметры запроса тогда описаны дважды, и если они разойдутся, ошибки никто не покажет.
Пример из статьи. В дашборд добавили ?asOf=дата, но только в компоненте. По ссылке loader грузит данные «на сегодня», а компонент потом запрашивает снимок заново. Лишний запрос и лишнее ожидание.
Автор предлагает класть
queryOptions в контекст маршрута. Их создаёт одна функция context, а loader и компонент через useRouteContext() берут один и тот же объект. Разойтись им просто негде.Учтите, что функция context на момент статьи была новой и не описанной в документации.
В статье ещё о том, как контекст наследуют дочерние маршруты и почему лишних ререндеров не будет.
👍 - если было полезно
👉 @frontendInterview
🧩 Один инпут в Angular для строки, функции, шаблона или компонента
Когда компонент принимает кастомный контент, приходится заранее решать: хватит строки или нужен TemplateRef? Потом тип проверяют через @if и ветвят шаблон.
Библиотека Polymorpheus от команды Taiga UI убирает этот выбор. Директива *polymorpheusOutlet одинаково отрисует любой из четырёх вариантов в переданном контексте:
Строка или результат функции попадает в primitive.
Пример с тултипом: обычно в нём просто текст. Когда понадобится ссылка или кнопка, пользователь передаст шаблон, и API тултипа переделывать не придётся.
Оговорка: функцию от конструктора компонента не отличить, поэтому компонент оборачивают в PolymorpheusComponent.
А в статье ещё и трюк, как сделать иконку, которую можно задать просто по имени или целым шаблоном.
👍 - если было полезно
👉 @frontendInterview
Когда компонент принимает кастомный контент, приходится заранее решать: хватит строки или нужен TemplateRef? Потом тип проверяют через @if и ветвят шаблон.
Библиотека Polymorpheus от команды Taiga UI убирает этот выбор. Директива *polymorpheusOutlet одинаково отрисует любой из четырёх вариантов в переданном контексте:
<ng-container *polymorpheusOutlet="content as primitive; context: { $implicit: user }">
{{ primitive }}
</ng-container>Строка или результат функции попадает в primitive.
Пример с тултипом: обычно в нём просто текст. Когда понадобится ссылка или кнопка, пользователь передаст шаблон, и API тултипа переделывать не придётся.
Оговорка: функцию от конструктора компонента не отличить, поэтому компонент оборачивают в PolymorpheusComponent.
А в статье ещё и трюк, как сделать иконку, которую можно задать просто по имени или целым шаблоном.
👍 - если было полезно
👉 @frontendInterview
❤2
✨ Любой кусок страницы в окне поверх всех окон
Обычный Picture-in-Picture выносит в плавающее окошко только видео. Оно остаётся на экране, даже если переключиться на другую вкладку или программу.
Document Picture-in-Picture API делает то же самое с любым HTML: тикер акций, чат, список дел. По сути, получается веб-виджет.
Окно создаёт
Стили сами не переедут, их копируют в head окна отдельно. А поправить вид только внутри окна поможет
Учтите, что API работает только на десктопе, а Safari на момент статьи его не поддерживал.
В статье есть полный код кнопки и разбор опций окна.
👍 - если было полезно
👉 @frontendInterview
Обычный Picture-in-Picture выносит в плавающее окошко только видео. Оно остаётся на экране, даже если переключиться на другую вкладку или программу.
Document Picture-in-Picture API делает то же самое с любым HTML: тикер акций, чат, список дел. По сути, получается веб-виджет.
Окно создаёт
requestWindow(), и внутри у него свой пустой document. В статье туда клонируют тикер:/* Select the component */
const stock = document.querySelector("#stock");
/* Clone the component and append it to the DPIP <body> */
DPIP.document.body.append(stock.cloneNode(true));
Стили сами не переедут, их копируют в head окна отдельно. А поправить вид только внутри окна поможет
@media (display-mode: picture-in-picture).Учтите, что API работает только на десктопе, а Safari на момент статьи его не поддерживал.
В статье есть полный код кнопки и разбор опций окна.
👍 - если было полезно
👉 @frontendInterview
👍1
🎯 Тултип, который сам перепрыгивает вниз, без JavaScript
Подсказка над кнопкой проста, пока кнопка не окажется у верхнего края экрана: подсказку обрезает. Обычно это чинили на JavaScript, и код выходил запутанным.
В CSS для этого есть Anchor Positioning API. Кнопке даём имя через
position-area: top ставит подсказку над кнопкой. Если там не хватает места, flip-block переворачивает её на другую сторону, вниз.
Подвох: с position: absolute переворот не сработает. Браузер ловит выход за содержащий блок, а у absolute он обычно прокручивается вместе с элементом. У fixed этот блок равен окну, поэтому край замечается.
Дальше в статье: как развернуть стрелочку тултипа и чем подстраховать старые браузеры.
👍 - если было полезно
👉 @frontendInterview
Подсказка над кнопкой проста, пока кнопка не окажется у верхнего края экрана: подсказку обрезает. Обычно это чинили на JavaScript, и код выходил запутанным.
В CSS для этого есть Anchor Positioning API. Кнопке даём имя через
anchor-name: --example-anchor, а подсказка привязывается к ней:.target {
position: fixed;
position-anchor: --example-anchor;
position-area: top;
position-try-fallbacks: flip-block;
}position-area: top ставит подсказку над кнопкой. Если там не хватает места, flip-block переворачивает её на другую сторону, вниз.
Подвох: с position: absolute переворот не сработает. Браузер ловит выход за содержащий блок, а у absolute он обычно прокручивается вместе с элементом. У fixed этот блок равен окну, поэтому край замечается.
Дальше в статье: как развернуть стрелочку тултипа и чем подстраховать старые браузеры.
👍 - если было полезно
👉 @frontendInterview
🌗 Переключателю тёмной темы хватит двух положений
Обычно на сайтах три варианта: «Системная», «Светлая», «Тёмная». Но системная тема в итоге всё равно светлая или тёмная. Поэтому один из трёх вариантов ничего видимого не делает: страница остаётся прежней.
Лиа Веру предложила оставить два состояния: «как в системе» и «наоборот». Второе сохраняется на устройстве, а возврат к первому стирает сохранённый выбор. Так каждый клик заметно меняет страницу.
Зак Лезерман поставил такой тумблер в подвал своего сайта. Режим, совпадающий с системным, подписан (auto):
Есть оговорка. Bramus в критике подхода указал на особый случай: пользователей, у которых системная тема переключается сама. Зак и сам из них, но считает простоту важнее. Это его личная оценка.
А ещё Зак уже выложил тумблер отдельным веб-компонентом Solar Eclipse Toggle.
👍 - если было полезно
👉 @frontendInterview
Обычно на сайтах три варианта: «Системная», «Светлая», «Тёмная». Но системная тема в итоге всё равно светлая или тёмная. Поэтому один из трёх вариантов ничего видимого не делает: страница остаётся прежней.
Лиа Веру предложила оставить два состояния: «как в системе» и «наоборот». Второе сохраняется на устройстве, а возврат к первому стирает сохранённый выбор. Так каждый клик заметно меняет страницу.
Зак Лезерман поставил такой тумблер в подвал своего сайта. Режим, совпадающий с системным, подписан (auto):
Dark (auto) [Toggle] LightЕсть оговорка. Bramus в критике подхода указал на особый случай: пользователей, у которых системная тема переключается сама. Зак и сам из них, но считает простоту важнее. Это его личная оценка.
А ещё Зак уже выложил тумблер отдельным веб-компонентом Solar Eclipse Toggle.
👍 - если было полезно
👉 @frontendInterview
👍1
🎨 Перекрасить SVG-иконку внутри <img> прямо из CSS
SVG-иконку в
Экспериментальная возможность CSS под названием Linked Parameters предлагает другой путь. В SVG-файле цвет берётся из переменной:
Браузер без поддержки просто возьмёт запасной #c1f07c, и иконка не сломается.
Оговорка: пока это работает только за флагами в Firefox Nightly и Safari Technology Preview. Когда появится во всех браузерах, неизвестно.
Если дойдёт, иконки можно будет держать отдельными кешируемыми файлами и красить их из CSS одной строкой.
👍 - если было полезно
👉 @frontendInterview
SVG-иконку в
<img> браузер кеширует, у неё есть alt и loading="lazy". Но поменять fill отдельной фигуры внутри неё из CSS нельзя. Поэтому иконки чаще вставляют инлайном и раздувают DOM.Экспериментальная возможность CSS под названием Linked Parameters предлагает другой путь. В SVG-файле цвет берётся из переменной:
fill="env(--color, #c1f07c)". А страница передаёт значение снаружи:img {
link-parameters: param(--color, #ff8052);
}Браузер без поддержки просто возьмёт запасной #c1f07c, и иконка не сломается.
Оговорка: пока это работает только за флагами в Firefox Nightly и Safari Technology Preview. Когда появится во всех браузерах, неизвестно.
Если дойдёт, иконки можно будет держать отдельными кешируемыми файлами и красить их из CSS одной строкой.
👍 - если было полезно
👉 @frontendInterview
👍2
✨ Минус 50 строк JS, а через неделю карточки исчезли
Container queries позволяют стилям реагировать на ширину самого компонента, а не окна. Ими удобно заменить ResizeObserver, который вешал класс на карточку.
Но строка
Габриэль Шойомбо (Smashing Magazine) описывает это так: код в файле стал проще, а система вокруг сложнее.
Возвращаться к JS он не предлагает. Исправление простое: повесить container-type на обёртку, которая уже получает ширину от раскладки.
В статье ещё три сценария. Например, два безобидных на вид правила с
👍 - если было полезно
👉 @frontendInterview
Container queries позволяют стилям реагировать на ширину самого компонента, а не окна. Ими удобно заменить ResizeObserver, который вешал класс на карточку.
Но строка
container-type: inline-size делает больше, чем просят. Элемент перестаёт брать ширину из своего содержимого. Карточки-теги в flex-ряду после этого сжимаются до 0px. Обычный блок цел: его ширину задаёт родитель.Габриэль Шойомбо (Smashing Magazine) описывает это так: код в файле стал проще, а система вокруг сложнее.
Возвращаться к JS он не предлагает. Исправление простое: повесить container-type на обёртку, которая уже получает ширину от раскладки.
В статье ещё три сценария. Например, два безобидных на вид правила с
:has(), которые в замере автора в Chrome вместе пересчитывают стили у 5001 элемента вместо одного.👍 - если было полезно
👉 @frontendInterview
📱 Как не дать экранной клавиатуре закрыть нижнюю панель
Когда на телефоне выезжает клавиатура, Chrome на Android и Safari уменьшают только видимую область. Раскладка страницы не меняется, и блок с position: fixed внизу экрана может уйти под клавиатуру.
Это настраивается директивой interactive-widget в meta-теге viewport:
С resizes-content уменьшается и сама раскладка, так что фиксированная панель встаёт над клавиатурой. resizes-visual оставляет поведение по умолчанию, а overlays-content ничего не уменьшает: клавиатура ложится поверх.
Chrome и Firefox на Android директиву уже понимают. В WebKit её реализовали, но в Safari на сентябрь 2026 года её ещё не было.
Bramus, участвовавший в её создании, сам собрал WebKit и показывает все три режима в симуляторе iPhone.
👍 - если было полезно
👉 @frontendInterview
Когда на телефоне выезжает клавиатура, Chrome на Android и Safari уменьшают только видимую область. Раскладка страницы не меняется, и блок с position: fixed внизу экрана может уйти под клавиатуру.
Это настраивается директивой interactive-widget в meta-теге viewport:
<meta name="viewport" content="width=device-width, initial-scale=1.0, interactive-widget=resizes-content">
С resizes-content уменьшается и сама раскладка, так что фиксированная панель встаёт над клавиатурой. resizes-visual оставляет поведение по умолчанию, а overlays-content ничего не уменьшает: клавиатура ложится поверх.
Chrome и Firefox на Android директиву уже понимают. В WebKit её реализовали, но в Safari на сентябрь 2026 года её ещё не было.
Bramus, участвовавший в её создании, сам собрал WebKit и показывает все три режима в симуляторе iPhone.
👍 - если было полезно
👉 @frontendInterview
🔍 Как спросить у CSS не «знаешь это свойство?», а «оно работает вот так?»
Для таких случаев придумали функцию
Например, CSS Anchor Positioning (привязка элемента к другому, якорному) сначала игнорировал
Подвох: в Chrome 144–150 и Safari 27 само поведение уже есть, а проверка дала бы ложное «нет».
В статье второе ключевое слово и объяснение, почему для flex gap своего так и не завели.
👍 - если было полезно
👉 @frontendInterview
@supports проверяет только одно: может ли браузер разобрать объявление. Поэтому, когда gap заработал во флексбоксе, это было не поймать. @supports (gap: 1em) отвечал «да», ведь в Grid gap работал давно.Для таких случаев придумали функцию
named-feature(). Она принимает ключевое слово из короткого списка в спецификации, и каждое слово означает конкретное поведение.Например, CSS Anchor Positioning (привязка элемента к другому, якорному) сначала игнорировал
transform у якоря. Теперь учитывает, и это можно проверить:@supports named-feature(anchor-position-follows-transforms) {
/* ✅ The browser supports transform-aware anchor positioning! */
}Подвох: в Chrome 144–150 и Safari 27 само поведение уже есть, а проверка дала бы ложное «нет».
В статье второе ключевое слово и объяснение, почему для flex gap своего так и не завели.
👍 - если было полезно
👉 @frontendInterview
Кто в IT платит выше рынка? И какой оффер считается хорошим?
Предлагаем опытным IT-специалистам поделиться мнением о зарплатах и бенефитах в крупных технологических компаниях.
Кто, на ваш взгляд, платит выше рынка?
У кого привлекательнее премии?
Насколько важны ДМС, спорт, питание и другие бонусы при выборе работы?
👉 Поделиться мнением: https://clck.ru/3WNjo9
Предлагаем опытным IT-специалистам поделиться мнением о зарплатах и бенефитах в крупных технологических компаниях.
Кто, на ваш взгляд, платит выше рынка?
У кого привлекательнее премии?
Насколько важны ДМС, спорт, питание и другие бонусы при выборе работы?
👉 Поделиться мнением: https://clck.ru/3WNjo9
🧹 Часть библиотек в package.json браузер уже умеет сам
Зависимость ставят один раз и забывают, а браузеры тем временем учатся делать то же сами. По оценке Smashing Magazine, в типичном среднем приложении так копится от 60 до 90 КБ (gzip) лишнего кода.
Возьмём timeago.js, который превращает дату в «3 часа назад». Встроенный Intl.RelativeTimeFormat умеет то же:
Что можно убрать, подскажет Baseline, статус фичи в основных браузерах. Widely available значит, что фича есть везде уже 30 месяцев: берите. Newly available значит, что она появилась недавно: сначала проверьте аудиторию или добавьте проверку с фолбэком.
В статье ещё <dialog> вместо модальных библиотек, fetch вместо axios и случай, когда библиотеку пока лучше оставить.
👍 - если было полезно
👉 @frontendInterview
Зависимость ставят один раз и забывают, а браузеры тем временем учатся делать то же сами. По оценке Smashing Magazine, в типичном среднем приложении так копится от 60 до 90 КБ (gzip) лишнего кода.
Возьмём timeago.js, который превращает дату в «3 часа назад». Встроенный Intl.RelativeTimeFormat умеет то же:
const rtf = new Intl.RelativeTimeFormat("en", { numeric: "auto" });
rtf.format(-1, "day"); // "yesterday"
rtf.format(3, "hour"); // "in 3 hours"
rtf.format(-2, "week"); // "2 weeks ago"Что можно убрать, подскажет Baseline, статус фичи в основных браузерах. Widely available значит, что фича есть везде уже 30 месяцев: берите. Newly available значит, что она появилась недавно: сначала проверьте аудиторию или добавьте проверку с фолбэком.
В статье ещё <dialog> вместо модальных библиотек, fetch вместо axios и случай, когда библиотеку пока лучше оставить.
👍 - если было полезно
👉 @frontendInterview
🌍 Почему «!» убегает не на ту сторону
Персидский и арабский пишутся справа налево. Вставьте персидскую фразу в английское предложение, допишите в её конце «!», и браузер нарисует знак не с той стороны фразы.
Текст раскладывает Unicode Bidirectional Algorithm. У букв направление задано жёстко. А пробелы и большинство знаков препинания нейтральны: они берут направление от соседей. Если соседи смотрят в разные стороны, решает базовое направление, а оно по умолчанию слева направо.
Браузеру надо подсказать: обернуть фразу в
В этом и ловушка: персидское название книги, начатое словом CSS, он примет за текст слева направо. Тут без скрипта, который оценит всю строку, не обойтись.
Ещё в статье: почему у ChatGPT разъезжаются стрелки и числа и почему виновата отрисовка, а не модель.
👍 - если было полезно
👉 @frontendInterview
Персидский и арабский пишутся справа налево. Вставьте персидскую фразу в английское предложение, допишите в её конце «!», и браузер нарисует знак не с той стороны фразы.
Текст раскладывает Unicode Bidirectional Algorithm. У букв направление задано жёстко. А пробелы и большинство знаков препинания нейтральны: они берут направление от соседей. Если соседи смотрят в разные стороны, решает базовое направление, а оно по умолчанию слева направо.
Браузеру надо подсказать: обернуть фразу в
<span dir="rtl"> или <bdi>. Второй сам определяет направление по первой букве с жёстким направлением.В этом и ловушка: персидское название книги, начатое словом CSS, он примет за текст слева направо. Тут без скрипта, который оценит всю строку, не обойтись.
Ещё в статье: почему у ChatGPT разъезжаются стрелки и числа и почему виновата отрисовка, а не модель.
👍 - если было полезно
👉 @frontendInterview