<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Sarah</title>
    <description>The latest articles on Forem by Sarah (@fossheim).</description>
    <link>https://forem.com/fossheim</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F162343%2F44361076-7f51-4b84-8c99-86c7e8824249.jpeg</url>
      <title>Forem: Sarah</title>
      <link>https://forem.com/fossheim</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/fossheim"/>
    <language>en</language>
    <item>
      <title>Dataviz accessibility review: what we can learn from the Norwegian 2023 election graphs</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Wed, 13 Sep 2023 16:55:44 +0000</pubDate>
      <link>https://forem.com/fossheim/dataviz-accessibility-review-what-we-can-learn-from-the-norwegian-2023-election-graphs-4ofk</link>
      <guid>https://forem.com/fossheim/dataviz-accessibility-review-what-we-can-learn-from-the-norwegian-2023-election-graphs-4ofk</guid>
      <description>&lt;p&gt;This was &lt;a href="https://fossheim.io/writing/posts/dataviz-accessibility-review-norwegian-elections-2023/"&gt;first posted on my blog: fossheim.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;--&lt;/p&gt;

&lt;p&gt;During the 2020 presidential election night in the United States, I &lt;a href="https://fossheim.io/writing/posts/accessible-dataviz-us-elections/"&gt;reviewed the data visualizations used by the major US news networks&lt;/a&gt;, and used it to illustrate some dataviz accessibility dos and don’ts.&lt;/p&gt;

&lt;p&gt;Now that Norway had its local elections (we were voting for a new regional and city government), it's time for a repeat of the concept and take a closer look at the dataviz accessibility of the graphs used in the Norwegian media.&lt;/p&gt;

&lt;p&gt;Earlier this week the Norwegian news NRK reported that &lt;a href="https://www.nrk.no/innlandet/kommunane-sine-nettsider-er-for-darleg-lagt-til-rette-for-svaksynte_-blinde-og-dyslektikarar-1.16544231?fbclid=IwAR2fER5zytJ41NmDGjHCtfFSrJZ-TzvzhzUQWitVfJOSENkJqkoYOMyoN8w"&gt;none of the Norwegian municipalities were conforming to web accessibility requirements&lt;/a&gt;, so I was definitely curious how the different charts would perform.&lt;/p&gt;

&lt;h2&gt;Scope&lt;/h2&gt;

&lt;p&gt;During the election night, I inspected the following websites:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://valgresultat.no/valg/2023/ko"&gt;valgresultat.no&lt;/a&gt;, the official results page by Valgdirektoratet (the electoral directorate)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nrk.no/valg/2023/resultat/"&gt;NRK.no's dashboard&lt;/a&gt;, created by the state-owned public broadcast&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.vg.no/valgnatt/2023/ko"&gt;VG's dashboard&lt;/a&gt;, a popular privately-owned newspaper&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aftenposten.no/norge/politikk/i/gEAOe9/valg-2023-valgresultater-fra-kommunevalget-og-fylkestingsvalget#/norge"&gt;Aftenposten's dashboard&lt;/a&gt;, another popular private newspaper&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Note: I don't currently work with/have a relationship to these companies, I picked their dashboards because they're popular websites and the first ones that up when I looked for the election results.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And paid attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Screen reader (VoiceOver) behavior&lt;/li&gt;
&lt;li&gt;Focus order and keyboard interaction&lt;/li&gt;
&lt;li&gt;Visual elements such as color contrast&lt;/li&gt;
&lt;li&gt;Semantics, labels, names, and ARIA best practices&lt;/li&gt;
&lt;li&gt;Understandability of the data and interactions&lt;/li&gt;
&lt;li&gt;Chart controls&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For client work I would test and review these areas in-depth, each with their own set of criteria, relate them back to WCAG where relevant, and also assess how the page frame and other elements on the page influence the chart accessibility.&lt;/p&gt;

&lt;p&gt;For this article, however, I mainly focused on finding examples that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Illustrate some of the important dataviz accessibility best practices, or&lt;/li&gt;
&lt;li&gt;Highlight common or interesting accessibility mistakes, or&lt;/li&gt;
&lt;li&gt;Explain certain accessibility concepts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And to not overcomplicate the article, the websites were only tested in Safari and with VoiceOver.&lt;/p&gt;

&lt;p&gt;Now, let's dive into some of the findings 👇🏻&lt;/p&gt;

&lt;h2&gt;Data visualizations require accessibility work&lt;/h2&gt;

&lt;p&gt;When it comes to web accessibility, writing semantic HTML will get you a long way already.&lt;/p&gt;

&lt;p&gt;For example, if you develop a website and do things like picking &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;s for buttons, adding &lt;code&gt;alt&lt;/code&gt; text to your images, structuring your content with heading elements (eg: &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;), then you have already started the work of making your website more accessible.&lt;/p&gt;

&lt;p&gt;It won't necessarily be WCAG compliant or lead to a good and equal experience for disabled people. Typically more accessibility work is needed for that (and not just from the development side), but it does create a solid base. For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt;s can by default be activated by clicking them, pressing enter &lt;em&gt;(key down)&lt;/em&gt;, or releasing space &lt;em&gt;(key up).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;alt="..."&lt;/code&gt; text on an &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elementgives blind people and those with low vision (and others using assistive tech like screen readers) a description of the image and access to the same info.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt;s come with their own screen reader navigation: when testing with voice over, I can use &lt;code&gt;cmd&lt;/code&gt; + &lt;code&gt;shift&lt;/code&gt; + &lt;code&gt;up&lt;/code&gt;/&lt;code&gt;down&lt;/code&gt;/&lt;code&gt;left&lt;/code&gt;/&lt;code&gt;right&lt;/code&gt; to move the screen reader focus/cursor between the cells in both dimensions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With dataviz accessibility, however, the situation is a bit different.&lt;/p&gt;

&lt;p&gt;Most visualizations are built using &lt;code&gt;&amp;lt;svg&amp;gt;&lt;/code&gt;s. With SVG, we use elements like rectangles, circles, or paths (&lt;code&gt;&amp;lt;rect&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;circle&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt;) to build our visualizations. Those elements carry no semantic meaning to assistive technologies, and come with no out-of-the-box keyboard interaction. The same is true when building visualizations from &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;s and &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt;s.&lt;/p&gt;

&lt;p&gt;Data visualizations typically are inaccessible by default. If we don't actively curate accessibility of our charts, we will end up with visualizations that are varying levels of inaccessible.&lt;/p&gt;

&lt;h3&gt;Graphical elements that carry meaning need text alternatives&lt;/h3&gt;

&lt;p&gt;Let's take a look at this chart from Aftenposten:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fossheim.io/writing/posts/dataviz-accessibility-review-norwegian-elections-2023/"&gt;Video in the original post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video description: Bar chart showing the shift in votes for each party compared to last elections. The category labels (logos of the parties) are displayed vertically. If a party has a negative shift, the bar is displayed on the left side, positive shifts to the right side. I'm using VoiceOver on the page, and the only items on the chart that are reachable by screen reader are the values next to the bars, but not the party names.&lt;/p&gt;

&lt;p&gt;The only thing read out are the text elements (&lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt;) which contain the numbers.&lt;/p&gt;

&lt;p&gt;The logos are added as an SVG element consisting only of &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt;s, which by default do not contain any info for screen readers to parse. As a result, only the result numbers are announced, not the parties they correspond to.&lt;/p&gt;

&lt;p&gt;This makes the entire chart inaccessible to screen reader users. It's just a sequence of unlabelled numbers.&lt;/p&gt;

&lt;h3&gt;We may need to provide additional context and structure to the data to make it screen reader accessible&lt;/h3&gt;

&lt;p&gt;Let's take this example is from VG.&lt;/p&gt;

&lt;p&gt;Here the screen reader announces all the images and text elements (in itself this is fine), but there is no extra info about what the numbers represent, how they relate to the images, or how the parties are structured.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fossheim.io/writing/posts/dataviz-accessibility-review-norwegian-elections-2023/"&gt;Video in the original post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video description: Comparison/progress chart showing how close the red coalition and blue coalition are to the 30 mandate majority. A screen reader is used to navigate between the different text elements of the chart, which are announced in the following order: left candidate picture alt text, the candidate name, party, amount of mandates on the left, the label "30 mandates", amount of mandates on the right, a list of text labels that just contain party abbreviations ("R", "MDG", "V") from left to right, right candidate picture alt text, the candidate name and party.&lt;/p&gt;

&lt;p&gt;Visually, we have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The left / red parties, on the left side of the graph&lt;/li&gt;
&lt;li&gt;The right / blue parties, on the right side of the graph&lt;/li&gt;
&lt;li&gt;Name, party, and picture of the main person on either side&lt;/li&gt;
&lt;li&gt;Total amount of mandates for either side, written next to the name&lt;/li&gt;
&lt;li&gt;How close either side is to the 30 mandate majority&lt;/li&gt;
&lt;li&gt;Underneath each side (red/blue), which parties belong to them&lt;/li&gt;
&lt;li&gt;The amount of mandates for each of the parties&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zr3cKx2M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/06d70162eaa71454541a2094c4dddd6137e60f56-1804x1052.png%3Fw%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zr3cKx2M--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/06d70162eaa71454541a2094c4dddd6137e60f56-1804x1052.png%3Fw%3D1200" alt="Comparison/progress chart showing how close the red coalition and blue coalition are to the 30 mandate majority, through a bar that's filled with red on the left, gray in the middle, and blue on the right.   There's a &amp;quot;30 mandates&amp;quot; text label in the middle. On either side of the bar there's the name and picture of their main candidate, the name of the main party, and the amount of mandates the coalition has. Underneath each coalition the parties that belong to it are listed, along with the amount/proportion of mandates that belong to them (1 square in their party color per mandate, grouped in colored areas with a text label on top). Underneath the chart are tickboxes for each party. On top of the chart there's a main title and a description explain the chart, a location title, and percentage of votes counted." width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The screen reader announced, in this order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Image description of the candidate on the left&lt;/li&gt;
&lt;li&gt;First name, last name, party of the candidate on the left&lt;/li&gt;
&lt;li&gt;Mandates of the left (without label, eg. just "27")&lt;/li&gt;
&lt;li&gt;"30 mandates"&lt;/li&gt;
&lt;li&gt;Mandates on the right (again without label)&lt;/li&gt;
&lt;li&gt;Initials of all the parties, from left to right&lt;/li&gt;
&lt;li&gt;The full name, party, image description of candidate on the right&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Without any of this data being structurally grouped, and without any additional labelling, this doesn't tell us the same as the visuals.&lt;/p&gt;

&lt;p&gt;We're not told any info about the size of the parties or which side they belong to, and the number of mandates for the right side is not announced along side its label (the candidate name and picture).&lt;/p&gt;

&lt;p&gt;Some of the following could be done to improve this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Grouping and/or connecting the candidate name, party and amount of mandates. This includes tidying up the order of the elements.
&lt;em&gt;Eg: "Raymond Johansen, AP:  28", "Eirik Lae Solberg, H: 30"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Grouping the parties by left/right side, and labelling each side.
&lt;em&gt;Eg: "Red block" (unordered list) and "R" (list item), "SV" (list item), etc.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Adding the amount of mandates to each of the parties.
_Eg: "R: 4 mandates",  "SV: 6 mandates" _&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The chart is interactive, and parties can be toggled on/off to see how many mandates each side would have, and how that changes who has the majority of mandates. Those updates are communicated visually, but screen reader users also need to know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which of those parties are toggled on/off when going through the visualization of the parties and their amount of mandates&lt;/li&gt;
&lt;li&gt;What the tick boxes are meant for (a label to explain their purpose), and also that the visualization updated after (un)ticking a box&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Similar charts can have different valid solutions&lt;/h3&gt;

&lt;p&gt;NRK's results have a similar percentage-based visualization as VG. NRK's isn't interactive, however, and so they can take a different approach to it: turning the visualization into an image with alt text.&lt;/p&gt;

&lt;p&gt;The alt text says the % of votes for the red parties, % of votes for the independent parties, and % of votes for the blue parties. This is the same as what's represented visually.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fossheim.io/writing/posts/dataviz-accessibility-review-norwegian-elections-2023/"&gt;Video in the original post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video description: I'm using the screen reader to navigate between the elements on the page. There is a list of visualizations showing the percentage of votes the red block (on the left) has compared to the blue block (on the right). The charts are announced as images with alt text, like for example: "Red block: 45.8%, Independent parties: 1.7%, Blue block: 52.5%. Red block. Blue block.". They're wrapped in links, along with the name of the city and the % of votes counted.&lt;/p&gt;

&lt;p&gt;Even though there's a small bug with it &lt;em&gt;(the actual "red parties" and "blue parties" are not announced, but their subtitles are)&lt;/em&gt;, alt text like this can be sufficient for this type of chart in the way NRK used it.&lt;/p&gt;

&lt;p&gt;The Aftenposten example from earlier used the same chart type and same data as NRK, but has interaction attached to it &lt;em&gt;(tickboxes on the bottom that filter results within the chart)&lt;/em&gt;, and also visualizes the party names according to the amount of mandates they have. This will also lead to different screen reader solutions, since turning the chart into an image with alt text would take some of that functionality away.&lt;/p&gt;

&lt;h3&gt;Not testing visually hidden labels can lead to incomprehensible data&lt;/h3&gt;

&lt;p&gt;A mistake I often come across in reviews is improper labelling elements.&lt;/p&gt;

&lt;p&gt;Sometimes people do indeed add screen reader alternatives, but either use the wrong properties, or add information that's incorrect or not useful.&lt;/p&gt;

&lt;p&gt;Often those type of issues happen because if you don't use assistive technology yourself, you won't notice the content and behavior of visually hidden labels until you inspect the code, test the page with a screen reader, or otherwise try to assess the accessibility.&lt;/p&gt;

&lt;p&gt;One of the charts where I suspect this type of testing was lacking is this distribution graph by Aftenposten.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fossheim.io/writing/posts/dataviz-accessibility-review-norwegian-elections-2023/"&gt;Video in the original post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video description: The screen reader focus is moved down a list of cities. For each city, the percentage of mandates each party has is visualized, and grouped by coalition. All the cities are announced as links with the following label: "Trondheim, 100% opptalt, parti navn,  parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn".&lt;/p&gt;

&lt;p&gt;It shows the distribution of mandates (per party, grouped by left/right coalition) for all of the major towns in Norway, all the parties were &lt;code&gt;aria-label&lt;/code&gt;led by (I assume) their placeholder text: &lt;em&gt;"Parti navn"&lt;/em&gt; aka &lt;em&gt;"Party name"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;As a result, VoiceOver announced results like: &lt;em&gt;"Trondheim, 100% opptalt, parti navn,  parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn, parti navn"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;One of the other things that's missing here as well is, again, the number of votes connected to each party.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lKB6-X00--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/63b7678a62db70266833aa23dcfe42dd50dd5e10-1694x1160.png%3Fw%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lKB6-X00--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/63b7678a62db70266833aa23dcfe42dd50dd5e10-1694x1160.png%3Fw%3D1200" alt='How the mandates are distributed in the major cities. For each city, a horizontal list of colored bars (in the colors of the corresponding party) is shown, grouped by block/coalition (red/left or blue/right). The coalitions with the least votes have their colors dimmed. Next to each city it shows the % of votes counted, and there is a vertical line going through the middle of the chart, labelled "majority", to indicate which point either side has to cross to reach a majority.' width="800" height="548"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Cyr60_S0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/3746daa3c2adc75d9923c9b51ef764778a14e9a8-1731x859.png%3Fw%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cyr60_S0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/3746daa3c2adc75d9923c9b51ef764778a14e9a8-1731x859.png%3Fw%3D1200" alt="Safari inspector showing the HTML structure of the chart. There's an unordered list containing an h6 and vertical separator (the majority label), and links with a href and title property set. Each link contains a list item, with several nested divs inside of it. One of the divs contains an em that has an aria-label (parti navn) set, and has the amount of mandates inside as text." width="800" height="397"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some issues can always go past us for several reasons &lt;em&gt;(everyone I know in tech is overworked and tired)&lt;/em&gt;, but to me, bugs like these potentially highlight an issue with (or a lack off) accessibility processes, because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Accessibility linters may or may not pick up on some of the issues connected to this&lt;/li&gt;
&lt;li&gt;Code reviews (by team members that are accessibility-aware) increase the chance of these getting caught early&lt;/li&gt;
&lt;li&gt;An accessibility focus in the content review process can discover issues with the alt text&lt;/li&gt;
&lt;li&gt;Testing features with a screen reader would definitely flag these&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So if those exist in production, it means that either the team isn't doing some/any of the above in their process, or they do a fair share of accessibility testing, but don't get the time to actually fix the issues that were raised.&lt;/p&gt;

&lt;h2&gt;Graphical elements require 3:1 contrast with the background&lt;/h2&gt;

