👩💻 Всем программистам посвящается!
Вот 14 авторских обучающих IT каналов по самым востребованным областям программирования:
Выбирай своё направление:
👩💻 Python — t.me/python_ready
🤔 InfoSec & Хакинг — t.me/hacking_ready
🖥 SQL & Базы Данных — t.me/sql_ready
👩💻 IT Новости — t.me/it_ready
🤖 AI & ML — t.me/neuro_ready
👩💻 Frontend — t.me/frontend_ready
👩💻 C/C++ — https://t.me/cpp_ready
👩💻 C# & Unity — t.me/csharp_ready
👩💻 Linux — t.me/linux_ready
👩💻 Java — t.me/java_ready
📖 IT Книги — t.me/books_ready
📱 JavaScript — t.me/javascript_ready
🖼️ DevOps — t.me/devops_ready
🖥 Design — t.me/design_ready
📌 Гайды, шпаргалки, задачи, ресурсы и фишки для каждого языка программирования!
Telegram
Telegram Channel Link
Senior Frontend - javascript, html, css Telegram Channel Link
👁
2
views
↗
0
joins
About this telegram channel link
Senior Front - канал для frontend программистов, практические задачки, проверки знаний, интересные статьи Админ, сотрудничество, реклама:@seniorFrontPromo, @maria_seniorfront Канал в реестре РКН https://rknn.link/bsU
Telegram Channel
Latest public posts from this Telegram channel.
554
subscribers
03 Nov 2026
latest post
⚡ React Context без лишних ререндеровВ контексте лежат
name и age. Меняется age, и заодно перерисовывается компонент, которому нужен только name. React сравнивает value провайдера целиком, а это каждый раз новый объект. useMemo и React.memo тут не спасают.Выход в статье простой. Через Context передают не данные, а объект доступа к ним:
{ get, set, subscribe }. Он создаётся один раз, ссылка не меняется, и сам контекст никого не ререндерит.Данные живут в
useRef. Каждый компонент через useSyncExternalStore подписывается только на своё поле. Ввели возраст, и перерисовался один инпут возраста.Есть цена: обновление такого стора внутри
startTransition всё равно станет блокирующим, в фон его не увести.В статье объяснено, зачем здесь именно useSyncExternalStore и почему store нельзя заворачивать в useMemo.
👍 - если было полезно
👉 @seniorFront
🔑 Один key, и редактор не путает заметкиQuill, WYSIWYG-редактор для браузера, хранит часть состояния внутри себя: выделение и историю undo. React об этом состоянии ничего не знает.
Когда один редактор по очереди показывает разные заметки, React переиспользует его экземпляр. Меняется только value, а выделение и история прошлой заметки остаются внутри Quill.
Решение простое: передать в key id заметки.
<NoteEditorClient
key={note.id}
value={note.contentHtml}
onChange={(nextHtml) =>
updateNoteContent(note.id, nextHtml)
}
/>
Новый key заставляет React размонтировать старый редактор и создать чистый. Состояние предыдущей заметки в следующую не переходит.
Обратная сторона: при каждом переключении история undo начинается с нуля.
В статье этот редактор ещё получает автосохранение после паузы во вводе и режим чтения с подсветкой кода.
👍 - если было полезно
👉 @seniorFront
🐶 Как пустить элемент гулять по краю блока на чистом CSSСвойство
offset-path задаёт путь, а offset-distance ставит элемент в точку на нём. Путь не обязательно рисовать вручную: подойдёт рамка родителя, offset-path: border-box.Например, с
offset-distance: 20% кружок встаёт на рамку, пройдя пятую часть периметра. Пикселей нет, поэтому при смене размера блока всё подстраивается.Дальше автор анимирует
offset-distance через @keyframes, а значения берёт из CSS-функции random(). Кружок бегает по краю в случайные точки. Сработало, только когда у каждого вызова появился свой ключ вроде --k1.Главная загвоздка в поддержке: на момент статьи random() есть в Safari, а в Chrome только за флагом. В других браузерах элемент просто стоит на месте, так что пригодится запасной вариант через @supports.
В статье из этого вырастают светящийся шар и собачка, гуляющая по краю блока.
👍 - если было полезно
👉 @seniorFront
✨ Стили в JS без возни со строкамиЧерез getComputedStyle() любое значение стиля приходит строкой, даже opacity. Прибавить 20px к ширине значит отрезать единицы, посчитать и склеить строку обратно.
CSS Typed OM, браузерное API, отдаёт значения объектами, где число и единица лежат отдельно:
const styles = $el.computedStyleMap();
const opacity = styles.get('opacity');
console.log(opacity); // CSSUnitValue
console.log(opacity.value); // 0.2
console.log(opacity.unit); // 'number
Chromium и Safari его поддерживают, а в Firefox оно пока за флагом. Новый полифил css-typed-om добавляет API туда, где его нет, и проходит около 95% официальных Web Platform Tests.
Но currentcolor полифил не распознает: его computedStyleMap() читает getComputedStyle(), а там уже готовый цвет вроде rgb(0, 0, 0).
Откуда остальные 5% и почему часть из них даже к лучшему, рассказано в оригинале.
👍 - если было полезно
👉 @seniorFront
🎬 Анимации в React без библиотеки анимацийВ React 19.3 стабильным стал компонент
<ViewTransition>. React обновляет DOM, а саму анимацию между старым и новым состоянием рисует браузер через View Transition API.Один разработчик собрал приложение дважды: на библиотеке Motion и на ViewTransition с CSS. Модалку, сортировку, навигацию и перелёт картинки в шапку удалось повторить и без библиотеки.
Есть ловушка, на которой спотыкаешься сразу. Обычный setState меняет экран мгновенно, а анимация запускается, только если обновление обёрнуто в startTransition:
setIsOpen(true); // instant cut
startTransition(() => setIsOpen(true)); // animates
JavaScript в сборке похудел примерно на 39 КБ в gzip. Но пришлось написать 179 строк CSS для анимаций, а JSX в обеих версиях вышел почти одинаковым.
В статье все четыре эффекта и разбор, почему по замерам скорости победителя пока не назвать.
👍 - если было полезно
👉 @seniorFront
🚀 Дробить тяжёлый JS через setTimeout(0) дороже, чем кажетсяJavaScript в браузере работает в одном главном потоке. Пока там крутится долгий цикл, клики ждут. Обычно работу режут на порции и между ними отдают управление браузеру, чтобы он успел обработать ввод.
Паузу часто делают через
setTimeout(0). Но при глубокой вложенности таймеров спецификация HTML добавляет минимум 4 мс, а продолжение уходит в конец очереди. На стенде автора (500 тыс. записей, Chromium) время до результата выросло со 106 мс без дробления до 380 мс.scheduler.yield() ставит продолжение в начало очереди своего приоритета, без искусственной паузы. Итог: 125 мс, а клик обрабатывается примерно через 1 мс.Учтите, что scheduler.yield() есть не во всех браузерах: по данным статьи, его нет в Safari. Автор предлагает запасной вариант на MessageChannel.
А ещё в статье сравнение с Web Worker и подбор размера порции по времени.
👍 - если было полезно
👉 @seniorFront
🛡️ Превью для HTML от нейросети может читать вашу страницуМодель вернула HTML, и его надо показать. Первое решение: iframe с srcdoc. Стили изолированы, выглядит аккуратно.
Но такой iframe без атрибута sandbox работает на том же origin, что и ваше приложение. Код, который никто не читал, может достать parent.document, localStorage и куки.
Защита начинается с одного атрибута:
<iframe sandbox="allow-scripts" srcdoc="..."></iframe>
Теперь у фрейма «непрозрачный» origin, который не совпадает ни с чьим. Та же попытка прочитать страницу падает с SecurityError.
Однако сеть sandbox не ограничивает. fetch() или картинка с внешним src всё ещё могут унести данные в URL. Это отдельно закрывает Content Security Policy с default-src 'none' в документе превью.
В статье ещё атаки через навигацию и popup и превью, собранное так, чтобы их выдерживать.
👍 - если было полезно
👉 @seniorFront
🎯 Почему иконка рядом с текстом встаёт кривоПо умолчанию иконка в строке садится на базовую линию шрифта и смотрится задранной. Flexbox ставит её в математический центр, но и это мимо: у шрифта разный запас «воздуха» над буквами и под ними.
Армен Аллахвердян из дизайн-системы Авито предлагает другой ориентир: оптический центр, то есть середину между базовой линией и верхом заглавных букв. Высоту заглавных в CSS даёт юнит cap.
Иконку абсолютно кладут в пустой inline-block span, который сидит на базовой линии. Дальше одна формула:
bottom: calc((1cap - var(--icon-height)) / 2);
Для Avito Sans в 15px выходит -2,1px: иконка выше заглавных и чуть уходит под базовую линию.
Формула, правда, не учитывает форму рисунка: сердечко с тяжёлым верхом всё равно кажется выше. На этот случай в статье есть второй способ.
А ещё там разобрано, почему популярный vertical-align: -0.125em работает далеко не со всеми шрифтами.
👍 - если было полезно
👉 @seniorFront
🧮 Откуда в магазинах вес 0.7000000000000001 кгТакие числа никто не вводит руками. Их посчитал JavaScript, а округлить забыли.
Number в JavaScript хранится в двоичном виде, в 64 битах. Как 1/3 не записать конечной десятичной дробью, так 0.1 не записать конечной двоичной: 0.000110011… и дальше без конца.
Поэтому хранится ближайшее число, которое влезает в 64 бита:
(0.1 + 0.2).toFixed(20);
// "0.30000000000000004441"
Оба слагаемых чуть больше настоящих, сумма округляется ещё выше, а 0.3 хранится чуть меньше. Отсюда
(0.1 + 0.2) === 0.3 даёт false.Округлить через toFixed() кажется решением, но и тут ловушка:
(1.005).toFixed(2) вернёт "1.00", потому что внутри это число чуть меньше 1.005.В оригинале по шагам разобран перевод 0.1 в двоичный код и то, как безопасно сравнивать числа и считать деньги.
👍 - если было полезно
👉 @seniorFront
🎨 Свой скроллбар, который двигает CSS, а не JSНативную полосу прокрутки почти не стилизуешь, поэтому её рисуют сами. Обычно ползунок двигают в JS: слушают scroll, читают scrollHeight, меняют DOM, а чтение размеров может вызывать перерасчёт раскладки.
Идея статьи: отдать это scroll-driven animations, то есть анимациям, которые идут по прокрутке, а не по времени. Родную полосу прячут через
scrollbar-width: none, а у своей анимируют две CSS-переменные: верхний и нижний край ползунка. Размер задаёт хитрость с animation-range, и ползунок занимает ровно долю видимой части.Колесо, клавиатура и тач работают сами, JS нужен только для перетаскивания.
Оговорка: на момент публикации это работало в Chrome и Safari, а в Firefox только в Nightly.
👍 - если было полезно
👉 @seniorFront
🧩 Компонент рисует, хук думаетВ React-компоненте разметка часто перемешана с подготовкой данных: проверками, вычислениями, обработкой ошибок. JSX от этого читается тяжело.
Статья на Хабре предлагает идею, подсмотренную во Vue 3: вынести подготовку в хук с именем useSetup[Component]. Для панели профиля ProfilePanel это useSetupProfilePanel. Он отдаёт флаг isLoading и готовые пропсы для карточки, а в компоненте остаётся только разметка.
Тесты от этого тоже упрощаются. В тесте компонента хук подменяется моком (vi.mock из Vitest), и проверяется одно: попали ли пропсы из хука в нужный элемент. Логику самого хука проверяют его собственные тесты.
Важно понимать, что shallow-тест через Shallowly (библиотеку автора статьи) не делает полный рендер. Дочерний компонент не выполняется, поэтому клики и изменения DOM проверяет отдельный DOM-тест.
В статье весь путь от исходного компонента до шести тестов и репозиторий, чтобы запустить их у себя.
👍 - если было полезно
👉 @seniorFront
🔍 Chrome ругается «Blocked aria-hidden»? Не глушите егоОбычно это всплывает при закрытии модалки. Окно уже помечено
aria-hidden="true", а фокус ещё на кнопке «Закрыть» внутри. Атрибут прячет элемент от скринридера, но не от Tab, и фокус застревает там, чего для скринридера «нет».Популярный фикс
blur() чистит консоль, но роняет фокус на <body>. Пользователь скринридера слышит тишину и листает Tab с самого начала страницы.Чинится это порядком действий. Сначала фокус возвращается на кнопку, которая открыла окно, и только потом окно прячется. На время анимации закрытия окну ставят
inert: оно выпадает из Tab, и скринридер его не видит.Подвох в том, что
inert блокирует и фокус. Если фон за окном был inert, снимите его до focus(), иначе фокус молча не переедет.В статье разобраны четыре сценария этой ошибки и готовый код для чистого JS и React.
👉 @seniorFront
✨ Подсветка текущего раздела в оглавлении без JavaScriptНа длинной странице оглавление может подсвечивать раздел, который вы сейчас читаете. Этот приём зовут scrollspy, и обычно он держится на JavaScript, например на IntersectionObserver.
Новое CSS-свойство scroll-target-group делает это без скриптов. Ставим его на обёртку с якорными ссылками:
.toc {
scroll-target-group: auto;
}Теперь браузер сам связывает каждую ссылку с её разделом. Ссылка на раздел, который сейчас на экране, получает псевдокласс
:target-current, и её остаётся только покрасить. Для пунктов выше и ниже есть :target-before и :target-after.Пока это работает только в браузерах на Chromium, Firefox и Safari не поддерживают. Но там остаётся обычный список ссылок, так что навигация не ломается.
В статье к этому добавляют линию, которая плавно доезжает до текущего пункта через anchor positioning.
👉 @seniorFront
🪄 Один URL: из списка открывается modal, по прямой ссылке целая страницаЕсли modal с деталями хранится только в useState, адрес не меняется. Ссылкой не поделиться, после reload снова список.
В Next.js App Router можно иначе. Страница events/[id] работает сама по себе. А рядом в layout живёт слот @modal (в URL он не попадает) с перехватчиком того же адреса:
@modal/
(.)events/
[id]/
page.tsx
Префикс (.) значит «перехватить events/[id] на том же уровне». Переход по Link без перезагрузки перехватывается, и детали рисуются поверх списка. Прямая ссылка или reload перехват не включают: открывается полная страница с metadata и canonical.
Подвох: слоту нужен default.tsx, возвращающий null. Без него при прямом открытии @modal остаётся без маршрута, и можно получить 404.
В статье разобрано, как после закрытия вернуть пользователя на ту же страницу поиска со всеми фильтрами.
👉 @seniorFront
🚀 Два атрибута у картинки, и страница перестаёт прыгатьПока файл картинки грузится, тег
img занимает на странице ноль места. Потом картинка приходит, блок раздвигается, и текст под ним резко уезжает вниз. Браузер тратит процессорное время на пересчёт раскладки, а читатель теряет строку, которую читал.Лечится это заранее заданными пропорциями. У тега указываем
width=800 height=600, а в CSS пишем width: 100% и height: auto. Картинка растянется на ширину контейнера, а высоту браузер вычислит из пропорции 800×600 ещё до загрузки файла. Место зарезервировано, ничего не дёргается.В демо-замере из статьи это сэкономило около 12% процессорного времени.
Не забудьте про
height: auto: без него картинки растягиваются неправильно.В статье ещё разобраны старые трюки с padding-bottom и aspect-ratio и объяснено, почему они проигрывают простым атрибутам.
👉 @seniorFront
🔗 Линия между двумя блоками без JavaScriptЛиния между двумя карточками сама тянется за ними, когда они двигаются. Скрипт для расчёта координат не нужен.
Это CSS Anchor Positioning: элемент с anchor-name становится якорем, а блок с position: absolute привязывает к нему свои стороны через anchor(), даже при разных родителях.
Главный трюк: привязать противоположные стороны блока к двум разным якорям, и он растянется между ними:
.anchor--one { anchor-name: --a1; }
.anchor--two { anchor-name: --a2; }
.route--one {
position: absolute;
left: anchor(--a1 right);
right: anchor(--a2 left);
top: anchor(--a1 center);
bottom: anchor(--a2 center);
}В фон кладём SVG с диагональю и background-size: 100% 100%. В самом SVG нужен preserveAspectRatio="none", иначе линия не растянется по обеим осям.
👉 @seniorFront
🔍 Почему overflow: hidden не удерживает размытиеСвойство backdrop-filter: blur() размывает всё, что видно сквозь полупрозрачный элемент. Так делают эффект матового стекла. Снимок фона браузер берёт из готовой GPU-текстуры (compositing layer), а не из DOM. Кто чей родитель, ему неважно.
В статье есть живой пример. Карточки с blur(70px) лежат в scroll-контейнере с overflow: hidden, а снаружи стоит яркая кнопка PLAY. Карточка подъезжает к кнопке ближе чем на 70px и начинает светиться её цветом.
Дело в том, что overflow: hidden обрезает на этапе paint, а размытие работает позже, на этапе composite. Помогает
clip-path: inset(0) на контейнере: автор называет его единственным надёжным способом.Только помните, что clip-path обрежет всё содержимое контейнера, включая дочерние элементы, которые выходят за его край.
В статье ещё три бага в Safari и на iPhone и разбор, почему большой радиус blur так дорого обходится GPU.
👉 @seniorFront
🔑 Обработчик postMessage без проверок слушается кого угодноВиджет в iframe (комментарии, форма оплаты) не может сам растянуться на странице. Поэтому он шлёт родителю сообщение через postMessage, а родитель в обработчике message выставляет iframe новую высоту.
Подвох в том, что браузер доставит сообщение от любого окна, у которого есть ссылка на ваше. Чужая страница одной строкой пришлёт
{ type: 'resize', height: 1 }, и виджет схлопнется до одного пикселя.Спасает строгое сравнение
event.origin с ожидаемым адресом. Это поле заполняет сам браузер, и отправитель не может его подделать.if (event.origin !== WIDGET_ORIGIN) return;
Но origin говорит, с какого сайта пришло сообщение, а не из какого окна. Два iframe с одного домена пройдут проверку оба, и сообщение от одного изменит размер другого.
Как это закрыть через
event.source и что ещё проверить в самих данных (вроде height: 1e9), автор показывает на живых демо с тремя атаками.👉 @seniorFront
📐 Как заменить CSS progress(), пока его поддерживают не вездеФункция progress() показывает, где значение лежит между двумя границами, и возвращает число от 0 до 1. Формула простая: (значение - начало) / (конец - начало).
Но поделить 180px на 880px и получить просто число умеют не все браузеры: Firefox, например, пока нет. Выручает tan(atan2()). Функция atan2() по сути сама делит первый аргумент на второй, а tan() отдаёт обычное число без единиц.
--progress: tan(atan2(500px - 320px, 1200px - 320px));
Так мы узнаём, где 500px между 320px и 1200px. Чтобы результат не выходил за пределы от 0 до 1, как у progress(), оберните его в clamp().
С разными единицами, например vw и px, трюк кроссбраузерно не сработает. Зарегистрируйте ширину экрана через @property как <length>, и браузер сам переведёт её в пиксели.
Как быть, если границы заданы в rem, и какой вариант выбрать под ваши единицы, автор разбирает в статье.
👉 @seniorFront
Telegram Channel Link information
Category
Science & Technology
Country
🌐 International
Language
English
Type
Telegram Channel Link
Subscribers
554