en
Feedback
Frontend Matrix

Frontend Matrix

Open in Telegram
2 296
Subscribers
+324 hours
-47 days
-2730 days
Attracting Subscribers
September '26
September '26
+5
in 0 channels
August '26
+13
in 0 channels
Get PRO
July '26
+6
in 0 channels
Get PRO
June '26
+3
in 0 channels
Get PRO
May '260
in 0 channels
Get PRO
April '26
+2
in 0 channels
Get PRO
March '26
+36
in 8 channels
Get PRO
February '26
+3
in 0 channels
Get PRO
January '26
+7
in 0 channels
Get PRO
December '25
+477
in 66 channels
Get PRO
November '25
+190
in 44 channels
Get PRO
October '25
+3
in 0 channels
Get PRO
September '25
+1
in 0 channels
Get PRO
August '25
+84
in 6 channels
Get PRO
July '25
+276
in 2 channels
Get PRO
June '25
+260
in 45 channels
Get PRO
May '25
+794
in 27 channels
Get PRO
April '25
+43
in 76 channels
Get PRO
March '25
+101
in 2 channels
Get PRO
February '25
+122
in 4 channels
Get PRO
January '25
+108
in 1 channels
Get PRO
December '24
+126
in 1 channels
Get PRO
November '24
+221
in 2 channels
Get PRO
October '24
+267
in 1 channels
Get PRO
September '24
+1 004
in 1 channels
Date
Subscriber Growth
Mentions
Channels
06 September+1
05 September+3
04 September+1
03 September0
02 September0
01 September0
\n \n \n \n","datePublished":"2026-07-10T16:35:22Z","dateModified":"2026-07-10T16:35:22Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":302},{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":1},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":10}]}},{"@type":"ListItem","position":11,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1997","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1997","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1997","headline":"🖼️ Адаптивные картинки без CSS-медиазапросов Загружать огромную десктопную картинку на мобильном телефоне — г…","articleBody":"🖼️ Адаптивные картинки без CSS-медиазапросов\n\nЗагружать огромную десктопную картинку на мобильном телефоне — грех против оптимизации и скорости загрузки. Вместо кучи классов в CSS, используй правильный HTML-тег .\n\n⚡ Идеальное решение для адаптивности:\n\n \n \n \n \n \n \n \n \"Главный\n\n\nБраузер скачает только один нужный файл, экономя трафик пользователя. А формат .webp уменьшит вес картинки ещё в пару раз.","datePublished":"2026-07-10T09:31:08Z","dateModified":"2026-07-10T09:31:08Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":244},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":10}]}},{"@type":"ListItem","position":12,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1996","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1996","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1996","headline":"🔥 CSS-киллер: Забудь про JavaScript там, где он больше не нужен Раньше, чтобы изменить стиль родительского эл…","articleBody":"🔥 CSS-киллер: Забудь про JavaScript там, где он больше не нужен\n\nРаньше, чтобы изменить стиль родительского элемента при клике или наведении на дочерний, нам приходилось подключать JS и навешивать классы вроде .is-active. Теперь CSS умеет делать это сам благодаря псевдоклассу :has().\n\nJS\nconst input = document.querySelector('.input-field');\ninput.addEventListener('focus', () => {\n input.parentElement.classList.add('highlight');\n});\n\n\nCSS\n/* Выбираем карточку (.card), ТОЛЬКО если внутри неё есть фокус на инпуте */\n.card:has(.input-field:focus) {\n border-color: #007bff;\n box-shadow: 0 0 10px rgba(0, 123, 255, 0.5);\n}","datePublished":"2026-07-09T16:23:02Z","dateModified":"2026-07-09T16:23:02Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":224},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":5}]}},{"@type":"ListItem","position":13,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1995","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1995","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1995","headline":"⚖️ JS: В чем разница между || и ??? До появления оператора (??) все использовали логическое ИЛИ для задания д…","articleBody":"⚖️ JS: В чем разница между || и ???\n\nДо появления оператора (??) все использовали логическое ИЛИ для задания дефолтных значений. Но у ИЛИ есть скрытая проблема: он считает ложными числа 0 и пустые строки:\nconst config = {\n volume: 0 // Пользователь выкрутил звук в ноль\n};\n\nconst currentVolume = config.volume || 50; \nconsole.log(currentVolume); // Выведет 50! Ноль проигнорировался.\nОператор (??) заменяет значение только в том случае, если слева стоит null или undefined.\nconst currentVolume = config.volume ?? 50;\nconsole.log(currentVolume); // Выведет 0. Всё работает корректно!\nИспользуйте (||) для строк или булевых значений, а (??) — когда работаете с числами, где ноль имеет значение.","datePublished":"2026-07-06T16:47:01Z","dateModified":"2026-07-06T16:47:01Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":259},{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":1},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":5}]}},{"@type":"ListItem","position":14,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1994","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1994","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1994","headline":"​🔍 Как поймать исчезающий :hover Вы хотите настроить стили для всплывающей подсказки или меню, которое появля…","articleBody":"​🔍 Как поймать исчезающий :hover\n\nВы хотите настроить стили для всплывающей подсказки или меню, которое появляется только при наведении мыши. Но стоит увести курсор в панель разработчика, как элемент исчезает\n\n​Как зафиксировать это состояние в Chrome/Firefox:\n1.​Откройте DevTools (F12) и выберите нужный элемент в инспекторе.\n2.​В правой панели со стилями найдите маленькую кнопку :hov (Toggle Element State).\n3.​Поставьте галочку напротив :hover, :focus или :active.\n\n​Теперь элемент намертво застынет в этом состоянии","datePublished":"2026-07-06T09:33:18Z","dateModified":"2026-07-06T09:33:18Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":257},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":3}]}},{"@type":"ListItem","position":15,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1993","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1993","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1993","headline":"🧹 Удаляем дубликаты из массива в одну строчку ​Забудьте про циклы for или сложные конструкции с .filter(), ес…","articleBody":"🧹 Удаляем дубликаты из массива в одну строчку\n\n​Забудьте про циклы for или сложные конструкции с .filter(), если вам нужно просто очистить массив от повторяющихся значений. Для этого есть встроенный объект Set, который хранит только уникальные элементы.\n\n​🛠 Код:\nconst numbers = [1, 2, 2, 3, 4, 4, 4, 5];\n\n// Используем Set и спред-оператор (...)\nconst uniqueNumbers = [...new Set(numbers)];\n\nconsole.log(uniqueNumbers); \n// Вывод: [1, 2, 3, 4, 5]\nРаботает максимально быстро и выглядит очень лаконично.","datePublished":"2026-07-05T16:37:01Z","dateModified":"2026-07-05T16:37:01Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":248},{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":1},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":1}]}},{"@type":"ListItem","position":16,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1992","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1992","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1992","headline":"​💡 Как правильно использовать useActionState в React 19 ​Новый хук useActionState — лучший способ обрабатыват…","articleBody":"​💡 Как правильно использовать useActionState в React 19\n\n​Новый хук useActionState — лучший способ обрабатывать отправку форм без ручного контроля.\n// React 19+\nconst [state, formAction, isPending] = useActionState(\n async (prevState, formData) => {\n const result = await updateProfile(formData.get(\"name\"));\n return result; // Новый state\n },\n { success: null } // Дефолтный state\n);\n\nreturn (\n
\n \n \n
\n);\nМинимум бойлерплейта, максимум производительности.","datePublished":"2026-07-02T09:05:30Z","dateModified":"2026-07-02T09:05:30Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":289},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":4}]}},{"@type":"ListItem","position":17,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1991","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1991","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1991","headline":"​🔥 CSS Anchor Positioning: Забудьте про тяжелые JS-библиотеки для поповеров и дропдаунов ​Раньше для создания…","articleBody":"​🔥 CSS Anchor Positioning: Забудьте про тяжелые JS-библиотеки для поповеров и дропдаунов\n\n​Раньше для создания всплывающих подсказок, дропдаунов или контекстных меню, которые привязаны к определенной кнопке и не ломаются при скролле, приходилось тащить тяжелые JS-библиотеки вроде Popper.js или Floating UI.\n\n​Теперь CSS Anchor Positioning полностью поддерживается всеми современными браузерами и позволяет делать это нативно\n\n/* 1. Элемент-источник (кнопка) */\n.anchor-button {\n anchor-name: --my-anchor;\n}\n\n/* 2. Всплывающее меню */\n.dropdown-menu {\n position: absolute;\n /* Привязываем топ меню к низу кнопки */\n top: anchor(--my-anchor bottom);\n /* Выравниваем по левому краю кнопки */\n left: anchor(--my-anchor left);\n}","datePublished":"2026-07-01T16:21:01Z","dateModified":"2026-07-01T16:21:01Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":269},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":7}]}},{"@type":"ListItem","position":18,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1990","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1990","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1990","headline":"​💡 CSS-магия: Свойство field-sizing для адаптивных инпутов ​Раньше, чтобы текстовое поле автоматически растяг…","articleBody":"​💡 CSS-магия: Свойство field-sizing для адаптивных инпутов\n\n​Раньше, чтобы текстовое поле автоматически растягивалось по высоте или ширине в зависимости от введенного пользователем текста, приходилось писать костыли на JavaScript.\n\n​Теперь в CSS появилось нативное решение — field-sizing: content;!\n\ntextarea {\n field-sizing: content;\n min-height: 40px;\n max-height: 200px;\n}\nПоле изначально имеет минимальную высоту, но как только пользователь начинает печатать и текст не помещается, инпут автоматически растет вниз, пока не упрется в max-height. Без единой строчки JS","datePublished":"2026-06-30T16:28:01Z","dateModified":"2026-06-30T16:28:01Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":260},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":8}]}},{"@type":"ListItem","position":19,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1989","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1989","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1989","headline":"Облегченные JS-фреймворки ​Пока React 19 и Next.js усложняют архитектуру, набирает обороты ренессанс легковес…","articleBody":"Облегченные JS-фреймворки\n\n​Пока React 19 и Next.js усложняют архитектуру, набирает обороты ренессанс легковесных инструментов (Arrow.js, Kasper). Их главная фишка — отсутствие этапа сборки, простая реактивность на прокси/сигналах и файлы llms.txt в корне.\n\n Разработчики этих фреймворков прямо заявляют: архитектура спроектирована так, чтобы ИИ-ассистенты (вроде Cursor или Copilot) могли писать код без галлюцинаций.","datePublished":"2026-06-27T09:29:01Z","dateModified":"2026-06-27T09:29:01Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":319},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":3}]}},{"@type":"ListItem","position":20,"item":{"@type":"SocialMediaPosting","@id":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1988","url":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1988","mainEntityOfPage":"https://telemetr.io/en/channels/2192399946-frontendno/posts/1988","headline":"Tailwind v4.0⚡️ Четвертая версия Tailwind окончательно захватила продакшен, и если вы еще не обновились — сам…","articleBody":"Tailwind v4.0⚡️\nЧетвертая версия Tailwind окончательно захватила продакшен, и если вы еще не обновились — самое время. Главная фишка релиза — тотальный уход от JavaScript в сторону нативного CSS.\n\nБольше никаких портянок на JS.\n\nДвижок Oxide на Rust: Скорость сборки выросла в 10 раз. Стили компилируются буквально за миллисекунды.\n\nTailwind теперь сам умеет в вендорные префиксы и нативную вложенность.","datePublished":"2026-06-24T09:19:01Z","dateModified":"2026-06-24T09:19:01Z","author":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"publisher":{"@type":"Organization","name":"Frontend Matrix","url":"https://telemetr.io/en/channels/2192399946-frontendno","image":"https://img.tlmtr.io/c/2on5ns/5244961814058044500?ty=x"},"commentCount":0,"interactionStatistic":[{"@type":"InteractionCounter","interactionType":"https://schema.org/ViewAction","userInteractionCount":348},{"@type":"InteractionCounter","interactionType":"https://schema.org/LikeAction","userInteractionCount":1},{"@type":"InteractionCounter","interactionType":"https://schema.org/ShareAction","userInteractionCount":4}]}}]}
Channel Posts
@starting-style: Анимации при первом рендере без JS Раньше, чтобы плавно анимировать появление элемента, который только что добавили в DOM, приходилось изворачиваться через requestAnimationFrame, setTimeout или тяжелые JS-библиотеки. Теперь во всех современных браузерах работает нативный метод @starting-style
/* Исходное состояние элемента в DOM */
.dialog {
  opacity: 1;
  transform: scale(1);
  transition: opacity 0.3s, transform 0.3s;
}

