fa
Feedback
Катерина | Про Frontend

Катерина | Про Frontend

رفتن به کانال در Telegram

⚡️ Пишу обучающие статьи по Frontend-разработке; ⚡️ Делюсь личным опытом, как профессионально, так и в личном формате. YouTube: https://www.youtube.com/@katerina_profrontend Life: https://t.me/life_nanivskaya Связь: @katrin_nanivskaya

نمایش بیشتر
3 007
مشترکین
اطلاعاتی وجود ندارد24 ساعت
-107 روز
-3730 روز
آرشیو پست ها
Компоненты-монолиты — как их распознать и что с этим делать 🧐 Формальное разделение системы на компоненты ещё не гарантирует
Компоненты-монолиты — как их распознать и что с этим делать 🧐 Формальное разделение системы на компоненты ещё не гарантирует модульности.
Компонент-монолит — это «папка», внутри которой смешаны UI, бизнес-логика и доступ к данным, и любое небольшое изменение требует погружения в большой объём кода. Такая структура создаёт иллюзию независимости: команды думают, что работают автономно, но правки в одной части часто ломают другие, потому что границы слишком проницаемы.
Как быстро понять, что перед вами компонент-монолит? ✔️ Невозможно объяснить ответственность компонента одной фразой; ✔️ Для добавления фичи нужно читать тысячи строк и разбираться в десятке вспомогательных модулей; ✔️ Локальное изменение функции вызывает регрессии в соседних модулях. 👨‍💻 Практический тест: возьмите небольшой сценарий использования внутри компонента и попробуйте вынести его в отдельный модуль. Если это делается быстро — границы в порядке. Если придётся переписывать значительную часть проекта — у вас мини-монолит. Если одна часть кода отвечает и за рендер, и за правила поведения, и за трансформацию данных для внешних сервисов, вы теряете возможность работать с этими частями отдельно: тестирование усложняется, интеграцию нельзя менять без риска повредить UI, да и логику трудно переиспользовать. Любая правка становится дорогой, потому что требует учёта нескольких контекстов одновременно. 😒 Решение: чётко разделяйте ответственность и вводите явные границы между слоями. Представьте «прослойки»: одна — рисует и собирает ввод, вторая — держит и обновляет состояние, третья — реализует бизнес-сценарии, четвёртая — общается с внешними сервисами и адаптирует данные. Между ними должны быть простые и стабильные контракты — тогда реализацию можно менять, не затрагивая остальные части. Как же можно начать прямо сейчас? 1️⃣ Выберите один очевидный сценарий компонента Например: форма с валидацией, таблица с фильтром, кнопка с подтверждением; 2️⃣ Определите минимальный контракт для компонента ⏺️ Что компонент получает на вход (props, inputs, параметры) ⏺️ Что компонент отдаёт или вызывает на выходе (события, callbacks, outputs) ⏺️ Контракт должен быть простым и понятным для других частей приложения; 3️⃣ Вынесите правила (алгоритм, валидацию, координацию действий) в отдельный модуль или сервис ⏺️ Логика должна быть независимой от UI: не обращаться к DOM, не содержать шаблонной разметки ⏺️ Делаем чистые функции или методы, возвращающие значения, Promise или Observable для удобного тестирования; 4️⃣ При необходимости напишите юнит-тесты для этого модуля; 5️⃣ Подключите UI через контракт. ⏺️ Компонент использует модуль/сервис через определённый контракт ⏺️ Для интеграции с API или внешними системами применяйте адаптеры или отдельные сервисы; ⏺️ При необходимости внедряйте feature-флаги для постепенного релиза. Такой инкрементный подход даёт быстрый эффект: вы сможете тестировать логику без моков UI, упростите переиспользование кода и сократите зону риска при изменениях — фактически превратите видимость модульности в реальную. 👍

Самозакрывающиеся теги: почему <img /> — это ок, а <div /> — обманка 🧐 Иногда кажется, что наличие слэша в конце тега — просто “красиво” или «современно». Видишь <img /> в коде и хочется делать так же везде: <div />, <span />, <section />. Визуально действительно похоже на аккуратный самозакрывающийся тег. Но в HTML это имеет чёткое семантическое значение — и распространяется далеко не на все элементы. Что такое void-elements и почему им можно? 🤓 В HTML есть очень узкий набор элементов, которые по определению не могут содержать контент. Они так и называются — void elements. Сюда входят, например: <img>, <br>, <input>, <meta>, <link> и так далее. У таких элементов нет закрывающего тега в принципе — потому что внутри ничего быть не может. Поэтому <img> и <img /> парсер HTML понимает одинаково. Запись с / — просто исторический рудимент времён XHTML, где строгий XML-синтаксис требовал слеш для пустых элементов. В HTML5 можно писать и так, и так — но только для void-элементов. Почему <div /> — не самозакрывается? 🤓 Потому что <div> — это контейнерный элемент, у которого по умолчанию есть содержимое, даже если оно пустое. Запись <div /> в HTML не закрывает тег. Более того — такой синтаксис считается parse error, который браузер обязан проигнорировать. HTML-парсер устроен по принципу: «исправить так, чтобы страница работала». Поэтому браузер восстанавливает структуру так, словно вы сами так написали. 😁 Вот пример:

<div />Hello


Вы ждёте, что Hello будет вне div, но парсер на это смотрит как на:


<div>Hello</div>
Текст оказывается внутри div, хотя визуально запись похожа на «закрытый тег». То есть:
<div />Hello не равно <div></div>Hello
👀 Это важный момент, который легко упустить, если смотреть только глазами, а не с точки зрения спецификации. А как быть с пользовательскими компонентами? ⚡ Если вы пишете кастомные элементы в Angular, например:

<app-header />
то это не HTML-самозакрывающийся тег, а синтаксис Angular. Фреймворк воспринимает такую запись как компонент без содержимого и ещё на этапе компиляции превращает её в код, который создаёт DOM-элемент программно. Браузер напрямую <app-header /> не видит — он получает уже готовый элемент. 😉 Как итог: ✔️ В чистом HTML самозакрывающиеся теги допустимы только для void-элементов; ✔️ Для контейнерных элементов и пользовательских компонентов используйте открывающий и закрывающий тег; ✔️ В Angular/React/JSX короткая форма <MyComponent /> — это синтаксис фреймворка для удобства. Она не меняет правила HTML и работает только благодаря компиляции в JS. 👍

URLPattern: простой способ разбираться в URL без боли 🧐 Работать с URL чаще всего означает вручную доставать путь, параметры
URLPattern: простой способ разбираться в URL без боли 🧐 Работать с URL чаще всего означает вручную доставать путь, параметры, хост, протокол и надеяться, что очередная регулярка не сломает половину приложения. Но в современных браузерах есть встроенный инструмент, который делает этот процесс куда понятнее — URLPattern.
Это аккуратный API, который позволяет описать шаблон URL и потом проверять совпадение или извлекать параметры ровно так, как они задумывались. Без сложных RegExp, без хаотичного парсинга, без десятков условий.
Главная идея простая: вы задаёте шаблон, вроде /users/:id, и дальше можете: ⏺️ проверить, подходит ли строка под этот шаблон; ⏺️ достать значения параметров — например, id; ⏺️ матчить не только путь, но и протокол, домен, порт, query-строку и даже hash. 📖 Как выглядит шаблон В конструктор лучше передавать объект с полями protocol, username, password, hostname, port, pathname, search, hash. Он позволяет матчить именно те части URL, которые вам важны, без необходимости формировать полную строку.

const p = new URLPattern({
  protocol: 'https',
  hostname: '*.example.com',
  pathname: '/blog/:year/:month/:slug'
});
Проверяем совпадения и получаем параметры

const url = 'https://tech.example.com/blog/2025/01/urlpattern-guide';
console.log(p.test(url)); // true
Если нужно больше деталей - на помощь приходит метод exec, который возвращает структуру, где у каждой части URL есть input и groups. Он разобран на картинке выше. 👆 Поэтому, если вы часто работаете с путями и параметрами, это один из тех API, которые приятно внедрить — и потом удивляться, как вообще раньше обходились без него. 👍

Как работает .gitlab-ci.yml? 🤔 Если GitLab CI/CD — это автоматизация ваших процессов, то файл .gitlab-ci.yml — это то место,
+2
Как работает .gitlab-ci.yml? 🤔 Если GitLab CI/CD — это автоматизация ваших процессов, то файл .gitlab-ci.yml — это то место, где вы объясняете GitLab, что именно нужно делать. Он лежит в корне проекта и содержит инструкции, по которым запускаются сборка, тесты и деплой. Чтобы начать разбираться, достаточно понять несколько ключевых элементов файла: ✔️ Формат файла:
.gitlab-ci.yml — это YAML-конфигурация. GitLab анализирует его целиком, поэтому порядок выполнения задач определяется не расположением в файле, а списком (stages) или зависимостями.
✔️ Две ключевые сущности, которые нужно понимать: ⏺️ stages (этапы) - определяют порядок выполнения ⏺️ jobs (задачи) - наполнение каждого этапа В одном stage может быть любое количество job’ов — и они запускаются параллельно, если нет прямых зависимостей. Для наглядности основной материал представлен на картинках выше. ⬆️ ✔️ Когда запускается pipeline: По умолчанию pipeline запускается на каждый push, если не задать соответствующие правила. ✔️ Runner — это исполнитель, который запускает job: Без доступного runner’а пайплайн просто не начнётся — но об этом подробно поговорим в следующем посте после того, как соберём 30 огоньков. 👍

