Как стать автором
Обновить
352.56
Конференции Олега Бунина (Онтико)
Профессиональные конференции для IT-разработчиков

Drag-&-Drop компоненты для слепых пользователей? Вы шутите?

Время на прочтение18 мин
Количество просмотров6.6K
Что объединяет Trello, Gmail, Aviasales и Google Keep? В каждом из них есть Drag-&-Drop-компоненты, которые можно перетаскивать мышкой. Использование этих компонентов делает сайты по-настоящему удобными, потому что в реальной жизни мы часто используем этот паттерн. Передвигать вещи для нас естественно, поэтому и в вебе мы хотим перемещать элементы по экрану с одного места на другое, как стикеры на доске или магнитики на холодильнике. Сортировка todo-списков, организация дашбордов, загрузка файлов — мы просто не можем себе представить все эти события без перетаскивания элементов на странице.



Но есть нюанс. Когда мы разрабатываем такие компоненты, то думаем, что наши пользователи физически видят экран и элементы, могут зацепиться за них мышкой и перетащить.Часто так и есть, но мы забываем о сегменте пользователей с проблемами зрения. Мы создаем компоненты, которыми невозможно пользоваться большой группе людей, которая просто их не видит. Как сделать так, чтобы им тоже были доступны Drag-&-Drop-компоненты?

Сергей Кригер — фронтенд-разработчик в компании SinnerSchrader. Кроме фронтенд-разработки, активно интересуется темой веб-доступности и расскажет о некоторых способах ее повышения. Под катом — рассказ о создании Drag-&-Drop-компонентов для тех, кто не видит экран и пользуется другими устройствами для взаимодействия с браузером.


Слайдер


Начнем с простого — на экране скрин сайта Aviasales. Вводим пункт отправления, пункт прибытия, и на экране появляется список рейсов для выбора. Рейсов может быть много, их можно фильтровать по цене, времени и другим параметрам. Для фильтрации результатов на сайте Aviasales используются слайдеры. Они выглядят здорово, ими удобно пользоваться.



Наша задача — представить, что мы разработчики этого сайта. Попробуем воссоздать эти слайдеры и посмотрим, как они работают в разных случаях.

Нативный слайдер


Первое, о чем стоит подумать — можно ли использовать нативные элементы для нашей функциональности? Если такая возможность есть, идем этим путем, потому что нативные элементы сэкономят много времени.

Сделаем простой пример: бегунок двигается по дорожке и значение слайдера меняется. Двигаем бегунок вправо — цвет экрана меняется от белого к ярко-розовому, если обратно — опять к белому.



Слайдер работает просто — бегунок скользит по дорожке и слайдер возвращает какое-то значение.

Такой слайдер никто не будет применять в продакшне. Ни один дизайнер не разрешит его использовать, потому что он смотрится слабо. Мы, как разработчики, можем эту проблему решить. Есть ряд CSS-свойств с префиксами для каждого браузера, где можно стилизовать отдельные элементы слайдера и облагородить его.

: : -webkit-slider-runnable-track 
: : -webkit-sHder-thumb 
: : -moz-range-track
: : -moz-range-thumb 
: : -ns-track :-ms-thumb 
: : -ms-fill-lower
: : -ms-fill-upper

Можно использовать свойства вручную, но это нетривиальная задача. Работа свойств отличается в разных браузерах, и тестирование займет много времени.

Но есть специальный инструмент, который решает задачу — сайт Daniel Stern. На нем вручную стилизуете слайдер, копируете CSS-код и все работает. Нативный слайдер теперь это HTML-элемент input с типом range, а работает как и раньше — перемещением бегунка.

Тестирование слайдера


Чтобы решить задачу до конца — тестируем. Дальше мы будем тестировать не только слайдер, но и другие элементы по трем параметрам.

  • Мышка. Тестирование работы компонента с мышкой — это ровно то, что мы всегда и делаем.
  • Клавиатура. Кроме мышки мы адаптируем слайдер для людей с проблемами по зрению. Эти пользователи не видят экрана и пользуются для взаимодействия с компьютером и браузером клавиатурой
  • Screen reader — специальное устройство, которое озвучивает информацию на экране.

Примечание: здесь и далее удобнее совмещать чтение и просмотр тестирования на видеозаписи — так нагляднее. Отрезок видео с тестированием слайдера запустится с нужного момента.


