en
Feedback
Frontend Matrix

Frontend Matrix

Open in Telegram
2 296
Subscribers
+324 hours
-47 days
-2730 days
Posts Archive
\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}]}}]}
@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);
  }
}

💡 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", ... }]
}
*/
Чистый, читаемый код без внешних зависимостей.

🛠 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('Запрос отменен, все в порядке');
    }
  }
}

🎯 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 "Гость";
}

⚡️ 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-кода.

🚀 Прекратите использовать 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)$

⚡️ Как ускорить рендеринг длинных страниц одной строчкой CSS Если у вас на странице много контента (лента постов, длинные таблицы, каталоги товаров), браузер тратит кучу ресурсов на расчет макета и отрисовку даже тех элементов, которые находятся глубоко за пределами экрана. Свойство content-visibility: auto указывает браузеру пропустить рендеринг элементов до тех пор, пока они не окажутся близко к области видимости.
.card-item {
  /* Браузер не рендерит содержимое карточки, пока она вне экрана */
  content-visibility: auto;
  
  /* Задаем примерную высоту, чтобы скроллбар не прыгал */
  contain-intrinsic-size: 0 300px; 
}

🧬 Хватит использовать 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"}

⚡️ Как победить «прыгающий» скроллбар в CSS? Бывало такое: переходишь между страницами, на одной контента мало (скроллбара нет), на другой много (скроллбар появляется), и весь интерфейс неприятно дергается в сторону?
html {
  /* Резервирует место под скроллбар заранее, предотвращая сдвиг контента */
  scrollbar-gutter: stable; 
}
Браузер сразу оставляет пустой трек под полосу прокрутки. Когда она появляется, контент никуда не смещается.

Куда ты вставляешь тег <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>

🖼️ Адаптивные картинки без 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 уменьшит вес картинки ещё в пару раз.

🔥 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);
}

⚖️ 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. Всё работает корректно!
Используйте (||) для строк или булевых значений, а (??) — когда работаете с числами, где ноль имеет значение.

​🔍 Как поймать исчезающий :hover Вы хотите настроить стили для всплывающей подсказки или меню, которое появляется только при наведении мыши. Но стоит увести курсор в панель разработчика, как элемент исчезает ​Как зафиксировать это состояние в Chrome/Firefox: 1.​Откройте DevTools (F12) и выберите нужный элемент в инспекторе. 2.​В правой панели со стилями найдите маленькую кнопку :hov (Toggle Element State). 3.​Поставьте галочку напротив :hover, :focus или :active. ​Теперь элемент намертво застынет в этом состоянии

🧹 Удаляем дубликаты из массива в одну строчку ​Забудьте про циклы 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]
Работает максимально быстро и выглядит очень лаконично.

​💡 Как правильно использовать 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>
);
Минимум бойлерплейта, максимум производительности.

​🔥 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);
}

​💡 CSS-магия: Свойство field-sizing для адаптивных инпутов ​Раньше, чтобы текстовое поле автоматически растягивалось по высоте или ширине в зависимости от введенного пользователем текста, приходилось писать костыли на JavaScript. ​Теперь в CSS появилось нативное решение — field-sizing: content;!
textarea {
  field-sizing: content;
  min-height: 40px;
  max-height: 200px;
}
Поле изначально имеет минимальную высоту, но как только пользователь начинает печатать и текст не помещается, инпут автоматически растет вниз, пока не упрется в max-height. Без единой строчки JS

Облегченные JS-фреймворки ​Пока React 19 и Next.js усложняют архитектуру, набирает обороты ренессанс легковесных инструментов
Облегченные JS-фреймворки ​Пока React 19 и Next.js усложняют архитектуру, набирает обороты ренессанс легковесных инструментов (Arrow.js, Kasper). Их главная фишка — отсутствие этапа сборки, простая реактивность на прокси/сигналах и файлы llms.txt в корне. Разработчики этих фреймворков прямо заявляют: архитектура спроектирована так, чтобы ИИ-ассистенты (вроде Cursor или Copilot) могли писать код без галлюцинаций.

Tailwind v4.0⚡️ Четвертая версия Tailwind окончательно захватила продакшен, и если вы еще не обновились — самое время. Главна
Tailwind v4.0⚡️ Четвертая версия Tailwind окончательно захватила продакшен, и если вы еще не обновились — самое время. Главная фишка релиза — тотальный уход от JavaScript в сторону нативного CSS. Больше никаких портянок на JS. Движок Oxide на Rust: Скорость сборки выросла в 10 раз. Стили компилируются буквально за миллисекунды. Tailwind теперь сам умеет в вендорные префиксы и нативную вложенность.