Давайте поговорим о том, что обычно относят к категории «было бы неплохо» в большинстве компаний, занимающихся разработкой: об accessibility (доступности). И я говорю не о том неловком моменте, когда кто-то упоминает соответствие требованиям WCAG, и все вдруг находят свои туфли очень интересными. Я говорю о чём-то более фундаментальном: должны ли наши языки программирования сами контролировать стандарты доступности?
Вот мой смелый взгляд на ситуацию: мы подходили к этой проблеме неправильно. Мы создали множество руководств, систем соответствия и нормативных требований, но по-прежнему относимся к доступности как к функции, которую можно добавить в конце, прямо перед запуском, когда кто-то вспоминает: «Ах да, существуют скринридеры». Тем временем наши языки программирования? Они нейтральны по этому вопросу. Им всё равно, доступен ваш код или нет. Они с радостью скомпилируют ваш недоступный код.
Текущее состояние доступности: краткий анализ реальности
Прежде чем углубиться, давайте определим, с чем мы на самом деле имеем дело. Стандарты доступности в интернете значительно изменились за последнее десятилетие. WCAG 2.1 Level AA стал стандартом де-факто для соответствия требованиям цифровой доступности, и основные юрисдикции теперь требуют его соблюдения. Министерство юстиции США установило конкретные требования к веб-контенту и мобильным приложениям государственных и местных органов власти, с сроками соблюдения, зависящими от размера организации. Федеральные агентства должны соблюдать стандарты Section 508 для доступности ИКТ.
Практическая реальность? Поставщики борются с этим уже много лет. Федеральные агентства, требующие добровольных шаблонов доступности продуктов (VPAT) и отчётов о соответствии доступности (ACR), почти никогда не видят заявлений о полном соответствии. И мы говорим не об obscure edge cases — это фундаментальные проблемы доступности, которые можно выявить и предотвратить на уровне языка.
Почему языки программирования были молчаливыми наблюдателями
Вот неудобная правда: языки программирования разработаны так, чтобы быть нейтральными. Python не судит о ваших именах переменных. JavaScript не заботится о том, является ли ваша структура DOM семантической. Go не навязывает вам свою философию обработки ошибок. Эта нейтральность часто преподносится как особенность — «мы не навязываем вам парадигму», — но это также означает, что языки с радостью становятся соучастниками нарушений доступности.
Когда вы пишете это на JavaScript:
// Лингвистический эквивалент преступления
<div onclick="handleClick()">Нажми на меня</div>
Ваша IDE не кричит. Ваш линтер не взрывается. TypeScript даже не моргает. Но вы только что создали недоступный интерфейс, с которым могут столкнуться пользователи клавиатуры и скринридеров. Нет предупреждения. Нет намёка. Ничего.
Сравните это с тем, как языки относятся к безопасности типов или нулевым ссылкам. После десятилетий головных болей с NullPointerException языки, такие как Rust, сделали обработку null невозможной для игнорирования. Kotlin сделал явным наличие null в системе типов. Но доступность? Мы в основном оставили это на усмотрение последующих инструментов, руководств по стилю и случайных комментариев при проверке кода, которые игнорируются, потому что «мы исправим это позже» (повествователь: они так и не сделали).
Аргументы ЗА контроль на уровне языка
Позвольте мне изложить утвердительный аргумент, потому что здесь есть веские доводы.
Во-первых, доступность как первоочередная задача. Когда что-то заложено в основу языка, это сигнализирует о важности. Если бы в TypeScript был тип AccessibilityViolation, который распространялся по вашей кодовой базе как ошибка, разработчики внезапно бы отнеслись к этому серьёзно. Не потому, что они плохие люди, а потому, что сопротивление было бы реальным и непосредственным.
Во-вторых, сетевой эффект. Как только основные языки начнут обеспечивать соблюдение шаблонов доступности, экосистема последует за ними. Библиотеки адаптируются. Соглашения фреймворков изменятся. Через несколько лет написание недоступного кода станет таким же странным, как написание кода без аннотаций типов в современном TypeScript — технически возможно, но социально неодобримо.
В-третьих, это действительно решаемо. Мы знаем, как выглядят доступные шаблоны. У нас есть руководства WCAG. Мы понимаем семантический HTML. Мы каталогизировали атрибуты ARIA. Это не теоретическая — это конкретная, реализуемая знания. Языки могут обеспечивать:
- проверку семантической структуры HTML;
- проверку атрибутов ARIA;
- требования к контрасту цветов для веб-фреймворков;
- проверку поддержки навигации с клавиатуры;
- требования к тексту alt для изображений;
- ассоциации меток форм;
- шаблоны управления фокусом.
В-четвёртых, соответствие становится демократичным. Сейчас для обеспечения доступности требуются специальные знания. Нужно знать WCAG, понимать, как работают скринридеры, понимать навигацию с клавиатуры. Большинство разработчиков этого не знают. Но если ваш язык говорит: «Эй, этому компоненту нужна семантическая разметка», внезапно вы не выбираете доступность — вы отказываетесь от неё намеренно. Дефолт меняется.
Представьте, что проверки доступности на уровне языка существуют: эта недоступная кнопка:
// Без принуждения — отправляется в продакшен
const Button = ({ onClick, children }) => (
<div
onClick={onClick}
style={{
padding: '10px',
backgroundColor: 'blue',
cursor: 'pointer'
}}
>
{children}
</div>
);
Может вместо этого выдать что-то вроде:
// Гипотетически, с линтингом доступности, встроенным в язык:
const Button = ({ onClick, children, ariaLabel }) => (
<button
onClick={onClick}
aria-label={ariaLabel}
type="button"
style={{
padding: '10px',
backgroundColor: 'blue',
}}
>
{children}
</button>
);
// Ошибка во время компиляции, если ariaLabel отсутствует, когда требуется
// Ошибка во время компиляции, если onClick не имеет обработчиков клавиатуры
// Ошибка, если контраст цветов недостаточен
Аргументы ПРОТИВ контроля на уровне языка
Но давайте будем честными. Противоположная точка зрения также имеет смысл.
Во-первых, доступность не универсальна. Банковское приложение имеет другие требования к доступности, чем игра в браузере. Инструмент визуализации данных имеет другие ограничения, чем интерфейс CRUD. Фиксированная ширина макета для специализированной области может быть вполне приемлемой. Жёсткое соблюдение требований на уровне языка может создать больше проблем, чем решить для специализированных случаев использования.
Во-вторых, это может фрагментировать экосистему. Представьте, что Python обеспечивает соответствие стандартам WCAG для веб-фреймворков. JavaScript делает то же самое для API Node.js. Rust обеспечивает соблюдение иначе. Теперь разработчики, работающие на разных языках, сталкиваются с непоследовательными правилами. Языковые войны станут ещё более интенсивными: «Ну, модель доступности Golang слишком строга для реальных приложений».
В-третьих, проблемы производительности и осуществимости. Добавление проверки доступности на уровне языка означает больше обработки, больше памяти, больше сложности в компиляторе или среде выполнения. В условиях ограниченных ресурсов или встроенных систем это может быть реально проблематично.
В-четвёртых, преждевременно обеспечивать то, что всё ещё развивается. WCAG 2.1 актуален, но что будет с WCAG 3.0? Что, если стандарты доступности значительно изменятся в ближайшие пять лет? Обеспечение соблюдения на уровне языка закрепит стандарты в самом языке, что сделает эволюцию болезненной.
В-пятых, это всё равно не решает фундаментальную проблему. Проблемы доступности не только технические — они часто связаны с архитектурными и дизайнерскими решениями. Вы можете обеспечить семантический HTML, но не можете заставить разработчика тестировать с реальными вспомогательными технологиями. Вы можете потребовать атрибуты ARIA, но не можете проверить их правильность. Обеспечение соблюдения на уровне языка — это как пластырь на пулевом ранении.
Срединный путь: что языки программирования могли бы реально сделать
Вместо жёсткого контроля вот что, по моему мнению, языки программирования могли бы реально сделать без перегибов:
1. Встроить линтинг доступности в стандартные инструменты
# Гипотетически, запуск линтера языка
$ python -m lint --accessibility myproject/
accessibility/warnings.txt:
forms.py:42 - Форма ввода без связанной метки
dashboard.py:156 - Изображение без альтернативного текста
navigation.py:89 - Меню навигации недоступно для клавиатуры
styles.py:201 - Коэффициент контраста цветов 3,2:1 (минимум 4,5:1 требуется)
2. Предоставить шаблоны доступности в стандартной библиотеке
Каждая стандартная библиотека языка включает лучшие практики для общих шаблонов. Почему бы не доступность? Компонент semantic_button, который корректен по умолчанию, лучше, чем написание недоступных div.
3. Создать системы типов, учитывающие доступность Для статически типизированных языков можно иметь типы, представляющие доступные компоненты:
type AccessibleButton = {
label: string;
ariaLabel?: string;
onClick: (event: KeyboardEvent | MouseEvent) => void;
disabled?: boolean;
};
// Система типов гарантирует, что вы обрабатываете как клавиатурные, так и мышиные события
// Она гарантирует, что вы предоставляете метку
// Это не принуждение — это руководство с зубами
4. Интеграция с тестовыми фреймворками Языки могут предоставить встроенные утилиты для тестирования, которые автоматически