Мышка. Цепляемся за бегунок, тянем вправо и влево. Если все меняется — замечательно.

Клавиатура. Клавишей «Tab» переходим на бегунок, и стрелками перемещаем его по дорожке.

Screen reader. Здесь сложнее. Представим, что мы не видим экрана — выключаем свет на экране и включаем screen reader. Он говорит:

— Функция VoiceOver включена в Chrome.

При помощи клавиши «Tab» переходим на бегунок.

— Ноль. Стилизованный слайдер-application.

Screen reader озвучивает информацию: значение, имя слайдера и другую сопутствующую информацию, которая тоже может быть полезна. Начинаем передвигать бегунок:

— Один, два, три, четыре, пять.

Если слышим отсчет — все работает. Включаем свет и проверяем результат. Тестирование закончено.

Кажется, что мы ничего не делали, а просто стилизовали слайдер. В чем заключается наша работа по обеспечению доступности? В том-то и дело, что никакой работы и не было — мы использовали нативный элемент.

Если в вашем приложении есть возможность пользоваться нативными элементами: слайдером, кнопками, чек-боксами, смело ими пользуйтесь и экономьте время. Эти элементы уже поддерживают клавиатуру и screen reader.

Кастомный слайдер


Предположим, что нативный слайдер не наш вариант. Посмотрим код Aviasales и увидим, что здесь не те слайдеры, которые мы только что тестировали.



Это не input с типом range, а набор div, стилизованных по желанию дизайнера. Кажется, что разницы нет, все работает.

Проверим это и аналогично усложним задачу, создав слайдер вручную. Ключом к передвижению бегунка по дорожке, будет абсолютное позиционирование:

position: absolute

Мы абсолютно позиционируем бегунок относительно дорожки и, в зависимости от его положения, высчитываем значение слайдера.

Для определения момента захвата элемента мышкой, используем событие mousedown. Чтобы отслеживать движение события — mousemove, а mouseup скажет нам, что мы отпустили бегунок. Для touch screen мы тоже будем пользоваться рядом событий: touchstart, touchmove и touchend.

Вроде все просто. Слайдер будет выглядеть так же, как в предыдущем примере, но в коде это никакой не слайдер, а набор стилизованных div, а за перемещение будет отвечать JavaScript. При работе с мышкой это будет незаметно, но с клавиатурой есть нюанс. Чтобы клавиатура могла поймать такой бегунок, придаем ему свойство принимать фокус атрибутом tabindex и с помощью JavaScript отслеживаем нажатие на стрелки.

Тестирование кастомного слайдера



Со screen reader будет сложнее. Выключаем свет, включаем screen reader и пробуем изменить значение. Screen reader озвучивает действия, и если говорит что-то странное, например, «групп» — думаем.

«Групп» — это то, что озвучивает screen reader, когда читает div или другой элемент, который не несет никакой семантической роли. Любое слово, которое не относится к слайдеру, говорит, что доступность пока не достигнута. Чтобы сделать слайдер доступным — воспользуемся ARIA-атрибутами:

<div class="thumb" tabindex="0"
    role="slider"
    aria-labеl="Слайдер"
    aria-valuenow="5"
    aria-valuemin="0"
    aria-valuenax="10"
></div>

Нам нужно сказать screen reader, что наш обычный элемент со стилем — это слайдер. Мы сделаем это при помощи role со значением slider. Затем даем имя слайдеру при помощи атрибута aria-labеl. Задаем текущее значение слайдера, минимальное и максимальное значение слайдера. При помощи этих пяти ARIA-атрибутов мы оживим слайдер и заставим screen reader произносить то, что нам нужно.

При помощи разметки, JavaScript и ARIA-элементов мы сделали кастомный слайдер доступным для всех пользователей.

Сортировка


Еще один подход к реализации Drag-&-Drop-компонентов — сортировка. Часто на сайтах есть списки элементов, которые хорошо бы сортировать.



Выше скриншот сайта Google Keep — это сервис, который позволяет создавать списки дел, to-do листы. У to-do листа есть имя, список элементов, возможность отметить каждый элемент, удалить его, а также добавить новый. Наша задача — воссоздать этот функционал.

Так выглядит виджет списка дел.



В нем есть:

  • название — список дел;
  • элементы: погулять с собакой, записаться к врачу, которые можно отмечать или удалять;
  • возможность добавлять новые элементы — добавить задачу.