Что такое GitLab CI/CD и зачем он нужен? 🤔 Представьте: вы пушите код в ветку, кофе ещё тёплый, а тесты уже прогнали сборку,
+2
Что такое GitLab CI/CD и зачем он нужен? 🤔 Представьте: вы пушите код в ветку, кофе ещё тёплый, а тесты уже прогнали сборку, проверили линтеры и подготовили артефакты — и всё это произошло автоматически, без ручного npm run build у каждого в команде. Рутинные, повторяющиеся шаги выполняются надёжно и последовательно, экономя ваше время и снижая риск ошибок. 😁
GitLab CI/CD — встроенный в GitLab инструмент, созданный именно для того, чтобы избавить вас от этой рутины. Он автоматизирует сборку, тестирование и деплой приложения, выполняя все действия одинаково каждый раз.
🔝 Базовый материал удобно представлен на наглядных картинках, чтобы вы могли быстро понять, как всё устроено. Как итог, вы получаете способ сделать релизы стабильными, быстрыми и предсказуемыми. Потратив немного времени на настройку, вы создаёте систему, которая экономит часы работы и избавляет от ночных исправлений в продакшене. Продолжение после того, как наберём 20 🔥

На сегодняшний день я работаю в компании чуть больше двух лет — и это первый раз, когда я по-настоящему отмечаю такое «двухлетие». 🥳 Раньше у меня была другая тактика: если мне хотелось расти — я меняла компанию. Сейчас, оглядываясь назад, понимаю, что изменилась не только картина вокруг, но и мой собственный взгляд на путь. Команда за это время прожила не одну маленькую «жизнь»: кто-то приходил, кто-то уходил, задачи менялись, а вместе с ними менялась и я. Самое важное, чему я научилась — это слышать людей и строить общение так, чтобы работа была не просто эффективной, а ещё и очень слаженной. Никто не знает, что принесёт следующий шаг, но умение выстраивать хорошие и прозрачные отношения с коллегами всегда окупается со временем. 😉 А ещё эти два года — это огромное количество написанных компонентов. Некоторые из них стали частью больших проектов, другие — остались в черновиках. А были и те, от которых пришлось отказаться одним нажатием клавиши Delete. 🙈 Немного грустно, конечно, но так и рождается опыт: мы переписываем свои же решения, выбираем, какие идеи оставить, а какие отпустить. Это такой тихий, но важный навык — уметь спокойно с принятием отпускать то, что уже утратило свою актуальность. Самое неожиданное открытие — рост внутри одной компании. Раньше я думала: хочешь сильно прыгнуть в зарплате — смотри наружу. Сейчас же всё больше вопрос звучит иначе: что я могу дать компании? Где мой вклад будет наибольшим? Оказалось, что когда работаешь глубже, а не шире, появляются совсем другие смыслы. 🧐 Ну и про точки роста. Когда долго погружаешься в одно направление, неизбежно встречаешься со своими сильными и слабыми сторонами. Сильные — радуют, слабые — иногда раздражают, но именно они двигают тебя дальше. И каждый раз ты немного апгрейдишься — только уже не сменой компании, а улучшением своих навыков. 🙂 За эти два года я поняла главное:
стабильность — это не про застой, а про возможность расти в глубину.
Два года прошло, а мне всё ещё нравится то, что я делаю, но при этом мне всё ещё интересно, что будет дальше. 😌

Grid vs Flex — когда сетка действительно нужна 🤓 Оба инструмента — мастхэв в современном CSS, но решают разные задачи. ✔️ Flexbox оптимален для одномерных раскладок, где важно выровнять элементы вдоль одной оси. ✔️ Grid — для двумерной разметки: строки и колонки одновременно, с декларативной структурой и прозрачным намерением автора. 🙂 Частая ошибка — выбирать инструмент по привычке. 😔 Если нужно расположить кнопки в ряд или выровнять элементы по центру — Flex остаётся самым компактным и читаемым решением. Но когда макет становится двумерным: карточная сетка, форма со сложной геометрией, области, которые должны занимать конкретные «ячейки» и изменять расположение при адаптиве — Grid даёт очевидные преимущества. Основные из них:Явный контроль структуры Вы задаёте строки и колонки явно:
grid-template-columns: 200px 1fr 2fr;
Макет становится предсказуемым. ➕ Размещение элементов по ячейкам Элементы можно «растягивать» на несколько рядов/колонок:
.item {
    grid-column: 1 / 3;
    grid-row: 2 / 4;
}
Никакой магии — всё видно в декларации. 😁 ➕ Автоматические адаптивные сетки Комбинация repeat(), minmax() и auto-fit/auto-fill позволяет строить адаптивные сетки без множества медиазапросов:
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
Единая геометрия без «костылей» Grid легко выравнивает строки и колонки, делает их одинаковыми и создаёт ровную сетку без вспомогательных контейнеров. 👨‍💻 Примеры ✨ Flex — компактная группа кнопок:
.actions {
    display: flex; 
    gap: 12px;
    align-items: center;
}
✨ Grid — адаптивная сетка карточек:
.grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 16px;
}
Как итог, ✔️ Если задача читается как «ряд/колонка» — Flex; ✔️ Если как «сетка/ячейки» — Grid. 🙂

