Home › Groups › Senior Frontend - javascript, html, css
Senior Frontend - javascript, html, css Telegram
Telegram Channel Link

Senior Frontend - javascript, html, css Telegram Channel Link

★★★★★ 0.0 (0 ratings)
👁 2 views ↗ 0 joins
🌐 International 🌐 English # Science & Technology
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
Click to fetch the latest posts directly from Telegram.
👩‍💻 Всем программистам посвящается!

Вот 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 post media ⚡ React Context без лишних ререндеров

В контексте лежат name и age. Меняется age, и заодно перерисовывается компонент, которому нужен только name. React сравнивает value провайдера целиком, а это каждый раз новый объект. useMemo и React.memo тут не спасают.

Выход в статье простой. Через Context передают не данные, а объект доступа к ним: { get, set, subscribe }. Он создаётся один раз, ссылка не меняется, и сам контекст никого не ререндерит.

Данные живут в useRef. Каждый компонент через useSyncExternalStore подписывается только на своё поле. Ввели возраст, и перерисовался один инпут возраста.

Есть цена: обновление такого стора внутри startTransition всё равно станет блокирующим, в фон его не увести.

В статье объяснено, зачем здесь именно useSyncExternalStore и почему store нельзя заворачивать в useMemo.

👍 - если было полезно

👉 @seniorFront
Telegram post media 🔑 Один 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
Telegram post media 🐶 Как пустить элемент гулять по краю блока на чистом CSS

Свойство offset-path задаёт путь, а offset-distance ставит элемент в точку на нём. Путь не обязательно рисовать вручную: подойдёт рамка родителя, offset-path: border-box.

Например, с offset-distance: 20% кружок встаёт на рамку, пройдя пятую часть периметра. Пикселей нет, поэтому при смене размера блока всё подстраивается.

Дальше автор анимирует offset-distance через @keyframes, а значения берёт из CSS-функции random(). Кружок бегает по краю в случайные точки. Сработало, только когда у каждого вызова появился свой ключ вроде --k1.

Главная загвоздка в поддержке: на момент статьи random() есть в Safari, а в Chrome только за флагом. В других браузерах элемент просто стоит на месте, так что пригодится запасной вариант через @supports.

В статье из этого вырастают светящийся шар и собачка, гуляющая по краю блока.

👍 - если было полезно

👉 @seniorFront
Telegram post media ✨ Стили в 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
Telegram post media 🎬 Анимации в 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
Telegram post media 🚀 Дробить тяжёлый JS через setTimeout(0) дороже, чем кажется

JavaScript в браузере работает в одном главном потоке. Пока там крутится долгий цикл, клики ждут. Обычно работу режут на порции и между ними отдают управление браузеру, чтобы он успел обработать ввод.

Паузу часто делают через setTimeout(0). Но при глубокой вложенности таймеров спецификация HTML добавляет минимум 4 мс, а продолжение уходит в конец очереди. На стенде автора (500 тыс. записей, Chromium) время до результата выросло со 106 мс без дробления до 380 мс.

scheduler.yield() ставит продолжение в начало очереди своего приоритета, без искусственной паузы. Итог: 125 мс, а клик обрабатывается примерно через 1 мс.

Учтите, что scheduler.yield() есть не во всех браузерах: по данным статьи, его нет в Safari. Автор предлагает запасной вариант на MessageChannel.

А ещё в статье сравнение с Web Worker и подбор размера порции по времени.

👍 - если было полезно

👉 @seniorFront
Telegram post media 🛡️ Превью для 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
Telegram post media 🎯 Почему иконка рядом с текстом встаёт криво

По умолчанию иконка в строке садится на базовую линию шрифта и смотрится задранной. Flexbox ставит её в математический центр, но и это мимо: у шрифта разный запас «воздуха» над буквами и под ними.

Армен Аллахвердян из дизайн-системы Авито предлагает другой ориентир: оптический центр, то есть середину между базовой линией и верхом заглавных букв. Высоту заглавных в CSS даёт юнит cap.

Иконку абсолютно кладут в пустой inline-block span, который сидит на базовой линии. Дальше одна формула:

bottom: calc((1cap - var(--icon-height)) / 2);


Для Avito Sans в 15px выходит -2,1px: иконка выше заглавных и чуть уходит под базовую линию.

Формула, правда, не учитывает форму рисунка: сердечко с тяжёлым верхом всё равно кажется выше. На этот случай в статье есть второй способ.

А ещё там разобрано, почему популярный vertical-align: -0.125em работает далеко не со всеми шрифтами.

👍 - если было полезно

👉 @seniorFront
Telegram post media 🧮 Откуда в магазинах вес 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
Telegram post media 🎨 Свой скроллбар, который двигает CSS, а не JS

Нативную полосу прокрутки почти не стилизуешь, поэтому её рисуют сами. Обычно ползунок двигают в JS: слушают scroll, читают scrollHeight, меняют DOM, а чтение размеров может вызывать перерасчёт раскладки.

