Рубрики
Без рубрики

Введение в доступные визуализации данных с D3.JS

Первоначально опубликовано на визуализации данных fossheim.io может быть великолепно для передачи сложных данных в … с меткой A11Y, учебным пособием, JavaScript, D3.

Первоначально опубликовано на fossheim.io

Визуализация данных может быть отличным, чтобы проще сообщать сложные данные. К сожалению, есть много, что может пойти не так, когда дело доходит до доступности. Несколько недель назад я решил ориентироваться в одном из самых высоких перечисленных COVID-19 DASHBOARDS С голос за кадром, и я едва мог пройти через первый график, прежде чем сдавать свой браузер.

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

Данные везде, и они должны быть доступны для всех. Поэтому я решил начать писать свой собственный сериал об этом!

Этот первый учебник будет довольно широким, но мы будем подробно рассмотрены в предстоящих постах. Вам нужно будет иметь базовое понимание D3.JS, чтобы следовать; Но не волнуйтесь, вступление в серию D3.JS также входит в марку.

Отправная точка

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

Есть несколько проблем с этим графиком:

  1. Цвета стержней и текста не имеют достаточного контраста с фоном
  2. Используемые цвета становятся менее различимыми для людей с раскраски
  3. Значение цветов не объясняется
  4. Мы не знаем масштаб оси Y, или что здесь визуализируется
  5. Нет упомянутых значений
    • Это не передает точное количество посетителей никому, есть только визуальная индикация, в каких днях больше посетителей, чем другие
    • Вспомогательные технологии (читатели экрана) также не будут иметь никаких ценностей, чтобы общаться с пользователем, поэтому слепые люди и люди с низким зрением не получат никакой информации из этого

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

Цвета

Давайте начнем с выбора цветов, которые соответствуют рекомендациям контрастности (соотношение AA или AAA), и при этом выглядеть достаточно разными для разных типов дальтонильной слепоты. Лично я предпочитаю использовать Figma Для этого, так как я уже использую его в дизайнерской фазе. Обычно я копирую цвета в отдельной раме и запустим Способный и Дальтоник плагин на это.

Если вы не используете какую -либо программу, которая поддерживает это или просто предпочитает работать в браузере, Colorblinding и Проверка Color Crown WCAG являются хромированными расширениями с той же функциональностью.

Ради простоты, я пошел на стандартное темное синее/красное решение, которое безопасно как, когда речь идет о цветовой связи, так и контрасте. Вы можете использовать такие инструменты, как Хрома , Coolors или Colorsafe Чтобы помочь вам создать доступные палитры.

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