Тернарный оператор 😊 Тернарный оператор предназначен для компактного и однозначного выбора значения: cond ? A : B. Он хорошо
Тернарный оператор 😊
Тернарный оператор предназначен для компактного и однозначного выбора значения: cond ? A : B. Он хорошо работает там, где логика проста и читается линейно.
Но в реальном коде его часто перегружают — выполняют в нём действия, вкладывают несколько проверок подряд или пытаются уместить сложную логику в одну строку. Именно в этих случаях тернарник перестаёт быть инструментом упрощения и превращается в источник ошибок и путаницы. 😥 ➕ Корректный пример:

const status = isActive ? "active" : "inactive";
Вложенные тернарники. С технической точки зрения они допустимы, но с практической — быстро становятся трудно читаемыми. Выражение с несколькими уровнями вложенности требует визуального разбора и легко прячет ошибки:

const result = a ? (b ? x : y) : (c ? z : w);
Даже если конструкция делает всего несколько проверок, читать её значительно сложнее, чем явно выраженную структуру через if/else, switch/case или вынесенную функцию. Вложенность может быть оправдана только в очень простых случаях, где оба уровня очевидны и помещаются в один экран без потери смысла. В остальных ситуациях предпочтительнее развернуть условие и улучшить читаемость. ❌ Эффект вместо значения — отдельная проблема. Тернарный оператор по своей природе предназначен для выбора значения. Если результат не используется, читающий ожидает конструкцию if/else, а не «маскировку» if под выражение:

cond ? doSomething() : logError();
Технически это корректно, но по стилю и читаемости такое решение считается нежелательным. Оно смешивает вычисление значения и выполнение побочных эффектов, что снижает ясность кода. Немного рекомендаций: ✔️ Используйте тернарный оператор только для выбора значения, которое затем действительно используется; ✔️ Не помещайте внутрь тернарника побочные эффекты — для этого предназначены управляющие конструкции; ✔️ Избегайте вложенных тернарников: если требуется более двух уровней логики, выберите if/else, switch или вынесите расчёт в именованную функцию; ✔️ Ориентируйтесь на читаемость: если выражение нельзя понять за одно сканирование строки, значит конструкция выбрана неверно. Как итог, тернарный оператор даёт пользу только там, где упрощает выражение. Если он начинает усложнять чтение, скрывать логику или собирать несколько проверок подряд, его лучше заменить на более явную структуру. 👍

Extract<keyof T, string> — фишка для надёжной типизации ключей 🤔 Простая и практичная техника, которая устраняет рассогласование между абстрактной моделью типов TypeScript и реальной моделью данных. Что такое Extract
Extract<T, U> — утилитный тип, который фильтрует объединение T, оставляя только те члены, которые совместимы с U.
Пример:
type R = Extract<'a' | 'b' | 1 | 2, string>; // 'a' | 'b'
➡️ Extract — фильтр по типам. Так что же делает Extract<keyof T, string>? keyof T в обобщённом коде часто оказывается не только string, но и number | symbol. TypeScript так делает по умолчанию — он не предполагает, что ключи всегда строковые. На практике это вызывает ошибки в местах, где ожидается именно строка: DOM API (setAttribute), обращения к nativeElement, Angular-шаблоны и прочие сценарии. Пример проблемы:
interface Test<T> {
  key: keyof T; // string | number | symbol
  value: string;
}

function setAttr<T>(el: HTMLElement, k: keyof T, v: string) {
  // если k может быть number | symbol — компилятор будет ругаться при setAttribute
  el.setAttribute(k, v); // ❌ Type 'string | number | symbol' не совпадает с ожидаемым string
}
Решение — оставить только строковые ключи:
export interface Test<T> {
  key: Extract<keyof T, string>;
  value: string;
}
Теперь key гарантированно строка. 😁 Как итог, Extract<keyof T, string> приводит типизацию ключей к тому, что реально ожидается в коде: строкам. За счёт этого исчезают лишние приведения типов, а API становится проще и безопаснее. Используй его в формах, конфигурациях и обёртках — и проблема с string | number | symbol больше не будет мешать. 👍

Разбираемся с Navigation API 🤔 Navigation API — это централизованный интерфейс для управления навигацией в окне браузера, до
+3
Разбираемся с Navigation API 🤔
Navigation API — это централизованный интерфейс для управления навигацией в окне браузера, доступный через window.navigation. Он объединяет все типы переходов — клики по ссылкам, отправку форм, программные переходы и действия браузера вроде back/forward.
Теперь у разработчика есть единый канал для отслеживания и контроля навигации. 🎯 Основная идея Navigation API позволяет реагировать на любые навигации с помощью события navigate. Оно срабатывает до того, как браузер выполнит переход — и даёт возможность решить: пропустить навигацию или перехватить и обработать её самостоятельно.

