d3 스트라이프 맵의 접근성
17128 단어 a11yd3javascript
안녕하세요!우선, 나는 모두가 이 문장에 대한 인내심에 감사할 것이다.내가 마지막 문장을 발표한 몇 주 후, 나의 스트레스는 매우 크다.물론 나는 긴장된 업무 기한 중에 기술적인 직위를 선택했다.그 마지막 기한은 이미 지나갔고, 나는 마침내 이 문장을 완성했다.
나는 트위터에 너에게 다음에 나에게 무엇을 쓰고 싶은지 물었다.많은 사람들이 나에게 데이터의 시각화된 접근성에 대해 이야기하라고 요구한다.나는 이미 데이터의 시각화에 관한 많은 지식을 배웠기 때문에, 나는 이 글을 시리즈로 만들기로 결정했다.
나의 시리즈 문장의 첫 부분에서, 나는 당신과 방문할 수 있는 조형도를 토론할 것입니다!
시작하다
익숙하기 때문에 d3 JavaScript 라이브러리를 사용할 것입니다.내연 SVG를 구축하고 있습니다. 우리가 토론한 원칙은 SVG에 적용됩니다.SVG의 장점은 자신이 무엇을 하고 있는지 알고 있다면 접근 가능한 막대 그림을 만드는 것이 매우 간단하다는 것이다.하지만, 한 가지, 네가 무엇을 하고 있는지 안다!
다음은 현재 사용 중인 데이터 세트입니다.
const data = [
{
name: 'Banana Production',
value: 20223290811,
},
{
name: 'Apple Production',
value: 8191091088.532,
},
{
name: 'Clementine Production',
value: 1162341399.19,
},
]
내가 본 대부분의 스트라이프 그래프의 문제는 지시 데이터가 무엇인지를 나타내는 텍스트 요소가 부족하다는 것이다.그것들은 축이 있는 가시 막대만 그 값을 표시한다.그럼 이게 뭐가 문제죠?우리는 줄과 축에 사용할 텍스트가 있습니다.그러나 화면 판독기는 데이터 세트와 관련된 값을 반영하지 않습니다.
외음 명령을 사용할 때, 탭만 읽고, 축은 똑딱똑딱 소리를 낸다.내가 읽고 싶은 것은 라벨이고, 그 다음은 데이터 값이다.
우리가 원하는 것은 시각적 의미를 가진
<text>
원소 다음에 <rect>
개의 원소가 있는 것이다.화면 판독기의 가장 좋은 방법은 읽을 수 있는 내용을 확보하는 것이다.내연 SVG는 이미지가 태그가 되기 때문에 접근성이 매우 좋습니다.너무 좋아요.그러나 막대 그림이 모양과 데이터만 통신하면 기본적으로 화면 판독기가 읽지 않습니다.잠재적 솔루션 #1
스트립트에 접근할 수 있도록 하는 첫 번째 해결 방안은 호출
xAxis
후에 텍스트 요소를 추가하는 것이다.d3.selectAll('.tick')
.append('text')
.text((d, i) =>
d3
.format('.2s')(data[i].value)
.replace('G', 'B')
)
.tick
클래스는 d3축에 기본적으로 첨부되며, 첨부된 <g>
요소에 추가됩니다.나는 모든 .tick
요소를 선택하고 포맷된 텍스트 요소를 추가했다.비록 이것은 화면 리더에 적용되지만, 나는 이것이 모든 사람이 가장 쉽게 얻을 수 있는 데이터 시각화 체험이라고 생각하지 않는다.yAxis의 범위가 매우 넓기 때문에 시각적 사용자의 가치를 이해하는 데 도전적일 수 있다.Y축이 160억이 넘기 때문에 안전하게 가정할 수 있습니다. 우리 사용자들은 데이터의 가치가 무엇인지 잘 모를 수도 있습니다.Y축 범위가 0-10이면 달라질 수 있습니다.
<text>
요소가 있는 것은 화면 판독기 사용자에게 더 좋은 체험이지만, 우리는 시력이 있는 사용자를 위해 그것을 개선할 수 있다.잠재적 솔루션 #2
또 다른 해결 방안은 도례를 추가하는 것이다.주의해야 할 것은 색맹에게 색코드 막대 그림을 사용하는 것이 가장 쉽게 얻을 수 있는 것이 아닐 수도 있다는 것이다.만약 우리가 이 길을 걷는다면, 우리는 모든 술집 간에 격렬하고 쉬운 대비를 확보해야 한다.
나는 여기서 약간의 변경을 했다.
+ const barColors = ['#000', '#d35f5f', '#fff'];
barGroups
.selectAll('rect')
.data(data)
.enter()
.append('rect')
- .attr("fill", "#d35f5f")
+ .attr('fill', (d, i) => barColors[i])
+ .attr('stroke', '#000')
.attr('class', 'bar')
.attr('x', d => xScale(d.name))
.attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', d => height - yScale(d.value));
나는 16진수 색깔이 가득한 수조로 변수를 추가했다.나는 색 채우기를 선택하기 위해 익명 함수를 사용했다.나는 흰색 줄을 표시하기 위해 펜 색도 추가했다.SVG를 더 넓게 하고 범례 너비도 추가했습니다.그렇지 않으면 전설이 끊어질 거야!
const margin = { top: 20, right: 20, bottom: 70, left: 90 };
const width = 600 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
+ const legendWidth = 300;
const svg = d3
.select("#chart")
- .attr("width", width + margin.left + margin.right)
+ .attr("width", width + margin.left + margin.right + legendWidth)
.attr("height", height + margin.top + margin.bottom);
근데 우리 아직 안 끝났어!우리는 여전히 전설을 추가해야 한다!내가 여기서 오류를 통해 배운 것은 우리가 코드를 약간 재구성해야 한다는 것이다.나는 좀 풋내기다.목적도 없이 시도하고 다른 방법을 취해야 한다는 것을 깨닫는 경우가 많다.내가 해야 할 일은 이렇게 재구성하는 것이다.+ const g = barGroups
+ .selectAll('g')
+ .data(data)
+ .enter()
+ .append('g')
- barGroups
- .selectAll("rect")
- .data(data)
- .enter()
- .append("rect")
+ g.append('rect')
.attr('fill', (d, i) => barColors[i])
.attr('stroke', '#000')
.attr('class', 'bar')
.attr('x', d => xScale(d.name))
.attr('y', d => yScale(d.value))
.attr('width', xScale.bandwidth())
.attr('height', d => height - yScale(d.value))
우리는 여러 개의 barColors
요소를 같은 데이터에 묶어야 한다.나는 데이터를 <rect>
에 연결하고 필요한 모든 요소를 추가하기로 결정했다.나는 도례를 위해 같은 데이터 귀속을 사용하고 싶어서 동의했다.그래서 저는 새로운
<g>
과 <rect>
라벨을 추가해서 도례를 만들기 시작했습니다!const lineItemHeight = 30
g.append('rect')
.attr('fill', (d, i) => barColors[i])
.attr('stroke', '#000')
.attr('width', 20)
.attr('height', 20)
.attr('x', width + margin.right)
.attr('y', (d, i) => lineItemHeight * (i + 1))
g.append('text')
.text(d => `${d.name} - ${d.value}`)
.attr('x', width + margin.right + 30)
.attr('y', (d, i) => lineItemHeight * (i + 1) + 15)
현재 우리는 실제 데이터와 라벨을 반영하는 텍스트가 좀 생겼다.우리가 마지막으로 해야 할 일 중 하나는 숫자를 포맷해서 읽을 수 있도록 하는 것이다.g.append('text')
- .text(d => `${d.name} - ${d.value}`)
+ .text(d => `${d.name} - ${d3.format(".2s")(d.value).replace("G", "B")}`)
이제 우리는 이 전설을 위해 제목을 하나 추가하고 B=수십억 위안을 말합시다.const svg = d3
.select("#chart")
.attr("width", width + margin.left + margin.right + legendWidth)
.attr("height", height + margin.top + margin.bottom)
.attr('aria-labelledby', 'title');
+ svg.append('text')
+ .text('Legend')
+ .attr('x', width + margin.right + margin.left)
+ .attr('y', 20)
+ svg.append('text')
+ .text('B = billion')
+ .attr('x',width + margin.right + margin.left)
+ .attr('y', 40)
우리는 도례 항목의 위치를 조정하고 싶다. 왜냐하면 도례 제목과 키가 공간을 차지하기 때문이다.g.append('rect')
.attr("fill", (d, i) => barColors[i])
.attr("stroke", "#000")
.attr('width', 20)
.attr('height', 20)
.attr('x', width + margin.right)
- .attr('y', (d, i) => lineItemHeight * (i + 1))
+ .attr('y', (d, i) => lineItemHeight * (i + 1) + 30)
g.append('text')
.text(d => `${d.name} - ${d3.format(".2s")(d.value).replace("G", "B")}`)
.attr('x', width + margin.right + 30)
- .attr('y', (d, i) => lineItemHeight * (i + 1) + 15)
+ .attr('y', (d, i) => lineItemHeight * (i + 1) + 45)
이게 최종 결과야!컨텍스트 추가
나는 Heather Migliorisi’s graph CodePen을 이 문장의 영감의 원천으로 삼았다.이제 화면 판독기에 시각화된 텍스트 버전이 있습니다.그러나 나는 그녀가 기묘하게 영탄조를 사용해서 그녀의 도표에 더 많은 상하문을 추가했다는 것을 알아차렸다.나는 그녀가 한 몇 가지 같은 원칙을 채택하여 그것들을 d3의 그림에 응용할 것이다. (그녀는 직접 SVG로 그녀의 글을 썼다.)
내가 해야 할 첫 번째 일은 나의 SVG에 제목을 추가하는 것이다.
const svg = d3
.select("#chart")
.attr("width", width + margin.left + margin.right + legendWidth)
.attr("height", height + margin.top + margin.bottom)
+ .attr('aria-labelledby', 'bar-chart-title');
+ svg.append('text')
+ .text('2018 Fruit Production')
+ .attr('id', 'bar-chart-title')
+ .attr("x", margin.left)
+ .attr("y", 250)
나는 그녀의 문장Accessible SVGs을 읽고 왜 이것이 좋은 방법인지 이해할 것을 건의한다.그녀는 나보다 SVG에 대한 이해가 많기 때문에 많은 연구를 했다.나는 그녀가 스트립트 그림을 목록처럼 읽는 것을 좋아한다.나도 그것들을 하나하나에 추가할 거야!또한
<text>
역할이 있는 그룹에 aria-label
을 추가합니다.const barGroups = svg
.append("g")
+ .attr('role', 'list')
+ .attr('aria-label', 'bar chart')
.attr("class", "data")
.attr("transform", `translate(${margin.left}, 0)`);
const barColors = ["#000", "#d35f5f", "#fff"];
const g = barGroups
.selectAll('g')
.data(data)
.enter()
.append('g')
+ .attr('role', 'listitem');
히세는 내가 여기서 하지 않으려는 일을 했다. 바로 축에 추가하는 것이다list
.내가 트위터에 이 문제를 올려 다른 대답을 얻었기 때문이다.린세 코파츠🐞
@littlekope 회사
나는 주관적인 문제가 하나 있다.SVG 그래픽을 만들려면 다른 방식으로 데이터 포인트를 선언하면 축이 화면 판독기에 약간 쓸모가 있지 않습니까?예를 들어 기부금 10만 달러.만약 그것이 읽힌다면, 축은 불필요한 것이 아닙니까?
12:오후 53시-2019년 5월 1일
0
십팔
나는 일찍이 스크린 리더의 군더더기를 생각해 보았지만, 다른 사람들은 아주 좋은 관점을 제기했다.
크리스 주방
꼭 그렇지는 않아요.사람들은 항상 당신이 원하는 방식으로 접근 가능한 도구를 사용하지 않는다.예를 들어 나는 다동증을 앓고 있어서 스크린 리더를 자주 사용하여 내가 글을 쓰는 것을 돕는다.시력 문제는 없다. 나는 단지 나에게 읽어줄 것이 좀 필요할 뿐이다. 그래서 나는 이 모든 것이 의미가 있다는 것을 안다.😊
2019년 5월 1일 오후 21:58
0
일.
내가 다동증 환자라고 해도 생각지도 못했던 일이다.하지만 나중에 DOM에 축을 배치하고 SVG의 그룹에 축을 추가하기로 했습니다
role="presentation"
.svg
.append("g")
.attr("class", "x-axis")
+ .attr('aria-label', 'x axis')
.attr("transform", `translate(${margin.left}, ${height})`)
.call(xAxis);
svg
.append("g")
.attr("class", "y-axis")
+ .attr('aria-label', 'y axis')
.attr("transform", `translate(${margin.left}, 0)`)
.call(yAxis);
결론
나는 내가 이런 가시화 효과를 크게 높일 수 있다고 믿는다.나는 SVG에 대해 아직 낯설다. 어떤 것은 주관적이다.축 점이 이중화되었는지 잘 모르겠습니다.화면 판독기에 그것을 숨겨야 하는지에 대해 나는 다양한 답을 얻었다.나는 이것이 매우 짜증나지 않는 한 더 많은 언어 환경을 사용하기로 결정했다.
당신은 어떻게 생각합니까?알려주세요!그리고 저 지금 하나 있어요patreon!만약 네가 나의 일을 좋아한다면, 스폰서가 되는 것을 고려해 봐라.만약 당신이 5달러 이상을 약속한다면, 당신은 미래의 블로그 게시물에 투표할 수 있습니다!건배!즐거운 한 주 되세요!
Reference
이 문제에 관하여(d3 스트라이프 맵의 접근성), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/lkopacz/accessibility-in-d3-bar-charts-55nl텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)