&lt;p&gt;Most of the news media did actually fine when it came to the color contrast of the text. The main body text was black on a white background (or light text on a dark background in dark mode).&lt;/p&gt;

&lt;p&gt;The text elements within the data visualizations generally had sufficient (4.5:1) contrast with their background as well, although there were a few hiccups.&lt;/p&gt;

&lt;p&gt;For example, Aftenposten's green and red text showing the shift between now and the last election does not have enough contrast against white. But overall, the text color contrast requirement seemed well understood.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AM4D9EBc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/1fccd87396566b14af206fbd07aaabe5beba39bd-2408x1442.png%3Fw%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AM4D9EBc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/1fccd87396566b14af206fbd07aaabe5beba39bd-2408x1442.png%3Fw%3D1200" alt="Bar chart with small green text that only has 2.59:1 contrast with the white background." width="800" height="479"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Things like bars on a bar chart, lines on a histogram, icons, and other graphical elements require a contrast ratio of at least 3:1 to make sure they're accessible to folks with low vision.&lt;/p&gt;

&lt;p&gt;This was something most of the charts didn't accommodate for.&lt;/p&gt;

&lt;p&gt;Aside from Valgresultat, all dashboards went for colors similar to the party's brand colors in their charts. That's good for cognitive accessibility, since people may recognize those already.&lt;/p&gt;