navigation.addEventListener('navigate', (event) => {
  console.log('Переход к:', event.destination.url);
});
На картинках выше я собрала ключевые возможности Navigation API — от программных переходов до работы с историей и состоянием. ⬆️ Navigation API задуман как преемник старого механизма навигации. Он решает многие ограничения history и location, но при этом сохраняет нативное поведение браузера. ✔️ Если вы не хотите вмешиваться — переход произойдёт как обычно; ✔️ А если хотите контролировать процесс — event.intercept() даёт полный контроль: можно показать лоадер, подгрузить HTML, обновить состояние и не потерять историю. Но, Navigation API не перехватывает все переходы подряд — и это сделано специально. Некоторые сценарии (например, переходы на другой домен или переход внутри iframe) браузер не позволит обработать вручную, чтобы сохранить безопасность и ожидаемое поведение пользователя. Как итог, это нативный способ управлять переходами без костылей и сторонних роутеров. API делает навигацию централизованной, гибкой и предсказуемой, сохраняя при этом поведение браузера максимально естественным. 👍

Utility-first — что это такое? 🤔
Utility-first — это подход к CSS, при котором интерфейс создаётся из множества маленьких, одноцелевых классов-утилит.
Каждый из них отвечает за одно конкретное свойство — например, text-center, mt-4 или bg-blue-500. Вместо того чтобы описывать крупные компоненты вроде .button или .card, разработчик комбинирует эти утилиты прямо в HTML и тем самым буквально «собирает» внешний вид элемента. ✔️ Главное преимущество utility-first — прозрачность и предсказуемость. Сразу видно, как выглядит элемент, не нужно искать стили в отдельных файлах; ✔️ Такие классы изолированы, они не зависят от контекста и не ломают соседние блоки; ✔️ Благодаря этому CSS становится проще сопровождать, а прототипы можно собирать значительно быстрее; ✔️ При правильной настройке сборки (например, через JIT или purge) итоговый CSS остаётся лёгким, потому что в нём остаются только реально используемые классы. Хотя utility-first часто ассоциируют с Tailwind CSS, этот подход не ограничивается одним инструментом. Tailwind лишь популяризировал идею, но её можно реализовать и вручную, или с помощью других решений вроде WindiCSS. ❕ Суть не в фреймворке, а в философии — описывать интерфейс через свойства, а не через компоненты. Пример простого элемента в таком стиле:
<button class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700">
  Сохранить
</button>
Чтобы сохранить порядок и удобство в команде, обычно: ⏺️ определяют единые токены дизайна (цвета, размеры, отступы) и, при необходимости, используют CSS-переменные; ⏺️ выносят часто повторяющиеся сочетания утилит в семантические классы или компоненты (например, .btn) или применяют @apply там, где это поддерживается; ⏺️ договариваются о соглашениях по порядку и структуре классов в HTML; ⏺️ настраивают сборку и очистку неиспользуемого CSS; ⏺️ документируют паттерны и проверяют доступность (focus-стили, aria-атрибуты) в code review. Минусы и подводные камни: увеличенная плотность классов в разметке (читаемость HTML), ограничения @apply в ряде случаев, возможные каскадные/специфичные конфликты и высокая зависимость от дисциплины и соглашений в команде. utility-first — это не просто техника верстки, а способ мыслить интерфейсами как набором предсказуемых свойств. Он делает код чище и работу быстрее, но раскрывает потенциал только тогда, когда в команде есть дисциплина и договорённость о правилах. 👍

Топ-7 медиа-запросов, которые пригодятся в проекте 👍 ✔️ width / height (вьюпорт-размеры, новый синтаксис диапазонов) Классика для адаптива, но теперь есть удобный «range» синтаксис (>, <, >=, <= и даже 900px < width < 1200px), что делает правила читабельнее по сравнению с min-/max- формой. Используйте, когда нужно подстраивать сетку/компоненты под разные ширины окна.

@media (width > 900px) { /* desktop */ }
@media (900px < width < 1200px) { /* medium */ }
✔️ hover / pointer / any-hover / any-pointer (вводные возможности устройства) Вместо угадывания «это телефон/планшет/ПК», проверяйте возможности ввода: умеет ли устройство поддерживать ховер-эффект, насколько точный указатель.

/* Основной ввод — мышь */
@media (hover: hover) and (pointer: fine) { ... }

/* Сенсорные экраны (крупные точки касания) */
@media (hover: none) and (pointer: coarse) { ... }
✔️ prefers-reduced-motion (снижение анимаций для пользователей) Очень важно для доступности — убираем/смягчаем анимации для людей с вестибулярными проблемами. Подробнее доступно по ссылке 👍

@media (prefers-reduced-motion: reduce) {
  .carousel { animation: none; }
}
✔️ prefers-color-scheme и forced-colors (тёмная/светлая тема + режимы высокой контрастности) ⏺️ prefers-color-scheme — стандартный способ подстраиваться под dark/light. Подробнее можно изучить по ссылке 😁 ⏺️ forced-colors — полезен для исправления проблем в режимах высокой контрастности (например, Windows High Contrast). Можно комбинировать с forced-color-adjust.

@media (prefers-color-scheme: dark) { ... }
@media (forced-colors: active) { /* high-contrast fixes */ }
✔️ resolution (для поддачи графики/битмапов высокого разрешения) Полезно для подбора растровых фоновых изображений и ресурсов (1x, 2x и т.д.).

