CSS Highlight API: отрисовка и доступность
Используйте пользовательские подсветки с семантическим текстом, ограниченными диапазонами, фокусом клавиатуры и безопасным fallback.
BotBrowser Team
Нужна структурированная документация по теме Платформа?
Эта статья относится к редакционной библиотеке. Для пошаговой настройки, справки и постоянных обновлений переходите сразу в соответствующий раздел docs.
CSS Highlight API может оформлять совпадения поиска, аннотации и выделения без изменения DOM, но подсветка является представлением, а не смыслом. BotBrowser может воспроизвести принадлежащий странице fixture подсветки и проверить видимые резервные состояния; он не подтверждает речь вспомогательной технологии, намерение чтения человека или полную реализацию отрисовки браузера. Держите контракт узким: оставляйте текст в DOM, регистрируйте ограниченные диапазоны, рисуйте различимый слой и сохраняйте семантический резерв с управлением клавиатурой.
TL;DR
CSS.highlights.set('search-hit', new Highlight(range))оформляетRange; он не добавляет текст, подписи или семантику доступности.- Пользовательский псевдоэлемент подсветки вроде
::highlight(search-hit)должен использовать безопасную по контрасту пару цветов и не быть единственным сигналом. - Храните исходный текст и объявляемый счётчик результатов в DOM; отдельно проверяйте фокус клавиатуры, выделение, принудительные цвета, масштаб и уменьшение движения.
- Перестраивайте диапазоны после изменений содержимого, ограничивайте их число и длину и удаляйте устаревшие записи через
CSS.highlights.delete(name). - BotBrowser может проверить состояние страницы и резерв, но статья основана на источниках и не заявляет запуск BotBrowser.
Содержание
- Сохраняйте смысл в DOM
- Регистрируйте ограниченные диапазоны
- Рисуйте видимый и доступный слой
- Обрабатывайте выбор, фокус и резервный режим
- Классифицируйте результаты таблицей решений
- Возможности и ограничения BotBrowser
- Заключение
Сохраняйте смысл в DOM
API подсветки меняет способ отрисовки существующего текста. Range указывает на уже существующие узлы, поэтому скринридеру, копированию, поиску текста и отображению без CSS нужен тот же исходный контент. Поместите счётчик результатов в область aria-live="polite", сохраните запрос в подписанном поле и покажите для аннотаций обычный список или абзац. Не кодируйте предупреждение только жёлтым фоном или пользовательским эффектом отрисовки.
API отделён от Selection API. Выделение пользователя может быть видно через ::selection; именованная пользовательская подсветка является объектом Highlight, которым владеет приложение. Дайте каждому состоянию стабильное имя, например search-hit или current-hit, и удаляйте его при пустом запросе. Имя является CSS-крючком, а не подписью для озвучивания вспомогательной технологией.
Связанную границу визуальной идентичности описывает материал управление цветом CSS и доступность дисплея: он отделяет видимый в браузере результат стиля от заявления о возможностях. Для работы с клавиатурой локаль и приватность Keyboard Layout API показывает, почему визуальная подпись не должна становиться выводом о человеке. Проверка совместимости Web Components добавляет матрицу, разделяющую обнаружение функции и обещание читателю.
Регистрируйте ограниченные диапазоны
Создавайте диапазоны из отрисованных текстовых узлов, а не из ненадёжной HTML-строки. Простой fixture обходит текстовые узлы, ищет запрос с приведением регистра и вызывает range.setStart() и range.setEnd() для каждого совпадения. Ограничьте MAX_HIGHLIGHTS и MAX_MATCH_CHARS; страница должна оставаться отзывчивой, даже если запрос совпадает с тысячами слов.
const MAX_HIGHLIGHTS = 100;
const MAX_MATCH_CHARS = 120;
function showMatches(root, query) {
CSS.highlights.delete('search-hit');
if (!query || !window.Highlight) return 0;
const ranges = [];
for (const node of root.querySelectorAll('[data-search-text]')) {
const text = node.textContent || '';
let offset = 0;
while (offset < text.length && ranges.length < MAX_HIGHLIGHTS) {
const start = text.toLocaleLowerCase().indexOf(query.toLocaleLowerCase(), offset);
if (start < 0) break;
if (query.length <= MAX_MATCH_CHARS) {
const range = new Range();
range.setStart(node.firstChild, start);
range.setEnd(node.firstChild, start + query.length);
ranges.push(range);
}
offset = start + Math.max(query.length, 1);
}
}
CSS.highlights.set('search-hit', new Highlight(...ranges));
return ranges.length;
}
Точный fixture зависит от границ текстовых узлов; рабочий код должен безопасно обрабатывать вложенные узлы и пустой firstChild. Пересчитывайте диапазоны после отрисовки, локализации, фильтрации или переработки виртуального списка. Диапазон, указывающий на удалённое содержимое, является устаревшим состоянием, а не причиной сохранять старый объект подсветки.
Рисуйте видимый и доступный слой
Используйте псевдоэлемент ::highlight(search-hit) для именованного слоя и держите объявления небольшими. Стилизация CSS Highlight API намеренно ограничена по сравнению с обычными элементами; браузер может игнорировать неподдерживаемые свойства. Выберите цвет текста и фона, соответствующие цели контраста продукта, и добавьте в интерфейс сигнал не через цвет: контур, значок или счётчик результатов.
::highlight(search-hit) {
color: #111827;
background-color: #fde68a;
text-decoration: underline 0.12em solid #92400e;
}
@media (forced-colors: active) {
::highlight(search-hit) {
color: CanvasText;
background-color: Highlight;
text-decoration: underline;
}
}
Не предполагайте, что пользовательская подсветка одинаково рисуется во всех движках и встроенных контекстах. Проверьте наличие CSS.highlights, Highlight и нужного селектора, а при отсутствии любой предпосылки покажите резервный класс или встроенный маркер. Видимый в браузере результат отрисовки не доказывает, что скринридер объявит «совпадение» или режим высокой контрастности сохранит выбранные цвета.
Обрабатывайте выбор, фокус и резервный режим
Поиск должен переводить фокус на настоящий элемент результата, а не на нарисованный диапазон. Добавьте кнопки «Назад» и «Далее» с доступным именем, сохраняйте запрос в поле и прокручивайте выбранный результат в область видимости, не запирая фокус клавиатуры. Если пользователь выделяет текст, не заменяйте ::selection подсветкой поиска: эти слои выражают разные состояния.
Если подсветки не поддерживаются, добавьте временный элемент mark или CSS-класс вокруг доверенного текстового узла и покажите тот же счётчик и навигацию. Удаляйте этот резерв перед новым запросом, чтобы вложенные оболочки не накапливались. Для аннотаций только для чтения соседняя заметка с aria-describedby надёжнее цветовой заплатки, исчезающей при печати или в режиме принудительных цветов.
Проверяйте масштаб 200%, интервалы текста, навигацию только клавиатурой, prefers-reduced-motion и скринридер. Подсветка не должна закрывать контуры фокуса, обрезать нижние выносные элементы или создавать мигание для каждого результата. Если запрос пуст или совпадений нет, очистите именованную подсветку и объявите «0 результатов» в live region.
Классифицируйте результаты таблицей решений
| Наблюдаемая стадия | Что она устанавливает | Безопасное следующее действие |
|---|---|---|
Нет CSS.highlights или Highlight | В этом контексте нельзя начать подсветку | Использовать mark или обычный текст и сохранить навигацию |
| API есть, запрос пуст | Нет диапазона для отрисовки | Удалить search-hit, объявить очистку и оставить фокус |
| Диапазоны созданы, отрисовка видна | Страница применила видимый слой | Проверить контраст, фокус, принудительные цвета и текст |
| Содержимое или локаль изменились | Старые смещения могут быть устаревшими | Перестроить диапазоны и удалить старый объект |
| Совпадений больше 100 | Fixture достиг ограниченной нагрузки | Показать предел и предложить сузить поиск |
| Принудительные цвета или печать иные | Пользовательский агент изменил представление | Сохранить DOM-результат и сигнал, не зависящий от цвета |
Каждая строка описывает наблюдаемое состояние, а не универсальную гарантию. Записывайте видимое состояние, длину запроса, число диапазонов и резервную ветвь; не сохраняйте весь документ и не выводите намерения читателя.
| Контрольная точка | Запись | Что нельзя заключать |
|---|---|---|
| Проверка API | CSS.highlights=true, Highlight=true | Что скринридер озвучит слой |
| Жизненный цикл | rangeCount=3, длина и узлы | Что текст связан с личностью |
| Резерв | fallback=mark и причина | Что нарушена вся система доступности |
| Фокус | focusTarget=result-2 и порядок клавиш | Намерение пользователя |
| Очистка | старый highlight удалён | Удаление удалённого состояния |
| Проверка | Минимальная запись | Чего нельзя заключать |
|---|---|---|
| Совместимость API | window.CSS, CSS.highlights, window.Highlight | Что поддерживаются все CSS-свойства |
| Лимиты запроса | MAX_HIGHLIGHTS=100, MAX_MATCH_CHARS=120 | Что безопасно хранить весь документ |
| Текущий диапазон | исходный узел и rangeCount | Что текст связан с личностью |
| Фокус | focusTarget=result-2 | Что человек хочет читать этот результат |
| Визуальный режим | обычный, принудительные цвета или печать | Что физический экран даёт те же цвета |
| Очистка | имя удалено перед новым рендерингом | Что удалено удалённое состояние |
Возможности и ограничения BotBrowser
BotBrowser может выполнить разрешённый fixture страницы и проверить счётчик DOM, число диапазонов и ветвь резервного режима. Он не может подтвердить речь программы чтения с экрана или гарантировать одинаковую отрисовку ::highlight() во всех движках.
BotBrowser может повторить разрешённый сценарий с диапазонами Highlight, стилем ::highlight() и видимым резервом; эта статья не заявляет запуск BotBrowser и не подтверждает речь скринридера или восприятие цвета.
{
"api": "CSS.highlights",
"highlightName": "search-hit",
"rangeCount": 3,
"maxHighlights": 100,
"domResultText": "3 matches",
"assistiveTechnologySpeech": "not-asserted"
}
Повторяйте fixture после выпуска браузера, изменения локализации, виртуального списка или токена контраста. Сравнивайте первое видимое расхождение и сохраняйте резерв DOM в записи приёмки.
Практическая последовательность проверки
Начните с семантического пути до проверки пикселей. Отправьте запрос с отключённым search-hit и убедитесь, что счётчик, заголовки и текст остаются понятными. Затем включите пользовательскую подсветку и сравните тот же диапазон при масштабе 100%, 200% и 400%. Такой порядок выявляет регрессию, когда аккуратный визуальный слой скрывает недоступный с клавиатуры список результатов.
Меняйте одну переменную за раз: замените текст переводом, переработайте элемент списка и включите forced-colors: active. Корректная реализация удаляет старый Highlight, создаёт диапазоны для нового текстового узла и сохраняет live-счётчик. Записывайте первое расхождение, а не объявляйте итоговый снимок вердиктом браузера.
Источники
- CSS Custom Highlight API specification
- MDN: CSS Custom Highlight API
- MDN: ::highlight()
- BotBrowser advanced features
Заключение
Используйте пользовательскую отрисовку CSS Highlight API, чтобы выделять текст с уже существующим смыслом. Ограничивайте и перестраивайте диапазоны, храните статус и навигацию в DOM и давайте безопасную по контрасту альтернативу без цвета. BotBrowser может воспроизвести состояние страницы, но запуск не заявлен; результат не доказывает озвучивание скринридером или понимание пользователя.
Эта статья входит в руководство: Поверхности веб-платформы: карта проверки
Похожие статьи
Управление цветом CSS и доступность отображения
ПлатформаМедиазапросы: уважение пользовательских предпочтений в интерфейсах
ПлатформаView Transitions API: состояние и навигация
ПлатформаПроверка совместимости Web Components в браузерах
ПлатформаГолоса синтеза речи и доступное воспроизведение в браузере
ПлатформаPopover API: фокус, закрытие и доступность
Переведите BotBrowser из исследований в продакшн
Используйте эти руководства, чтобы понять модель, а затем перейти к кроссплатформенной валидации, изолированным контекстам и масштабируемому браузерному развертыванию.