Когда мы добавляем новый элемент, например, «приготовить ужин», он помещается в конец списка. Это логичное поведение — мы добавили элемент последним. Но давайте предположим, что эти списки имеют какой-то приоритет. Например, после рабочего дня ужин важнее, чем прогулка с собакой. Мы бы хотели его переместить в начало — приоритетные элементы должны быть в начале списка. Как решить эту задачу?

Как вообще передвигать элементы, даже пока не думая о доступности? Мы уже знаем один способ — при помощи абсолютного позиционирования. Другой способ — добавить к элементам атрибут draggable. Если любому элементу добавить этот атрибут со значением true, он становится перетаскиваемым. Вы можете ухватиться за него мышкой и перетащить в другое место на экране.

Но не всем элементам нужен атрибут draggable. Картинкам и ссылкам не нужен — они по умолчанию перетаскиваемые.

Только если мы хотим отменить этот функционал, мы поставим draggable со значением false.

Перенос данных


На экране есть два элемента. Слева — картинка, у которой атрибут draggable подразумевается по умолчанию. Справа — элемент с пунктирной линией. Наша задача — перетащить картинку внутрь пунктирного квадрата, который я назвал «drop-zone».



Хватаем за картинку и тянем в сторону «drop-zone». Этот знакомый функционал: можем зайти картинкой в зону перетаскивания, выйти, когда отпустим — картинка оказывается внутри. Такой же функционал мы хотим в нашем списке дел.



Но перед тем, как посмотреть, как работает список дел с этой технологией, разберемся, что происходит, когда мы перетаскиваем элемент с места на место. На каждом из этих элементов случается список событий.

На перетаскиваемом элементе это три события.

Dragstart, которое происходит один раз, когда мы начинаем процесс перетаскивания элемента. Это важное событие, позже узнаем почему.

Drag, которое регулярно случается каждые несколько сотен миллисекунд. В зависимости от браузера значение меняется, но событие частое. При помощи этого атрибута можно отслеживать положение на экране элемента, который мы перетаскиваем.

Dragend — срабатывает, когда мы отпускаем элемент мышкой.

На элементе, который принимает другие элементы, срабатывают четыре события.

Dragenter — оповещает, когда мы перетащили элемент внутрь другого элемента.

Dragover — говорит, когда мы начинаем перемещать его внутри.

Dragleave — когда выносим элемент за пределы другого элемента, не отпуская мышки.

Drop — самое важное событие, которое срабатывает, когда мы отпускаем мышку внутри принимающего элемента.

Все эти 4 события только предпосылки к перемещению элемента — они сами ничего не делают. При помощи них можно решить перемещение элемента. Делаем это так:

draggable.addEventListener("dragstart", e => {
e.dataTransfer.setData("text/plain", e.target.id);
});
dropZone.addEventListener("drop", e => {
const id = e.dataTransfer.getData("text/plain");
const el = document.getElementById(id);
});

Мы можем вручную добавить данные в событие, которое срабатывает в начале на том элементе, который мы перетаскиваем. При помощи метода dataTransfer.setData мы добавляем селектор элемента, который перетаскиваем. В нашем случае это id.

В конце, когда срабатывает событие Drop на принимающем элементе, эти данные достаем при помощи метода dataTransfer.getData. Получается, что у нас будет id элемента, который хотим передвинуть. Дальше, при помощи обычного JavaScript выбираем этот элемент на странице и перемещаем в любое место. Мы перемещаем вручную. Чтобы оно стало возможным, пользуемся событиями на перетаскиваемом и принимающем элементе.

Мы можем перетаскивать любой элемент над любым другим элементом. Просто таскаем элемент по странице и наводим его на другие. Перемещение не значит, что один элемент должен принять другой, если это не планировалось. Поэтому браузеры так и работают: по умолчанию никакой элемент не может принимать другие.

Если мы хотим научить элемент принимать другие, нам нужно отменить это значение при помощи метода e.preventDefault() на dragenter и dragover, которые срабатывают на элементе dropZone.

dropZone.addEventListener("dragenter", e => {
e.preventDefault();
});

dropZone.addEventListener("dragover«, e => {
e.preventDefault();
});

Я тестировал эту комбинацию в разных браузерах. Например, в Chrome работает только dragenter, поэтому для кроссбраузерного решения используйте и dragenter, и dragover.

Посмотрим, как это работает на списке дел.