Идея статьи: отдать это scroll-driven animations, то есть анимациям, которые идут по прокрутке, а не по времени. Родную полосу прячут через scrollbar-width: none, а у своей анимируют две CSS-переменные: верхний и нижний край ползунка. Размер задаёт хитрость с animation-range, и ползунок занимает ровно долю видимой части.

Колесо, клавиатура и тач работают сами, JS нужен только для перетаскивания.

Оговорка: на момент публикации это работало в Chrome и Safari, а в Firefox только в Nightly.

👍 - если было полезно

👉 @seniorFront
Telegram post media 🧩 Компонент рисует, хук думает

В React-компоненте разметка часто перемешана с подготовкой данных: проверками, вычислениями, обработкой ошибок. JSX от этого читается тяжело.

Статья на Хабре предлагает идею, подсмотренную во Vue 3: вынести подготовку в хук с именем useSetup[Component]. Для панели профиля ProfilePanel это useSetupProfilePanel. Он отдаёт флаг isLoading и готовые пропсы для карточки, а в компоненте остаётся только разметка.

Тесты от этого тоже упрощаются. В тесте компонента хук подменяется моком (vi.mock из Vitest), и проверяется одно: попали ли пропсы из хука в нужный элемент. Логику самого хука проверяют его собственные тесты.

Важно понимать, что shallow-тест через Shallowly (библиотеку автора статьи) не делает полный рендер. Дочерний компонент не выполняется, поэтому клики и изменения DOM проверяет отдельный DOM-тест.

В статье весь путь от исходного компонента до шести тестов и репозиторий, чтобы запустить их у себя.

👍 - если было полезно

👉 @seniorFront
Telegram post media 🔍 Chrome ругается «Blocked aria-hidden»? Не глушите его

Обычно это всплывает при закрытии модалки. Окно уже помечено aria-hidden="true", а фокус ещё на кнопке «Закрыть» внутри. Атрибут прячет элемент от скринридера, но не от Tab, и фокус застревает там, чего для скринридера «нет».

Популярный фикс blur() чистит консоль, но роняет фокус на <body>. Пользователь скринридера слышит тишину и листает Tab с самого начала страницы.

Чинится это порядком действий. Сначала фокус возвращается на кнопку, которая открыла окно, и только потом окно прячется. На время анимации закрытия окну ставят inert: оно выпадает из Tab, и скринридер его не видит.

Подвох в том, что inert блокирует и фокус. Если фон за окном был inert, снимите его до focus(), иначе фокус молча не переедет.

В статье разобраны четыре сценария этой ошибки и готовый код для чистого JS и React.

👉 @seniorFront
Telegram post media ✨ Подсветка текущего раздела в оглавлении без 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
Telegram post media 🪄 Один 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
Telegram post media 🚀 Два атрибута у картинки, и страница перестаёт прыгать

Пока файл картинки грузится, тег img занимает на странице ноль места. Потом картинка приходит, блок раздвигается, и текст под ним резко уезжает вниз. Браузер тратит процессорное время на пересчёт раскладки, а читатель теряет строку, которую читал.

Лечится это заранее заданными пропорциями. У тега указываем width=800 height=600, а в CSS пишем width: 100% и height: auto. Картинка растянется на ширину контейнера, а высоту браузер вычислит из пропорции 800×600 ещё до загрузки файла. Место зарезервировано, ничего не дёргается.

В демо-замере из статьи это сэкономило около 12% процессорного времени.

Не забудьте про height: auto: без него картинки растягиваются неправильно.

В статье ещё разобраны старые трюки с padding-bottom и aspect-ratio и объяснено, почему они проигрывают простым атрибутам.

👉 @seniorFront
Telegram post media 🔗 Линия между двумя блоками без 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
Telegram post media 🔍 Почему 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
Telegram post media 🔑 Обработчик postMessage без проверок слушается кого угодно

Виджет в iframe (комментарии, форма оплаты) не может сам растянуться на странице. Поэтому он шлёт родителю сообщение через postMessage, а родитель в обработчике message выставляет iframe новую высоту.

Подвох в том, что браузер доставит сообщение от любого окна, у которого есть ссылка на ваше. Чужая страница одной строкой пришлёт { type: &apos;resize&apos;, height: 1 }, и виджет схлопнется до одного пикселя.

Спасает строгое сравнение event.origin с ожидаемым адресом. Это поле заполняет сам браузер, и отправитель не может его подделать.

if (event.origin !== WIDGET_ORIGIN) return;


Но origin говорит, с какого сайта пришло сообщение, а не из какого окна. Два iframe с одного домена пройдут проверку оба, и сообщение от одного изменит размер другого.

Как это закрыть через event.source и что ещё проверить в самих данных (вроде height: 1e9), автор показывает на живых демо с тремя атаками.

👉 @seniorFront
Telegram post media 📐 Как заменить 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
Country 🌐 International
Language English
Type Telegram Channel Link
Subscribers 554
Rate this group

How would you rate this community?

0.0 ★ Join Group →