/* Свойства, из которых НАЧИНАЕТСЯ анимация при монтировании */
@starting-style {
  .dialog {
    opacity: 0;
    transform: scale(0.9);
  }
}

2
💡 JS: Удобная группировка массивов без сторонних библиотек Забудь про громоздкие .reduce() или подключение lodash.groupBy только ради одной операции. В современном JS группировка уже встроена const products = [ { name: "MacBook", category: "electronics" }, { name: "T-Shirt", category: "clothes" }, { name: "iPhone", category: "electronics" } ]; // Группируем по ключу category: const grouped = Object.groupBy(products, product => product.category); console.log(grouped); /* { electronics: [{ name: "MacBook", ... }, { name: "iPhone", ... }], clothes: [{ name: "T-Shirt", ... }] } */ Чистый, читаемый код без внешних зависимостей.
272
3
🛠 JS / API: AbortController — отмена запросов при размонтировании компонента Если пользователь переключает табы или быстро вводит текст в инпут, старые фетч-запросы продолжают лететь по сети и могут затереть актуальные данные let currentController = null; async function searchData(query) { // Отменяем предыдущий запрос, если он еще выполняется if (currentController) { currentController.abort(); } currentController = new AbortController(); try { const res = await fetch(`/api/search?q=${query}`, { signal: currentController.signal // Передаем сигнал отмены }); const data = await res.json(); renderResults(data); } catch (err) { if (err.name === 'AbortError') { console.log('Запрос отменен, все в порядке'); } } }
229
4
🎯 TypeScript: Забудь про as — используй Type Guards для безопасной типизации Использование as SomeType (Type Assertion) отключает проверку типов и приводит к багам в рантайме. Пиши кастомные предикаты типов interface User { id: number; name: str; } interface Guest { isGuest: true; } type Account = User | Guest; # ❌ Опасно (компилятор поверит на слово, а в рантайме может быть undefined): function getNameUnsafe(account: Account) { return (account as User).name; } # ✅ Безопасно через Type Guard (возвращаемый тип `account is User`): function isUser(account: Account): account is User { return 'name' in account; } function getNameSafe(account: Account) { if (isUser(account)) { return account.name; // TS автодополнением видит, что это User } return "Гость"; }
231
5
⚡️ CSS: Забудь про JavaScript для отслеживания размера элементов Раньше для адаптива компонентов в зависимости от размера их контейнера приходилось подключать ResizeObserver на JS. Теперь это чистое CSS-решение. /* 1. Объявляем контейнер */ .card-wrapper { container-type: inline-size; } /* 2. Верстаем внутри карточки с привязкой к ее ширине */ .card-title { /* 5cqw = 5% от ширины КОНТЕЙНЕРА, а не экрана */ font-size: clamp(1rem, 5cqw, 2rem); } /* Адаптируем сетку карточки по ее собственному размеру */ @container (min-width: 400px) { .card { display: grid; grid-template-columns: 1fr 2fr; } } Виджет/карточка будет идеально выглядеть и в узкой боковой колонке, и во всю ширину экрана без единой строчки JS-кода.
196
6
🚀 Прекратите использовать Array.includes() внутри циклов Типичная ошибка при работе с большими массивами на фронтенде — поиск элементов через .includes() или .find() внутри фильтрации. const blockedUserIds = [102, 405, 981, ...10000_items]; const allUsers = [...50000_items]; // ❌ ПЛОХО: O(N * M) — массив фильтруется очень долго const activeUsers = allUsers.filter(user => !blockedUserIds.includes(user.id) ); // ✅ БЫСТРО: O(N) — переводим список в Set const blockedSet = new Set(blockedUserIds); const activeUsers = allUsers.filter(user => !blockedSet.has(user.id) // Хеш-таблица: поиск за O(1) ); Замена массива на Set с операцией .has() ускоряет фильтрацию больших списков в десятки раз за счет константного времени поиска $O(1)$
184
7
⚡️ Как ускорить рендеринг длинных страниц одной строчкой CSS Если у вас на странице много контента (лента постов, длинные таблицы, каталоги товаров), браузер тратит кучу ресурсов на расчет макета и отрисовку даже тех элементов, которые находятся глубоко за пределами экрана. Свойство content-visibility: auto указывает браузеру пропустить рендеринг элементов до тех пор, пока они не окажутся близко к области видимости. .card-item { /* Браузер не рендерит содержимое карточки, пока она вне экрана */ content-visibility: auto; /* Задаем примерную высоту, чтобы скроллбар не прыгал */ contain-intrinsic-size: 0 300px; }
194
8
🧬 Хватит использовать JSON.parse(JSON.stringify()) для глубокого клонирования Когда нужно сделать полную копию сложного объекта (со всеми вложенностями), старый трюк с JSON часто ломает данные: он не умеет копировать даты (Date), регулярные выражения (RegExp), карты (Map/Set) и циклические ссылки. В современном JS есть встроенный инструмент, который делает это правильно и быстро — structuredClone. const user = { name: "Alex", joined: new Date(), skills: new Set(["JS", "React"]), }; // ❌ Старый костыльный метод (дата превратится в строку, Set просто исчезнет) const badCopy = JSON.parse(JSON.stringify(user)); // ✅ Современный нативный метод (все типы данных сохраняются!) const goodCopy = structuredClone(user); console.log(goodCopy.joined instanceof Date); // true console.log(goodCopy.skills); // Set(2) {"JS", "React"}
306
9
⚡️ Как победить «прыгающий» скроллбар в CSS? Бывало такое: переходишь между страницами, на одной контента мало (скроллбара нет), на другой много (скроллбар появляется), и весь интерфейс неприятно дергается в сторону? html { /* Резервирует место под скроллбар заранее, предотвращая сдвиг контента */ scrollbar-gutter: stable; } Браузер сразу оставляет пустой трек под полосу прокрутки. Когда она появляется, контент никуда не смещается.
253
10
Куда ты вставляешь тег <script>? Если до сих пор по старинке бросаешь его в самый низ тега <body>, пора это менять. Современный стандарт — подключать скрипты в <head>, но с правильными атрибутами. 💡 Коротко разница: 1️⃣ Обычный <script src="..."> Блокирует чтение HTML. Браузер видит скрипт, останавливает загрузку страницы, скачивает его, выполняет, и только потом строит сайт дальше. Привет, тормоза. 2️⃣ <script async src="..."> Загружается параллельно с HTML. Как только скачался — HTML замирает, и скрипт выполняется. Отлично подходит для независимых вещей: метрики, аналитика, пиксели соцсетей. 3️⃣ <script defer src="..."> Загружается в фоновом режиме, вообще не мешая HTML. Выполняется строго после того, как вся страница полностью загрузилась. Идеально для основного кода твоего приложения. ⚡ Как делать правильно: <head> <!-- Основная логика сайта: загрузится в фоне, выполнится вовремя --> <script defer src="app.js"></script> <!-- Яндекс.Метрика или Google Analytics: не ждет никого --> <script async src="analytics.js"></script> </head>
302
11
🖼️ Адаптивные картинки без CSS-медиазапросов Загружать огромную десктопную картинку на мобильном телефоне — грех против оптимизации и скорости загрузки. Вместо кучи классов в CSS, используй правильный HTML-тег <picture>. ⚡ Идеальное решение для адаптивности: <picture> <!-- Если экран смартфона (до 768px), грузим легкую вертикальную фотку --> <source media="(max-width: 768px)" srcset="mobile-hero.webp"> <!-- Для больших экранов — полноценный баннер --> <source media="(min-width: 769px)" srcset="desktop-hero.webp"> <!-- Запасной вариант для старых браузеров --> <img src="desktop-hero.jpg" alt="Главный баннер" loading="lazy"> </picture> Браузер скачает только один нужный файл, экономя трафик пользователя. А формат .webp уменьшит вес картинки ещё в пару раз.
244
12
🔥 CSS-киллер: Забудь про JavaScript там, где он больше не нужен Раньше, чтобы изменить стиль родительского элемента при клике или наведении на дочерний, нам приходилось подключать JS и навешивать классы вроде .is-active. Теперь CSS умеет делать это сам благодаря псевдоклассу :has(). JS const input = document.querySelector('.input-field'); input.addEventListener('focus', () => { input.parentElement.classList.add('highlight'); }); CSS /* Выбираем карточку (.card), ТОЛЬКО если внутри неё есть фокус на инпуте */ .card:has(.input-field:focus) { border-color: #007bff; box-shadow: 0 0 10px rgba(0, 123, 255, 0.5); }
224
13
⚖️ JS: В чем разница между || и ??? До появления оператора (??) все использовали логическое ИЛИ для задания дефолтных значений. Но у ИЛИ есть скрытая проблема: он считает ложными числа 0 и пустые строки: const config = { volume: 0 // Пользователь выкрутил звук в ноль }; const currentVolume = config.volume || 50; console.log(currentVolume); // Выведет 50! Ноль проигнорировался. Оператор (??) заменяет значение только в том случае, если слева стоит null или undefined. const currentVolume = config.volume ?? 50; console.log(currentVolume); // Выведет 0. Всё работает корректно! Используйте (||) для строк или булевых значений, а (??) — когда работаете с числами, где ноль имеет значение.
259
14
​🔍 Как поймать исчезающий :hover Вы хотите настроить стили для всплывающей подсказки или меню, которое появляется только при наведении мыши. Но стоит увести курсор в панель разработчика, как элемент исчезает ​Как зафиксировать это состояние в Chrome/Firefox: 1.​Откройте DevTools (F12) и выберите нужный элемент в инспекторе. 2.​В правой панели со стилями найдите маленькую кнопку :hov (Toggle Element State). 3.​Поставьте галочку напротив :hover, :focus или :active. ​Теперь элемент намертво застынет в этом состоянии
257
15
🧹 Удаляем дубликаты из массива в одну строчку ​Забудьте про циклы for или сложные конструкции с .filter(), если вам нужно просто очистить массив от повторяющихся значений. Для этого есть встроенный объект Set, который хранит только уникальные элементы. ​🛠 Код: const numbers = [1, 2, 2, 3, 4, 4, 4, 5]; // Используем Set и спред-оператор (...) const uniqueNumbers = [...new Set(numbers)]; console.log(uniqueNumbers); // Вывод: [1, 2, 3, 4, 5] Работает максимально быстро и выглядит очень лаконично.
248
16
​💡 Как правильно использовать useActionState в React 19 ​Новый хук useActionState — лучший способ обрабатывать отправку форм без ручного контроля. // React 19+ const [state, formAction, isPending] = useActionState( async (prevState, formData) => { const result = await updateProfile(formData.get("name")); return result; // Новый state }, { success: null } // Дефолтный state ); return ( <form action={formAction}> <input name="name" /> <button disabled={isPending}> {isPending ? 'Сохранение...' : 'Обновить'} </button> </form> ); Минимум бойлерплейта, максимум производительности.
289
17
​🔥 CSS Anchor Positioning: Забудьте про тяжелые JS-библиотеки для поповеров и дропдаунов ​Раньше для создания всплывающих подсказок, дропдаунов или контекстных меню, которые привязаны к определенной кнопке и не ломаются при скролле, приходилось тащить тяжелые JS-библиотеки вроде Popper.js или Floating UI. ​Теперь CSS Anchor Positioning полностью поддерживается всеми современными браузерами и позволяет делать это нативно /* 1. Элемент-источник (кнопка) */ .anchor-button { anchor-name: --my-anchor; } /* 2. Всплывающее меню */ .dropdown-menu { position: absolute; /* Привязываем топ меню к низу кнопки */ top: anchor(--my-anchor bottom); /* Выравниваем по левому краю кнопки */ left: anchor(--my-anchor left); }
269
18
​💡 CSS-магия: Свойство field-sizing для адаптивных инпутов ​Раньше, чтобы текстовое поле автоматически растягивалось по высоте или ширине в зависимости от введенного пользователем текста, приходилось писать костыли на JavaScript. ​Теперь в CSS появилось нативное решение — field-sizing: content;! textarea { field-sizing: content; min-height: 40px; max-height: 200px; } Поле изначально имеет минимальную высоту, но как только пользователь начинает печатать и текст не помещается, инпут автоматически растет вниз, пока не упрется в max-height. Без единой строчки JS
260
19
Облегченные JS-фреймворки ​Пока React 19 и Next.js усложняют архитектуру, набирает обороты ренессанс легковесных инструментов
Облегченные JS-фреймворки ​Пока React 19 и Next.js усложняют архитектуру, набирает обороты ренессанс легковесных инструментов (Arrow.js, Kasper). Их главная фишка — отсутствие этапа сборки, простая реактивность на прокси/сигналах и файлы llms.txt в корне. Разработчики этих фреймворков прямо заявляют: архитектура спроектирована так, чтобы ИИ-ассистенты (вроде Cursor или Copilot) могли писать код без галлюцинаций.
319
20
Tailwind v4.0⚡️ Четвертая версия Tailwind окончательно захватила продакшен, и если вы еще не обновились — самое время. Главна
Tailwind v4.0⚡️ Четвертая версия Tailwind окончательно захватила продакшен, и если вы еще не обновились — самое время. Главная фишка релиза — тотальный уход от JavaScript в сторону нативного CSS. Больше никаких портянок на JS. Движок Oxide на Rust: Скорость сборки выросла в 10 раз. Стили компилируются буквально за миллисекунды. Tailwind теперь сам умеет в вендорные префиксы и нативную вложенность.
348