Мы можем добавить шаблоны в качестве фона, создав <Паттерн> Элемент внутри SVG. Нам нужно дать шаблону идентификатор, ширину и высоту. Внутри <Паттерн> Мы можем нарисовать любой объект SVG, который мы хотим. Затем в объекте мы хотим добавить фоновый шаблон, мы можем установить наполнение на URL (#IDOFOURPATTERN)


  
  

.bar {
  fill: url(#dots)
}

Объясняя цвета, добавив легенду

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

Не все увидят цвет одинаково (например, из -за цветовой пузыря), а цвета несут разные значения для разных людей и культур. Так что для всех пользователей не будет очевидно, что в нашем примере красная полоса означает, что в тот день посетил наш сайт менее 100 человек. Вот где легенды вступают в игру.

Начнем с добавления группы ( ) и назначить ее легенда постоянный.

const legend = chart.append("g");

Нам также нужно добавить либо Ария-Лейбл атрибут или в сопровождении Aria-Labelledby Атрибут, поэтому вспомогательные технологии могут предоставить пользователю немного больше информации о том, что читается.

const legend = chart.append("g").attr("aria-label", "Legend");

В качестве альтернативы, мы можем отобразить визуальный заголовок:

const legend = chart.append("g");
legend.append("text")
    .text("Legend")
    .attr("x", margin.left / 2)
    .attr("y", margin.top)
    .attr("class", "legendTitle");

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

// First color: blue with dots
legend.append("rect")
  .attr("fill", "url(#dots)")
  .attr("width", 13)
  .attr("height", 13)
  .attr("rx", 2)
  .attr("x", margin.left / 2)
  .attr("y", margin.top);

// First color: explanation
legend.append("text")
  .text("Over 100 daily visitors")
  .attr("x", margin.left / 2 + 20)
  .attr("y", margin.top + 10);

// Second color: red with lines
legend.append("rect")
  .attr("fill", "url(#lines)")
  .attr("width", 13)
  .attr("height", 13)
  .attr("rx", 2)
  .attr("x", margin.left / 2)
  .attr("y", margin.top + 30);

// Second color: explanation
legend.append("text")
  .text("Under 100 daily visitors")
  .attr("x", margin.left / 2 + 20)
  .attr("y", margin.top + 40);

Читатели экрана читают элементы DOM в том порядке, который они появляются в вашем коде. Итак, в моем примере я добавил код для легенды сверху, перед кодом оси X, по двум причинам:

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

Маркировка данных

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

Нам нужно добавить значения в верхнюю часть стержней и пометить ось Y, чтобы указать, какова единица наших данных (в нашем случае является количество уникальных посетителей).

Для каждой строки в наших данных это будет печатать количество посетителей:

chart.selectAll(".label")
  .data(data)
  .enter().append("text")
  .text(row => row.visitors);

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

chart.selectAll(".label")
  .data(data)
  .enter().append("text")
  .text(row => row.visitors)
    .attr("text-anchor", "middle");

Далее мы установим x координируйтесь с тем же, что и бар. Поскольку бар в нашем примере 10px широко, и хотим, чтобы текст был центрирован, нам нужно переместить текст дополнительно (10/2) px Направо. y координата должна быть на несколько пикселей меньше, чем у бара y Координируйте.

chart.selectAll(".label")
  .data(data)
  .enter().append("text")
  .text(row => row.visitors)
    .attr("text-anchor", "middle")
    .attr("x", (row, index) => x(index + 1) + 5)
  .attr("y", row => y(row.visitors) + margin.top / 2 - 5)
    .attr("class", "label");

Это должно сделать это для значений. Наконец, мы можем добавить этикетку к оси Y, как это:

chart.append("text")
  .text("Amount of unique visitors")
  .attr("class", "yAxis")
  .attr("transform", "rotate(-90)")
  .attr("text-anchor", "middle")
  .attr("x", -height / 2 - margin.top)
  .attr("y", margin.left / 2 + 5);

Маркированные данные и считыватели экрана

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

Мы получаем доступ ко всей информации, и поскольку мы имеем дело только с 7 точками данных, невозможно отслеживать карты стоимости, в какой день. Но чем больше наш набор данных, тем труднее следовать.

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

Решение A: Добавьте этикетки и клещи в тот же элемент

Одним из вариантов может быть реструктурирование кода и группировать дни и значения внутри одного элемента. То, как наш код D3 структурирован прямо сейчас, это будет вывод в HTML:


    

    
    Mon
    Tue
    Wed
    ...

    
    Amount of unique visitors

    
    
    ...

    
    100
    172
    92
    ...

Лучшим опытом может быть, если голос за кадром прочитайте наш график таким образом: «Сумма уникальных посетителей в понедельник: 100, вторник: 172, среда: 92, …». Это соединяется каждый день на оси X со значением каждого графика одновременно, что облегчает следование.

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

const ticks = chart.selectAll(".tick")
  .data(data)
  .enter().append("g")
  .attr("class", "tick");

Это выведет для каждой точки в наборе данных. Тогда мы можем позвонить ticks.append () дважды, один раз, чтобы добавить метки оси X и один раз добавить значения.

ticks.append("text")
  .text((data) => data.day)
  .attr("x", function(row, index) { return x(index + 1) + 5; })
  .attr("y", height + margin.top)
  .attr("width", 30)
  .attr("text-anchor", "middle");

ticks.append("text")
  .text(row => row.visitors)
    .attr("text-anchor", "middle")
    .attr("x", (row, index) => x(index + 1) + 5)
  .attr("y", row => y(row.visitors) + margin.top / 2 - 5)
  .attr("class", "label");

Это выведет следующий HTML:

    
        Mon
        100
    
    
        Tue
        172
    
    
        Wed
        92
    
    ...

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

Решение B: Добавление больше контекста к этикеткам

Приведенное выше решение читается вполне естественно, но также поставляется с ограничением для больших наборов данных, где не каждая полоса будет иметь соответствующую метку на оси X. Иногда мы не хотим маркировать каждый пункт по оси X, особенно при работе с более крупными наборами данных.

Итак, давайте рассмотрим и другую возможность. В этом решении читатель экрана будет читать ось X, как это было изначально (“Понедельник вторник среда Четверг Пятница Суббота воскресенье”). Тогда он будет читать этикетку оси Y. И когда он дойдет до ярлыков над стержнями, он повторит x-значение каждого из них.

В нашем примере это звучит как “Ось Х: Дни недели. Понедельник вторник , … Ось Y: количество уникальных посетителей. Понедельник: 100. Вторник: 172. Среда: 92…” .

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

const barLabels = chart.selectAll(".label")
  .data(data)
  .enter().append("text");

Далее мы повторно добавим нашу этикетку, которая считывает значение из оси Y. Мы будем использовать Tspan элемент для этого и добавьте его к Barlabels Анкет

barLabels.append("tspan")
  .text(row => row.visitors)
    .attr("text-anchor", "middle")
    .attr("x", (row, index) => x(index + 1) + 5)
  .attr("y", row => y(row.visitors) + margin.top / 2 - 5);

Но прежде чем он прочитает это значение, мы также хотим, чтобы оно прочитало соответствующее значение по оси X. Мы можем скопировать код из выше, но изменить row => row.visitors к row => row.day .

/* Shows the corresponding value from the x-axis (day of the week). */
barLabels.append("tspan")
  .text(row => row.day)
    .attr("text-anchor", "middle")
    .attr("x", (row, index) => x(index + 1) + 5)
  .attr("y", row => y(row.visitors) + margin.top / 2 - 5)
    .attr("class", "xLabel");

/* Shows the corresponding value from the y-axis (# visitors). */
barLabels.append("tspan")
  .text(row => row.visitors)
    .attr("text-anchor", "middle")
    .attr("x", (row, index) => x(index + 1) + 5)
  .attr("y", row => y(row.visitors) + margin.top / 2 - 5)
    .attr("class", "yLabel");

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

Мы не можем добавить что -то вроде дисплей: нет; или Видимость: скрыто к нашему xlabel , как эти свойства также скрывают элемент от считывателей экрана.

Возможный обходной путь – это изменить x и y позиционирование, чтобы вывести его из кадра.

/* Shows the corresponding value from the x-axis (day of the week). */
barLabels.append("tspan")
  .text(row => row.day)
    .attr("text-anchor", "middle")
    .attr("x", -width)
  .attr("y", -height)
    .attr("class", "xLabel");

/* Shows the corresponding value from the y-axis (# visitors). */
barLabels.append("tspan")
  .text(row => row.visitors)
    .attr("text-anchor", "middle")
    .attr("x", (row, index) => x(index + 1) + 5)
  .attr("y", row => y(row.visitors) + margin.top / 2 - 5)
    .attr("class", "yLabel");

Возможные другие улучшения

Еще одна хорошая практика – добавить заголовок и описание в ваши графики. Это то, что можно сделать в чистом HTML, как это:

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

Также хорошая практика добавлять клещи на оси Y в дополнение к этикеткам над барами.

Также рекомендуется добавить те же данные в (доступную!) Таблицу в другом месте на вашей странице или предоставить ссылку на другую страницу, которая перечисляет данные в таблице.

Результат

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

Это будет постоянная серия. Предстоящие учебные пособия будут сосредоточены на различных типах графиков, больших наборах данных, сложных визуализациях и пользовательских функциях.

Если есть конкретная тема, тип визуализации или вопрос, который вы хотите, чтобы я поднял, вы можете сообщить мне, сообщая мне в Твиттере ( @liatrisbian ). Если вам нравится этот вид контента, рассмотрите Покупать мне кофе или Стать покровителем Анкет

Больше ресурсов

Оригинал: “https://dev.to/fossheim/an-introduction-to-accessible-data-visualizations-with-d3-js-3aih”