На 31 июля 2026 года строгий аудит проходит 166 из 358 созданных материалов. Остальные 192 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить.
На 31 июля 2026 года строгий аудит проходит 169 из 358 созданных материалов. Остальные 189 не считаются «почти готовыми»: их нужно заменить, а не косметически удлинить.
- Голос: М5, системный frontend-практик 2022 года. Текст остаётся коротким и предметным: симптом, граница, проверка, действие, ограничение. Нет выдуманного опыта руководства продуктом или «проверенных» пользовательских эффектов.
- Граница модели: profile `narrow/coarse/unavailable` задаётся вручную. Fixture не запускает DOM, CSS, `matchMedia`, browser, pointer events, сетевой запрос, метрику, usability-тест или реальный rollback. `platform: not-observed` и `usability: not-measured` являются частью возвращаемого результата.
- Источники проверены до написания и отвечают 200: [Media Queries Level 4 CRD, 25.12.2021](https://www.w3.org/TR/2021/CRD-mediaqueries-4-20211225/), [Pointer Events Level 2 Recommendation, 04.04.2019](https://www.w3.org/TR/2019/REC-pointerevents2-20190404/), [WCAG 2.1 Recommendation, 05.06.2018](https://www.w3.org/TR/2018/REC-WCAG21-20180605/). Все URL неизменяемые и документы были доступны в сентябре 2022 года.
## Проход 1 — факты и техника
- Сверена историческая статусная рамка. Media Queries Level 4 на 25.12.2021 — Candidate Recommendation Draft, Pointer Events Level 2 и WCAG 2.1 — Recommendations; поздние WCAG 2.2 и текущие плавающие URL не используются.
- Убрана ложная логика «`hover: none` означает touch-only» и «pointer event доказывает достижимость действия». В тексте остаётся ограниченное утверждение: media features описывают capability среды, а не намерение и удобство конкретного пользователя.
- Fixture проверяет только локальные строки и массив route: валидный explicit path, порядок `summary > details > confirm`, блокировку `hover-only` для teaching profile, invalid input и неизмеряемость platform/usability. В ней нет claims о device, browser, trace, результатах людей или продуктовой метрике.
- Пройдены после последней правки: `node --check web/scripts/upgrade-2022-09.mjs`; `node web/scripts/upgrade-2022-09.mjs --verify-fixture`; `npm run audit:draft -- scripts/upgrade-2022-09.mjs`; import-safe `--print-revisions` без `date` и `author`.
## Проход 2 — голос, полнота и объём
- Первые два абзаца всех трёх статей называют конкретный сценарий и цену: скрытое основное действие, незавершённая операция, ошибочное подтверждение и невозможность объяснить релиз. Нет общих фраз о «важности мобильности».
- В каждой статье есть проблема, таблица, проверяемый JS-пример, figure с осмысленными alt/caption, нумерованный маршрут «симптом → причина → проверка → действие», rollback boundary, перечень того, что модель не покрывает, и следующий отдельный шаг для реальной реализации.
- Основной текст (без источников): practice 7 562 знака, mechanism 8 362 знака, field 8 253 знака. Каждая статья находится в заданном диапазоне 5 000–15 000 знаков и не описывает учебный contract как реальный браузерный или пользовательский результат.
## Проход 3 — визуал и безопасность
- SVG разделены по вопросу: route (`summary → details → confirm`), граница четырёх слоёв и diagnosis/rollback. Они не изображают device screenshot, человека, продуктовый funnel или реальные тестовые данные.
-В SVG нет `script`, `foreignObject`, external URL, `data:image` и event-handler attributes. У всех есть `<title>`, `<desc>`, role `img`, содержательная подпись в статье и контрастные блоки для чтения на узком экране.
- Пройдены: `xmllint --noout` для трёх SVG; safety scan без совпадений; Sharp-рендер каждого SVG на 375 px и визуальный просмотр. Sidecar меняет ровно пять новых файлов; registry, README, `articles.json`, очередь, Git и прочие месяцы не менялись.
## Независимый редакторский приём
### Проход 1 — факты и контракт
Проверены все три датированных W3C URL: Media Queries Level 4 Candidate
Recommendation Draft 25 декабря 2021 года, Pointer Events Level 2
Recommendation 4 апреля 2019 года и WCAG 2.1 Recommendation 5 июня 2018 года
ответили `200`. Источники разделены по роли: Media Queries описывает
<descid="desc">Четыре слоя: input environment, component route, product decision и research. Учебная модель получает заданный профиль и проверяет только маршрут компонента.</desc>
<rectx="545"y="330"width="360"height="104"rx="16"fill="#4c1d95"stroke="#c4b5fd"stroke-width="3"/><textx="570"y="370"fill="#ddd6fe"font-weight="700">4. Research</text><textx="570"y="404"fill="#fff">отдельный метод и данные</text>
<titleid="title">Диагностика скрытого действия и граница отката</title>
<descid="desc">Наблюдение о скрытом действии проверяется в DOM и контракте. Hover-only блокируется для учебного профиля; явный control идёт в отдельную browser-проверку. При неизвестности сохраняется текущий путь.</desc>
<rectx="286"y="472"width="388"height="46"rx="12"fill="#3f1d2e"stroke="#fb7185"stroke-width="2"/><textx="316"y="502"fill="#fecdd3">неизвестность → rollback: current path</text>
<textx="396"y="225"fill="#7dd3fc"font-size="18"font-weight="700">02 · details</text><textx="396"y="262"fill="#fff"font-size="24">Проверить</text><textx="396"y="294"fill="#cbd5e1"font-size="17">данные перед действием</text>
note:'датированный CRD, доступный в сентябре 2022 года. Он определяет media features pointer, hover, any-pointer и any-hover; это характеристики среды, а не тест удобства конкретного человека.',
};
constpointerEvents={
title:'W3C Pointer Events Level 2, Recommendation от 4 апреля 2019 года',
note:'неизменяемая Recommendation. В ней есть критерии, относящиеся к pointer gestures, ориентации и reflow; соответствие продукту требует отдельной оценки, которой здесь нет.',
excerpt:'Как разложить одно действие на видимые шаги и проверить учебный контракт, не выдавая его за тест телефона или пользователей.',
readingMinutes:11,
},
[
paragraph('Десктопный поток часто ломается на телефоне не из-за одного маленького шрифта. В нём может быть меню, которое открывается только при наведении, таблица с действием в последней колонке или подтверждение, доступное лишь рядом с широким экраном. Человек не видит следующего шага и не может закончить действие. Цена ошибки конкретна: незавершённая форма, лишнее обращение в поддержку или изменение, которое никто не может повторить по описанию.'),
paragraph('Начинать с «сделаем mobile version» бесполезно. Сначала выбираем один путь: например, проверить данные и подтвердить операцию. Для него записываем, что должно быть видно до действия, что открывается после него и чем можно активировать переход. Ниже — учебный контракт. Он не запускает браузер и не доказывает, что кому-то удобно. Он только не даёт коду принять маршрут, в котором на узком экране единственный переход спрятан за hover.'),
heading('Один путь вместо переделки всей страницы'),
paragraph('У пути есть три состояния: краткий итог, доступные детали и явное подтверждение. Итог не обязан повторять все поля; он даёт контекст, чтобы не нажать кнопку вслепую. Детали раскрываются отдельным контролом, а действие имеет текстовую метку. В этой схеме «явный» значит, что контракт описывает контрол как видимый шаг. Это не означает, что элемент уже имеет правильный размер, фокус, семантику или раскладку: такие свойства проверяются в реализации отдельно.'),
table('Контракт одного маршрута',['Часть пути','Что обязан увидеть человек','Чего контракт не обещает','Проверка в fixture'],[
['summary','название действия и краткий контекст','что текст поместится в любом языке','шаг существует первым'],
['details','явный переход к данным перед подтверждением','что accordion удобен конкретному человеку','шаг существует вторым'],
['confirm','контрол «Продолжить» с видимой меткой','размер hit-area и реакцию браузера','на narrow/coarse/no-hover требует explicit control'],
['rollback','старый путь остаётся, пока новый контракт не принят','откат сервера или эксперимента','для blocked route возвращается keep-current-path'],
]),
heading('Проверяемый пример: только локальная модель'),
paragraph('Пример использует строки <code>narrow</code>, <code>coarse</code> и <code>unavailable</code> как входные данные. Они не считываются через <code>matchMedia</code>, не соответствуют модели устройства и не являются результатом real trace. Поэтому fixture можно запустить в Node и получить один и тот же результат. Она годится для ревью договора о маршруте, а не для ответа на вопрос «работает ли сайт на конкретном телефоне».'),
codeBlock(modelExample),
paragraph('Важная отрицательная ветка находится рядом: <code>hover-only</code> на этом профиле отклоняется. Это не правило CSS и не интерпретация W3C как готового дизайна. Это выбранная продуктовая оговорка одного учебного пути: если мы заранее заявили отсутствие удобного hover, основное действие нельзя сделать достижимым только так. На широком profile модель допускает такой способ, но всё равно честно пишет <code>usability: not-measured</code>.'),
figure('/assets/editorial/2022/mobile-ux-2022-route.svg','Схема одного учебного мобильного маршрута: summary ведёт к details, затем к видимому confirm; ветка hover-only на narrow, coarse и unavailable останавливается и сохраняет текущий путь.','Рисунок показывает проектный контракт переходов. Он не является картой экрана, браузерным снимком или результатом usability-теста.'),
heading('Почему hover нельзя считать маршрутом'),
paragraph('Media Queries Level 4 различает способность primary pointing device к hover и возможности всех pointing devices. Из этого не следует, что сайт может узнать намерение человека или что на устройстве никогда не появится мышь. Документ прямо советует не делать layout полностью зависимым от hovering. Для редактора это полезная граница: conditional styling допустим, но главный путь должен иметь наблюдаемую альтернативу, описанную без надежды на случайный long press.'),
paragraph('Pointer Events Level 2 даёт общий словарь pointer input, но не гарантирует, что пользователь выполнит жест или увидит подсказку. WCAG 2.1 также не превращает один code sample в доказанное соответствие. Поэтому в этой статье нет выдуманной диагонали экрана, среднего пальца, конверсии и «проверили на iPhone». Такие данные появились бы только после отдельного исследования с условиями, участниками и артефактами.'),
heading('Маршрут: симптом → причина → проверка → действие'),
orderedList([
'<strong>Симптом.</strong> Запишите одно недоступное действие: например, итог открывается только при hover или кнопка стоит за таблицей, которую нельзя читать без горизонтального поиска.',
'<strong>Причина.</strong> Найдите, какой элемент владеет переходом и есть ли у него видимая текстовая альтернатива. Не называйте это «проблемой мобильности» до этого шага.',
'<strong>Проверка договора.</strong> Выполните <code>node web/scripts/upgrade-2022-09.mjs --verify-fixture</code>. PASS означает лишь то, что local model отклоняет hover-only на выбранном profile и сохраняет порядок summary → details → confirm.',
'<strong>Действие.</strong> В реализации добавьте явный control, оставьте context перед подтверждением и договоритесь, что именно является необратимым действием.',
'<strong>Проверка реализации.</strong> Отдельно зафиксируйте browser, viewport, способ ввода, язык, масштаб, данные формы и ожидаемые видимые состояния. Только так начинается реальная проверка UI.',
'<strong>Откат.</strong> Если новый путь скрывает важные данные или меняет предметное действие, верните прежний route contract; не удаляйте старую ветку до проверки продукта и владельца операции.',
]),
heading('Что проверить в коде до выпуска'),
paragraph('Я бы начал с DOM-структуры: label действия, порядок controls, видимое раскрытие details и состояние после активации. Затем проверил бы keyboard path, потому что отсутствие hover не отменяет другие способы ввода. После этого имеет смысл смотреть CSS: нет ли правила, которое делает единственную кнопку прозрачной, выносит её за scroll-area или показывает только в <code>:hover</code>. Для этого нужен отдельный browser test; модель ниже таких данных не содержит.'),
paragraph('Если команда использует media queries, полезно хранить их как адаптацию интерфейса, а не как определитель «телефона». Датированный CRD предлагает query к свойству среды, а не к личности пользователя. Поэтому условие <code>@media (hover: none)</code> может включить дополнительную подсказку или всегда видимую панель, но не должно отменять основное действие для тех, кто подключил иной указатель или пользуется клавиатурой.'),
heading('Граница и следующий шаг'),
paragraph('Контракт не измеряет время, не вызывает CSS, не проверяет target size и не даёт продуктовой метрики. Его action labels, три шага и branch rollback — проектные значения этого материала. Если в реальной операции есть авторизация, цена повторного подтверждения или серверный статус, их надо добавить в отдельный контракт. Нельзя назвать модель полным usability-test только потому, что в ней есть слово mobile.'),
paragraph('Следующий шаг — взять один настоящий экран и провести совместный review с дизайнером, разработчиком и владельцем операции. На бумаге или в прототипе перечислите summary, details, confirm, отмену и ошибки. Затем напишите browser-check с фиксированными условиями. Если проверка расходится с учебным route, меняйте контракт осознанно и добавляйте новую assertion; не подгоняйте результат под красивую схему.'),
heading('Историческая граница сентября 2022'),
paragraph('В пакете используются только доступные к сентябрю 2022 датированные документы W3C: Media Queries Level 4 CRD от 25 декабря 2021 года, Pointer Events Level 2 Recommendation от 4 апреля 2019 года и WCAG 2.1 Recommendation от 5 июня 2018 года. Они задают терминологию и ограничения, а не данные о реальном устройстве, браузере или пользователях.'),
],
);
constmechanism=revision(
{
slug:'editorial-2022-09-mechanism-mobile-ux',
title:'Мобильный сценарий: контракт ввода, состояния и границы',
excerpt:'Разбираем, как отделить возможности среды от проектного контракта и не назвать детерминированную модель настоящим поведением браузера.',
readingMinutes:13,
},
[
paragraph('Когда десктопный интерфейс требует hover, точного указателя или двух рук, команда часто лечит симптом одним media query. Это может добавить отступ, но не отвечает на главный вопрос: какой путь остаётся доступным, если привычный способ активации неудобен или отсутствует. Цена ошибки — скрытое действие и спор в ревью, где один человек видит меню мышью, а другой получает неподвижный экран без следующего шага.'),
paragraph('Полезнее отделить три слоя. Платформа сообщает некоторые характеристики input environment. Компонент рисует controls и управляет состоянием. Продукт принимает решение, какое действие нельзя прятать. Учебная fixture работает только на третьем слое: получает заранее заданный profile и возвращает route. Она не вызывает <code>matchMedia</code>, не ловит pointer event и не говорит, что любой реальный браузер вернёт те же значения.'),
heading('Три слоя, которые нельзя склеивать'),
table('Разделение ответственности',['Слой','Наблюдаемый факт','Допустимое решение','Чего нельзя заключать'],[
['Среда','media feature описывает capability primary или any pointer','добавить адаптацию layout','каким способом действует конкретный человек'],
['Компонент','route содержит summary, details, confirm','показать отдельный control и state','что control физически доступен без browser-check'],
['Продукт','действие признано главным','запретить hover-only как единственный путь','что изменится конверсия или скорость выполнения'],
['Исследование','есть метод и протокол наблюдения','собрать отдельные результаты','что fixture уже заменила исследование'],
]),
paragraph('Эта таблица нужна не для бюрократии. Она делает ошибку локальной. Если на narrow profile кнопка пропала, сначала видно, является ли это CSS condition, ошибкой route state или продуктовым решением. Без такого разделения слова «touch», «mobile» и «responsive» начинают означать всё сразу. Тогда невозможно назначить владельца исправления и нельзя объяснить, почему один откат безопасен, а другой может вернуть потерю контекста.'),
heading('Как читаются pointer и hover'),
paragraph('В Media Queries Level 4 <code>pointer</code> и <code>hover</code> относятся к primary pointing device, а <code>any-pointer</code> и <code>any-hover</code> — к совокупности доступных указателей. В датированном CRD отдельно сказано, что даже при <code>hover: none</code> автору не следует предполагать, что <code>:hover</code> никогда не совпадёт. Следствие для интерфейса простое: capability можно использовать как подсказку для presentation, но основной маршрут нельзя определять только видимостью hover-menu.'),
paragraph('Pointer Events Level 2 описывает унифицированную модель событий и значение <code>pointerType</code>. Оно не говорит, достаточно ли места для таблицы, как человек держит устройство и замечает ли маленький control. WCAG 2.1 содержит критерии для взаимодействия, но проверка соответствия требует конкретного контента и реализации. Поэтому этот материал использует источники для точности терминов, а не превращает их в обещание доступности готового продукта.'),
figure('/assets/editorial/2022/mobile-ux-2022-contract.svg','Диаграмма разделяет input environment, component route, product decision и research; narrow/coarse/no-hover передаётся в учебную модель как проектный профиль, а не считывается из браузера.','Граница важнее стрелок: fixture проверяет только contract route, а проверка платформы и исследования остаются отдельными работами.'),
heading('Детерминированная модель и её ответ'),
paragraph('Функция <code>planTeachingMobilePath</code> принимает profile и action. Если профиль неполный, она возвращает <code>invalid-teaching-profile</code>. Если у действия нет идентификатора или метки, возвращается <code>invalid-action-contract</code>. При корректном input модель всегда строит три шага. Для narrow, coarse или unavailable она помечает confirm как требующий явного control. А <code>hover-only</code> в этой ветке даёт <code>hover-only-blocked-by-contract</code> и rollback <code>keep-current-path</code>.'),
paragraph('Этот ответ намеренно скромный. В нём нет selector, координат, CSS box, фокуса, screen reader, event loop, реального указателя или network. Это не недостаток unit-fixture, если назвать её правильно: она фиксирует наш выбор о том, что основной переход не должен быть только hover-only. Когда нужна проверка другого слоя, создаётся другой артефакт — browser test, accessibility audit или исследовательский протокол — с собственными входами и критериями.'),
codeBlock(modelExample),
heading('Где модель ловит полезную ошибку'),
paragraph('Представим, что карточка на desktop показывает «Открыть итог» при hover. Разработчик переносит это условие в мобильный компонент, но текстовой кнопки не добавляет. В учебном profile путь будет отклонён ещё до запуска UI. Это не доказывает, что реальная карточка сломана именно так. Зато code review получает ясный вопрос: какое явное действие заменяет hover? Если ответа нет, нельзя считать migration завершённой только по красивому breakpoint.'),
table('Симптом и минимальная проверка',['Симптом','Вероятная граница','Проверка','Действие'],[
['Меню видно лишь при наведении','product route','есть ли explicit control в контракте','добавить кнопку с меткой'],
['Таблица скрывает действие справа','component layout','какой step содержит confirm','вынести confirm после summary или details'],
['На hybrid input разные состояния','environment assumption','не перепутан ли primary с any capability','оставить hover enhancement, но не единственный маршрут'],
['Изменение трудно откатить','release boundary','сохраняется ли старый route','включить rollback до удаления прежней ветки'],
]),
heading('Маршрут: симптом → причина → проверка → действие'),
orderedList([
'<strong>Симптом.</strong> Опишите, что именно невозможно завершить без мыши, широкого контейнера или второй руки. Не используйте общий ярлык «плохой UX».',
'<strong>Причина.</strong> Разложите зависимость: это условие среды, CSS visibility, component state или решение спрятать действие?',
'<strong>Проверка контракта.</strong> Прогоните fixture и убедитесь, что selected profile не принимает hover-only для главного действия. Это проверка локальной функции, не браузера.',
'<strong>Проверка среды.</strong> Если implementation читает media features, отдельно документируйте браузер и реальные query results. Не подменяйте их литералами fixture.',
'<strong>Действие.</strong> Добавьте explicit control, сохраните порядок context → details → action и назначьте owner для состояния после клика.',
'<strong>Откат.</strong> Перед выпуском договоритесь, как вернуть старый route без удаления данных и без повторного необратимого действия.',
]),
heading('Почему «адаптивно» не является критерием готовности'),
paragraph('Слово responsive описывает способ строить layout, но не подтверждает маршрут. Страница может красиво перестроиться в одну колонку и всё равно скрыть единственную команду за hover или переполнением. Обратная ошибка тоже встречается: команда делает огромный control, но не объясняет, что он подтверждает. Поэтому в контракте первым остаётся summary, а затем details. До нажатия человек должен видеть предмет действия, а не только форму кнопки.'),
paragraph('Точно так же нельзя выбрать фиксированный viewport и объявить его «всеми мобильными». Наше <code>narrow</code> не равно 320, 375 или 412 CSS-pixels. Оно означает, что для выбранного route мы не полагаемся на широкое одновременное представление. Настоящие breakpoints определяет конкретный layout и контент. Их можно проверить только рендером со шрифтами, локалью, масштабом и данными, которые относятся к продукту.'),
heading('Граница и следующий шаг'),
paragraph('Fixture не является polyfill, design system или аналитикой. Она не покрывает фокус-порядок, ошибку валидации, экранную клавиатуру, orientation change, внешнюю мышь, assistive technology и конкурентные обновления. Она не говорит, что user agent действительно считает pointer coarse. Изменять значения profile ради зелёного результата нельзя: тогда исчезнет проверяемая связь между заявленным ограничением и маршрутом.'),
paragraph('Следующий шаг — оформить component contract рядом с исходным кодом. Для каждого main action перечислите label, состояние enabled/disabled, способ получения details, результат, отмену и rollback. Затем в отдельном окружении проведите visual и keyboard checks. Если один из них обнаружит новый случай, добавьте его как новый input или отдельный тест, а не присваивайте его задним числом этой in-memory модели.'),
heading('Историческая граница сентября 2022'),
paragraph('Здесь не используются будущие WCAG 2.2 или текущие страницы документации. Источники — неизменяемые W3C Media Queries Level 4 CRD 25.12.2021, Pointer Events Level 2 Recommendation 04.04.2019 и WCAG 2.1 Recommendation 05.06.2018. Их статус и дата указаны в ссылках; на сентябрь 2022 они уже были доступны.'),
],
);
constfield=revision(
{
slug:'editorial-2022-09-field-mobile-ux',
title:'Мобильный сценарий: разбор скрытого действия и безопасный откат',
excerpt:'Полевой маршрут для случая, когда десктопный control исчезает на узком экране: как собрать наблюдение, не выдумать результат и вернуть безопасный путь.',
readingMinutes:14,
},
[
paragraph('Фраза «на телефоне нельзя продолжить» уже полезнее, чем «сделайте адаптив». В ней есть симптом, но ещё нет причины. Возможно, кнопка ушла за горизонтальный scroll, details открываются только hover, label стал пустой иконкой или код показывает другой state. Если сразу менять breakpoint, можно временно вернуть кнопку и потерять контекст перед подтверждением. Цена — ошибочное действие, повторная поддержка и release, который невозможно объяснить после жалобы.'),
paragraph('Для разбора возьмём один учебный маршрут. Он не опирается на отчёт пользователей, конкретный телефон, heatmap или real browser trace. Мы задаём narrow/coarse/unavailable вручную, планируем summary → details → confirm и смотрим, примет ли модель hover-only. Такой артефакт не отвечает на вопрос, удобно ли людям. Зато он помогает сделать следующий шаг проверяемым: отделить факт скрытого control от предположения о причине.'),
heading('Соберите наблюдение, которое можно опровергнуть'),
table('Карточка разбора',['Наблюдение','Что ещё неизвестно','Где искать','Безопасное действие'],[
['Нет видимого перехода к подтверждению','скрыт ли он CSS или не построен route','DOM, component state, route contract','не выпускать замену до явной альтернативы'],
['Действие доступно только при hover','есть ли keyboard или explicit control','правила visibility и markup','добавить явный control, не удаляя контекст'],
['Итог не читается без широкой таблицы','какие данные обязательны перед confirm','summary model и предметный owner','выделить краткий summary и details'],
['Новый маршрут вызывает сомнение','какой откат не повторит действие','release plan и state ownership','сохранить current path до отдельной проверки'],
]),
paragraph('Запись «кнопка пропала» должна содержать условия, иначе следующий инженер не сможет отличить bug от другой конфигурации. В реальном отчёте полезны URL или экран, версия приложения, browser, viewport, zoom, способ ввода, локаль, тестовые данные, начальное состояние и ожидаемый result. В этой статье мы не заполняем эти поля выдуманными значениями. Сам список — не доказательство; он только не даёт принять случайную картинку за воспроизводимый диагноз.'),
heading('Минимальный разбор одной ветки'),
paragraph('Сначала смотрим, является ли «Открыть итог» единственным переходом. Если да, hover-only уже противоречит выбранному учебному контракту. Затем выделяем information before action: заголовок, сумма или иной предметный summary — конкретный набор решает владелец операции. После этого добавляется явный button/link и отдельная ветка details. Только после появления такого маршрута имеет смысл обсуждать spacing, animation и breakpoint. Иначе косметика закрепляет ту же скрытую зависимость.'),
paragraph('В fixture отрицательный результат возвращает <code>hover-only-blocked-by-contract</code> и <code>keep-current-path</code>. Последняя строка не означает автоматический rollback приложения. Она лишь запрещает модели объявить новый путь принятым. До выпуска команда всё равно выбирает, как выключать реализацию: флагом, отдельным route, revert commit или ограничением rollout. Этот выбор зависит от репозитория и предметной операции; его нельзя вывести из трёх строк JavaScript.'),
codeBlock(modelExample),
figure('/assets/editorial/2022/mobile-ux-2022-diagnosis-rollback.svg','Диагностический маршрут: наблюдение о скрытом действии ведёт к проверке DOM и contract; hover-only на учебном narrow profile блокируется; явный control ведёт к отдельной browser-проверке; при неопределённости сохраняется current path как rollback boundary.','Схема разделяет доказанный local contract и ещё не выполненные проверки реализации и людей. Она не изображает реальные результаты тестирования.'),
heading('Как не перепутать диагностику с интерпретацией'),
paragraph('Наблюдение: control не отображается в заданном состоянии. Интерпретация: «у всех мобильных пользователей это неудобно». Первое можно проверить DOM и стилями; второе требует метода исследования. Ещё одна опасная интерпретация — «hover: none означает touch-only». Media Queries Level 4 этого не утверждает: primary capability и все доступные input mechanisms различаются. Поэтому код не должен строить бизнес-логику на такой ярлыковой классификации.'),
paragraph('Похожая граница есть у pointer events. Наличие pointer event не гарантирует, что action достижим, и отсутствие записи в нашей fixture не означает, что браузер не работает. WCAG 2.1 помогает сформулировать, почему взаимодействие нельзя рассматривать только как mouse path, но не может заменить проверку конкретной разметки. Хороший разбор называет, какой факт подтверждён, а какой ещё является гипотезой.'),
heading('Порядок исправления'),
orderedList([
'<strong>Симптом.</strong> Зафиксируйте одно состояние, в котором основной action не виден или требует hover. Сохраните реальные условия только если они действительно известны.',
'<strong>Причина.</strong> Проверьте DOM, visibility rules, component state и table/overflow. Не сводите всё к ширине до просмотра владельца route.',
'<strong>Проверка модели.</strong> Запустите <code>node web/scripts/upgrade-2022-09.mjs --verify-fixture</code>. Она должна принять explicit route и блокировать hover-only для teaching profile.',
'<strong>Действие.</strong> Добавьте видимый labelled control, summary перед ним и доступный путь к details. Не прячьте отмену или ошибку в другом hover menu.',
'<strong>Проверка реализации.</strong> Создайте отдельно browser/visual/keyboard сценарий с указанными условиями. Соберите результат, который другой инженер может повторить.',
'<strong>Откат.</strong> Если новый путь меняет семантику или скрывает данные, верните current route; не используйте модель как основание для автоматического отката сервера.',
'<strong>Последующая проверка.</strong> Если исследование или support signal покажет другой барьер, добавьте его в план работы как новый факт, а не меняйте прошлую диагностику без следа.',
]),
heading('Что именно проверять после исправления'),
paragraph('В изолированной реализации проверяем, что label совпадает с предметным действием, details доступны до confirm, focus не теряется при раскрытии, error не исчезает из-за нового layout и control не зависит от hover как единственного условия. Потом смотрим узкие и широкие контейнеры с фактическим текстом продукта. Таблица, сложная локаль и длинное имя могут менять layout сильнее, чем условный размер экрана. Эти результаты должны попасть в тест или отчёт, а не в комментарий «на глаз нормально».'),
paragraph('Если нужен usability-test, ему потребуются отдельные вопросы, способ набора людей, задача без наводящей подсказки, согласованные заметки и осторожная интерпретация. У этого текста таких данных нет. Нельзя превратить fixture 9/9 в «девять пользователей успешно прошли». Это разные единицы наблюдения: assertion в модели проверяет сравнение строк и порядок массива, а исследование изучает человеческое выполнение задачи.'),
table('Граница доказательств',['Артефакт','Что он подтверждает','Что он не подтверждает'],[
['Учебная fixture','условный route и ветку rollback','браузерный рендер или поведение пользователя'],
['Browser test','реализацию при зафиксированных условиях','потребности всех аудиторий'],
['Accessibility review','конкретные критерии и разметку в scope','коммерческий эффект'],
['Usability study','наблюдения выбранного метода и выборки','вечную истину о каждом устройстве'],
]),
heading('Граница и следующий шаг'),
paragraph('Эта диагностика не даёт готового breakpoint, не задаёт размер target и не выбирает библиотеку компонентов. В ней также нет настоящего rollback, потому что нет production state. Попытка добавить туда реальные browser facts без запуска браузера была бы подменой. Достаточно более узкого, но честного результата: отдельный основной control должен существовать в route, а hover-only на выбранном teaching profile отклоняется.'),
paragraph('Следующий шаг — завести короткий issue с наблюдением и неизвестными полями, приложить unit fixture как contract, а затем получить visual evidence в отдельном процессе. Перед rollout запишите владельца, условие остановки и путь отката. После проверки обновите contract только если изменился сам маршрут. Так следующий разбор начнётся не со слов «на мобильном плохо», а с конкретного состояния, границы и действия.'),
heading('Историческая граница сентября 2022'),
paragraph('Фактологическая рамка ограничена тремя исторически доступными документами W3C с immutable URL: Media Queries Level 4 CRD (25.12.2021), Pointer Events Level 2 Recommendation (04.04.2019) и WCAG 2.1 Recommendation (05.06.2018). Будущие версии спецификаций, реальные product signals и device claims в тексте не используются.'),
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.