Наша задача — перетащить последний элемент в знакомом списке дел на первую позицию. Наводим мышку на элемент и слева появляется элемент с 6 вертикальными точками. В UX-мире это значит, что его можно перетаскивать.



Наводим на него курсор, и если он меняется, то это сигнал, что элемент готов к перемещению. Мышкой тянем вверх и появляется розовый индикатор, который указывает, куда можно переместить элемент. Остальное дело техники — поместить объект на требуемую позицию.



Теперь научим клавиатуру, а потом screen reader правильно обрабатывать то, что мы сейчас сделали.

Клавиатура


С клавиатурой все не так сложно. С клавиатурой мы используем такой же паттерн, как и для мышки: находим элемент, который хотим переместить, выбираем, перемещаем, а потом отпускаем, и он встает на место.

Нажимаем «Tab», и слева от первого элемента появляется знакомая иконка с 6 точками, который говорит, что можно перетаскивать. По умолчанию он скрыт, а его появление означает, что он принял фокус. Это сигнал, что можно взаимодействовать с этим элементом.

Вводим первый элемент «приготовить ужин» в «режим выбора» нажатием «Enter». Сейчас этот элемент получает дополнительные опции. Стрелками «Вверх» и «Вниз» можно его перемещать на другие позиции. Нажимаем стрелку «Вниз», элемент перемещается на одну позицию, еще раз — на другую. Так мы вручную перемещаем компонент. В браузере по умолчанию этого нет — пишем это вручную на JavaScript.



Когда захотим снять выделение с элемента, нажимаем «Esc» или любую другую клавишу — элемент теряет выделение и остается на нужном месте.

Screen reader


С клавиатурой все понятно. Мы почти решили проблему. Осталось научить screen reader произносить действия. Все, что нужно — это добавить инструкции, чтобы screen reader знал, что происходит: где он находится и что делать.

Для контроля того, что произносит screen reader, воспользуемся технологией Live region. Это набор атрибутов, которые можно добавлять к элементам. В зависимости от атрибутов screen reader произносит разные фразы.

<div class="live-region visually-hidden"
    aria-live="polite"
    role="status"
></div>