&lt;p&gt;Some of those party colors are light, and for those NRK changed the font color for the overlaying text is changed to be at least 3:1 with the party color. That's good, and really improves the readability of those icons.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oAcZCCaA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/60d3f9eb2f25ba659caa0038490ee6547fe72df5-2848x1382.png%3Fw%3D1400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oAcZCCaA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/60d3f9eb2f25ba659caa0038490ee6547fe72df5-2848x1382.png%3Fw%3D1400" alt="Dark green text against light green background: 8.42:1; Dark teal against light teal: 6.91:1" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;However, the same brand colors are used on the bars, which are graphical elements that require 3:1 contrast with the background. For example, SP's light green (#BBCB4D) has a color contrast of 1.79:1 against the white background, which is too low.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DGbMn7I1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/5e463d7ebfd573cf24425ea9e9128e21c5090ae2-2848x1382.png%3Fw%3D1400" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DGbMn7I1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/5e463d7ebfd573cf24425ea9e9128e21c5090ae2-2848x1382.png%3Fw%3D1400" alt="Light green bar against the white background has 1.79:1 contrast, light teal bar 2.18:1" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Additionally, all the dimmed versions of the brand colors (that visualize the numbers from last election) lack color contrast agaisn the white background.&lt;/p&gt;

&lt;p&gt;The other newspapers had similar contrast issues. The bright green and yellow that Aftenposten used had less than 3:1 contrast with the white background, and VG's blue was at 2.63:1 too low to be used for both the text and chart elements.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TyQPmdv1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/f6eb14b92976e541625d630b65b5597bdd3525ef-2408x1112.png%3Fw%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TyQPmdv1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/f6eb14b92976e541625d630b65b5597bdd3525ef-2408x1112.png%3Fw%3D1200" alt="Blue text and bar chart that's 2.63:1 color contrast" width="800" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;The page itself needs to be accessible too&lt;/h2&gt;

&lt;p&gt;It's great if a visualization is accessible, but if it exists within a page filled with accessibility issues, then people may not even reach the chart at all.&lt;/p&gt;

&lt;h3&gt;Heading levels are important for explaining the structure and relationships of the content&lt;/h3&gt;

&lt;p&gt;Using subtitles/headings on a page introduces, structures, and sometimes summarizes the content. The text sets the expectation of what you're about to read, and the styling (font-size, weight, position, etc) of the headings says something about the relationship they have to each other.&lt;/p&gt;

&lt;p&gt;Semantically, this structure and relationship is communicated by using heading elements with the correct heading level (&lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt;, etc).&lt;/p&gt;

&lt;p&gt;Valgresultat used an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; for all of their subtitles, not just the main page title. This took away the structure for screen reader users, and announces the page title on the same level as the subtitles in the footer, for example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m3jPiVz2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/c4610e9d078e016a0cbebce72efcb9d7cae4c911-1310x680.png%3Fw%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m3jPiVz2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/c4610e9d078e016a0cbebce72efcb9d7cae4c911-1310x680.png%3Fw%3D1200" alt="Headings menu in VoiceOver showing 7 heading elements, all level 1 (h1): valgresultat, lanfsoversikt, eksport av data for landsoversikt, adresse, postadresse, lenker, kontakt" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In comparison, VG added headings to each of their sections, and the heading levels reflect the structure of the page. When browsing the headings menu in VoiceOver, I can tell that &lt;em&gt;"Kampen om storbyene"&lt;/em&gt; ("The battle for the big cities") has information for 6 different places: Oslo, Bergen, Stavanger, Trondheim, Kristiansand, and Tromsø.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GIMJR87N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/8b7300e6ba41fc02a50deced653eb6fc88add8b2-2856x1566.png%3Fw%3D1200" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GIMJR87N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://cdn.sanity.io/images/njlrbdui/production/8b7300e6ba41fc02a50deced653eb6fc88add8b2-2856x1566.png%3Fw%3D1200" alt="VG's page with VoiceOver headings menu open, showing a structure with one H1, and underneath a good structure with nested H2-4s." width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;Internationalization needs to be considered&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://valgresultat.no/valg/2023/ko"&gt;Valgresultat&lt;/a&gt; has a language selector which lets the user switch between four different languages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;English&lt;/li&gt;
&lt;li&gt;Norwegian (Bokmål)&lt;/li&gt;
&lt;li&gt;Norwegian (Nynorsk)&lt;/li&gt;
&lt;li&gt;Northern Sami (Sámegiella)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The text on the page updates to the selected language, but the language of the page never gets updated programmatically. According to the HTML &lt;code&gt;lang&lt;/code&gt; code, the page is in English regardless of which language is selected.&lt;/p&gt;

&lt;p&gt;Screen readers change their language and pronunciation based on the language of the content, and translation tools can base themselves on the &lt;code&gt;lang&lt;/code&gt; attribute as well, so it's important that the language tag reflects the language on the page.&lt;/p&gt;

&lt;h2&gt;Maps are tricky to get right, often ignored&lt;/h2&gt;

&lt;p&gt;Across my client work I notice that maps are the chart type that spark the most conversation.&lt;/p&gt;

&lt;p&gt;Do people want to navigate them sorted by name, continent, or value? What should the default sorting order be? Should keyboard users be able to navigate around spatially to neighboring countries? How do we visualize the data of small countries? Do we allow zoom? How does that work for keyboard users? How do we visualize borders? What are the color contrast requirements for adjacent regions?&lt;/p&gt;

&lt;p&gt;All that to say: there's a lot to be considered when it comes to map accessibility. This is clear from how the different media made their maps accessible: they didn't.&lt;/p&gt;

&lt;h3&gt;The election maps were lacking screen reader interaction, table alternatives, and informative text alternatives&lt;/h3&gt;

&lt;p&gt;Across all the websites that used maps to visualize the election results, none provided a table alternative or direct screen reader interaction with the map.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fossheim.io/writing/posts/dataviz-accessibility-review-norwegian-elections-2023/"&gt;Video in the original post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video description: A bar chart and map. The screen reader announces the text labels on the bar chart, in chronological order (but reads the letters of the party names one by one), then the zoom buttons on the map are announced, and the map itself is skipped. There is no table alternative.&lt;/p&gt;

&lt;p&gt;VoiceOver just skipped past VG's and Aftenposten's map. The zoom buttons were announced correctly, but the actual informative content was not there.&lt;/p&gt;

&lt;p&gt;NRK's map was turned into an image with alt text, but the alt text only described what type of info was visualized on the map, not the actual data. NRK also made some extra effort to summarize the graph for everyone. Underneath the map they listed the parties and in how many municipalities they were in the majority.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://fossheim.io/writing/posts/dataviz-accessibility-review-norwegian-elections-2023/"&gt;Video in the original post&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Video description: A map that's announced as a figure, and a text alternative is announced. However the text just describes the purpose of the graph (it's a map of Norway with the municipalities colored in the color of the party that had the most votes there). There is no table alternative. There is a list below the map, summarizing the amount of municipalities where each party is the largest.&lt;/p&gt;

&lt;p&gt;This in a way provides a text format of the amount of color that's observable on the map, but there is no place to view a list or table with all of the municipalities and their biggest party.&lt;/p&gt;

&lt;h2&gt;Public vs. private sector?&lt;/h2&gt;

&lt;p&gt;Before this I was curious how the graphs of the public vs. private sector performed. While I did feel like more emphasis had been placed on accessibility and compliance on the two sources from the public sector, all the dashboards I inspected had room for improvement, including those from the public sector.&lt;/p&gt;

&lt;p&gt;Currently the Norwegian public sector has to follow &lt;a href="https://www.uutilsynet.no/wcag-standarden/wcag-standarden/86?f%5B0%5D=t2%3A129"&gt;47 WCAG success criteria&lt;/a&gt;, the &lt;a href="https://www.uutilsynet.no/wcag-standarden/wcag-standarden/86?f%5B0%5D=t2%3A130"&gt;private sector&lt;/a&gt; 35. All websites are tested had some issues against those. A couple of examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VG's map did not have a text alternative (SC 1.1.1)&lt;/li&gt;
&lt;li&gt;NRK's bar charts were lacking color contrast (SC 1.4.11)&lt;/li&gt;
&lt;li&gt;VG's party-colored text was lacking color contrast (SC 1.4.3)&lt;/li&gt;
&lt;li&gt;Aftenposten's positive/negative numbers were lacking text contrast (SC 1.4.3)&lt;/li&gt;
&lt;li&gt;NRK's map used only color to convey information (SC 1.4.1)&lt;/li&gt;
&lt;li&gt;Valgresultat's language doesn't update (SC 3.1.1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both with the &lt;a href="https://www.uutilsynet.no/regelverk/gjeldende-regelverk-og-krav/746"&gt;current&lt;/a&gt; and &lt;a href="https://online-accessibility-countdown.eu/?lang=en"&gt;upcoming&lt;/a&gt; regulations in mind, the Norwegian news media and government definitely have some work left to do.&lt;/p&gt;

&lt;h2&gt;Looking for an accessibility review, training, or help with dataviz accessibility?&lt;/h2&gt;

&lt;p&gt;Are you after reading this article curious about how accessible your data visualizations are? Want to onboard your team on dataviz accessibility with a training session? Or have a chat about how to incorporate accessibility work into your organization?&lt;/p&gt;

&lt;p&gt;🙋🏻 I'm an independent accessibility specialist with an offering focused on dataviz accessibility. If you're interested in hiring me for an accessibility project or are wondering how we can &lt;a href="https://fossheim.io/work/"&gt;collaborate&lt;/a&gt;, feel free to reach out through &lt;a href="mailto:accessibility@fossheim.io"&gt;accessibility@fossheim.io&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>datascience</category>
      <category>frontend</category>
      <category>ux</category>
    </item>
    <item>
      <title>Looking back at the past year (and a half) of accessibility work.</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Sat, 31 Dec 2022 21:25:10 +0000</pubDate>
      <link>https://forem.com/fossheim/looking-back-at-the-past-year-and-a-half-of-accessibility-work-3jca</link>
      <guid>https://forem.com/fossheim/looking-back-at-the-past-year-and-a-half-of-accessibility-work-3jca</guid>
      <description>&lt;p&gt;Originally published &lt;a href="https://fossheim.io/writing/posts/looking-back-at-the-past-year-and-a-half-of-accessibility-work/"&gt;on my blog&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It's the end of the year, which means time to look back at what has been and write a year in review blog post about it. Because it's been silent on my blog for a while, I decided to combine it with another post that's long overdue: a recap of my first year (and a half) of freelancing.&lt;/p&gt;

&lt;p&gt;In 2021, after roughly 7 years of working in tech, I decided to quit my job as a senior front-end developer in favour of starting my own company and work as an independent consultant. It was a scary decision, but has absolutely proven to be the right one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Work
&lt;/h2&gt;

&lt;p&gt;While having to find your own clients and plan your own work is one of the things that makes freelancing scary, it also has allowed me to find projects that are much more in line with my skills and interests.&lt;/p&gt;

&lt;p&gt;I've gotten to work on some really interesting projects the past year and a half, including the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Contributed with accessibility reviews and guidelines to the &lt;a href="https://data.who.int/products/data-design-language"&gt;WHO Data Design Language&lt;/a&gt;, for which I was part of &lt;a href="https://truth-and-beauty.net/projects/who"&gt;Truth &amp;amp; Beauty's&lt;/a&gt; team.&lt;/li&gt;
&lt;li&gt;Accessibility and front-end consulting for a long-term and repeat client: I assist with fixing accessibility bugs, implement and review new features, and help build internal accessibility documentation and knowledge.&lt;/li&gt;
&lt;li&gt;Performed accessibility reviews for different products and websites. For example, I audited on all of &lt;a href="https://www.finn.no"&gt;FINN.no&lt;/a&gt;'s data visualizations, and provided the designers and developers with guidance, documentation and tools to implement the fixes and make improvements. I also reviewed all of the &lt;a href="https://stateofjs.com/en-us/"&gt;State of JS&lt;/a&gt; and &lt;a href="https://stateofcss.com/en-us/"&gt;State of CSS&lt;/a&gt; graphs, based on &lt;a href="https://chartability.fizz.studio"&gt;Chartability&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Accessibility talks and training: including at &lt;a href="https://graphichuntersshow.nl"&gt;SHOW conference&lt;/a&gt;, &lt;a href="https://www.youtube.com/watch?v=b-DvfAc52mg"&gt;FINN.no UX Design Evenings&lt;/a&gt;, Little Miss Robot's Product Design Meetup (hosted @ my old university, almost felt like being back in school), and more. I also started doing internal accessibility training, office hours, and Q&amp;amp;As with some clients.&lt;/li&gt;
&lt;li&gt;Wrote an essay on screen reader accessible data visualizations as part of The Urban Institute's &lt;a href="https://www.urban.org/sites/default/files/2022-12/Do%20No%20Harm%20Guide%20Centering%20Accessibility%20in%20Data%20Visualization.pdf"&gt;Do No Harm Guide on Centering Accessibility In Data Visualizations&lt;/a&gt;. The report has 9 different essays by different folks in the dataviz accessibility field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also implemented a whole lot of new features across projects. Most of those were written in React and TypeScript, but I also had a couple of fun projects that combined Eleventy/Sanity as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessibility Observations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Many people actually do want to prioritize accessibility.
&lt;/h3&gt;

&lt;p&gt;Throughout my career (almost 10 years at the time of writing this post) I've frequently come across people who just refused to care about accessibility. My main experience was always having to advocate hard for it.&lt;/p&gt;

&lt;p&gt;I remember leaving a comment about changing a &lt;/p&gt; (with a clickhandler and styled like a button) to an actual  on a PR and the developer refusing to address it because they felt accessibility wasn't important enough. Or another time having to justify myself to a manager for spending time on making a color scheme more accessible. The conversation with him took longer than the actual work.

&lt;p&gt;However, the past year and a half I've mainly come across the opposite: people who are looking to improve their accessibility skills, who ask a lot of questions, and who are excited to deliver an accessible quality product from the start, rather than moving fast and breaking things.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The tech industry faces a lot of accessibility blockers.
&lt;/h3&gt;

&lt;p&gt;The enthusiasm for accessibility work doesn't necessarily translate into accessible products, though. One of the most asked questions I get after talks is still some variation of "how do I convince my boss to let me work on accessibility?" (and a blog post about this is coming soon™).&lt;/p&gt;

&lt;p&gt;But when talking to project managers, I also often hear similar struggles: they're trying to plan around a deadline someone else has set or work within a budget they didn't decide on.&lt;/p&gt;

&lt;p&gt;Amongst the type of companies that contact me are also design agencies and consultancies, where the time and money the team can spend on accessibility work also depends on the budget they received from their clients.&lt;/p&gt;

&lt;p&gt;On top of that, I feel like there are a lot of accessibility knowledge gaps in the industry. Bootcamps and college degrees don't always touch upon the topic, so not everyone enters the workforce knowing about it. Learning new skills also takes time and energy, and if you want to follow a course or book, also money. Doing this after work and out of own pocket isn't achievable for everyone (and shouldn't be the expectation), but how much learning can be done on the job really depends on the company.&lt;/p&gt;

&lt;p&gt;Long story short: there are a a lot of things that may block accessibility efforts, even if we have good intentions or come prepared. We can't necessarily control our circumstances, but we can control how we prepare for them and respond to them.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Accessibility bugs are more often caused by broken processes than broken code.
&lt;/h3&gt;

&lt;p&gt;Yes, a lot of accessibility issues are caused by how the code is written (or how the product is designed), but the fact that inaccessible features make it into a live version that's released to users, that's typically an issue with our processes.&lt;/p&gt;

&lt;p&gt;Most people do some sort of quality control of their products before pushing them out. Depending on the size of the company or poject that could be anything from a designer taking a quick look at what the developer built, or a detailed process involving multiple code reviews, and automatic and manual tests.&lt;/p&gt;

&lt;p&gt;Some of the most common issues I come across are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Missing or unclear alt text&lt;/strong&gt;. Missing alt text can be picked up by linters, unit tests, and automatic testing tools such as Axe and Lighthouse. It can also be viewed in the browser through the inspector (developer tools), or by using a screen reader on the page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Divs used as buttons, without the appropriate roles, labels, or keyboard controls&lt;/strong&gt;. Most issues related to semantics will be flagged by linters and automatic testing tools, but this is also something that can be flagged in code reviews. The behavior can be tested manually by using the keyboard in the browser.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Elements disappearing/blocking the view when zooming in&lt;/strong&gt;. This can be tested during and after development by zooming in to the browser when doing manual tests. Designers can also document how the page should behave on zoom/resize.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lack of color contrast&lt;/strong&gt;. This can be tested by design tool plugins (eg. Able, Stark), automated testing tools, and web-based color contrast checkers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If a lot of those bugs are created while building, it can be an indication the team needs accessibility training (or hire more accessibility-focused engineers/designers). But if they make it into production, in my experience it usually means one or more of those things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There wasn't enough testing/quality assurance in general.&lt;/li&gt;
&lt;li&gt;The testing and quality assurance didn't include accessibility.&lt;/li&gt;
&lt;li&gt;Accessibility issues were flagged, but fixes deprioritized.&lt;/li&gt;
&lt;li&gt;Fixes for the bugs were prioritized, but no one's available to fix them.&lt;/li&gt;
&lt;li&gt;There's no way to discover or track accessibility issues.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  4. Progress over perfection: small improvements can go a long way.
&lt;/h3&gt;

&lt;p&gt;It's not always possible to fix all accessibility issues a website has at once, and we may not alwyas find the best solution on the first or second try. If we try to fix everything in one go, we risk ending up fixing nothing at all.&lt;/p&gt;

&lt;p&gt;If there's a limited accessibility budget, it can be more productive to do a high-level review of the product and help the team understand how to fix and prevent the issues, rather than holding out from doing anything until someone wants to set resources aside for a full in-depth WCAG audit (which may never happen).&lt;/p&gt;

&lt;p&gt;Some of the "smallest" accessibility issues to solve can also be some of the largest blockers. I've come across menus where each menu item was labelled "image" and none of the pages had unique titles. From a technical point of view, that's a small mistake with an easy fix (add a text label to the icons). But for screen reader users this made the app unusable.&lt;/p&gt;

&lt;p&gt;The more of those seemingly small issues get fixed, the deeper the conversation around accessibility can be, and the more useful user feedback can be collected.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking into 2023.
&lt;/h2&gt;

&lt;p&gt;I'm mainly hoping to continue what I started in 2023: do a good mix of projects, with a focus on accessibility. That means front-end/accessibility consulting work, accessibility reviews, documentation, and training. I especially want to streamline and strengthen my auditing and training offering.&lt;/p&gt;

&lt;p&gt;I have also set time aside for building accessibility tools for my company to start using (stay tuned 👀), and am also planning on writing and building in the open a whole lot more.&lt;/p&gt;

&lt;p&gt;For example, I'm currently building a customizable links page:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preview: &lt;a href="https://links.fossheim.io"&gt;links.fossheim.io&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sarahfossheim/links.fossheim.io"&gt;GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I genuinely had a good year in 2022. I got to spend a lot of time with my wife and dogs, recovered from burnout and CPTSD, and had a fully-booked year of interesting clients. I hope I'm not jinxing it by writing this post, but I wouldn't mind another year like this ❤️&lt;/p&gt;

&lt;p&gt;Happy New Year!&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>career</category>
      <category>freelance</category>
    </item>
    <item>
      <title>Building an accessible theme picker with HTML, CSS and JavaScript.</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Wed, 21 Dec 2022 18:42:19 +0000</pubDate>
      <link>https://forem.com/fossheim/building-an-accessible-theme-picker-with-html-css-and-javascript-p5d</link>
      <guid>https://forem.com/fossheim/building-an-accessible-theme-picker-with-html-css-and-javascript-p5d</guid>
      <description>&lt;p&gt;This post was &lt;a href="https://fossheim.io/writing/posts/accessible-theme-picker-html-css-js/" rel="noopener noreferrer"&gt;originally posted on my own blog over @ fossheim.io&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;In the recent refresh of my website’s design I implemented different theming options. It’s now possible to choose between six different color schemes when reading my blog.&lt;/p&gt;

&lt;p&gt;The need for more than just a light theme and dark theme came from the fact that personally, when I get bad migraines and headaches, low contrasting colors feel easier on my eyes, so I wanted to at least add a dark low-contrast option in addition to a regular light and dark theme. &lt;/p&gt;

&lt;p&gt;Once the support for the first two themes was added, providing more options became really straightforward, so it was easy to let myself have some fun creating different themes!&lt;/p&gt;

&lt;p&gt;I’ve received a lot of positive feedback on the theme switcher, and implementing light/dark modes and high contrast themes has been a recurring accessibility task across several of the projects I’ve been involved in, so I figured it was time for a write-up on how to implement a theme switcher like this.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;HTML&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CSS&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;JavaScript (vanilla)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What we’ll be building
&lt;/h2&gt;

&lt;p&gt;At the end of this tutorial, we'll have an example with four different themes that can instantly be activated through a group of toggle buttons in the interface, and our choice will be remembered through localStorage.&lt;/p&gt;

&lt;p&gt;You can find the final code on my &lt;a href="https://github.com/sarahfossheim/theme-switcher" rel="noopener noreferrer"&gt;theme-switcher GitHub repo&lt;/a&gt;, and all the demos used in this tutorial are bundled in a &lt;a href="https://codepen.io/collection/OLMmdg?cursor=eyJjb2xsZWN0aW9uX2lkIjoiT0xNbWRnIiwiY29sbGVjdGlvbl90b2tlbiI6bnVsbCwibGltaXQiOjQsIm1heF9pdGVtcyI6MTEsIm9mZnNldCI6OCwicGFnZSI6Mywic29ydF9ieSI6InBvc2l0aW9uIiwic29ydF9vcmRlciI6IkFzYyJ9" rel="noopener noreferrer"&gt;Theme Switcher collection&lt;/a&gt; on my CodePen as well.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/WNyBwMy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Variable styling with CSS.
&lt;/h2&gt;

&lt;p&gt;Let's start by getting our CSS ready to support themes. &lt;/p&gt;

&lt;p&gt;We’ll need to use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;CSS variables (or custom properties)&lt;/a&gt; to make changing out colors across components easier.&lt;/p&gt;

&lt;p&gt;Let's say we start with a CSS file that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#00CC66&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="nc"&gt;.callout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="nc"&gt;.footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can add a custom data attribute on the html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;data-selected-theme=&lt;/span&gt;&lt;span class="s"&gt;"pink"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  ...
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And use that to overwrite the colors for each theme. If we continued with the CSS from above, without adding variables, we'd have to do something along these lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* variables.css */&lt;/span&gt;
&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#00CC66&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="nc"&gt;.callout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="nc"&gt;.footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* pink.css */&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"pink"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;#DFB2F4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;#F06EFC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#463546&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"pink"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.callout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#463546&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#DFB2F4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"pink"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="nc"&gt;.footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#DFB2F4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="m"&gt;#463546&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a small tutorial example this might still seem like a feasible approach, but it would become really difficult to keep track of over time. The larger the website or component library, the more properties we'd need to remember to manually overwrite for each new theme. So in come ✨ CSS variables ✨, which we can set on  :root like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#00CC66&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can then use those variables everywhere else in the CSS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;linear-gradient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.callout&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="nc"&gt;.footer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;border-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means that instead of having to update the styling of each element individually, we can now limit ourselves to only overwriting the variables that are defined in the :root :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#00CC66&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"pink"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#DFB2F4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#463546&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#F06EFC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This looks a lot tidier already! 🥳&lt;/p&gt;

&lt;p&gt;We can now relatively quickly scale up the amount of themes we support, without having to change anything inside the individual components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; 
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"green"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#A4F3A2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#034435&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#00CC66&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"blue"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#55dde0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2B4150&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#00D4E7&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"pink"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#DFB2F4&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#463546&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#F06EFC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-selected-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"orange"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#FA7D61&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#1E1E24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#F3601C&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have written the CSS support for our different themes, and we can switch between them by manually updating the data-selected-theme property on the body of the page to our different theme class names.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/oNyRbKv?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Now we will need to create a component that lets us switch themes directly from the UI instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a theme selector component in HTML.
&lt;/h2&gt;

&lt;p&gt;There are several ways you could go about implementing theming like this. For example, GitHub has a place in the setting where it’s possible to select a color theme by using radio buttons.&lt;/p&gt;

&lt;p&gt;I decided to go for a group of  elements. It’s how I designed them visually, so it makes sense to match that pattern in the semantics.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tink.uk/perceived-affordances-and-the-functionality-mismatch/" rel="noopener noreferrer"&gt;Léonie Watson has an excellent article&lt;/a&gt; explaining why an element’s visuals and semantics should match, but in short: different elements (buttons, radio buttons, links, etc) have their own keyboard and screen reader controls, and we want to make sure the actual interaction available lines up with the user’s expectation. &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/QWxRNwa?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Communicating the selected theme.
&lt;/h3&gt;

&lt;p&gt;Now we have our buttons, but we don’t have anything in place yet to indicate which theme is selected. We’re using the green by default, so we can already pre-select that button by adding aria-pressed="true".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-switcher"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Green&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Blue&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Pink&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Orange&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The aria-pressed tells assistive technology whether or not a button is checked. For example, VoiceOver will read the above selected button as:&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;Green, selected, toggle button&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We can use the same aria-pressed property in the CSS to style the selected button differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;aria-pressed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"true"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--color-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/NWzVxZv?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
 &lt;/p&gt;
&lt;h2&gt;
  
  
  Updating the selected theme with JavaScript.
&lt;/h2&gt;

&lt;p&gt;So now comes the fun part: making the buttons interactive using JavaScript. When we activate a button (using click, space, or enter), we want:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The class name on the body to update with the corresponding theme.&lt;/li&gt;
&lt;li&gt;The button’s aria-pressed property to be set to true.&lt;/li&gt;
&lt;li&gt;All other theme buttons to be toggled off (aria-pressed=”false”).&lt;/li&gt;
&lt;li&gt;The choice to be saved for next time we visit the page.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Reacting to button clicks.
&lt;/h3&gt;

&lt;p&gt;We’ll first need to detect which button has been clicked. We can do so by selecting all the theme buttons on the page, and then looping through them and adding a click event listener to each of them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Logs the clicked button */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* Selects all buttons */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeSwitcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.theme-switcher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;themeSwitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Adds the handleThemeSelection as a click handler to each of the buttons */&lt;/span&gt;
&lt;span class="nx"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleThemeSelection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we used the  element, the click event will also be called when using the space and enter key to activate it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding theming info to the buttons.
&lt;/h3&gt;

&lt;p&gt;If we interact with the buttons on our page, we’ll notice that we can indeed detect the selected element this way.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/QWxRNjQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;But it doesn’t give us much we can use in the code to update the themes. So before we continue, now is a good time to add some more custom properties to our HTML.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"theme-switcher"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-theme=&lt;/span&gt;&lt;span class="s"&gt;"green"&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Green&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-theme=&lt;/span&gt;&lt;span class="s"&gt;"blue"&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Blue&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-theme=&lt;/span&gt;&lt;span class="s"&gt;"pink"&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Pink&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;data-theme=&lt;/span&gt;&lt;span class="s"&gt;"orange"&lt;/span&gt; &lt;span class="na"&gt;aria-pressed=&lt;/span&gt;&lt;span class="s"&gt;"false"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Orange&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Updating the theme.
&lt;/h3&gt;

&lt;p&gt;Now we can actually target the data-theme value in our click handler:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And use it to update the data-selected-theme property programatically. The following code will be enough to get the color scheme to update:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-selected-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;themeSwitcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.theme-switcher&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;buttons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;themeSwitcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;buttons&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;click&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleThemeSelection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we click on the different options now, the styling of the page indeed updates, but the state of the buttons is still unchanged. Even if we select the pink theme, the default green options still is shown as active instead.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/gOKJrWW?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the button properties.
&lt;/h3&gt;

&lt;p&gt;We need to reflect this change in our button group as well. When clicking a button, we can set its aria-pressed attribute to true:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will select the newly clicked button, but still won’t update the aria-pressed value of whichever color themes were selected previously, meaning several buttons can be selected at the same time.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/gOKJrxR?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;To fix this we'll want to reset all aria-pressed buttons to false. Before updating our clicked button's value, we can first select the button that was still active:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prevBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-theme][aria-pressed="true"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then set aria-pressed to false:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prevBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-theme][aria-pressed="true"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;prevBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we targeted [aria-pressed="true"] to style our selected state, we don’t need to do anything else to update the styling.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/WNyBwEJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Saving the choice.
&lt;/h2&gt;

&lt;p&gt;Our theme selector works! 🥳 &lt;/p&gt;

&lt;p&gt;The final step will be to remember our choice, so we don’t need to re-select the theme each time we visit the page. &lt;/p&gt;

&lt;h3&gt;
  
  
  Updating the local storage.
&lt;/h3&gt;

&lt;p&gt;We can save the user selected theme in the local storage using the localStorage.setItem() function when clicking the button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-selected-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prevBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-theme][aria-pressed="true"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;prevBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On page load, we can then check which theme has been stored in the local storage by calling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const savedTheme = localStorage.getItem('selected-theme');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a theme has been saved, we'll need to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Unselect the default selected button&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Select the button that matches the saved theme&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Change the data-selected-theme to the saved theme&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In order to avoid performing unnecessary actions, we'll only execute this code when the saved theme is different from the default theme:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;defaultTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;green&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;defaultTheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prevBtn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-theme][aria-pressed="true"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;prevBtn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-theme="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;savedTheme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-selected-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;savedTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/eYKaZGo?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Code cleanup
&lt;/h2&gt;

&lt;p&gt;There is still some repeated code. The way the aria-pressed and data-selected-theme are updated after loading the page and after clicking a button is more or less the same. So we can move this part into its own function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;applyTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-theme="&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"]`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;data-selected-theme&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;[data-theme][aria-pressed="true"]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;false&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same function can then be called when clicking an option (from within handleThemeSelection), and when loading the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleThemeSelection&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isPressed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-pressed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="cm"&gt;/* if clicked theme is different from current theme */&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isPressed&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        
    &lt;span class="nf"&gt;applyTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;selected-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* if saved theme is different from current theme */&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;savedTheme&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;defaultTheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;applyTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;savedTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final result
&lt;/h2&gt;

&lt;p&gt;And done! We have a basic theme switcher, that works with keyboard navigation and screen reader! &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/WNyBwMy?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;p&gt;The code for this tutorial is available through my &lt;a href="https://github.com/sarahfossheim/theme-switcher" rel="noopener noreferrer"&gt;theme-switcher GitHub repo&lt;/a&gt;, and all the demos used in this tutorial are bundled in a &lt;a href="https://codepen.io/collection/OLMmdg?cursor=eyJjb2xsZWN0aW9uX2lkIjoiT0xNbWRnIiwiY29sbGVjdGlvbl90b2tlbiI6bnVsbCwibGltaXQiOjQsIm1heF9pdGVtcyI6MTEsIm9mZnNldCI6OCwicGFnZSI6Mywic29ydF9ieSI6InBvc2l0aW9uIiwic29ydF9vcmRlciI6IkFzYyJ9" rel="noopener noreferrer"&gt;Theme Switcher collection&lt;/a&gt; on my CodePen as well.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://lea.verou.me/2022/07/button-group/" rel="noopener noreferrer"&gt;What's the best way to mark up an exclusive button group? by Lea Verou&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tink.uk/perceived-affordances-and-the-functionality-mismatch/" rel="noopener noreferrer"&gt;Perceived affordances and the functionality mismatch by Léonie Watson&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-pressed" rel="noopener noreferrer"&gt;MDN: aria-pressed&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;MDN: Using CSS custom properties (CSS variables)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'd love to see the result if you end up using my tutorial to add a theme selector to your website 🎨✨&lt;/p&gt;




&lt;p&gt;Like my content? → Subscribe to &lt;a href="https://fossheim.io/newsletter/" rel="noopener noreferrer"&gt;my newsletter&lt;/a&gt; or my &lt;a href="https://fossheim.io/feed.xml" rel="noopener noreferrer"&gt;RSS feed&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Want to collaborate? → I work as an independent consultant, &lt;a href="https://fossheim.io/work/" rel="noopener noreferrer"&gt;open for new projects&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>indonesia</category>
      <category>cryptocurrency</category>
    </item>
    <item>
      <title>I drew a synthesizer with HTML &amp; CSS</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Sun, 04 Apr 2021 22:42:26 +0000</pubDate>
      <link>https://forem.com/fossheim/i-drew-a-synthesizer-with-html-css-53f</link>
      <guid>https://forem.com/fossheim/i-drew-a-synthesizer-with-html-css-53f</guid>
      <description>&lt;p&gt;I made a new drawing using only HTML &amp;amp; CSS. This time I chose to go for the Muson toy synthesizer. I've been wanting to draw it for a while now, so I'm glad I finally got to do it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjdlai6h50v0dvnounf8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvjdlai6h50v0dvnounf8.png" alt="Muson drawn in HTML &amp;amp; CSS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Tutorials: &lt;a href="https://fossheim.io" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://fossheim.io" rel="noopener noreferrer"&gt;https://fossheim.io&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Patreon: &lt;a href="https://patreon.com/fossheim" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://patreon.com/fossheim" rel="noopener noreferrer"&gt;https://patreon.com/fossheim&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Buy Me A Coffee: &lt;a href="https://buymeacoffee.com/fossheim" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;a href="https://buymeacoffee.com/fossheim" rel="noopener noreferrer"&gt;https://buymeacoffee.com/fossheim&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/BapZQjK?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Other CSS drawings I've made:&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/fossheim/embed/BaKQGZJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/fossheim/embed/jObGxQQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;br&gt;
&lt;iframe height="600" src="https://codepen.io/fossheim/embed/xxboBzO?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;More on &lt;a href="https://codepen.io/collection/nwzQJq?cursor=ZD0wJm89MCZwPTEmdj00" rel="noopener noreferrer"&gt;CodePen&lt;/a&gt; and &lt;a href="https://github.com/sarahfossheim/art.css" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>codepen</category>
      <category>html</category>
      <category>css</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How to create a screen reader accessible graph like Apple's with D3.js</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Tue, 23 Mar 2021 13:38:07 +0000</pubDate>
      <link>https://forem.com/fossheim/how-to-create-a-screen-reader-accessible-graph-like-apple-s-with-d3-js-18c9</link>
      <guid>https://forem.com/fossheim/how-to-create-a-screen-reader-accessible-graph-like-apple-s-with-d3-js-18c9</guid>
      <description>&lt;p&gt;Originally posted on &lt;a href="https://fossheim.io/writing/posts/apple-dataviz-a11y-tutorial/" rel="noopener noreferrer"&gt;fossheim.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After previously writing about the accessibility of Apple Health’s data visualizations, I felt inspired to recreate one of them with D3.js. I already covered some of the basics in the form of a bar chart, so this time I decided to go for a different type of graph: the activity rings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomptze4zx3uf45g3mebe.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomptze4zx3uf45g3mebe.png" alt="Apple Activity app: small donut charts for activity every day of the week, large donut chart for current day, bar chart for the current day broken down by the hour"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Before we start
&lt;/h2&gt;

&lt;p&gt;While we will build the graph together step by step, this tutorial does require some previous knowledge or experience with D3.js. If you haven’t used D3 before, I suggest starting with some of these tutorials:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://observablehq.com/@d3/gallery" rel="noopener noreferrer"&gt;Examples&lt;/a&gt; &amp;amp; &lt;a href="https://github.com/d3/d3/wiki" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wattenberger.com/blog/d3" rel="noopener noreferrer"&gt;How to learn D3.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.a11ywithlindsey.com/blog/accessibility-d3-donut-charts" rel="noopener noreferrer"&gt;Accessible donut charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.a11ywithlindsey.com/blog/accessibility-d3-bar-charts" rel="noopener noreferrer"&gt;Accessible bar charts&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Part 1: Drawing the rings.
&lt;/h2&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/MWbBRrx?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;First, we’ll need to add a container in the HTML, and (optionally) style the page with CSS already. Next, we’ll draw an SVG element using JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Define properties */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;450&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/* Add SVG inside &amp;lt;div id="activity"&amp;gt;&amp;lt;/div&amp;gt; */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#activity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have an  we can start adding elements to it. First, we’ll create a group to draw the rings in, and center it within its parent ().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;g&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`translate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we’ll need to draw our three rings for moving, exercising, and standing. For now, we’ll be using the following input data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
 &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Moving&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;122&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;perc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.35&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kcal&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hotpink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Exercising&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;perc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;min&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;limegreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Standing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;perc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;h&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;turquoise&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a few different ways to draw the rings, but I chose to drawpaths in combination with the d3.arc() function by looping through the stats and using the perc &lt;em&gt;(percentage)&lt;/em&gt; to define start and stop positioning.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;rings.append('path')
    .attr('d', d3.arc()
      .innerRadius(150)
      .outerRadius(200)
      .startAngle(0)
      .endAngle(Math.PI) // full circle: Math.PI * 2
     )
    .attr('fill', 'white');
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would give us half a donut that’s 200px in radius (400px in diameter), has a band width of 50px and a gap of 2px.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/Vwmqaje?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;When we look back at the activity rings, we can see that each ring should decrease in size, and we should have a small gap between each of the rings.&lt;/p&gt;

&lt;p&gt;Concretely, this means that for each row of data, the innerRadius and outerRadius should get smaller.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzawejkk44jgrpnf0a70g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzawejkk44jgrpnf0a70g.png" alt="1st ring: moving: outerRadius: radius, innerRadius: radius - stroke. 2nd ring: exercising: outerRadius: radius - stroke - gap, innerRadius: radius - 2 * stroke - gap. 3rd ring: standing: outerRadius: radius - 2*stroke - 2*gap, innerRadius: radius - 3*stroke - 2*gap."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we set our radius to &lt;code&gt;(width - margin) / 2&lt;/code&gt; (so it takes up the entire space of the SVG minus a predefined margin) and the stroke/donut width to 50, the first row of data would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;innerRadius&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outerRadius&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mf"&gt;0.35&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hotpink&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because Math.PI * 2 gives us a full circle, we can multiply it with the goal completion percentage (stat.perc) to calculate the correct endAngle.&lt;/p&gt;

&lt;p&gt;For the second ring, this would have to be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;innerRadius&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outerRadius&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;limegreen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which we can generalize as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;innerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleStroke&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleSpace&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleStroke&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleSpace&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we’ll need to add a similar &lt;code&gt;&amp;lt;path&amp;gt;&lt;/code&gt; for the darker, uncompleted part of the circle. The only thing we need to do for that is set the &lt;code&gt;startAngle&lt;/code&gt; to &lt;code&gt;fullCircle * stat.perc&lt;/code&gt;, so that it starts where the bright circle ends and set the &lt;code&gt;endAngle&lt;/code&gt; to &lt;code&gt;Math.PI * 2&lt;/code&gt;. We’ll also turn down the opacity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;rings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;innerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleStroke&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleSpace&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleStroke&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleSpace&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;rings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;innerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleStroke&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleSpace&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outerRadius&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleStroke&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleSpace&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endAngle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opacity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.25&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I made a few more modifications to this and moved part of the code into a drawRings function, so I wouldn’t have to repeat the calculations for the inner and outer radius. You can see the full code for this part in the pen below 👇🏻.&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/MWbBRrx?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;If we listen to this with a screen reader, such as VoiceOver or Narrator, we won’t hear much useful. In fact, we won’t hear anything at all. That is because so far we have only drawn shapes, which doesn’t really tell a screen reader what to do.&lt;/p&gt;

&lt;p&gt;In my previous tutorial we used  elements to read out the data, but for this one I decided to go for another option: the aria-labelledby property in combination with a  and  element. This is inspired by how FiveThirtyEight labeled their graphs in their &lt;a href="https://projects.fivethirtyeight.com/2020-election-forecast/" rel="noopener noreferrer"&gt;2020 presidential election forecast (I reviewed those graphs before)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ll want to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the role of the graph to img.&lt;/li&gt;
&lt;li&gt;Include a  and  inside the SVG, and give each a unique id.&lt;/li&gt;
&lt;li&gt;Link the title and description to image by adding aria-labelledby=”titleID descID” to the graph.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we want to mimic Apple’s native behavior, the completion percentage for all three rings should be read simultaneously. Eg. “Moving: 35%. Exercising: 100%. Standing: 75%“.&lt;/p&gt;

&lt;p&gt;To generate this text, we’ll create a function that extracts the label (moving, exercising, standing) and the values (35%, 100%, 75%) from the array with the data and then puts it in a sentence.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;generateDescription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%.`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we loop through the objects inside the stats array and replace each of them with a string. So after we’re finished looping through the stats, this is our output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Moving: 35%.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Exercising: 100%.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Standing: 75%.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lastly, we’ll use .join(' ') at the end to create one long description, and use the output of the function to fill out the text inside the  element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Create the chart. */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#activity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// SR support&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-labelledby&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;activityTitle activityDesc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// SR support&lt;/span&gt;

&lt;span class="cm"&gt;/* Add title. */&lt;/span&gt;
&lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;title&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Activity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;activityTitle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Add the description. */&lt;/span&gt;
&lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;desc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;generateDescription&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;activityDesc&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/YzpOZLp?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternative: Using aria-label
&lt;/h3&gt;

&lt;p&gt;We can achieve the same result by using aria-label instead of aria-labelledby in combination with the same generateDescription() function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;d3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#activity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;role&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;img&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;generateDescription&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/ExNrwLz?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Part 3: Explaining the data.
&lt;/h2&gt;

&lt;p&gt;So now we have three screen reader accessible rings, but visually those don’t tell us that much yet. Pink, green and blue don’t really mean anything, and don’t work well for color blind folks either.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowpt00hwhw7s70dmsvab.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowpt00hwhw7s70dmsvab.png" alt="3 progress circles (activity rings) in pink, green and blue. They have icons for moving, exercising and standing, which are circled."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s start by adding icons. For the sake of simplicity, I didn’t draw or import any icons but used existing symbols as text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/* Define icons */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;moving&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;↦&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;exercising&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;↠&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;standing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;↟&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="cm"&gt;/* Inside of stats.forEach(...), 
  at the end of the loop */&lt;/span&gt;
&lt;span class="nx"&gt;rings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;icons[stat.name.toLowerCase()]&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#000&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`translate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;circleSpace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, -&lt;/span&gt;&lt;span class="p"&gt;${(&lt;/span&gt;&lt;span class="nx"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;outer&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;arc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;circleSpace&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;font-size&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.5rem&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/WNogjQP?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;In addition, we should explain what the colors and symbols mean in a legend. Apple combines this explanation with statistics that show the data in a more detailed way.&lt;/p&gt;

&lt;p&gt;This doesn’t just add context to the colors of the graph, but also makes the same data available in different formats, which also improves accessibility.&lt;/p&gt;

&lt;p&gt;We can implement a simplified version of this by adding  elements containing the label, total, goal and percentage values. We’ll also need to add the corresponding icons and colors, and adjust the vertical position for each row.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()]}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%)`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-anchor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;middle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`translate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The text is added directly to the , not to the same group as the rings, so that it can be focused when using VoiceOver.&lt;/p&gt;

&lt;p&gt;Right now the icons in the legend will still be read. If we want that to prevent that from happening, we can add the aria-hidden='true' attribute to the icons this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;legend&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;chart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-anchor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;middle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;transform&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`translate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fill&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tspan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;()]}&lt;/span&gt;&lt;span class="s2"&gt; `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;aria-hidden&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;true&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tspan&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;goal&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;unit&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;perc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/gOLdGQq?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternative: Expanding the aria-label solution
&lt;/h3&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/GRNPgxJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Next steps.
&lt;/h3&gt;

&lt;p&gt;We can keep styling the graph to make it look more similar to Apple’s graphs, or apply our own styling to it. A few possible next steps could be to move the color scheme to the CSS file, replace the icons or add gradients and shadows.&lt;/p&gt;

&lt;p&gt;If you’re new to working with D3.js, SVGs or (dataviz) accessibility, here are a few more articles that can help you with this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://css-tricks.com/svg-properties-and-css/" rel="noopener noreferrer"&gt;SVG properties and CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.d3-graph-gallery.com/graph/line_color_gradient_svg.html" rel="noopener noreferrer"&gt;Adding gradients onto a line chart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.highcharts.com/blog/tutorials/accessible-descriptions-for-interactive-charts/" rel="noopener noreferrer"&gt;How to write accessible descriptions for interactive charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wattenberger.com/blog/d3#animation" rel="noopener noreferrer"&gt;Add animation with D3.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/SVG_and_CSS" rel="noopener noreferrer"&gt;SVG and CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://codepen.io/pnowell/pen/eJbaeN" rel="noopener noreferrer"&gt;Adding shadows to an SVG (demo)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Feel free to share the results with me (you can tag me on &lt;a href="https://twitter.com/liatrisbian" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; or mention it in the comments here) if you build something similar using this tutorial or have a different way of solving this 👀&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus solutions:
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Different type of input.
&lt;/h3&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/ExNebwL?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h3&gt;
  
  
  Navigate through the activity rings.
&lt;/h3&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/wvoEpKd?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>frontend</category>
      <category>javascript</category>
      <category>data</category>
    </item>
    <item>
      <title>Excluding non-binary people by design: How sign-up forms can lead to discrimination</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Mon, 01 Mar 2021 15:00:01 +0000</pubDate>
      <link>https://forem.com/fossheim/excluding-non-binary-people-by-design-how-sign-up-forms-can-lead-to-discrimination-2802</link>
      <guid>https://forem.com/fossheim/excluding-non-binary-people-by-design-how-sign-up-forms-can-lead-to-discrimination-2802</guid>
      <description>&lt;p&gt;What happens when we don’t include trans and non-binary people in our products? How do our products cause harm? Why is education so important and change so hard?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://themarkup.org/google-the-giant/2021/02/11/google-has-been-allowing-advertisers-to-exclude-nonbinary-people-from-seeing-job-ads" rel="noopener noreferrer"&gt;The Markup’s discovery&lt;/a&gt; that Google allowed advertisers to exclude non-binary people from job and housing ads, while blocking them from excluding men or women for those ads, is a great example of what happens when we don’t include or prioritize minoritized groups in our products. &lt;/p&gt;

&lt;h2&gt;
  
  
  How Google allowed this discrimination to happen.
&lt;/h2&gt;

&lt;p&gt;TL;DR: Careless design leads to broken features that open the door for discriminating trans and non-binary people. &lt;/p&gt;

&lt;p&gt;When signing up for a Google account, users are given four gender options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Male&lt;/li&gt;
&lt;li&gt;Female&lt;/li&gt;
&lt;li&gt;Rather Not Say&lt;/li&gt;
&lt;li&gt;Custom&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8smkkrgd66lzhpoqwx9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8smkkrgd66lzhpoqwx9.png" alt="Gender selection on a form, 3 radio buttons (male, female, rather not say) with "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When picking the “custom” option, users can write their gender in a text box.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3mcqteqvcfq61cttufq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn3mcqteqvcfq61cttufq.png" alt="gender selection box, custom gender is chosen, pop-up with free text field for gender (non-binary entered) and 3 choices for pronouns: female, male, other"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But advertisers only get three checkboxes they can use when picking an audience for their ads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Male&lt;/li&gt;
&lt;li&gt;Female&lt;/li&gt;
&lt;li&gt;Unknown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Markup found that everyone who picked the “rather not say” or “custom” option in their settings gets grouped within the “unknown” category for advertisers. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8uwjfx3u4b3c355m6ulj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8uwjfx3u4b3c355m6ulj.png" alt="advertiser pop-up to pick demographic targeting. Gender options for female, male, unknown. All are selected by default."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Image from &lt;a href="https://themarkup.org/google-the-giant/2021/02/11/google-has-been-allowing-advertisers-to-exclude-nonbinary-people-from-seeing-job-ads" rel="noopener noreferrer"&gt;The Markup&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;While Google doesn’t allow ads to exclude men or women from jobs, housing, or financial products, they did allow advertisers to exclude the “unknown” category, leaving those outside of the gender binary excluded as well. All of this during a time where housing, jobs and financial aid are crucial for everyone, but even more so to those who are already more often exposed to discrimination and abuse.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alienated and erased by design.
&lt;/h2&gt;

&lt;p&gt;Just the way this data gets labeled already shows how much of an afterthought gender diverse people were. &lt;/p&gt;

&lt;p&gt;As non-binary people, we first have to indicate that we’re “other” or “custom”, and then that information gets disregarded and Google categorizes us as “unknown”. First we’re alienated, then we’re forgotten about. &lt;/p&gt;

&lt;p&gt;We should include and prioritize the needs and safety of minoritized groups, as early in the process as possible. &lt;/p&gt;

&lt;p&gt;The data that comes out of these gender selection boxes isn’t just sitting in a database somewhere, it’s actively being used. In this case, it decides who gets to see certain ads, and Google’s lack of care for trans and non-binary people led to vulnerable groups being excluded from housing and job ads. &lt;/p&gt;

&lt;p&gt;But data on gender is frequently collected and used, from targeted ads and demographic research to dating apps and medicine. &lt;/p&gt;

&lt;p&gt;If we don’t include and prioritize people from minoritized groups in our design and tech practices, we risk not only collecting incomplete or incorrect data, but also processing and using it in biased and harmful ways. &lt;/p&gt;

&lt;p&gt;Preventing this goes beyond just hiring more trans people or doing user tests with a diverse audience, though. &lt;/p&gt;

&lt;p&gt;While trans and non-binary people would probably have flagged the potential harm behind those features, they do need to be given trust, safety and support as well. &lt;/p&gt;

&lt;p&gt;Just recently, &lt;a href="https://www.wired.com/story/behind-paper-led-google-researchers-firing/" rel="noopener noreferrer"&gt;Google fired Timnit Gebru and Meg Mitchell from their ethical AI team&lt;/a&gt; because of a research paper critical of AI systems that process language. And meanwhile &lt;a href="https://www.vice.com/en/article/n7vy4m/amazon-is-paying-employees-to-quit-right-before-critical-union-vote" rel="noopener noreferrer"&gt;Amazon is paying its employees to quit&lt;/a&gt; as a way to block unionization efforts. &lt;/p&gt;

&lt;h2&gt;
  
  
  Lack of data, visibility and accountability.
&lt;/h2&gt;

&lt;p&gt;As The Markup’s story pointed out as well, it’s difficult to know which ads you’re missing out on because of your gender. While we often can get information about why we’re seeing a certain ad, we can’t ask “why am I not seeing this ad?” for ads we’re not seeing, making it hard to hold companies accountable. &lt;/p&gt;

&lt;p&gt;Similarly, if those companies analyze our data to understand what issues we’re facing on their platform but don’t have accurate data on non-binary people, they’ll easily ignore our needs. &lt;/p&gt;

&lt;p&gt;I’ve participated in countless of employee satisfaction surveys where non-binary wasn’t an option at all, meaning the answers of non-binary folks are miscategorized. When companies then use that data to analyze what they need to improve upon, the issues that are trans and non-binary specific are lost because there simply is no data for it. &lt;/p&gt;

&lt;p&gt;This is why data isn’t neutral or objective, but influenced by those who collect it, and later possibly further compromised by the biases of those that use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Preventable accidents.
&lt;/h2&gt;

&lt;p&gt;It’s easy to label these inequalities as “accidental” or “unintended side effects”. But how accidental or innocent is this really? This isn’t the first example of Google (and other tech companies) causing harm to minoritized people, and it won’t be the last one either.&lt;/p&gt;

&lt;p&gt;Even Google’s gender selection form at sign-up has received criticism for a long time (&lt;a href="https://fossheim.io/writing/posts/non-binary-design/" rel="noopener noreferrer"&gt;I wrote about it as well&lt;/a&gt;), and neither ethical design nor trans people are new concepts. &lt;/p&gt;

&lt;p&gt;Most trans and non-binary people are all but surprised that something like this could happen, given we constantly experience the consequences of trans-exclusionary design. &lt;/p&gt;

&lt;p&gt;As I touched upon earlier, to me this shows that either no trans or non-binary folks were involved or consulted on this (which is a lack of user research as well, aka bad design), or their concerns just weren’t listened to. &lt;/p&gt;

&lt;p&gt;We must, collectively, be better at including and protecting minoritized groups in our designs. After all, features and data don’t exist in a vacuum. Technology is so embedded into our society that even seemingly small features can cause real-world harm.&lt;/p&gt;

</description>
      <category>inclusion</category>
      <category>design</category>
    </item>
    <item>
      <title>What we can learn from Apple's dataviz accessibility</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Sun, 21 Feb 2021 17:47:42 +0000</pubDate>
      <link>https://forem.com/fossheim/what-we-can-learn-from-apple-s-dataviz-accessibility-6fa</link>
      <guid>https://forem.com/fossheim/what-we-can-learn-from-apple-s-dataviz-accessibility-6fa</guid>
      <description>&lt;p&gt;Originally posted on &lt;a href="https://fossheim.io/writing/posts/apple-health-dataviz-a11y/"&gt;fossheim.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Ever since getting the Apple Watch in November, I’ve been looking at my Apple Health &amp;amp; Activity data on a daily basis. After being disappointed by all the inaccessible graphs around the US Presidential Elections, I wanted to find out how Apple’s visualizations are handled by VoiceOver. &lt;/p&gt;

&lt;p&gt;They’re an inspiration to many when it comes to visual design, so I was especially wondering if we can use them as an example for accessible data visualizations as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Daily activity breakdown
&lt;/h2&gt;

&lt;p&gt;The health app has a page dedicated to activity, which visualizes movement, exercise and standing hours per day, week, month and year.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--TquwgxFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftkxiop7fzkosy5a43bv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TquwgxFD--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ftkxiop7fzkosy5a43bv.png" alt="apple health activity breakdown: moving, exercising and standing percentage in a donut chart, total for the day for each item, and hourly breakdown bar chart underneath"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this view, VoiceOver allows us to navigate through the graph by the hour. Each hour is a column, which includes the movement, exercise and standing values. While the labels on the axes aren’t read, that information does get added at the start of each of the columns' labels. &lt;/p&gt;

&lt;p&gt;All the data is communicated clearly with pretty straightforward navigation. One thing I personally found inconvenient was having to keep moving along hour by hour even when there’s no data. &lt;/p&gt;

&lt;p&gt;Video Transcript: &lt;a href="https://pastebin.com/raw/6puRSntf"&gt;https://pastebin.com/raw/6puRSntf&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For example, between 1AM and 9AM the values for moving, exercising and standing are usually always zero. &lt;/p&gt;

&lt;p&gt;But on the other hand Apple’s current solution is consistent: VoiceOver reads three values for each hour in the day, no matter what. &lt;/p&gt;

&lt;p&gt;Whether or not consecutive blocks with no activity should be grouped is more of a screen reader usability question, for which I recommend doing user tests with blind or visually impaired folks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hAapY8xx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64tso86xmag9isq8i6in.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hAapY8xx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/64tso86xmag9isq8i6in.png" alt="Apple watch with 3 donut charts for moving, standing and exercise, next to it an Apple Watch with the total daily activity written out, and last another Apple Watch with the daily activity broken down by hour"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Apple Watch activity breakdown provides a similar experience. The only difference is that the values aren’t grouped, and movement, exercise and standing are read as three different graphs. Each of them is broken down by the hour, and includes the timeslot and value in the label.&lt;/p&gt;

&lt;h2&gt;
  
  
  Activity rings
&lt;/h2&gt;

&lt;p&gt;The activity rings display how far you’ve gotten towards achieving your daily goals. VoiceOver pronounces the (visually hidden) label of each bar, followed by the percentage that’s visualized. &lt;/p&gt;

&lt;p&gt;In the Activity app, the rings seem to have a bug (or feature?!) that causes the activity to be read twice, but overall I feel like this one works quite well. &lt;/p&gt;

&lt;p&gt;Video Transcript: &lt;a href="https://pastebin.com/raw/3FT6r8pe"&gt;https://pastebin.com/raw/3FT6r8pe&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’m personally a big fan of visualizing the same data in different formats. Not everyone processes data the same way, so providing enough alternatives makes the data both more accessible and user friendly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---9ZzwCMQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mf2jc6d3018ww0dtf419.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---9ZzwCMQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mf2jc6d3018ww0dtf419.png" alt="Apple Activity app: small donut charts for activity every day of the week, large donut chart for current day, bar chart for the current day broken down by the hour"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ECG
&lt;/h2&gt;

&lt;p&gt;I was really curious about how they’d solve something like the ECG functionality on the Apple Watch with VoiceOver, since it continuously has to update the data. But the answer is actually very logical: just like physical heart rate monitors do it, by using beeping noises that change in tone and frequency. After the ECG is finished, the result gets displayed (and read by VoiceOver) as text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Blood oxygen
&lt;/h2&gt;

&lt;p&gt;Measuring the blood oxygen also relies on sounds rather than words. There’s a beep every second for the duration of the countdown, which starts speeding up during the last few seconds. Once the timer is done, the results get focused and are browsable using VoiceOver.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mdXtjLSr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i3v5206mpbhfwkzo0yln.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mdXtjLSr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i3v5206mpbhfwkzo0yln.png" alt="blood oxygen on iphone, blue and red lines animated in the middle, time left written under"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch faces
&lt;/h2&gt;

&lt;p&gt;One of the watch faces that I use a lot has several activity visualizations on it. It’s basically the same as the iPhone’s activity app, but much more compact. Unfortunately, it doesn’t always work very smoothly with VoiceOver.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--YfoPobla--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9nen23lln08odbyhiqn9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--YfoPobla--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9nen23lln08odbyhiqn9.png" alt="apple watchface: date on top, underneath activity rings and time, under that total activity stats and activity breakdown by hour"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The first time I tried using it, VoiceOver didn’t read the values of the activity rings and only said “Activity” with no further information. After opening the activity app and going back to the home screen, the values were read correctly. &lt;/p&gt;

&lt;p&gt;Similarly, there are some issues with the bar chart underneath as well. The values are grouped in four six-hour intervals, which is probably because swiping through 24 hours of data on a small surface isn’t necessarily a pleasant experience. The buttons and text on the screen are also focusable by clicking on them, which is expected behavior but also tends to make navigating with VoiceOver on a bit of a challenge. &lt;/p&gt;

&lt;p&gt;However, it’s never mentioned that these are 6-hour blocks, and VoiceOver only reads the “00”, “06”, “12” and “18” labels, which is a bit confusing.&lt;/p&gt;

&lt;p&gt;Video Transcript: &lt;a href="https://pastebin.com/raw/EiQN60XD"&gt;https://pastebin.com/raw/EiQN60XD&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The grouping also has a few bugs. In the example above, the standing hours that get read (2 hours, 2 hours, 6 hours, 1 hour) don’t match the visuals or text (9 dots or hours). And even though the total amount of burnt calories is 453 for the day, VoiceOver consistently reads 0 calories for each block.&lt;/p&gt;

&lt;h2&gt;
  
  
  Highlight cards
&lt;/h2&gt;

&lt;p&gt;One last thing I appreciate in the health app are the cards that show highlights of the data. Each graph that’s displayed in those has a short summary written on top, eg.  “During your last walk, your heart rate was 114–158 beats.” or “On average, you’re walking less this year compared to last year.”. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AohJB1N---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/60ng3xckhjamhty4hqlo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AohJB1N---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/60ng3xckhjamhty4hqlo.png" alt="summary card: heart rate recovery, in the 3 minutes after your last walk, your heart rate went down by 21 beats per minute, scatterplot underneath&amp;lt;br&amp;gt;
"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For me personally, the summary makes it quicker to get the information I need, since I don’t have to find and read labels or process any visuals at all. But it’s also a nice addition to the graphs when listening to them with VoiceOver. The goal of the graph is to get a quick summary or highlight of the data, which isn’t easily solved by swiping through a list of numbers.&lt;/p&gt;

&lt;p&gt;The graphs in the cards were read differently for different types of graphs as well, but seemed to follow a pattern: the graph wasn’t read when it served as an illustration of what was already described in the summary, but numbers bringing more detail to the summary were included.&lt;/p&gt;

&lt;h2&gt;
  
  
  Apple’s graphs set a good example, so what can we learn from them to make our own visualizations more screen reader accessible?
&lt;/h2&gt;

&lt;p&gt;Whether Apple’s solution would work well for our own graphs is entirely dependent on what we’re trying to visualize, who our visualizations are for and how they want to use them. In fact, I’m quite sure Apple can still improve their graphs as well. As we improve our products, we will always find new insights, and as technology changes we will also keep finding better technical solutions to the same problems. &lt;/p&gt;

&lt;p&gt;But that’s also why I think Apple’s visualizations are good to take an example of: they have a basic level of decent accessibility that can be used to get actual feedback. The data gets read by VoiceOver, and does so in a way that makes sense. Regardless of what can be improved, the graphs are both usable and testable. &lt;/p&gt;

&lt;p&gt;This is a big contrast with the visualizations around the US Presidential Elections I examined in November 2020, where almost nothing was accessible to screen reader users. The main feedback people will give about a graph that reads “Image” over and over again will be to at the very least add alt text. Not much can be said with regards to the data experience if the data isn’t even there. &lt;/p&gt;

&lt;p&gt;That’s why it’s important to have accessibility included from the start. The faster the data is accessible, the faster we can test how blind folks are using the graph and what would give them an even better experience. As long as we have that plan in place, we can start small and still end up with a good and accessible solution relatively quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources to get you started
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/dataviza11y/resources"&gt;Dataviz Accessibility Resources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fossheim.io/writing/posts/accessible-dataviz-d3-intro/"&gt;An introduction to accessible data visualizations with D3.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.a11ywithlindsey.com/blog/accessibility-d3-bar-charts"&gt;Accessibility in d3 Bar Charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.a11ywithlindsey.com/blog/accessibility-d3-donut-charts"&gt;Accessibility in d3 Donut Charts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/nightingale/writing-alt-text-for-data-visualization-2a218ef43f81"&gt;Writing Alt Text for Data Visualization&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🎙 I also give talks about dataviz accessibility. My &lt;a href="https://www.outlierconf.com/"&gt;Outlier&lt;/a&gt; talk (together with &lt;a href="https://twitter.com/FrankElavsky"&gt;Frank Elavsky&lt;/a&gt; and &lt;a href="https://twitter.com/LareneLg"&gt;Larene Le Gassick&lt;/a&gt;) will be published in the upcoming weeks, and I’ll also be giving an introduction to design accessible graphs at &lt;a href="https://www.deque.com/axe-con/sessions/accessible-data-visualizations-101/"&gt;axe-con&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>a11y</category>
      <category>datascience</category>
      <category>design</category>
      <category>ios</category>
    </item>
    <item>
      <title>I launched a directory with ethical design resources</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Sun, 27 Dec 2020 23:40:11 +0000</pubDate>
      <link>https://forem.com/fossheim/i-launched-a-directory-with-ethical-design-resources-51n8</link>
      <guid>https://forem.com/fossheim/i-launched-a-directory-with-ethical-design-resources-51n8</guid>
      <description>&lt;p&gt;Tech is all around us, developing fast, and not going away. Which is why ethics should be a part of the design and engineering curriculum, and a focus for everyone working in the industry.&lt;/p&gt;

&lt;p&gt;Earlier this year I wrote about what it's like &lt;a href="https://fossheim.io/writing/posts/non-binary-design/"&gt;navigating the internet as a non-binary person&lt;/a&gt; and &lt;a href="https://fossheim.io/writing/posts/ai-bias-genderify/"&gt;the harm transphobic AI can cause&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There were also stories about &lt;a href="https://www.theverge.com/2020/2/7/21128236/gender-app-giggle-women-ai-screen-trans-social"&gt;social media apps using facial recognition failing trans people&lt;/a&gt;, &lt;a href="https://www.wired.com/story/algorithm-set-students-grades-altered-futures/"&gt;algorithms keeping students out of college&lt;/a&gt;, &lt;a href="https://twitter.com/tg_bomze/status/1274098682284163072?lang=en"&gt;software that can depixelate faces&lt;/a&gt;, &lt;a href="https://thegradient.pub/how-the-police-use-ai-to-track-and-identify-you/"&gt;surveillance AI&lt;/a&gt;, fake news around COVID and the US elections, and countless more examples of tech failing the people.&lt;/p&gt;

&lt;p&gt;This is nothing new, and should not be surprising. Tech isn't neutral. It's made by people and consumed by people. People write algorithms, collect training data, design websites, write code and prioritize features.&lt;/p&gt;

&lt;p&gt;Countless of books, articles and even documentaries have been made about the issue, and there are plenty of resources out there that can educate us on the issues the tech industry is facing and can guide us to do better.&lt;/p&gt;

&lt;p&gt;So I decided to build a directory that lists them: &lt;a href="https://ethicaldesign.guide"&gt;ethicaldesign.guide&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--U5vR0s9W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xxqfxjuoinoy0qw77670.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--U5vR0s9W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xxqfxjuoinoy0qw77670.png" alt="Screenshot of ethicaldesign.guide: a description on top, recommended topics on the left, and resources on the right"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a growing directory of tools, articles, books, podcasts, and other learning resources to guide us in creating more ethical and more inclusive products. I'll be editing it continuously with new resources (&lt;a href="https://ethicaldesign.guide/submit/"&gt;and anyone can suggest a resource&lt;/a&gt;, so keep an eye out on the page or subscribe to the &lt;a href="https://www.getrevue.co/profile/ethicaldesignguide"&gt;mailing list&lt;/a&gt; to receive a monthly update on new resources and interesting reading material.&lt;/p&gt;

&lt;p&gt;Originally posted on &lt;a href="https://fossheim.io/writing/posts/ethical-design-guide/"&gt;fossheim.io&lt;/a&gt;&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>ethics</category>
      <category>design</category>
      <category>sideprojects</category>
    </item>
    <item>
      <title>How (not) to make accessible data visualizations, illustrated by the US presidential election.</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Mon, 23 Nov 2020 17:42:21 +0000</pubDate>
      <link>https://forem.com/fossheim/how-not-to-make-accessible-data-visualizations-illustrated-by-the-us-presidential-election-1co8</link>
      <guid>https://forem.com/fossheim/how-not-to-make-accessible-data-visualizations-illustrated-by-the-us-presidential-election-1co8</guid>
      <description>&lt;p&gt;&lt;a href="https://fossheim.io/writing/posts/accessible-dataviz-us-elections/" rel="noopener noreferrer"&gt;Originally published @ fossheim.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the days around the 2020 US presidential elections a lot of people have been following predictions, results and exit polls quite closely. The majority of news sources published plenty of visualizations, but very few of them in an accessible format.&lt;/p&gt;

&lt;p&gt;Let’s take a look at some important aspects of accessible data visualizations, based on what &lt;a href="https://www.nytimes.com/interactive/2020/11/03/us/elections/results-president.html?action=click&amp;amp;module=Spotlight&amp;amp;pgtype=Homepage" rel="noopener noreferrer"&gt;The New York Times&lt;/a&gt;, &lt;a href="https://edition.cnn.com/election/2020/results/president?iid=politics_election_national_map#mapfilter=keyrace#mapmode=call" rel="noopener noreferrer"&gt;CNN&lt;/a&gt;, &lt;a href="https://projects.fivethirtyeight.com/2020-election-forecast/?cid=rrpromo" rel="noopener noreferrer"&gt;FiveThirtyEight&lt;/a&gt;, &lt;a href="https://www.theguardian.com/us-news/live/2020/nov/04/us-election-2020-votes-live-updates-donald-trump-joe-biden-latest-presidential-news-updates" rel="noopener noreferrer"&gt;The Guardian&lt;/a&gt; and &lt;a href="https://www.foxnews.com/elections/2020/general-results" rel="noopener noreferrer"&gt;Fox News&lt;/a&gt; are doing right and wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Screen reader support
&lt;/h2&gt;

&lt;p&gt;The main visualizations across all news sources are the electoral college votes for each candidate, and a map showing the results per state.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F96axvk0ub2frw1jt8lvs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F96axvk0ub2frw1jt8lvs.png" alt="Map of the US with red and blue states highlighted, and a red and blue progress bar on top. From The New York Times"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most of these graphs are built either using the &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; element or SVGs (Scalable Vector Graphics), which both require a bit of extra work to be made accessible. When not keeping accessibility in mind, &lt;a href="https://twitter.com/liatrisbian/status/1324427612723253250" rel="noopener noreferrer"&gt;things like this might happen&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is an example from The Guardian (using Safari on iPhone), where VoiceOver reads every state on the map as “image”. &lt;/p&gt;

&lt;h3&gt;
  
  
  Read all the data
&lt;/h3&gt;

&lt;p&gt;One option to improve a graph like The Guardian’s is to add the state name and results to each focusable element of the map by either &lt;a href="https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/" rel="noopener noreferrer"&gt;adding visually hidden labels&lt;/a&gt; or using the aria-label property (or alt tags when dealing with images). &lt;/p&gt;

&lt;p&gt;The New York Times takes this approach with their graphs showing the percentage of votes each candidate got, but not in an accessible way. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5xeghbnfb79nb45zslib.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F5xeghbnfb79nb45zslib.png" alt="Line graphs with the percentage of votes each candidate has in different states"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;VoiceOver reads through the labels on the x and y axis, and the percentage of votes, but not who those votes belong to &lt;a href="https://pastebin.com/raw/DyLfwnNt" rel="noopener noreferrer"&gt;full audio transcript&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Adding an extra "Biden" and "Trump" (or "Democrat" and "Republican") label to the numbers would solve this issue, and also add more visual context. &lt;/p&gt;

&lt;p&gt;Fox News has a similar issue on their electoral college votes progress bars. The main progress bar for each candidate is built by grouping smaller bars representing the states they won. &lt;/p&gt;

&lt;p&gt;Visually, it’s rather easy to distinguish between the two candidates’ votes. However, VoiceOver does not make any difference between them at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7h210ejbi439ymyf9fd5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7h210ejbi439ymyf9fd5.png" alt="Fox News visualization: two bars, one in blue for Biden and a much shorter one in red for Trump, representing the amount of electoral college votes for each candidate"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Instead, the only information it reads is one long sequence of numbers along the lines of "3 votes, 14 votes, 7 votes," etc (&lt;a href="https://pastebin.com/raw/YdtDPhSP" rel="noopener noreferrer"&gt;full audio transcript&lt;/a&gt;). So the context of how many votes belong to Biden, and how many to Trump, gets lost entirely.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bars"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bar dem state-va"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        13 votes
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bar-tooltip"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"display: none;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; 
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Virginia&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"value"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;13&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"party"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Declared Democrat&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's not much they would need to do to give the "13 votes" some more context for asssistive technology, most of the code is already there. Removing the &lt;code&gt;display: none;&lt;/code&gt; property and instead visually hiding the tooltip would allow screen readers to read "Virgina, 13, Declared Democrat". &lt;/p&gt;

&lt;h3&gt;
  
  
  Link to an accessible data table
&lt;/h3&gt;

&lt;p&gt;Another option for the Fox News graph is to only read the total number of votes and link to an accessible table for the breakdown instead. That link could be provided in a caption, description or alt text, depending on the format of the graph. This is similar to how complex images are treated.&lt;/p&gt;

&lt;p&gt;But it's also important to remember that while a table might make the data available for people using screen readers, it doesn't necessarily provide a good experience. The user still has to the table, and also misses out on context a graph provides. &lt;/p&gt;

&lt;h3&gt;
  
  
  Give data a human voice by using titles and descriptions
&lt;/h3&gt;

&lt;p&gt;In their election forecast, FiveThirtyEight takes a different approach from most other sources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbv5hyl6vm3nf135t5w0l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbv5hyl6vm3nf135t5w0l.png" alt="Two FiveThirtyEight election forecast simulations: Electoral college vote distribution for Trump, who wins in 10.4% of simulated outcomes. And Electoral college vote distribution for Biden, who wins in 89.2% of simulated outcomes."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s take the outcome simulations as an example. These are split into two different visualizations, a red one for Trump and a blue one for Biden.&lt;/p&gt;

&lt;p&gt;The x-axis shows the amount of electoral votes, and the y-axis the amount of simulations in which the candidate gets that amount of votes. The length and position of the bars tells us which candidate is more likely to win according to their simulations. There are a lot of blue bars on the right side of Biden’s graph, which means he won in most of their simulations. &lt;/p&gt;

&lt;p&gt;Since the exact numbers aren’t as important here, VoiceOver doesn’t read or link to them and instead reads a summary of what’s displayed. In this case, it says Trump won in 10% of the simulations and Biden won in 89% of them &lt;a href="https://pastebin.com/raw/51fueMYS" rel="noopener noreferrer"&gt;full transcript&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"svg"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"title-biden desc-biden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"title-biden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;desc&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"desc-biden"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Electoral college vote distribution for Biden,
        who wins in 89.2% of simulated outcomes.
    &lt;span class="nt"&gt;&amp;lt;/desc&amp;gt;&lt;/span&gt;
    ...
&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This method works really well for FiveThirtyEight’s type of data. It doesn’t matter in exactly how many simulations Biden got 300 electoral votes, what matters is that in the majority of simulations he won the presidency. &lt;/p&gt;

&lt;p&gt;Unfortunately, the graphs aren’t keyboard accessible, and sighted people still get access to more details than those using assistive technology. &lt;/p&gt;

&lt;p&gt;And while on Mac using VoiceOver their solution provides a much better experience than most other election visualizations, it's also not fully accessible across platforms and browsers. When for example using Edge on Windows, Narrator does not read the title and description of the graph. &lt;/p&gt;

&lt;h2&gt;
  
  
  Keyboard navigation
&lt;/h2&gt;

&lt;p&gt;When graphs are interactive, keyboard navigation should be taken into consideration as well. &lt;/p&gt;

&lt;p&gt;For example, some graphs show more information or filter data when clicking or hovering. It should also be possible to achieve this without a mouse or trackpad, using only the keyboard. Almost none of the election graphs provided this functionality.&lt;/p&gt;

&lt;p&gt;When using &lt;a href="https://t.co/zlYi9K6jfm" rel="noopener noreferrer"&gt;Chrome on Mac with VoiceOver&lt;/a&gt;, the electoral map from ABC did make it possible to navigate through the different states using the arrow keys. &lt;/p&gt;

&lt;p&gt;But it’s also important to note that this isn’t something that works across all screen readers or browsers. Safari and FireFox on Mac had a less optimal experience and on iPhone the navigation didn’t work at all.&lt;/p&gt;

&lt;p&gt;And as &lt;a href="https://twitter.com/FrankElavsky?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1324471343002255361%7Ctwgr%5E&amp;amp;ref_url=https%3A%2F%2Fpublish.twitter.com%2F%3Fquery%3Dhttps3A2F2Ftwitter.com2FFrankElavsky2Fstatus2F1324471343002255361widget%3DTweet" rel="noopener noreferrer"&gt;Frank Elavsky&lt;/a&gt; explains really well on &lt;a href="https://twitter.com/FrankElavsky/status/1324475334247395328" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, there are a lot of problems with both VoiceOver and keyboard navigation with NVDA (Windows) on Chrome and IE.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accessible colors
&lt;/h2&gt;

&lt;p&gt;Color is another important aspect to keep in mind when designing data visualizations. Not enough color contrast can make visualizations and text hard to read, and only relying on color can make the experience for color blind visitors very confusing.&lt;/p&gt;

&lt;p&gt;This is a simulation (Monochromacy/Achromatopsia) of The New York Times their main map. Suddenly it's no longer possible to tell which states are Democrat (originally blue) or Republican (originally red):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl94b0du1veit99lzrwgy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fl94b0du1veit99lzrwgy.png" alt="imulation of The New York Times' election map for full color blindness: everything is the exact same gray, it's no longer possible to tell which state is Democrat or Republican"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Patterns can be used in addition to colors, although depending on the type of graph and amount of different colors that can become quite distracting.&lt;/p&gt;

&lt;p&gt;Very busy visualizations aren’t accessible either, so an alternative is to pick colors with high contrast, or adding additional text labels with context. For example, this map could also have either the candidate or party names listed in addition to the state name.&lt;/p&gt;

&lt;h3&gt;
  
  
  Brightness
&lt;/h3&gt;

&lt;p&gt;Another important factor to keep in mind is the brightness or intensity of the color. Very bright colors affect readability, and also make it much harder to focus. For some users it can also &lt;a href="https://ukhomeoffice.github.io/accessibility-posters/autism" rel="noopener noreferrer"&gt;cause anxiety or pain&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;CNN uses a very bright variation of red and blue throughout their election results page. In addition, they also use a relatively bright yellow to label the battleground states in some of their tables.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdylluoihudc9gwulzgta.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdylluoihudc9gwulzgta.png" alt="CNN's election results: lots of cards with bright blue, bright red and bright yellow colors. It contains the state name, label (battleground state), number of electoral votes, projected winner, votes per candidate, percentage of votes per candidate, follow button, read more button, last updated time, and animated next refresh time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Make data easy to find and understand
&lt;/h2&gt;

&lt;p&gt;Accessibility and usability go closely together. Making data visualizations accessible isn’t just about meeting the technical requirements or making them compatible with assistive technology. It also means making them easy to understand for everyone. Everyone consumes information differently, which is something important to keep in mind.&lt;/p&gt;

&lt;p&gt;Adding explanations or conclusions in clear language can be beneficial for people with anxiety, while dyslexic people might prefer visuals over numbers.&lt;/p&gt;

&lt;p&gt;FiveThirtyEight’s election forecast does a pretty good job at explaining their graphs. Each graph has a title and description with context, and most of them have a side note with more information about the data or type of visualization.&lt;/p&gt;

&lt;p&gt;Two FiveThirtyEight election forecast simulations: Electoral college vote distribution for Trump, who wins in 10.4% of simulated outcomes. And Electoral college vote distribution for Biden, who wins in 89.2% of simulated outcomes.&lt;/p&gt;

&lt;p&gt;On top of that, the graphs also have more labels and explanations directly where relevant. The outcome simulations don’t just show the numbers, they also have labels that indicate which candidate is more likely to win. They also highlight some important points, for example the 270 electoral votes threshold.&lt;/p&gt;

&lt;p&gt;The Guardian has cards for each of the key states to watch that contain a map, the vote count and breakdown, and an explanation for why the state is important to the election. They were one of the few sources that had an overview like that per state without hiding it behind hover functionality. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhpwkvml9867k0nngd3pr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fhpwkvml9867k0nngd3pr.png" alt="The Guardian's election results: grid of cards. Each card contains: state name, US map with state highlighted, electoral college votes, when the polls closed, how much is left to count, votes for each candidate, percentage of votes for each candidate, and why the results of that state matter"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the same time, these cards do contain a lot of information and text, and they all end up looking more or less the same. While the colors are not as bright as those in CNN’s graphs, the view still becomes a bit too cluttered and can be painful on the eyes.&lt;/p&gt;

&lt;p&gt;The electoral college votes play a big role in the election. Getting to that information easily is crucial, especially as more results come in and people start calculating which scenarios can still happen. The Guardian’s cards do provide that information, although it gets a bit lost in between all the other content. &lt;/p&gt;

&lt;p&gt;The Guardian, and CNN, have the same information available when hovering over a state on their map as well, although not in a keyboard or screen reader accessible way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fezgztppbteef3rlo58xg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fezgztppbteef3rlo58xg.png" alt="CNN's election map. The mouse is hovering over Nevada, and a pop-up shows a card with Nevada's voting info, containing the same info as in their card overview"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The New York Times has a separate map for the electoral college votes, where each state is represented by a number of circles representing the amount of votes. While it visualizes the distribution of votes more precisely than a regular map, it doesn’t write out the number anywhere in an accessible way.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faztj43td8mdlbrxeiply.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faztj43td8mdlbrxeiply.png" alt="The New York Times' election map. The electoral college view is shown, where each state is made out of the same amount of circles as it has electoral college votes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The only map that has the numbers available without needing to hover is Fox News. They write the number of votes underneath the state name. For the states that are too small, they write the number of votes next to the map. This is an easier way of getting the exact number than the other news sites provided, and could even be combined with The New York Times map. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyfervsqkmlvrm9ocznqw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fyfervsqkmlvrm9ocznqw.png" alt="Fox News' election map. Each state is either red or blue, and contains the state code and number of electoral votes available. For states whose surface area is too small for a label, the state and number of electoral votes are written next to the map"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, the map from Fox news isn’t screen reader accessible either. VoiceOver reads the entire map as an image without alt text, and then just moves on to the next section. And while they kept the map relatively clean, there's still a lot going on visually &lt;em&gt;(that white text on the bright red background, for example)&lt;/em&gt;, which in combination with the legend not being easy to spot can feel overwhelming for some as well. &lt;/p&gt;

&lt;h2&gt;
  
  
  How to continue from here
&lt;/h2&gt;

&lt;p&gt;If you’d like to learn more about making graphs like these accessible, I suggest to start by reading the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/nightingale/data-visualization-accessibility-where-are-we-now-and-whats-next-b2c9eeac4e8b" rel="noopener noreferrer"&gt;Data Visualization Accessibility: Where Are We Now, and What’s Next?&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://towardsdatascience.com/data-visualization-and-accessibility-three-recommended-reads-and-top-tips-9c5e862b464e" rel="noopener noreferrer"&gt;Data Visualization and Accessibility: Three Recommended Reads and Top Tips&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.deque.com/blog/creating-accessible-svgs/" rel="noopener noreferrer"&gt;Creating accessible SVGs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.visualisingdata.com/2019/08/five-ways-to-design-for-red-green-colour-blindness/" rel="noopener noreferrer"&gt;Five Ways To Design For Red-Green Colour-Blindness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://accessibility.digital.gov/visual-design/data-visualizations/" rel="noopener noreferrer"&gt;Data visualization accessibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://uxdesign.cc/making-data-visualization-accessible-a-case-study-e5fb41ac62ad" rel="noopener noreferrer"&gt;Making data visualization accessible: a case study&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://guides.library.cornell.edu/c.php?g=898087&amp;amp;p=6489216" rel="noopener noreferrer"&gt;Data Visualization: Methods, Tools, Resources: Accessibility and Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fossheim.io/writing/posts/accessible-dataviz-design/" rel="noopener noreferrer"&gt;An Intro To Designing Accessible Data Visualizations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fossheim.io/writing/posts/accessible-dataviz-d3-intro/" rel="noopener noreferrer"&gt;An Introduction To Accessible Data Visualizations With D3.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://keen.io/blog/accessibility-in-data-vis/" rel="noopener noreferrer"&gt;Accessibility Considerations In Data Visualization Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.sarasoueidan.com/blog/accessible-data-charts-for-khan-academy-2018-annual-report/" rel="noopener noreferrer"&gt;Case Study: Implementing Accessible Data Charts for the Khan Academy 2018 Annual Report&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/nightingale/accessibility-is-at-the-heart-of-data-visualization-64a38d6c505b" rel="noopener noreferrer"&gt;Why Accessibility Is at the Heart of Data Visualization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simplyaccessible.com/article/7-solutions-svgs/" rel="noopener noreferrer"&gt;7 solutions for creating more accessible SVGs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or if you want to learn more about accessibility in general:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/standards-guidelines/wcag/" rel="noopener noreferrer"&gt;WCAG (Web Content Accessibility Guidelines)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.a11yproject.com/" rel="noopener noreferrer"&gt;The Accessibility Project&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.solidstart.info/" rel="noopener noreferrer"&gt;Solid Start&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://webaim.org/projects/million/" rel="noopener noreferrer"&gt;WebAIM Million&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ashleemboyer.com/three-starting-points-for-making-accessible-digital-content" rel="noopener noreferrer"&gt;Three Starting Points for Making Accessible Digital Content&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.a11ywithlindsey.com/blog/beginning-demystify-aria" rel="noopener noreferrer"&gt;Beginning to Demystify ARIA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://simplyaccessible.com/article/listening-web-part-three-working-screen-readers/" rel="noopener noreferrer"&gt;Listening to the web, part three: working with screen readers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.a11ymatters.com/" rel="noopener noreferrer"&gt;Accessibility Matters&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you like my work, consider following me on &lt;a href="https://twitter.com/liatrisbian" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;, &lt;a href="https://www.patreon.com/fossheim" rel="noopener noreferrer"&gt;becoming a patron&lt;/a&gt; or &lt;a href="https://www.buymeacoffee.com/fossheim" rel="noopener noreferrer"&gt;buying me a coffee&lt;/a&gt;. I also occasionally &lt;a href="https://www.twitch.tv/sarahfossheim" rel="noopener noreferrer"&gt;stream on Twitch&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>design</category>
      <category>html</category>
      <category>datascience</category>
    </item>
    <item>
      <title>🎹🎵 I made a new HTML &amp; CSS only drawing 🎵🎹</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Sat, 22 Aug 2020 00:38:42 +0000</pubDate>
      <link>https://forem.com/fossheim/i-made-a-new-html-css-only-drawing-3h58</link>
      <guid>https://forem.com/fossheim/i-made-a-new-html-css-only-drawing-3h58</guid>
      <description>&lt;p&gt;Photorealistic illustration of a &lt;a href="https://i.pinimg.com/564x/a2/02/a4/a202a4ab33dff87b53ba54db33518b78.jpg"&gt;Roland MC-500&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wrote a few tutorials on how to create art like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://fossheim.io/writing/posts/css-polaroid-camera/"&gt;How to make a Polaroid camera in CSS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://fossheim.io/writing/posts/css-macintosh/"&gt;How to recreate a Macintosh in CSS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/fossheim/embed/BaKQGZJ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.patreon.com/fossheim"&gt;Patreon&lt;/a&gt; | &lt;a href="https://www.buymeacoffee.com/fossheim"&gt;Buy me a coffee&lt;/a&gt; | &lt;a href="https://fossheim.io/writing/"&gt;Blog&lt;/a&gt; | &lt;a href="https://twitter.com/liatrisbian"&gt;Twitter&lt;/a&gt; | &lt;a href="https://codepen.io/collection/nwzQJq"&gt;CodePen&lt;/a&gt; | &lt;a href="https://github.com/sarahfossheim/art.css"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>codepen</category>
      <category>css</category>
      <category>html</category>
    </item>
    <item>
      <title>Thoughts on Genderify, gender discrimination, transphobia, and (un)ethical AI.</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Wed, 29 Jul 2020 15:53:19 +0000</pubDate>
      <link>https://forem.com/fossheim/thoughts-on-genderify-gender-discrimination-transphobia-and-un-ethical-ai-168g</link>
      <guid>https://forem.com/fossheim/thoughts-on-genderify-gender-discrimination-transphobia-and-un-ethical-ai-168g</guid>
      <description>&lt;p&gt;Earlier this week Genderify, an AI-driven platform that “identifies” users’ gender based on their name, launched and listed themselves on ProductHunt. They received &lt;strong&gt;a lot&lt;/strong&gt; of very valid criticism both on ProductHunt and other social media, but I think they’re worthy of some more criticism from me on here as well.&lt;/p&gt;

&lt;p&gt;Let’s start by taking a closer look at what Genderify really does. This is what they state on their ProductHunt page:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Genderify is an AI-based platform that instantly identifies the person’s gender by their name, username, or email. Our system can check an unlimited number of names, usernames, and emails to determine even the false ones and most incomprehensible combinations.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In practice, this means that you can enter any name, username or email address, and their platform will return two numbers: how confident they are that the person is male or female.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O_e1VSZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ey8wojnr5om0slx755wy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O_e1VSZs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/ey8wojnr5om0slx755wy.png" alt="Search field of Genderify's website showing we searched for the name Riley. The results show two percentages: 4% likely to be male, 96% likely to be female"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They make those predictions using data they obtained from publicly available governmental sources, and information from social networks.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bKT94QIz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tz04p8c969kjlkjimq00.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bKT94QIz--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/tz04p8c969kjlkjimq00.png" alt="Screenshot of Genderify'ss Q&amp;amp;A page. The question is how they collect data. They answer the following: We use integrated multi-source data to deliver the most accurate results. We combine the data from publicly available governmental sources with the information obtained from the social networks to ensure the best possible matches. Each name is added to our database by verifying the data obtained from different sources."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;They’re not clear how they obtained this information though, or which social networks they’re talking about, so it’s hard to go into detail about their training data, but they definitely did start with a biased dataset, even &lt;a href="https://twitter.com/genderify/status/1288241503488204800"&gt;admitting so themselves on Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Ndg91K0m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/59ofoa2sbm1apozdz3kh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ndg91K0m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/59ofoa2sbm1apozdz3kh.png" alt="Tweet by Genderify that says the following: Thanks for the feedback, since AI is trained on existing data, this is an excellent example to show bias is in the data available around us."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most surveys, sign-up forms and government data don’t include genders outside of the binary. As I talked about in &lt;a href="https://fossheim.io/writing/posts/non-binary-design/"&gt;a previous article&lt;/a&gt;, the majority of forms only offer two options: male or female, entirely erasing non-binary people from their training data.&lt;/p&gt;

&lt;p&gt;Then there’s also the issue that whether names are considered &lt;em&gt;“male”&lt;/em&gt;, &lt;em&gt;“female”&lt;/em&gt; or &lt;em&gt;“neutral”&lt;/em&gt; is dependent on country, culture and language. It’s unclear how those are all represented in the datasets they used, but there’s a good chance that it’s heavily skewed towards the American perspective, if that’s where the majority of their data is collected.&lt;/p&gt;

&lt;p&gt;So that biased, incorrect and incomplete dataset is what will decide which gender a user has based on their name or email address. What could possibly go wrong?&lt;/p&gt;

&lt;p&gt;As of now, their AI only outputs numbers for two options, male and female. By doing so they not only erase, but also misgender, everyone outside of the gender binary.&lt;/p&gt;

&lt;p&gt;A non-binary person will be given a male or female label based on their name. Even if they have a name that to most people in their culture is considered neutral, the system will still say they’re “most likely male” or “most likely female”.&lt;/p&gt;

&lt;p&gt;This is very similar to giving people only two options to choose from on a form, which is already bad, with the difference being that an automated guess takes all choice away from the user and just assumes gender, making Genderify even worse.&lt;/p&gt;

&lt;p&gt;Being misgendered is harmful enough already as it is, we don’t need tech companies to provide it as a service.&lt;/p&gt;

&lt;p&gt;They are consistent though, as their predictions are wildly inaccurate for everyone, and don’t just affect trans and non-binary people. Some &lt;a href="https://twitter.com/seldo/status/1288151563588919297"&gt;men got classified as women&lt;/a&gt;, &lt;a href="https://twitter.com/leamiserables/status/1288225862119313409"&gt;Hillary Clinton was assumed to be a man&lt;/a&gt;, &lt;a href="https://twitter.com/schock/status/1288241823543169030"&gt;so was Oprah Winfrey&lt;/a&gt;, &lt;a href="https://twitter.com/liatrisbian/status/1288184469082517504"&gt;trash is considered male&lt;/a&gt; and &lt;a href="https://twitter.com/downziggurat/status/1288240128909905920"&gt;male is considered female&lt;/a&gt;. I also tried to run the same name &lt;em&gt;(Mathilde)&lt;/em&gt; once by itself, and once with a last name included &lt;em&gt;(Mathilde Fossheim)&lt;/em&gt;. Mathilde was labeled as a woman (85%), Mathilde Fossheim was labeled as a man (77%).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iQuFWNXW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8ckrfu7cki3x92hyzw7u.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iQuFWNXW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/8ckrfu7cki3x92hyzw7u.png" alt="Screenshot of the results for Mathilde (85% chance of being a woman) versus Mathilde Fossheim (77% chance of being a man)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And as was pointed out by several Twitter users, they give very biased and sexists results for personality traits and professions as well. &lt;a href="https://twitter.com/schock/status/1288243807369256960"&gt;Adding a Dr. title to someone’s name instantly changes their assumed gender from female to male&lt;/a&gt;, &lt;a href="https://twitter.com/_alialkhatib/status/1288179135211114497"&gt;nurse is labeled as female while doctor is labeled as male&lt;/a&gt;, &lt;a href="https://twitter.com/RWerpachowski/status/1288232561592991750"&gt;wise is male and pretty is female&lt;/a&gt;, and the list goes on.&lt;/p&gt;

&lt;p&gt;It’s not particularly clear what they expect people to use this API for, since in general it’s cheaper, easier and more accurate to just ask people their gender (or even better, to not collect any gender information at all).&lt;/p&gt;

&lt;p&gt;Some are suggesting their service could mainly be “useful” for automating marketing and advertising, but there focusing on gender can again lead to reinforcing stereotypes.&lt;/p&gt;

&lt;p&gt;But nothing is restricting their software from being used for other purposes. They praise themselves for having an easy to use API, starting from as low as $10 after the free trial expires. This means anyone can set it up and run an analysis on any dataset they have access to.&lt;/p&gt;

&lt;p&gt;In those instances, being mislabeled by the system could for example mean being addressed with the wrong pronouns, losing access to information (&lt;a href="https://www.theverge.com/2020/2/7/21128236/gender-app-giggle-women-ai-screen-trans-social"&gt;think of the “women only” app that used facial recognition to determine whether you were allowed access&lt;/a&gt;), or facing discrimination based on assumed gender.&lt;/p&gt;

&lt;p&gt;They now also provide the option to correct them if they mislabeled anyone, saying they’re &lt;em&gt;“going to improve [their] gender detection algorithms for the LGBTQ+ community”.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--sRmIhUPd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pyij10xlzeew4sznbwoi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--sRmIhUPd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pyij10xlzeew4sznbwoi.png" alt="Screenshot of Genderify's new hompage, where after getting the results for a name there's an extra input box saying: Don't agree with the results, suggest yours. In the screenshot non-binary is entered as a correction for the name Sarah"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This means that at some point they could even try to predict whether someone is more likely to be non-binary or trans based on their name. Given the harassment trans people already face (&lt;a href="https://www.lgbtqnation.com/2020/07/trump-supporter-starts-kill-transgenders-chant-rally/"&gt;just earlier a Trump rally chanted to “Kill transgenders!”&lt;/a&gt;), providing free software to anyone that wants to try and identify them is outright dangerous.&lt;/p&gt;

&lt;p&gt;Products like Genderify are harmful. They’re built on top of biased and inaccurate data, by people who seem to have no interest in risk management or the societal impact of their product, and released for basically no cost into the open for anyone to use.&lt;/p&gt;

&lt;p&gt;I talked a lot about Genderify in this article, but the same criticism goes for a lot of other tech products. In 2017, researchers tried to &lt;a href="https://medium.com/@blaisea/do-algorithms-reveal-sexual-orientation-or-just-expose-our-stereotypes-d998fafdf477"&gt;build an AI that based on pictures predicts if someone’s gay&lt;/a&gt;, and just as recent as June 2020 a &lt;a href="https://twitter.com/tg_bomze/status/1274098682284163072?lang=en"&gt;tool to depixelate faces was released&lt;/a&gt;. Then there’s also the examples of &lt;a href="https://www.propublica.org/article/machine-bias-risk-assessments-in-criminal-sentencing"&gt;law enforcement using racist AI&lt;/a&gt;, &lt;a href="https://venturebeat.com/2020/06/12/researchers-find-racial-discrimination-in-dynamic-pricing-algorithms-used-by-uber-lyft-and-others/"&gt;racist AI being used for dynamic pricing&lt;/a&gt; and &lt;a href="https://www.academia.edu/1975319/Missed_Connections_What_Search_Engines_Say_About_Women"&gt;racist search engines&lt;/a&gt;, and the list goes on.&lt;/p&gt;

&lt;p&gt;We really need to do better as an industry, and make ethics and risk management a larger priority. We can’t keep building unethical, discriminatory, racist, sexist, homophobic and transphobic products. If it’s not ethical, inclusive and accessible, it’s not innovation.&lt;/p&gt;

&lt;p&gt;More reading material:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.goodreads.com/book/show/34762552-algorithms-of-oppression"&gt;Algorithms of oppression&lt;/a&gt; (book)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goodreads.com/book/show/34964830-automating-inequality"&gt;Automating inequality&lt;/a&gt; (book)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goodreads.com/book/show/42527493-race-after-technology"&gt;Race after technology&lt;/a&gt; (book)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goodreads.com/book/show/28186015-weapons-of-math-destruction?ac=1&amp;amp;from_search=true&amp;amp;qid=dN1hL0r66S&amp;amp;rank=1"&gt;Weapons of math destruction&lt;/a&gt; (book)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.goodreads.com/book/show/38212110-technically-wrong"&gt;Technically wrong&lt;/a&gt; (book)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.wired.com/story/how-surveillance-reinforced-racism/"&gt;How surveillance has always reinforced racism&lt;/a&gt; (article)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.coursera.org/learn/data-science-ethics"&gt;Data science ethics&lt;/a&gt; (course)&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://tarotcardsoftech.artefactgroup.com/"&gt;Tarot cards of tech&lt;/a&gt; (tool)&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Edit 1&lt;/em&gt;: At the time of writing more issues related to Genderify popped up. For example, &lt;a href="https://twitter.com/MarieChatfield/status/1288154244047163392"&gt;they show a list of live requests&lt;/a&gt; (including full names and gender prediction) on the front page of their website, making it disappointing with regards to privacy as well.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T-OI9X6z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vb2lhbs88sgvbvhxupkd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T-OI9X6z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/vb2lhbs88sgvbvhxupkd.png" alt="Screenshot of a table on Genderify's website showing all the recent searches, including name (firsr column), male percentage (second column) and female percentage (third column)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit 2&lt;/em&gt;: A bit more research into their company also showed that they’re owned by &lt;a href="https://smartclick.ai/"&gt;SmartClick&lt;/a&gt;, an Armenian company creating AI solutions for a wide range of industries, &lt;a href="https://smartclick.ai/industries/"&gt;creating products that predict tax fraud&lt;/a&gt; and &lt;a href="https://twitter.com/GameDadMatt/status/1288355360806379521"&gt;face obstruction detection systems&lt;/a&gt;. &lt;a href="https://twitter.com/GameDadMatt"&gt;@GameDadMatt&lt;/a&gt; on Twitter &lt;a href="https://twitter.com/GameDadMatt/status/1288355353692758016"&gt;posted his research&lt;/a&gt; into their company as well.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Edit 3&lt;/em&gt;: Genderify is meanwhile removed from Twitter, ProductHunt and SmartClick’s LinkedIn profile, and the website seems to be taken offline as well. No official statement has been made by them yet, and it’s unclear whether they killed the product entirely, or will still be selling it to their customers, or re-release it later under a different name again.&lt;/p&gt;

&lt;p&gt;Either way, this kind of tech is harmful, both because of its own biases and the possibility it gives third parties to cause (un)intentional harm to minoritized communities.&lt;/p&gt;




&lt;p&gt;This was originally posted on &lt;a href="https://fossheim.io/writing/posts/ai-bias-genderify/"&gt;fossheim.io&lt;/a&gt;, with early access through &lt;a href="https://www.patreon.com/fossheim"&gt;Patreon&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>machinelearning</category>
      <category>ai</category>
      <category>ethics</category>
      <category>inclusion</category>
    </item>
    <item>
      <title>Navigating the internet as a non-binary designer</title>
      <dc:creator>Sarah</dc:creator>
      <pubDate>Thu, 16 Jul 2020 13:25:20 +0000</pubDate>
      <link>https://forem.com/fossheim/navigating-the-internet-as-a-non-binary-designer-4n14</link>
      <guid>https://forem.com/fossheim/navigating-the-internet-as-a-non-binary-designer-4n14</guid>
      <description>&lt;p&gt;This post was originally published on &lt;a href="https://fossheim.io/writing/posts/non-binary-design/"&gt;fossheim.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Earlier I wrote a &lt;a href="https://twitter.com/liatrisbian/status/1281978736318001152?s=21"&gt;twitter thread&lt;/a&gt; about small things designers can do to make their products more inclusive for trans and non-binary people. It’s a topic quite close to my heart, since I am non-binary myself, so I want to address some of the points in a proper post as well.&lt;/p&gt;

&lt;p&gt;As I mentioned in the tweets, there’s more to trans/non-binary inclusive design than what will be discussed in this post, and there’s more to inclusive and ethical design practices than just gender issues. This will be an overview of things that can be done at relatively small cost, but will have a positive impact for gender diverse people.&lt;/p&gt;

&lt;p&gt;But let’s first take a closer look at what it actually means to be non-binary. In short, &lt;a href="https://www.selfdefined.app/definitions/non-binary/"&gt;non-binary is an umbrella term for genders that fall outside of the male/female gender binary&lt;/a&gt;. This includes several things, such as having no gender, multiple genders, a gender different than or between male and female, and culturally specific genders (such as two-spirit). &lt;/p&gt;

&lt;p&gt;A lot of us use gender neutral pronouns (such as they/them), but it’s also common and perfectly valid for non-binary people to use binary pronouns (he or she), &lt;a href="https://www.selfdefined.app/definitions/neopronouns/"&gt;neopronouns&lt;/a&gt;, or a combination of different pronouns. &lt;/p&gt;

&lt;p&gt;In my case, being non-binary means I am neither a man nor a woman, and exclusively use gender neutral pronouns (singular they/them). &lt;/p&gt;

&lt;p&gt;I will include more resources explaining non-binary identities at the bottom of this post. For now, let’s get back to the topic of non-binary inclusive design. &lt;/p&gt;

&lt;h2&gt;
  
  
  Forms and data collection
&lt;/h2&gt;

&lt;p&gt;Forms and surveys, both physical and digital ones, are notorious for excluding non-binary people. &lt;/p&gt;

&lt;p&gt;Most often, they only have two gender options: male or female. Always presented as radio buttons or drop down menus, meaning only one gender can be chosen, and usually made obligatory, meaning a choice has to be made. &lt;/p&gt;

&lt;p&gt;As a non-binary person, there’s no good outcome for me there. Either I lie and misgender myself as a woman, or I lie and misgender myself as a man.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--14bT5fQ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ucgzqzdl9d9r58ag04z.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--14bT5fQ6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/3ucgzqzdl9d9r58ag04z.jpeg" alt="Tinder's gender selector: two options, man or woman"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Gender options on the Tinder iOS app. We want to find friendships or love that accept and respect our identities, but there’s no option for non-binary people to specify our gender. We have to list ourselves as a man or as a woman. Either way, we’re misrepresented from the start, opening the door for misgendering, harassment and abuse later on.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;Fortunately, some forms do include a third option. Unfortunately, that third option is often “Rather not say”. I would very much like to say my gender, it’s just not possible. &lt;/p&gt;

&lt;p&gt;Another option that’s often used is “Other”. While I assume that one exists because of the combination of good intentions (making sure people who fall outside the gender binary can answer) and technical limitations (both internationalization and limiting the amount of possible responses), it can easily come across as dehumanizing. &lt;/p&gt;

&lt;p&gt;Society already emphasizes a lot that we’re different, falling outside the norms of binary cis women and men, so having to label ourselves as “other” online as well can feel rather damaging. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3xBMktWR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/k6ebv4487105opcvraar.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3xBMktWR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/k6ebv4487105opcvraar.jpeg" alt="Facebook's gender selector: three options, man, woman or custom"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The three genders according to Facebook: Female, Male, Custom.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--836FX0Pc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/leh3rq9d52f86fihmovx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--836FX0Pc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/leh3rq9d52f86fihmovx.jpeg" alt="Gmail's gender selector: three options, man, woman or custom, and three options for pronouns: male, female, other"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A similar situation over at Gmail. After choosing a “custom” gender, I had to choose whether my pronouns were male or female (gendering pronouns for someone who doesn’t gender themself feels wrong too; better would be to show an example), or other. There’s no information if “other” in this context means they/them, and there’s also no opportunity to actually write down my pronouns myself instead.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;A better way would be to include a lot more gender options along with a “write in” or “not listed here” open text field. By calling the missing options “not listed”, the responsibility is also put on the designer/platform (they didn’t list us), rather than on the people filling in the form (we’re too “other”). &lt;/p&gt;

&lt;p&gt;Another option is to just go for an open text field from the start, and make everyone write down their gender. Autocomplete suggestions can be used to discourage spelling mistakes, and with some post-processing of the data similar answers (eg “female”, “woman”, “women”, “womxn”) can be grouped as well. &lt;/p&gt;

&lt;p&gt;Additionally, when offering different options, it’s also wise to consider going for checkboxes, rather than radio buttons or drop downs, allowing people to select several genders, rather than the one they most identify with. &lt;/p&gt;

&lt;p&gt;But the way gender is asked for on forms doesn’t just exclude or harm non-binary folks, often the issues extend to binary trans people as well. &lt;/p&gt;

&lt;p&gt;The following example comes from an &lt;a href="https://www.surveygizmo.com/resources/blog/how-to-write-survey-gender-questions/amp/"&gt;article from 2016 about how to ask for gender in surveys&lt;/a&gt;, but is one I’ve come across &lt;em&gt;in the wild&lt;/em&gt; on products as well. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--poXj8_TQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y7ozd5jy8ki7mq5fkejm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--poXj8_TQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y7ozd5jy8ki7mq5fkejm.png" alt="Example of a gender selector on a survey, the options are: male, female, transgender male, transgender female, gender variant, not listed, prefer not to say"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;By putting “female” and “transgender female” against each other, it’s implied that trans women aren’t real women (and same for trans men), or at the very least that they’re not “normal” women and men. &lt;/p&gt;

&lt;p&gt;There’s not many instances where you’ll need to specifically know whether someone is a trans man or a cis man, for example. If you’re creating an interface for a doctor’s office, it might be useful for the doctor to know in order to call in their patients for the correct examinations.&lt;/p&gt;

&lt;p&gt;Another example of where it could be useful to ask, is when collecting demographics data for research where the differences in answers between trans and cis people might be important (for example, a survey about online harassment).&lt;/p&gt;

&lt;p&gt;In those cases, the example above could be adapted to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cis woman&lt;/li&gt;
&lt;li&gt;Cis man&lt;/li&gt;
&lt;li&gt;Trans woman&lt;/li&gt;
&lt;li&gt;Trans man&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Or alternatively, whether the respondent is cis or trans can be a follow-up question. &lt;/p&gt;

&lt;h3&gt;
  
  
  Do you even need to know gender?
&lt;/h3&gt;

&lt;p&gt;The best way of avoiding most of these issues is by not asking for gender (or pronouns, or title) if you don’t actually need to known them for a specific and valid reason. For example Instagram uses they/them for everyone by default. &lt;/p&gt;

&lt;p&gt;If you do need to ask, start by answering the following questions for yourself: why do you need to know this data, and how is it going to be used?&lt;/p&gt;

&lt;p&gt;This will usually give you some more insights in what exactly you should ask the user, and which format the input should have. Gender and pronouns are not interchangeable - for example, as I mentioned earlier, not all non-binary people use they/them pronouns. Trans people their gender doesn’t match their gender assigned at birth. Gender and chromosomes/body parts aren’t interchangeable either. &lt;/p&gt;

&lt;p&gt;Be specific, ask only what you need to know, and explain what the data will be used for. &lt;/p&gt;

&lt;h3&gt;
  
  
  Allowing for change
&lt;/h3&gt;

&lt;p&gt;Gender, pronouns, titels and names might change over time, and it’s important to keep this in mind when designing and developing products.&lt;/p&gt;

&lt;p&gt;When I changed my last name in 2019, I experienced first hand how difficult or even impossible it is to update my name in some systems. We use Jira at work, and despite having changed my name what feels like a million times in their settings, my old name I initially signed up with keeps being displayed. &lt;/p&gt;

&lt;p&gt;And because it’s taking a while to receive an updated passport, there are still some services where I cannot sign up using my new legal name, but also cannot make or receive payments because my username doesn’t match my legal name, causing an administrative nightmare. &lt;/p&gt;

&lt;p&gt;I also heard from trans people who, for a long time after changing their name in their account settings on Netflix, kept receiving promotional emails using their old name. &lt;/p&gt;

&lt;h3&gt;
  
  
  One form can cause a lot of harm
&lt;/h3&gt;

&lt;p&gt;The examples above, and non-inclusive forms in general, can cause harm in several ways. &lt;/p&gt;

&lt;p&gt;First of all, whether it’s non-binary people not having an option, or trans women not being classified as women, people end up being excluded and demonized. We’re either not represented at all, misrepresented, or invalidated. &lt;/p&gt;

&lt;p&gt;In the Tinder example, where non-binary people might be shown as a wrong gender to potential new partners, the platform also puts all responsibility on its non-binary users to come out to their new matches, correct their pronouns and explain their identity. It’s tiring to constantly have to correct and educate those around us. Tinder had a good opportunity to take some of that burden away from us, but chose not to. &lt;/p&gt;

&lt;p&gt;But in the age of big data, there’s more risks attached to misrepresentation in forms. The data we collect gets used and analyzed, and often fed into algorithms. Broken data leads to broken algorithms. And an entire group being erased from data collection leads to an entire group being erased from its analysis. Non-binary people are already invisible enough in society, forgotten about, underrepresented or not recognized as a real and valid identity. Our voices shouldn’t be erased even more. &lt;/p&gt;

&lt;p&gt;A good example of this is a survey I had to take at some point in my career. The company I worked for at the time wanted to poll how we were doing, and what we thought about our work culture. The first question, obligatory to answer, was asking about our gender. There were only two options to choose from, male and female. &lt;/p&gt;

&lt;p&gt;Further on we were asked if we thought the company was a safe place for queer people (sidenote, we were not asked about our sexuality either), and if we experienced discrimination based on our gender identity. &lt;/p&gt;

&lt;p&gt;My answer, along with the answers of other non-binary employees, blended in together with those of men and women. Later on, both the external company conducting the survey and internal management patted themselves on the back: the company received a near top-score for gender equality, and almost no one reported feeling unwelcome because of their gender or sexuality. &lt;/p&gt;

&lt;p&gt;Except with the majority of the company being cis and straight, and the answers of queer people not being represented correctly, an “almost perfect score” sounds a lot less perfect. How can they know that non-binary people experience harassment if they don’t even give us a proper voice?&lt;/p&gt;

&lt;h2&gt;
  
  
  Language matters
&lt;/h2&gt;

&lt;p&gt;Being a non-binary person in tech, I often am wrongly put on “women in tech to follow” lists. While it’s a nice sentiment, who doesn’t like some validation for their work, it puts us in an uncomfortable position. First of all, because I’m not a woman. It’s misgendering me. But pointing that out, no matter how politely, also opens the door to a lot of (verbal) abuse. &lt;/p&gt;

&lt;p&gt;And similarly, a lot of events and communities try to show they’re inclusive by saying they’re open for “women and non-binary people”.&lt;/p&gt;

&lt;p&gt;For non-binary people like me, it feels bad to always be grouped together with women, especially because I often get misgendered as one. So to me, it’s never clear whether people will actually welcome me for who I am, a non-binary person who’s neither a man nor a woman, and respect my identity, or if they will look at me as a woman or a “woman-lite”. &lt;/p&gt;

&lt;p&gt;And for some non-binary women the division between “women” and “non-binary people” can be experienced as harmful as well. &lt;/p&gt;

&lt;p&gt;So while the intention behind “women and non-binary people” might*** be good, the language can actually end up excluding more people than includes, and create unsafe and unwelcoming spaces. &lt;/p&gt;

&lt;p&gt;*&lt;em&gt;sometimes it’s also just a meaningless phrase to seem more inclusive without having to put in the work.&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;The same goes for language used when advertising products. “Menstrual products” is more inclusive than “feminine hygiene”, similar to how “people who menstruate” in context is more inclusive than “women”; because not all women menstruate and not all those who menstruate are women. &lt;/p&gt;

&lt;h2&gt;
  
  
  Representation, harassment, discrimination and a lot more.
&lt;/h2&gt;

&lt;p&gt;In this post I only highlighted a few challenges and and possible solutions. Non-binary and trans people are still underrepresented, both in the design and tech industry, in our research, and in the content published on our platforms. We‘re underpaid, discriminated against and face harassment. Just the other day I had to block someone on Twitter for harassing me and a non-binary friend of mine about our identity. &lt;/p&gt;

&lt;p&gt;There’s a lot more to dive into in follow-up posts, but until then I suggest checking out the following resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://alistapart.com/article/trans-inclusive-design/"&gt;Trans inclusive design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.selfdefined.app/definitions/non-binary/"&gt;Non-binary definition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.selfdefined.app/definitions/transgender/"&gt;Transgender definition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/national-center-for-institutional-diversity/nonbinary-identities-and-individuals-in-research-community-and-the-academy-e2b8a3f23684"&gt;Non-binary identities in the research community&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.stonewall.org.uk/about-us/blog/10-ways-step-ally-non-binary-people"&gt;10 Ways to be a better ally to non-binary people&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.them.us/story/this-is-what-gender-nonbinary-people-look-like/amp#click=https://t.co/5xqnm15T2E"&gt;This is what non-binary people look like&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.invisionapp.com/inside-design/designing-products-gender-inclusion/"&gt;Designing products for gender inclusion&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/queer-design-club/marginalized-by-design-e4ecf543dc4d"&gt;Marginalized by design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://pronoun.is/"&gt;http://pronoun.is&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.instagram.com/p/CCjqgMWBKeW/?igshid=1f6mvh1tm3qk8"&gt;Why is it "pronouns" and not "preferred pronouns"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.instagram.com/p/CCjqAVPhGi_/?igshid=1gq32gktllnv1"&gt;Why is it "they are non-binary" and not "they identify as non-binary"&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.instagram.com/p/CCROKKXpA3-/?igshid=valux41hji5e"&gt;Things not to say to a trans person&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/NBWeek"&gt;Non-binary awareness week on Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/bihistory/status/1282977182051074048?s=21"&gt;What does it mean to be non-binary (via Twitter)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  #InternationalNonbinaryDay and #NonbinaryAwarenessWeek
&lt;/h2&gt;

&lt;p&gt;I’d also like to end this post on a positive note. While navigating the internet as a non-binary person brings up uncomfortable, painful and sometimes abusive situations, the internet has been a massive help for me as well to help me come to terms with my gender identity. &lt;/p&gt;

&lt;p&gt;There’s a lot of information out there, hashtags that are meant to raise awareness and uplift non-binary voices, and even an entire &lt;a href="https://queerdesign.club/"&gt;community of queer designers&lt;/a&gt; and &lt;a href="https://twitter.com/tgdintech"&gt;transgender and gender diverse people in tech&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;And as I’m writing this post, it’s International Non-Binary People’s Day, as a part of Non-Binary Awareness Week. It’s great to see effort being put into raising awareness and visibility, and I’m really damn proud to be non-binary.&lt;/p&gt;




&lt;p&gt;If you found this post useful, consider sharing it on social media, or subscribing to my &lt;a href="https://www.patreon.com/fossheim"&gt;Patreon&lt;/a&gt; for early access to future posts.&lt;/p&gt;

</description>
      <category>theycoded</category>
      <category>design</category>
      <category>inclusion</category>
    </item>
  </channel>
</rss>