background-image: url(image-1x.jpg);
@media (resolution > 1x) {
  background-image: url(image-2x.jpg);
}
✔️ orientation (портрет/ландшафт) Простая, но иногда важная вещь: если компоновка сильно меняется при перевороте экрана — ориентируйся на orientation. Но не полагайтесь на него для определения «типа устройства» (у людей бывают вертикальные мониторы и т.д.).

@media (orientation: portrait) { ... }
✔️ inverted-colors (инвертированные цвета) / альтернатива — prefers-contrast inverted-colors полезен, когда ОС/браузер инвертирует цвета; помогает корректно отображать тени и медиа. ❓ А какие чаще всего вы используете медиа-запросы?

Часть 4, финальная. Заменит ли ИИ людей? 🧐 Моё общее мнение: в руках профессионала вайб-кодинг — нереально мощный инструмент. Он многократно увеличивает продуктивность миддлов и сеньоров. У новичков же он несёт с собой риски: ложное ощущение «всемогущества», быстрая потеря интереса к дальнейшему развитию/обучению и рост числа низкокачественных продуктов. 😥 Что это значит для рынка труда? ✔️ Низкоуровневые рутинные задачи будут автоматизированы — часть таких специалистов будут заменены. Зачем нанимать джуна, давать ему лёгкие задачи, долго взращивать его "под себя", платить ему зарплату, долго ждать выполнения лёгких и быстрых задач, он ещё и уйти может, если теперь это можно "навайб-кодить" за 10-30 минут?! Пример из жизни: раньше дом стоила бригада из 500 человек. Сейчас это всего 100 человек, но более высоко-квалифицированных и с крутыми инструментами. Так будет и здесь; ✔️ Миддлы/сеньоры станут продуктивнее, смогут уделять больше времени сложным задачам и вести большие проекты чуть-ли не в соло; ✔️ Требования к глубине знаний в долгосрочной перспективе вырастут, ведь сейчас на собеседованиях люди всё чаще используют ИИ, а не свои собственные знания. Работодателю придётся ещё более жёстко отбирать кандидатов. Плюс, как написала в пункте выше, джунов просто перестанут "искать". И что с этим делать?! Для новичков: 🧩 Не пропускайте базу. Учите алгоритмы, структуры данных и принципы ООП — хотя бы в мини-проектах; 🧩 Делайте маленькие эксперименты вручную: пиши простой код и реализуй простые задачи без ИИ; 🧩 После генерации кода ИИ — разбирайте и анализируйте его по кусочкам так, как если бы вы писали этот код полностью самостоятельно. Для команд: 🌷 Внедряйте код-ревью, автоматическое тестирование и проверку безопасности. В ином случае - вы будете нести на себе высочайшие риски, а в дальнейшем - финансовую и юридическую ответственность; 🌷 Не полагайтесь на ИИ во всём. Это лишь инструмент, а, как всем известно, результат применения любого инструмента в большей степени зависит от того, кто и как этим инструментом орудует. Подведём итог:
Вайб-кодинг — это невероятно-мощный инструмент. Как любой инструмент, он полезен в руках профессионала и опасен при бездумном применении.
✔️ Если вы учитесь — не отказывайтесь от дисциплины, даже если с ней вам приходится двигаться значительно медленнее - это нормально. ✔️ Если вы профессионал — используйте ИИ, но не забывайте про качество и безопасность готового продукта, а также про репутационную + финансовую + юридическую ответственности. А ты уже пробовал "вайб-кодить"? Кайфанул? Замечал ли какие-то "странности" в сгенерированном ИИ коде? ⤵️

Часть 3. Что случится, когда ИИ не справится — и почему это опасно 😨 Представьте: вы вайб-кодили проект, всё шло отлично — а потом ИИ «упирается» в какую-то задачу и не может её решить. Даже после 20–30 промптов проблема остаётся нерешённой. Знаний самостоятельно разобраться и всё сделать - у вас нет. Что дальше? Варианты: ✔️ Пользователь растеряется. Он не знает, как искать корень проблемы; ✔️ Мотивация упадёт. Эйфория закончилась, а без неё идти дальше — очень тяжело; ✔️ Код останется «чёрным ящиком». Новичок не понимает, где искать баги, уязвимости и как нужно доработать код. Ещё хуже — массовая проблема качества: ☑️ быстро созданные MVP могут быть полны багов и уязвимостей; ☑️ большая часть приложений начнёт «лагать» или сбоить; ☑️ пользовательский опыт и доверие — страдают. Пример из жизни: ⏺️ навигация в Москве и приложение Яндекс.Карты, которое, порой, очень-часто перестраивает маршрут из-за сбитой геопозиции; ⏺️ водители такси пропускают нужные им повороты, злятся и просто ужасно высказываются в адрес разработчиков приложения, хотя до начала всем известных событий данное приложение было просто спасением для всех водителей в Москве, особенно недавно приехавших в город. К хорошему быстро привыкаешь 🙂 И это реальная потеря доверия к продукту. А теперь представьте, что так будут работать 90% приложений и сайтов? Это будет просто невыносимо. Вайб-кодинг даёт быстрый результат, но увеличивает риск низкого качества и падения устойчивой мотивации у новичков в процессе обучения. Получается, что вайб-кодинг может сделать "минус вайб" 😅 В следующей части — заменит ли ИИ людей? Набираем 60 огоньков и я публикую финальную часть. 🙂 Ссылка на вторую часть: перейти