const liveRegion = document.querySelector(".live-region«);
liveRegion.textContent = ’Новый текст’;

Я задал обычному пустому div атрибут aria-live со значением polite, и role со значением status. Для screen reader это сигнал слушать элемент. Когда нужно, чтобы screen reader что-то произнес, обновим контент при помощи JavaScript, и screen reader произнесёт этот контент.

Посмотрим, как это работает.


Наша задача — переместить первый элемент на последнюю позицию. Выключаем свет на экране, включаем screen reader.

— Функция VoiceOver включена в Chrome.

Начинаем перемещать элемент. Сначала ищем наш виджет — перемещаемся по заголовкам. Screen reader озвучивает:

— Уровень заголовка два. Список дел.

Нашли. Продолжаем кликать клавишей «Tab».

— Объектов список 3. Нажмите клавишу «Enter», чтобы выбрать элемент «Погулять с собакой». Групп.

Сначала screen-reader произносит количество элементов в списке, чтобы пользователи понимали, сколько элементов в списке. Затем произносит инструкцию — что делать, чтобы выбрать элемент: «Нажмите клавишу ’’Enter»" и имя этого компонента. Стало понятно, что делать — нажать клавишу «Enter».

— Элемент «Погулять с собакой» выбран. Используйте клавиши «Вверх» и «Вниз» для перемещения. Используйте клавишу «Esc», чтобы снять выделение.

Именно об этих инструкциях я и говорил: при выборе элемента screen reader дает инструкции, который мы придумали. Обновляем screen reader и он произносит текст, который мы поместили в элемент Live region. С инструкциями понятно, что делать — использовать клавиши «Вверх/Вниз» и «Esc» для снятия выделения. Перемещаем вниз на одну позицию.

— Элемент «Погулять с собакой» перемещен на позицию 2.

Еще раз.

— Элемент «Погулять с собакой» перемещен на позицию 3.

Элемент оказывается в конце списка. Включаем свет и проверяем — действительно ли элемент на последней позиции.

Нажимаем «Esc» для снятия выделения.

— С элемента «Погулять с собакой» снят выбор.

При помощи фантазии, разметки, JavaScript и атрибутов ARIA мы сделали доступным виджет, который сложен для пользователей с проблемами зрения.

Перемещение


Посмотрим на перемещение элементов по странице на примере Trello. Это трекер задач с колонками, которые означают состояния задач. Перетаскивание задач из одной колонки в другую соответствует смене статуса выполнения задачи.



Сымитируем упрощенную версию Trello, чтобы разобраться, как сделать этот компонент доступным. В моей версия три колонки: «Сделаю», «Делаю», «Сделано».



Задачи можно перетаскивать с места на место: хватаем мышкой компонент, перетаскиваем в следующую колонку, отпускаем — он остается там.

Клавиатура


С мышкой все просто, мы знакомы с атрибутом draggable со значением true с предыдущего примера. С клавиатурой можно было сделать такой же вариант — перемещать стрелками задачу из колонки в колонку. Но, если колонок 5 или 8, то перемещение из первой в последнюю займет 5 или 8 нажатий на клавишу «Вправо» 5 или 8 раз, что неудобно и затруднит работу с компонентом.

Мы поступим иначе. Нажимаем клавишу «Tab», фокус перемещается на первый элемент. Нажимаем клавишу еще раз, и появляется новый элемент, который был скрыт, пока в него не переместился фокус.



Три вертикальные точки означают, что у элемента появится меню. Иконка может быть и другая. Дефолтное браузерное поведение при работе с элементом select — нажатие на «Пробел», после которого появляется меню.

Мы будем решать проблему используя JavaScript, но дадим пользователям возможность выбрать, в какую колонку они захотят переместить ту или иную задачу.



Перемещаем в последнюю колонку, нажимаем «Enter», и все заработало — элемент переместился туда, куда мы захотели.

Screen reader


Добавляем инструкции для screen reader. Использование обычного элемента select дает большое преимущество, потому что он доступен по умолчанию. Посмотрим, что мы можем сделать.


Выключаем свет на экране, включаем screen reader.

— Функция VoiceOver включена в Chrome.

Переходим на первый элемент. Screen reader произносит, сколько объектов в списке. Мы использовали семантический элемент список, и имя элемента. Нажимаем «Tab».

— «Сделать»: выберите колонку свернутая pop-up button «Поиграть в футбол», групп.

Screen reader может произносить странные слова. Он называет имя колонки «Сделать», а "свернутая pop-up«для screen reader означает то, что мы будем пользоваться меню — для пользователя screen reader это будет понятно. «Свернутая», «pop-up» и «button» будут произноситься по-разному в разных screen reader, но нам не нужно сильно об этом беспокоиться, потому что пользователи screen reader знакомы с таким поведением.

Наша задача — выбрать из списка нужную колонку.

— Не ноль, сделать. Выберите колонку, развернутая pop-up, button. Ваша текущая позиция на экране… «Делаю». «Сделано»… Элемент «Поиграть в футбол» перемещен в колонку «Сделано».

Это тот текст, что мы добавили вручную, чтобы информировать пользователей о том, что мы это сделали. Так работает наша доска задач.

Загрузка файлов


Функционал загрузки файлов тоже встречается на сайтах, например, чтобы приложить файлы к письму. Поэтому в моем примере электронная почта Gmail.



На экране есть элемент с пунктирной линией. Именно туда мы будем перетаскивать наши файлы.



Для этого открываем проводник, где находятся файлы и перетаскиваем мышкой файл в пунктирную зону. После перетаскивания элемент меняет состояние, отпускаем файл, начинается загрузка. В конце загрузки мы уведомим пользователя, что все успешно загружено.

Клавиатура


С клавиатурой все решается просто. Существует семантический элемент input с типом file. Если вы его используете, то при нажатии на этот элемент автоматически откроется диалоговое окно «Проводника», где можно выбирать файлы из ОС. Стили окна могут быть заданы абсолютно любые в зависимости от желаний вашего дизайнера.



Нажимаем на элемент, открывается диалоговое окно. Так как мы использовали нативный элемент input, можно также переместиться на этот элемент клавишей «Tab», нажать пробел и откроется диалоговое окно. Элементы выбираются клавишей «Enter», и все происходит аналогично.

Все работает и с мышкой, и с клавиатурой. Для screen reader можно сделать кое-что, что по умолчанию не предусмотрено нативными элементами.

Screen reader


Элемент «выберите» будет работать и мы все еще можем открыть диалоговое окно с ОС. Но когда нагрузка начнется мы можем сделать очень интересную вещь, чтобы обновлять screen reader пользователя о статусе загрузки.


Включаем screen reader. При помощи клавиатуры переходим на элемент, чтобы выбрать файлы из ОС.

— Выберите button application.

Нажимаем кнопку со screen reader.

— Пресс выберите button. Начало диалога… Изображение image.png. 31 марта… 0%… 16% загружено… 73% загружено… 100% загружено. Файл image.png успешно загружен.

Что произошло? После того, как мы открыли диалоговое окно, выбрали файл. Нам здесь не нужно ничего делать, потому что screen reader относится к ОС, а к браузеру отношения не имеет. Файловую систему, как и все другие приложения, они умеют хорошо обрабатывать.

Когда мы выбрали файл и началась загрузка, мы начали обновлять screen reader пользователя о статусе загрузки. На индикаторе выполнения мы визуально видим сколько файла загружено — 10%, 20%...100%. Когда загрузка завершается успешно, мы видим уведомление, о том, что файл загружен.

Здесь мы использовали обычную технологию Live region, о которой говорили. Мы обновляли screen reader, помещали текст в тот элемент, который создали с атрибутами ARIA Live, и пользователи screen reader знали сколько процентов загружено и что загрузка прошла успешно. Таким нехитрым образом можно создавать доступные элементы.

Резюме


Нативные элементы. Использование нативных элементов — это здорово. Вы сэкономите время, нервы, и окажете большую услугу пользователям со screen reader. Они привыкли к этим элементам, знают, как они работают и вам не надо ломать знакомый опыт использования.

HTML + JavaScript + ARIA. Почти все Drag-&-Drop элементы можно сделать доступными грамотной разметкой, JavaScript и атрибутами ARIA. Я бы добавил фантазии, но даже без нее много возможностей.

«Режим выбора». Чтобы перемещать элементы по сайту, воспользуйтесь «режимом выбора»: выбираем элемент, перемещаем, снимаем выбор, а элемент остается там, где его оставили.

Контекстное меню. Альтернатива предыдущему способу: из списка выбирается место перемещения элемента, выбираем, элемент переходит туда.

Ресурсы


Если интересуетесь доступностью, рекомендую почитать W3.org — сайт с документацией по доступности. На нем огромное количество элементов с демо и инструкциями для перевода элементов в доступные.

Для продвинутых разработчиков, которые разбираются как работает доступность, рекомендую посмотреть сайт Хейдона Пикеринга inclusive-components.design. Он создал ряд довольно сложных компонентов и сделал их доступными. Главное, что он сопроводил все демки подробными инструкциями, как и почему он сделал элементы доступными, какими способами пользовался, какими не пользовался, и почему.

Книга. Если вы ничего не знаете про доступность, но хотите делать доступные интерфейсы, начните с книги Лауры Калбаг «Доступность для каждого». Доступно объяснено о доступных элементах — как и зачем их создавать. Я сам начинал с этой книги.

На русском. Сайты и книги на английском языке, и если он вам пока недоступен — рекомендую почитать серию англоязычных статей на Medium в переводе Татьяны Фокиной. Она брала известные иностранные статьи по теме, переводила на русский язык и сопровождала примерами. Сам пользуюсь этим сайтом регулярно, потому что экономит время. Например, статьи Хейдона Пикеринга написаны сложным и специфическим языком, а у Татьяны все понятно. При этом упрощение не умаляет смысла. Если хочу освежить знания, всегда пользуюсь этой серией статей.

Сергей регулярно выступает с докладами о доступности. Эта статья подготовлена на основе выступления на FrontendConf РИТ++ в мае. На FrontendConf 2019 в октябре нас ждет интерактивный доклад от Leonie Watson — разработчика, члена консультативного совета W3C и сопредседателя рабочей группы W3C по веб-приложениям. При этом Leonie слепа и будет выступать со screen reader. Участники ощутят, как себя чувствуют люди с проблемами зрения, как им помогают устройства и решения повышающие доступность, и что делать, чтобы повысить доступность своих сайтов и приложений прямо сейчас.

До FrontendConf осталось меньше месяца, программа и расписание полностью готовы. Если хотели забронировать билет на конференцию, сейчас самое время до последнего повышения цен.
Теги:
Хабы:
Всего голосов 29: ↑28 и ↓1+27
Комментарии0

Публикации

Информация

Сайт
www.ontico.ru
Дата регистрации
Дата основания
Численность
51–100 человек
Местоположение
Россия