Часть 2. Быстрый результат не всегда хорошо. Что меня в этом пугает? 😨 Начнём с хорошего: видеть, как идея превращается в работающий продукт за считанные дни — невероятно приятно. 😊Быстрая реализация вызывает всплеск эндорфинов, который сильно мотивирует и открывает новые горизонты. Но есть и обратная сторона медали. Мой личный "тревожный" список: ✔️ Невидимые ошибки. Новичок видит итог — картинку — и радуется. Код для него вторичен и, в большинстве своём, непонятен; ✔️ Отсутствие «шлифовки». Раньше алгоритм десятки раз продумывался и "оттачивался" в голове перед сном или во время кодинга; теперь этого этапа может не быть вовсе — пара промптов и готово (промпт - это вводные данные для ИИ, будь то текст и/или файл); ✔️ Отсутствие контекстных знаний. Пока ИИ генерирует код, "вайб-кодер" не изучает смежные темы при поиске решения в десятках офф-топиках на форуах, как это было раньше, и не понимает, как всё работает. Я помню, как радовалась маленьким результатам — выводу пары-тройки символов в консоли в правильном порядке, которого добивалась несколько дней — и как это учило терпению и исследовательской дисциплине. Эти «маленькие победы» формировали навык разбираться в проблемах по кусочкам, упорно идя шаг за шагом и решая все трудности на пути к одной большой цели. С вайб-кодингом такого нет. 🫤
Вайб-кодинг ускоряет результат, но может лишать новичка процесса, который формирует профессиональные навыки, которые действительно ценятся.
Продолжение — в следующем посте: что будет, если ИИ «упрётся» и не сможет помочь? По-старинке, набираем 35 огоньков и я публикую продолжение 🙂 Ссылка на первую часть: перейти

Что такое «вайб-кодинг» и почему мой брат меня шокировал 😳 Мой 14-летний брат за пару ночей «навайб-кодил» игру. 🤩 Для тех, кто не в теме:
«вайб-кодить» — значит использовать встроенный ИИ прямо в IDE, который пишет код в ваших файлах, создаёт папки, может запускать команды в терминале и в целом реализует большую часть проекта по простым человеческим инструкциям на любом языке.
Игра получилась действительно вполне играбельная и работающей - простенький аналог мобильной игры "Clash Royal". 👍 Он сделал: ⏺️ игровые механики; ⏺️ drag-and-drop; ⏺️ сложную работу с графикой; ⏺️ восстановление ресурсов, базу и различных юнитов; ⏺️ атаки, навыки, ману, защиту и анимацию. Раньше новичку на это понадобилось бы несколько месяцев, а то и год. У моего брата — всего пару бессонных ночей и куча эндорфинов от результата. Вот что важно: это не просто «быстро». Это — вообще принципильно другой путь к готовому продукту. Это являение одновременно меня и вдохновляет, и будоражит. 🤔 Продолжение — в следующем посте: почему быстрый результат — это не всегда хорошо и что меня во всём этом пугает?! Всего 20 огонёчков и я публикую продолжение. 😉

Intl.ListFormat — форматируем список 😉 Intl.ListFormat «знает», как правильно соединять элементы через запятую, союз и/или,
Intl.ListFormat — форматируем список 😉 Intl.ListFormat «знает», как правильно соединять элементы через запятую, союз и/или, сокращать связки и т.д. в зависимости от языка и стиля. Использовать — проще простого, но знание опций и «подводных камней» делает результат аккуратнее. 😁 📚 Что делает и зачем нужен ✔️ Форматирует массив значений в человекочитаемую строку с учётом правил языка (пример: яблоки, бананы и апельсины); ✔️ Позволяет выбирать тип (conjunction/disjunction/unit) и стиль (long/short/narrow); ✔️ Предоставляет format() (строка), formatToParts() (структурированные части) и resolvedOptions() (какие опции реально применены). 📚 Базовый синтаксис

const lf = new Intl.ListFormat(locales, { type, style });
// locales: 'ru' или ['ru-RU', ...]
// type: 'conjunction' | 'disjunction' | 'unit'  (по умолчанию 'conjunction')
// style: 'long' | 'short' | 'narrow'           (по умолчанию 'long')

lf.format(['яблоко', 'банан', 'апельсин']); // => строка, зависимая от локали/опций
📚 Когда может пригодиться formatToParts() Иногда требуется не просто вернуть строку, а стилизовать элементы по-отдельности (ссылки, теги, подсветка). В таких случаях formatToParts() возвращает массив частей с типами element и literal, что даёт полный контроль над разметкой без ошибок в пунктуации:

const parts = new Intl.ListFormat('ru').formatToParts(['а','б','в']);
/* Примерный результат:
[
  {type: "element", value: "а"},
  {type: "literal", value: ", "},
  {type: "element", value: "б"},
  {type: "literal", value: " и "},
  {type: "element", value: "в"}
]
*/
Это удобно, если элементы нужно обернуть в <a> или <strong>, сохранив корректную пунктуацию между ними. Как итог, используя Intl.ListFormat, вы избавляетесь от ручной обработки правил соединения элементов, получаете единообразный стиль вывода и уменьшаете шанс допустить ошибку в пунктуации или переводе. 👍

Иногда кажется, что узнать состояние модификаторов клавиш, таких как Shift, Control, Alt, Meta — это что-то сложное. 🙈 «Как это вообще сделать? Нужно ли проверять каждую клавишу вручную?» 🤔 Не волнуйтесь, решение есть, и оно гораздо проще, чем кажется! Все, что вам нужно, — это метод getModifierState() в JavaScript, который доступен в событиях клавиатуры.
Данный метод позволяет легко узнать, активирован ли конкретный модификатор клавиши (например, Shift, Ctrl, Alt) в момент события клавиатуры.
Использовать можно вот так:
document.addEventListener('keydown', function(event) {
  if (event.getModifierState('Shift')) {
    console.log('Shift нажата!');
  }
  if (event.getModifierState('Control')) {
    console.log('Control нажата!');
  }
});
Метод возвращает true, если клавиша-модификатор нажата, и false, если нет. Всё просто! 😁 Перейдём к практике! Допустим, мы хотим предотвратить стандартное поведение при нажатии Ctrl + S (сохранение страницы):
document.addEventListener('keydown', function(event) {
  if (event.getModifierState('Control') && event.key === 's') {
    event.preventDefault(); // Предотвращаем стандартное поведение
    console.log('Сохранение отменено!');
  }
});
Как итог, мы отключили стандартное поведение браузера с помощью двух строк кода. 😌 Как итог, с помощью getModifierState() мы можем отслеживать необходимые модификаторы. 👍

Разбираемся с navigator.userAgent 🧐
Это строка, возвращаемая объектом navigator, которая содержит информацию о браузере, операционной системе и устройстве пользователя.
Это позволяет адаптировать ваш сайт под конкретное устройство или браузер. 😁 Пример строки:
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36"
Основные компоненты строки userAgent: ⏺️ Mozilla/5.0 — историческая метка; ⏺️ Macintosh; Intel Mac OS X 10_15_7 — информация об ОС; ⏺️ AppleWebKit/537.36 — движок рендеринга; ⏺️ Chrome/141.0.0.0 — версия браузера; ⏺️ Safari/537.36 — дополнительные компоненты. Зачем это нужно? ✔️ Определение устройства и платформы: можно точно узнать, используется ли мобильное устройство, планшет или ПК; ✔️ Совместимость: знание браузера позволяет адаптировать сайт под его особенности. ✔️ Адаптивный дизайн: помогает корректно отображать сайт на разных устройствах. Как же использовать userAgent?

if (/Chrome/.test(navigator.userAgent)) {
    console.log("Пользователь использует Chrome");
}
На что важно обратить внимание? ✔️ Изменяемость строки: пользователи могут изменить userAgent с помощью плагинов; ✔️ Надежность данных: информация может быть неточной, браузеры могут маскироваться под другие. Для более надежной лучше использовать feature detection (обнаружение возможностей), а не полагайтесь только на userAgent, но об этом будет в следующем посте 😉

Предвыбор цвета с использованием <datalist> 🎨 Элемент <datalist> часто остаётся незамеченным, хотя он предлагает полезный функционал. 😔
С помощью этого тега можно предоставить пользователю список заранее заданных опций, из которых он может выбрать, при этом не ограничивая его возможностью ввести собственное значение.
Это идеально подходит, когда нужно предложить популярные или часто используемые варианты, но не навязывать их. 📚 Для работы с <datalist> нужно выполнить несколько шагов: ✔️ Создать элемент <datalist> с уникальным идентификатором; ✔️ Внутри <datalist> добавить элементы <option>, представляющие возможные варианты; ✔️ В поле ввода (<input>) указать атрибут list, значение которого должно совпадать с id элемента <datalist>. Пример:

<label for="browsers">Выберите браузер:</label>
<input list="browser-list" name="browsers" id="browsers">
<datalist id="browser-list">
  <option value="Chrome">
  <option value="Firefox">
  <option value="Safari">
  <option value="Edge">
</datalist>
Так вот, помимо текстовых полей, <datalist> можно использовать для выбора цвета. Браузеры предоставляют стандартный цветовой пикер, который позволяет пользователю выбрать значение из предложенной палитры. Он может либо выбрать один из предложенных цветов, либо ввести свой собственный, а это даёт даёт гибкость в выборе, тем самым упрощая процесс. 👍